A hash table is very universal data structure. It does most of it’s operations in O(1) average time. The library contains a header to generate hash tables suiting your needs.
They are generic data structures.
Mandatory macros
-
HASH_NODE— a data type where a node dwells. It is usually a structure. -
HASH_PREFIX(x)— the name generating macro. -
Key type and name. Must be one of following.
-
HASH_KEY_ATOMIC— the key (node\->HASH_KEY_ATOMIC) is an atomic type which can be compared using==. -
HASH_KEY_STRING— the key is a zero-terminated string, allocated separately from the rest of the node. -
HASH_KEY_ENDSTRING— a zero-terminated string which lives at the end of node (it is allocated together with the node). It should be declared aschar key[1]. -
HASH_KEY_MEMORY— thenode\->HASH_KEY_MEMORYis to be compared using memcmp() function. In this case, you need to provideHASH_KEY_SIZEmacro as well, to specify the length of the key. -
HASH_KEY_COMPLEX(x)— the key is compound of more than one component. The macro should expand tox key1, x key2, ..., x kn. Furthermore, you need to provide aHASH_KEY_DECLmacro. It is used to define function parameters. Therefore it should expand totype1 key1, type2 key2, ..., typen keyn. AndHASH_GIVE_HASHFNandHASH_GIVE_EQare mandatory for this key type.
-
Optional function switches
You can define any of these macros and provide corresponding functions to customize the behaviour. The macros are:
-
HASH_GIVE_HASHFN— the table will useuint HASH_PREFIX(hash)(key)to calculate hash ofkey. There is a sensible default for integers and strings. In the case ofHASH_KEY_COMPLEX, it is mandatory to provide this macro and function. -
HASH_GIVE_EQ— tells the table to useint HASH_PREFIX(eq)(key1, key2)function to decide ifkey1andkey2are equal. Default for atomic types is==and strcmp() or strcasecmp() for strings (depends onHASH_NOCASEswitch). It is mandatory when you useHASH_KEY_COMPLEX. -
HASH_GIVE_EXTRA_SIZE— functionint HASH_PREFIX(extra_size)(key)returns how many bytes after the node should be allocated. It defaults to0or to length of key in case ofHASH_KEY_ENDSTRING. -
HASH_GIVE_INIT_KEY— functionvoid HASH_PREFIX(init_key)(node *, key)is used to initialize key in newly created node. The default is assignment for atomic keys and static strings (HASH_KEY_ATOMIC,HASH_KEY_STRING) and strcpy() forHASH_KEY_ENDSTRING. -
HASH_GIVE_INIT_DATA— functionvoid HASH_PREFIX(init_data)(node *)is used to initialize the rest of node. Useful if you useHASH_PREFIX(lookup()) -
HASH_GIVE_ALLOC— you need to providevoid \*HASH_PREFIX(alloc)(uint sizeandvoid HASH_PREFIX(free)(void \*)to allocate and deallocate the nodes. Default uses xmalloc() and xfree(), [mempool routines] or [eltpool routines], depending onHASH_USE_POOL,HASH_AUTO_POOL,HASH_USE_ELTPOOLandHASH_AUTO_ELTPOOLswitches. -
[
HASH_GIVE_TABLE_ALLOC] — you need to providevoid \*HASH_PREFIX(table_alloc)(uint sizeandvoid HASH_PREFIX(table_free)(void \*)to allocate and deallocate the table itself. Default uses xmalloc() and xfree() or the functions fromHASH_GIVE_ALLOCdepending on [HASH_TABLE_ALLOC] switch.
Optional parameters
You can customize the hash table a little more by these macros:
-
HASH_NOCASE— use case-insensitive comparison for strings. -
HASH_DEFAULT_SIZE— use approximately this many elements when creating the hash table. -
HASH_CONSERVE_SPACE— use as little space as possible. -
HASH_FN_BITS— the hash function only provides this many significants bits. -
HASH_ATOMIC_TYPE— the type of atomic key (HASH_KEY_ATOMIC) is notint, but this type. -
HASH_USE_POOL— tells to use mempool allocation to allocate the nodes. You should define it to the name of mempool variable to be used for this purpose. -
HASH_AUTO_POOL— like above, but it creates it’s own mempool. Define it to the block size of the pool. -
HASH_USE_ELTPOOL— tells to use eltpool allocation to allocate the nodes. You should define it to the name of eltpool variable to be used for this purpose. -
HASH_AUTO_ELTPOOL— like above, but it creates it’s own mempool. Define it to the number of preallocated nodes in each chunk of memory. -
HASH_ZERO_FILL— initialize new nodes to all zeroes. -
HASH_TABLE_ALLOC— allocate the table the same way as nodes. If not provided, xmalloc() is used. -
HASH_TABLE_GROWING— never decrease the size of allocated table of nodes. -
HASH_TABLE_DYNAMIC— By default, only one global hash table is used. With this macro defined, all functions gain new first parameter of typeHASH_PREFIX(table) *to allow them work with multiple hash tables. -
HASH_TABLE_VARS— extra variables to be defined at head ofHASH_PREFIX(table) *structure. It can be useful in combination with [HASH_TABLE_DYNAMIC] to access per-table custom variables from macros or function switches before you include the generator. -
HASH_LOOKUP_DETECT_NEW— the prototype for lookup is changed tonode *lookup(key, int *new_item),new_itemmust not be NULL and returns 1 whether lookup just created a new item in the hashtable or 0 otherwise.
Functionality switches
Each of these macros enables some of the functionality the table has.
-
HASH_WANT_CLEANUP—HASH_PREFIX((cleanup() -
HASH_WANT_FIND—HASH_PREFIX((find() -
HASH_WANT_FIND_NEXT—HASH_PREFIX((find_next() -
HASH_WANT_NEW—HASH_PREFIX((new() -
HASH_WANT_LOOKUP—HASH_PREFIX((lookup() -
HASH_WANT_DELETE—HASH_PREFIX((delete() -
HASH_WANT_REMOVE—HASH_PREFIX((remove()
Generated functions
These are the function that the header file generates for you. The
strange first parameter of each function is a place where the
HASH_PREFIX(table) * resides when you define
HASH_TABLE_DYNAMIC. If you do not, the parameter
is empty.
static void HASH_PREFIX(init)(TA);
Initializes the hash table.
This one is available no matter what HASH_WANT_ macros you defined or not.
static void HASH_PREFIX(cleanup)(TA);
Deallocates the hash table, including the nodes.
It is available if you defined HASH_WANT_CLEANUP.
static HASH_NODE* HASH_PREFIX(find)(TAC HASH_KEY_DECL);
Finds a node with given key (specified in the HAS_KEY_DECL parameter). If it does not exist, NULL is returned.
Enabled by the HASH_WANT_FIND macro.
static HASH_NODE* HASH_PREFIX(find_next)(TAC P(node) *start);
Finds next node with the same key. Returns NULL if it does not exist.
Enabled by the HASH_WANT_FIND_NEXT macro.
static HASH_NODE * HASH_PREFIX(new)(TAC HASH_KEY_DECL);
Generates a new node with a given key.
Enabled by the HASH_WANT_NEW macro.
static HASH_NODE* HASH_PREFIX(lookup)(TAC HASH_KEY_DECL, int *new_item);
Finds a node with a given key. If it does not exist, a new one is created.
It is strongly recommended to use HASH_GIVE_INIT_DATA.
This one is enabled by the HASH_WANT_LOOKUP macro.
The new_item argument is available only if HASH_LOOKUP_DETECT_NEW was given.
static int HASH_PREFIX(delete)(TAC HASH_KEY_DECL);
Removes a node with the given key from hash table and deallocates it.
Success is returned.
This one is enabled by HASH_WANT_DELETE macro.
static void HASH_PREFIX(remove)(TAC HASH_NODE *n);
Removes a given node and deallocates it.
It differs from HASH_PREFIX(delete)()
in its type of parameter — this one deletes a specific node, that one looks for it by a key.
Enabled by HASH_WANT_REMOVE macro.
Iterator
You can use the HASH_FOR_ALL iterator macro to run trough all the
nodes. Lets say your HASH_PREFIX(x) macro is defined as
prefix_##x. Then you would do something like:
HASH_FOR_ALL(prefix, node_variable)
{
do_something_with_node(node_variable);
}
HASH_END_FOR;
If you use HASH_TABLE_DYNAMIC, use
HASH_FOR_ALL_DYNAMIC(prefix, table, node_variable) instead.
You may not modify the table inside the block. Use HASH_BREAK and
HASH_CONTINUE instead of break and continue statements.