/* Declarations for hash tables.
 *
 * IRC Services is copyright (c) 1996-2002 Andrew Church.
 *     E-mail: <achurch@achurch.org>
 * Parts written by Andrew Kempe and others.
 * This program is free but copyrighted software; see the file COPYING for
 * details.
 */

/* This file is controlled by the following #define's:
 *    EXPIRE_CHECK(node): Define to an expression used to check whether a
 *                        given entry has expired.  Initially defined to
 *                        "0" (no expiration) if not otherwise defined when
 *                        this file is included.
 *    HASH_STATIC: Define before using DEFINE_* macros to either `static'
 *                 or nothing, to control whether hash functions are
 *                 declared static or not.  If not defined, defaults to
 *                 nothing (functions are not static).  The hash table
 *                 structures themselves are always static.
 *    HASHFUNC: Define this (and HASHSIZE) if you want your own hash
 *              function.
 *    HASH_HAVELOOKUP: Define before including this file if you use the
 *                     standard hash function but separately define a
 *                     `hashlookup' array.
 *
 * Note that the above defines (except HASH_HAVELOOKUP) take effect when
 * hash tables are actually defined, not when this file is included, so the
 * defines can be changed at will for different hashes.
 */

#ifndef HASH_H
#define HASH_H

/*************************************************************************/

#ifndef EXPIRE_CHECK
# define EXPIRE_CHECK(node) 0
#endif

#ifndef HASH_STATIC
# define HASH_STATIC /*nothing*/
#endif

/*************************************************************************/

#ifndef HASHFUNC

/* Generic hash function and associated lookup table. */

# define HASHFUNC(key) (hashlookup[(uint8)(*(key))]<<5 \
		        | (*(key) ? hashlookup[(uint8)((key)[1])] : 0))
# define HASHSIZE     1024

# ifndef HASH_HAVELOOKUP
static const uint8 hashlookup[256] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

     0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
    16,17,18,19,20,21,22,23,24,25,26,27,28,29, 0, 0,
     0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
    16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,30,

    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,

    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
    31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,
};
# endif /* !HASH_HAVELOOKUP */

#endif /* !HASHFUNC */

/*************************************************************************/

/* Templates for creating hash table definitions and functions.
 * HASH_STATIC should be defined to either `static' or nothing beforehand.
 */

#undef DEFINE_HASHTABLE
#define DEFINE_HASHTABLE(name, type) \
    static type *hashtable_##name[HASHSIZE];

#undef DEFINE_HASH_ITER
#define DEFINE_HASH_ITER(name,type)					\
    static type *hashiter_##name;					\
    static int hashpos_##name;						\
    static void _next_##name(void)					\
    {									\
	if (hashiter_##name)						\
	    hashiter_##name = hashiter_##name->next;			\
	while (hashiter_##name == NULL && ++hashpos_##name < HASHSIZE)	\
	    hashiter_##name = hashtable_##name[hashpos_##name];		\
    }									\
    HASH_STATIC type *first_##name(void)				\
    {									\
	hashpos_##name = -1;						\
	hashiter_##name = NULL;						\
	_next_##name();							\
	return next_##name();						\
    }									\
    HASH_STATIC type *next_##name(void)					\
    {									\
	type *retval;							\
	do {								\
	    retval = hashiter_##name;					\
	    if (retval == NULL)						\
		return NULL;						\
	    _next_##name();						\
	} while (!noexpire && EXPIRE_CHECK(retval));			\
	return retval;							\
    }

#undef DEFINE_HASH_ADD
#define DEFINE_HASH_ADD(name,type,key)					\
    HASH_STATIC void add_##name(type *node)				\
    {									\
	type **listptr = &hashtable_##name[HASHFUNC(node->key)];	\
	LIST_INSERT_ORDERED(node, *listptr, irc_stricmp, key);		\
    }

#undef DEFINE_HASH_ADD_SCALAR
#define DEFINE_HASH_ADD_SCALAR(name,type,key)				\
    HASH_STATIC void add_##name(type *node)				\
    {									\
	type **listptr = &hashtable_##name[HASHFUNC(node->key)];	\
	LIST_INSERT(node, *listptr);					\
    }

#undef DEFINE_HASH_DEL
#define DEFINE_HASH_DEL(name,type,key)					\
    HASH_STATIC void del_##name(type *node)				\
    {									\
	if (node == hashiter_##name)					\
	    _next_##name();						\
	LIST_REMOVE(node, hashtable_##name[HASHFUNC(node->key)]);	\
    }

#undef DEFINE_HASH_GET
#define DEFINE_HASH_GET(name,type,key)					\
    HASH_STATIC type *get_##name(const char *what)			\
    {									\
	type *result;							\
	LIST_SEARCH_ORDERED(hashtable_##name[HASHFUNC(what)],		\
			    key, what, irc_stricmp, result);		\
	if (result && !noexpire && EXPIRE_CHECK(result))		\
	    result = NULL;						\
	return result;							\
    }

#undef DEFINE_HASH_GET_SCALAR
#define DEFINE_HASH_GET_SCALAR(name,type,key,keytype)			\
    HASH_STATIC type *get_##name(keytype what)				\
    {									\
	type *result;							\
	LIST_SEARCH_SCALAR(hashtable_##name[HASHFUNC(what)],		\
			   key, what, result);				\
	if (result && !noexpire && EXPIRE_CHECK(result))		\
	    result = NULL;						\
	return result;							\
    }

/* Macros to create everything at once for a given type.
 *	name: Name to use in the hash functions (the XXX in add_XXX, etc.)
 *	type: Type of the hash (NickInfo, etc.)
 *	key: Key field in given type
 */

#undef DEFINE_HASH
#define DEFINE_HASH(name,type,key)			\
    DEFINE_HASHTABLE(name, type)			\
    DEFINE_HASH_ITER(name, type)			\
    DEFINE_HASH_ADD(name, type, key)			\
    DEFINE_HASH_DEL(name, type, key)			\
    DEFINE_HASH_GET(name, type, key)

#undef DEFINE_HASH_SCALAR
#define DEFINE_HASH_SCALAR(name,type,key,keytype)	\
    DEFINE_HASHTABLE(name, type)			\
    DEFINE_HASH_ITER(name, type)			\
    DEFINE_HASH_ADD_SCALAR(name, type, key)		\
    DEFINE_HASH_DEL(name, type, key)			\
    DEFINE_HASH_GET_SCALAR(name, type, key, keytype)

/*************************************************************************/

#endif	/* HASH_H */
