--- /dev/null
+/******************************************************************
+ *
+ * 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 <ft2build.h>
+#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__ */