1 /******************************************************************
3 * fthash.h - fast dynamic hash tables
6 * David Turner, Robert Wilhelm, and Werner Lemberg
8 * This file is part of the FreeType project, and may only be used,
9 * modified, and distributed under the terms of the FreeType project
10 * license, LICENSE.TXT. By continuing to use, modify, or distribute
11 * this file you indicate that you have read the license and
12 * understand and accept it fully.
15 * This header is used to define dynamic hash tables as described
16 * by the article "Main-Memory Linear Hashing - Some Enhancements
17 * of Larson's Algorithm" by Mikael Petterson.
19 * Basically, linear hashing prevents big "stalls" during
20 * resizes of the buckets array by only splitting one bucket
21 * at a time. This ensures excellent response time even when
22 * the table is frequently resized..
25 * Note that the use of the FT_Hash type is rather unusual in order
26 * to be as generic and efficient as possible. See the comments in the
27 * following definitions for more details.
38 /***********************************************************
43 * handle to a @FT_HashRec structure used to model a
46 typedef struct FT_HashRec_* FT_Hash;
49 /***********************************************************
54 * handle to a @FT_HashNodeRec structure used to model a
55 * single node of a hash table
57 typedef struct FT_HashNodeRec_* FT_HashNode;
60 /***********************************************************
62 * @type: FT_HashLookup
65 * handle to a @FT_HashNode pointer. This is returned by
66 * the @ft_hash_lookup function and can later be used by
67 * @ft_hash_add or @ft_hash_remove
69 typedef FT_HashNode* FT_HashLookup;
72 /***********************************************************
74 * @type: FT_Hash_EqualFunc
77 * a function used to compare two nodes of the hash table
80 * node1 :: handle to first node
81 * node2 :: handle to second node
84 * 1 iff the 'keys' in 'node1' and 'node2' are identical.
87 typedef FT_Int (*FT_Hash_EqualFunc)( FT_HashNode node1,
91 /***********************************************************
96 * a structure used to model a dynamic hash table.
99 * memory :: memory manager used to allocate
100 * the buckets array and the hash nodes
102 * buckets :: array of hash buckets
104 * node_size :: size of node in bytes
105 * node_compare :: a function used to compare two nodes
106 * node_hash :: a function used to compute the hash
107 * value of a given node
113 * 'p', 'mask' and 'slack' are control values managed by
114 * the hash table. Do not try to interpret them directly.
116 * You can grab the hash table size by calling
117 * '@ft_hash_get_size'.
119 typedef struct FT_HashRec_
121 FT_HashNode* buckets;
123 FT_UInt mask; /* really maxp-1 */
125 FT_Hash_EqualFunc node_equal;
131 /***********************************************************
133 * @struct: FT_HashNodeRec
136 * a structure used to model the root fields of a dynamic
139 * it's up to client applications to "sub-class" this
140 * structure to add relevant (key,value) definitions
143 * link :: pointer to next node in bucket's collision list
144 * hash :: 32-bit hash value for this node
147 * it's up to client applications to "sub-class" this structure
148 * to add relevant (key,value) type definitions. For example,
149 * if we want to build a "string -> int" mapping, we could use
153 * typedef struct MyNodeRec_
155 * FT_HashNodeRec hnode;
159 * } MyNodeRec, *MyNode;
163 typedef struct FT_HashNodeRec_
171 /****************************************************************
173 * @function: ft_hash_init
176 * initialize a dynamic hash table
179 * table :: handle to target hash table structure
180 * node_equal :: node comparison function
181 * memory :: memory manager handle used to allocate the
182 * buckets array within the hash table
185 * error code. 0 means success
188 * the node comparison function should only compare node _keys_
189 * and ignore values !! with good hashing computation (which the
190 * user must perform itself), the comparison function should be
191 * pretty seldom called.
193 * here is a simple example:
196 * static int my_equal( MyNode node1,
199 * // compare keys of 'node1' and 'node2'
200 * return (strcmp( node1->key, node2->key ) == 0);
205 * ft_hash_init( &hash, (FT_Hash_EqualFunc) my_compare, memory );
210 ft_hash_init( FT_Hash table,
211 FT_Hash_EqualFunc compare,
215 /****************************************************************
217 * @function: ft_hash_lookup
220 * search a hash table to find a node corresponding to a given
224 * table :: handle to target hash table structure
225 * keynode :: handle to a reference hash node that will be
226 * only used for key comparisons with the table's
230 * a pointer-to-hash-node value, which must be used as followed:
232 * - if '*result' is NULL, the key wasn't found in the hash
233 * table. The value of 'result' can be used to add new elements
234 * through @ft_hash_add however..
236 * - if '*result' is not NULL, it's a handle to the first table
237 * node that corresponds to the search key. The value of 'result'
238 * can be used to remove this element through @ft_hash_remove
241 * here is an example:
244 * // maps a string to an integer with a hash table
245 * // returns -1 in case of failure
247 * int my_lookup( FT_Hash table,
253 * // set-up key node. It's 'hash' and 'key' fields must
254 * // be set correctly.. we ignore 'link' and 'value'
256 * noderec.hnode.hash = strhash( key );
259 * // perform search - return value
261 * pnode = (MyNode) ft_hash_lookup( table, &noderec );
265 * return (*pnode)->value;
271 FT_BASE_DEF( FT_HashLookup )
272 ft_hash_lookup( FT_Hash table,
273 FT_HashNode keynode );
276 /****************************************************************
278 * @function: ft_hash_add
281 * add a new node to a dynamic hash table. the user must
282 * call @ft_hash_lookup and allocate a new node before calling
286 * table :: hash table handle
287 * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup
288 * new_node :: handle to new hash node. All its fields must be correctly
289 * set, including 'hash'.
292 * error code. 0 means success
295 * this function should always be used _after_ a call to @ft_hash_lookup
296 * that returns a pointer to a NULL handle. Here's an example:
299 * // sets the value corresponding to a given string key
301 * void my_set( FT_Hash table,
309 * // set-up key node. It's 'hash' and 'key' fields must
310 * // be set correctly..
311 * noderec.hnode.hash = strhash( key );
314 * // perform search - return value
315 * pnode = (MyNode) ft_hash_lookup( table, &noderec );
318 * // we found it, simply replace the value in the node
319 * (*pnode)->value = value;
323 * // allocate a new node - and set it up
324 * node = (MyNode) malloc( sizeof(*node) );
325 * if ( node == NULL ) .....
327 * node->hnode.hash = noderec.hnode.hash;
329 * node->value = value;
331 * // add it to the hash table
332 * error = ft_hash_add( table, pnode, node );
337 ft_hash_add( FT_Hash table,
338 FT_HashLookup lookup,
339 FT_HashNode new_node );
342 /****************************************************************
344 * @function: ft_hash_remove
347 * try to remove the node corresponding to a given key from
348 * a hash table. This must be called after @ft_hash_lookup
351 * table :: hash table handle
352 * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup
355 * this function doesn't free the node itself !! Here's an example:
358 * // sets the value corresponding to a given string key
360 * void my_remove( FT_Hash table,
366 * noderec.hnode.hash = strhash(key);
370 * pnode = ft_hash_lookup( table, &noderec );
372 * if ( node != NULL )
374 * error = ft_hash_remove( table, pnode );
382 ft_hash_remove( FT_Hash table,
383 FT_HashLookup lookup );
387 /****************************************************************
389 * @function: ft_hash_get_size
392 * return the number of elements in a given hash table
395 * table :: handle to target hash table structure
398 * number of elements. 0 if empty
401 ft_hash_get_size( FT_Hash table );
405 /****************************************************************
407 * @functype: FT_Hash_ForeachFunc
410 * a function used to iterate over all elements of a given
414 * node :: handle to target @FT_HashNodeRec node structure
415 * data :: optional argument to iteration routine
417 * @also: @ft_hash_foreach
419 typedef void (*FT_Hash_ForeachFunc)( const FT_HashNode node,
420 const FT_Pointer data );
423 /****************************************************************
425 * @function: ft_hash_foreach
428 * parse over all elements in a hash table
431 * table :: handle to target hash table structure
432 * foreach_func :: iteration routine called for each element
433 * foreach_data :: optional argument to the iteration routine
436 * this function is often used to release all elements from a
437 * hash table. See the example given for @ft_hash_done
440 ft_hash_foreach( FT_Hash table,
441 FT_Hash_ForeachFunc foreach_func,
442 const FT_Pointer foreach_data );
446 /****************************************************************
448 * @function: ft_hash_done
451 * finalize a given hash table
454 * table :: handle to target hash table structure
455 * node_func :: optional iteration function pointer. this
456 * can be used to destroy all nodes explicitely
457 * node_data :: optional argument to the node iterator
460 * this function simply frees the hash table's buckets.
461 * you probably will need to call @ft_hash_foreach to
462 * destroy all its elements before @ft_hash_done, as in
463 * the following example:
466 * static void my_node_clear( const MyNode node )
471 * static void my_done( FT_Hash table )
473 * ft_hash_done( table, (FT_Hash_ForeachFunc) my_node_clear, NULL );
478 ft_hash_done( FT_Hash table,
479 FT_Hash_ForeachFunc item_func,
480 const FT_Pointer item_data );
484 /* compute bucket index from hash value in a dynamic hash table */
485 /* this is only used to break encapsulation to speed lookups in */
486 /* the FreeType cache manager !! */
489 #define FT_HASH_COMPUTE_INDEX(_table,_hash,_index) \
491 FT_UInt _mask = (_table)->mask; \
492 FT_UInt _hash0 = (_hash); \
494 (_index) = (FT_UInt)( (_hash0) & _mask ) ); \
495 if ( (_index) < (_table)->p ) \
496 (_index) = (FT_uInt)( (_hash0) & ( 2*_mask+1 ) ); \
502 #endif /* __FT_HASH_H__ */