/****************************************************************** * * fthash.h - fast dynamic hash tables * * Copyright 2002 by * David Turner, Robert Wilhelm, and Werner Lemberg * * This file is part of the FreeType project, and may only be used, * modified, and distributed under the terms of the FreeType project * license, LICENSE.TXT. By continuing to use, modify, or distribute * this file you indicate that you have read the license and * understand and accept it fully. * * * This header is used to define dynamic hash tables as described * by the article "Main-Memory Linear Hashing - Some Enhancements * of Larson's Algorithm" by Mikael Petterson. * * Basically, linear hashing prevents big "stalls" during * resizes of the buckets array by only splitting one bucket * at a time. This ensures excellent response time even when * the table is frequently resized.. * * * Note that the use of the FT_Hash type is rather unusual in order * to be as generic and efficient as possible. See the comments in the * following definitions for more details. */ #ifndef __FT_HASH_H__ #define __FT_HASH_H__ #include #include FT_TYPES_H FT_BEGIN_HEADER /*********************************************************** * * @type: FT_Hash * * @description: * handle to a @FT_HashRec structure used to model a * dynamic hash table */ typedef struct FT_HashRec_* FT_Hash; /*********************************************************** * * @type: FT_HashNode * * @description: * handle to a @FT_HashNodeRec structure used to model a * single node of a hash table */ typedef struct FT_HashNodeRec_* FT_HashNode; /*********************************************************** * * @type: FT_HashLookup * * @description: * handle to a @FT_HashNode pointer. This is returned by * the @ft_hash_lookup function and can later be used by * @ft_hash_add or @ft_hash_remove */ typedef FT_HashNode* FT_HashLookup; /*********************************************************** * * @type: FT_Hash_EqualFunc * * @description: * a function used to compare two nodes of the hash table * * @input: * node1 :: handle to first node * node2 :: handle to second node * * @return: * 1 iff the 'keys' in 'node1' and 'node2' are identical. * 0 otherwise. */ typedef FT_Int (*FT_Hash_EqualFunc)( FT_HashNode node1, FT_HashNode node2 ); /*********************************************************** * * @struct: FT_HashRec * * @description: * a structure used to model a dynamic hash table. * * @fields: * memory :: memory manager used to allocate * the buckets array and the hash nodes * * buckets :: array of hash buckets * * node_size :: size of node in bytes * node_compare :: a function used to compare two nodes * node_hash :: a function used to compute the hash * value of a given node * p :: * mask :: * slack :: * * @note: * 'p', 'mask' and 'slack' are control values managed by * the hash table. Do not try to interpret them directly. * * You can grab the hash table size by calling * '@ft_hash_get_size'. */ typedef struct FT_HashRec_ { FT_HashNode* buckets; FT_UInt p; FT_UInt mask; /* really maxp-1 */ FT_Long slack; FT_Hash_EqualFunc node_equal; FT_Memory memory; } FT_HashRec; /*********************************************************** * * @struct: FT_HashNodeRec * * @description: * a structure used to model the root fields of a dynamic * hash table node. * * it's up to client applications to "sub-class" this * structure to add relevant (key,value) definitions * * @fields: * link :: pointer to next node in bucket's collision list * hash :: 32-bit hash value for this node * * @note: * it's up to client applications to "sub-class" this structure * to add relevant (key,value) type definitions. For example, * if we want to build a "string -> int" mapping, we could use * something like: * * { * typedef struct MyNodeRec_ * { * FT_HashNodeRec hnode; * const char* key; * int value; * * } MyNodeRec, *MyNode; * } * */ typedef struct FT_HashNodeRec_ { FT_HashNode link; FT_UInt32 hash; } FT_HashNodeRec; /**************************************************************** * * @function: ft_hash_init * * @description: * initialize a dynamic hash table * * @input: * table :: handle to target hash table structure * node_equal :: node comparison function * memory :: memory manager handle used to allocate the * buckets array within the hash table * * @return: * error code. 0 means success * * @note: * the node comparison function should only compare node _keys_ * and ignore values !! with good hashing computation (which the * user must perform itself), the comparison function should be * pretty seldom called. * * here is a simple example: * * { * static int my_equal( MyNode node1, * MyNode node2 ) * { * // compare keys of 'node1' and 'node2' * return (strcmp( node1->key, node2->key ) == 0); * } * * .... * * ft_hash_init( &hash, (FT_Hash_EqualFunc) my_compare, memory ); * .... * } */ FT_BASE( FT_Error ) ft_hash_init( FT_Hash table, FT_Hash_EqualFunc compare, FT_Memory memory ); /**************************************************************** * * @function: ft_hash_lookup * * @description: * search a hash table to find a node corresponding to a given * key. * * @input: * table :: handle to target hash table structure * keynode :: handle to a reference hash node that will be * only used for key comparisons with the table's * elements * * @return: * a pointer-to-hash-node value, which must be used as followed: * * - if '*result' is NULL, the key wasn't found in the hash * table. The value of 'result' can be used to add new elements * through @ft_hash_add however.. * * - if '*result' is not NULL, it's a handle to the first table * node that corresponds to the search key. The value of 'result' * can be used to remove this element through @ft_hash_remove * * @note: * here is an example: * * { * // maps a string to an integer with a hash table * // returns -1 in case of failure * // * int my_lookup( FT_Hash table, * const char* key ) * { * MyNode* pnode; * MyNodeRec noderec; * * // set-up key node. It's 'hash' and 'key' fields must * // be set correctly.. we ignore 'link' and 'value' * // * noderec.hnode.hash = strhash( key ); * noderec.key = key; * * // perform search - return value * // * pnode = (MyNode) ft_hash_lookup( table, &noderec ); * if ( *pnode ) * { * // we found it * return (*pnode)->value; * } * return -1; * } * } */ FT_BASE_DEF( FT_HashLookup ) ft_hash_lookup( FT_Hash table, FT_HashNode keynode ); /**************************************************************** * * @function: ft_hash_add * * @description: * add a new node to a dynamic hash table. the user must * call @ft_hash_lookup and allocate a new node before calling * this function. * * @input: * table :: hash table handle * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup * new_node :: handle to new hash node. All its fields must be correctly * set, including 'hash'. * * @return: * error code. 0 means success * * @note: * this function should always be used _after_ a call to @ft_hash_lookup * that returns a pointer to a NULL handle. Here's an example: * * { * // sets the value corresponding to a given string key * // * void my_set( FT_Hash table, * const char* key, * int value ) * { * MyNode* pnode; * MyNodeRec noderec; * MyNode node; * * // set-up key node. It's 'hash' and 'key' fields must * // be set correctly.. * noderec.hnode.hash = strhash( key ); * noderec.key = key; * * // perform search - return value * pnode = (MyNode) ft_hash_lookup( table, &noderec ); * if ( *pnode ) * { * // we found it, simply replace the value in the node * (*pnode)->value = value; * return; * } * * // allocate a new node - and set it up * node = (MyNode) malloc( sizeof(*node) ); * if ( node == NULL ) ..... * * node->hnode.hash = noderec.hnode.hash; * node->key = key; * node->value = value; * * // add it to the hash table * error = ft_hash_add( table, pnode, node ); * if (error) .... * } */ FT_BASE( FT_Error ) ft_hash_add( FT_Hash table, FT_HashLookup lookup, FT_HashNode new_node ); /**************************************************************** * * @function: ft_hash_remove * * @description: * try to remove the node corresponding to a given key from * a hash table. This must be called after @ft_hash_lookup * * @input: * table :: hash table handle * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup * * @note: * this function doesn't free the node itself !! Here's an example: * * { * // sets the value corresponding to a given string key * // * void my_remove( FT_Hash table, * const char* key ) * { * MyNodeRec noderec; * MyNode node; * * noderec.hnode.hash = strhash(key); * noderec.key = key; * node = &noderec; * * pnode = ft_hash_lookup( table, &noderec ); * node = *pnode; * if ( node != NULL ) * { * error = ft_hash_remove( table, pnode ); * if ( !error ) * free( node ); * } * } * } */ FT_BASE( FT_Error ) ft_hash_remove( FT_Hash table, FT_HashLookup lookup ); /**************************************************************** * * @function: ft_hash_get_size * * @description: * return the number of elements in a given hash table * * @input: * table :: handle to target hash table structure * * @return: * number of elements. 0 if empty */ FT_BASE( FT_UInt ) ft_hash_get_size( FT_Hash table ); /**************************************************************** * * @functype: FT_Hash_ForeachFunc * * @description: * a function used to iterate over all elements of a given * hash table * * @input: * node :: handle to target @FT_HashNodeRec node structure * data :: optional argument to iteration routine * * @also: @ft_hash_foreach */ typedef void (*FT_Hash_ForeachFunc)( const FT_HashNode node, const FT_Pointer data ); /**************************************************************** * * @function: ft_hash_foreach * * @description: * parse over all elements in a hash table * * @input: * table :: handle to target hash table structure * foreach_func :: iteration routine called for each element * foreach_data :: optional argument to the iteration routine * * @note: * this function is often used to release all elements from a * hash table. See the example given for @ft_hash_done */ FT_BASE( void ) ft_hash_foreach( FT_Hash table, FT_Hash_ForeachFunc foreach_func, const FT_Pointer foreach_data ); /**************************************************************** * * @function: ft_hash_done * * @description: * finalize a given hash table * * @input: * table :: handle to target hash table structure * node_func :: optional iteration function pointer. this * can be used to destroy all nodes explicitely * node_data :: optional argument to the node iterator * * @note: * this function simply frees the hash table's buckets. * you probably will need to call @ft_hash_foreach to * destroy all its elements before @ft_hash_done, as in * the following example: * * { * static void my_node_clear( const MyNode node ) * { * free( node ); * } * * static void my_done( FT_Hash table ) * { * ft_hash_done( table, (FT_Hash_ForeachFunc) my_node_clear, NULL ); * } * } */ FT_BASE( void ) ft_hash_done( FT_Hash table, FT_Hash_ForeachFunc item_func, const FT_Pointer item_data ); /* */ /* compute bucket index from hash value in a dynamic hash table */ /* this is only used to break encapsulation to speed lookups in */ /* the FreeType cache manager !! */ /* */ #define FT_HASH_COMPUTE_INDEX(_table,_hash,_index) \ { \ FT_UInt _mask = (_table)->mask; \ FT_UInt _hash0 = (_hash); \ \ (_index) = (FT_UInt)( (_hash0) & _mask ) ); \ if ( (_index) < (_table)->p ) \ (_index) = (FT_uInt)( (_hash0) & ( 2*_mask+1 ) ); \ } FT_END_HEADER #endif /* __FT_HASH_H__ */