www.pudn.com > freetype.rar > fthash.h


/****************************************************************** 
 * 
 *  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 "freetype/fttypes.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__ */