2 Hash table library using separate chaining
\r
4 > Created (Julienne Walker): August 7, 2005
\r
5 > Modified (Julienne Walker): August 11, 2005
\r
6 Added a cast for malloc to enable clean
\r
9 #include "jsw_hlib.h"
\r
20 typedef struct jsw_node {
\r
21 void *key; /* Key used for searching */
\r
22 void *item; /* Actual content of a node */
\r
23 struct jsw_node *next; /* Next link in the chain */
\r
26 typedef struct jsw_head {
\r
27 jsw_node_t *first; /* First link in the chain */
\r
28 size_t size; /* Length of the chain */
\r
32 jsw_head_t **table; /* Dynamic chained hash table */
\r
33 size_t size; /* Current item count */
\r
34 size_t capacity; /* Current table size */
\r
35 size_t curri; /* Current index for traversal */
\r
36 jsw_node_t *currl; /* Current link for traversal */
\r
37 hash_f hash; /* User defined key hash function */
\r
38 cmp_f cmp; /* User defined key comparison function */
\r
39 keydup_f keydup; /* User defined key copy function */
\r
40 itemdup_f itemdup; /* User defined item copy function */
\r
41 keyrel_f keyrel; /* User defined key delete function */
\r
42 itemrel_f itemrel; /* User defined item delete function */
\r
45 static jsw_node_t *new_node ( void *key, void *item, jsw_node_t *next )
\r
47 jsw_node_t *node = (jsw_node_t *)malloc ( sizeof *node );
\r
59 static jsw_head_t *new_chain ( void )
\r
61 jsw_head_t *chain = (jsw_head_t *)malloc ( sizeof *chain );
\r
63 if ( chain == NULL )
\r
66 chain->first = NULL;
\r
73 Create a new hash table with a capacity of size, and
\r
74 user defined functions for handling keys and items.
\r
76 Returns: An empty hash table, or NULL on failure.
\r
78 jsw_hash_t *jsw_hnew ( size_t size, hash_f hash, cmp_f cmp,
\r
79 keydup_f keydup, itemdup_f itemdup,
\r
80 keyrel_f keyrel, itemrel_f itemrel )
\r
82 jsw_hash_t *htab = (jsw_hash_t *)malloc ( sizeof *htab );
\r
88 htab->table = (jsw_head_t **)malloc ( size * sizeof *htab->table );
\r
90 if ( htab->table == NULL ) {
\r
95 /* Empty chains have no head */
\r
96 for ( i = 0; i < size; i++ )
\r
97 htab->table[i] = NULL;
\r
100 htab->capacity = size;
\r
102 htab->currl = NULL;
\r
105 htab->keydup = keydup;
\r
106 htab->itemdup = itemdup;
\r
107 htab->keyrel = keyrel;
\r
108 htab->itemrel = itemrel;
\r
113 /* Release all memory used by the hash table */
\r
114 void jsw_hdelete ( jsw_hash_t *htab )
\r
118 /* Release each chain individually */
\r
119 for ( i = 0; i < htab->capacity; i++ ) {
\r
120 jsw_node_t *save, *it;
\r
122 if ( htab->table[i] == NULL )
\r
125 it = htab->table[i]->first;
\r
127 for ( ; it != NULL; it = save ) {
\r
129 htab->keyrel ( it->key );
\r
130 htab->itemrel ( it->item );
\r
134 free ( htab->table[i] );
\r
137 /* Release the hash table */
\r
138 free ( htab->table );
\r
143 Find an item with the selected key
\r
145 Returns: The item, or NULL if not found
\r
147 void *jsw_hfind ( jsw_hash_t *htab, void *key )
\r
149 unsigned h = htab->hash ( key ) % htab->capacity;
\r
151 /* Search the chain only if it exists */
\r
152 if ( htab->table[h] != NULL ) {
\r
153 jsw_node_t *it = htab->table[h]->first;
\r
155 for ( ; it != NULL; it = it->next ) {
\r
156 if ( htab->cmp ( key, it->key ) == 0 )
\r
165 Insert an item with the selected key
\r
167 Returns: non-zero for success, zero for failure
\r
169 int jsw_hinsert ( jsw_hash_t *htab, void *key, void *item )
\r
171 unsigned h = htab->hash ( key ) % htab->capacity;
\r
172 void *dupkey, *dupitem;
\r
173 jsw_node_t *new_item;
\r
175 /* Disallow duplicate keys */
\r
176 if ( jsw_hfind ( htab, key ) != NULL )
\r
179 /* Attempt to create a new item */
\r
180 dupkey = htab->keydup ( key );
\r
181 dupitem = htab->itemdup ( item );
\r
183 new_item = new_node ( dupkey, dupitem, NULL );
\r
185 if ( new_item == NULL )
\r
188 /* Create a chain if the bucket is empty */
\r
189 if ( htab->table[h] == NULL ) {
\r
190 htab->table[h] = new_chain();
\r
192 if ( htab->table[h] == NULL ) {
\r
193 htab->keyrel ( new_item->key );
\r
194 htab->itemrel ( new_item->item );
\r
200 /* Insert at the front of the chain */
\r
201 new_item->next = htab->table[h]->first;
\r
202 htab->table[h]->first = new_item;
\r
204 ++htab->table[h]->size;
\r
211 Remove an item with the selected key
\r
213 Returns: non-zero for success, zero for failure
\r
215 int jsw_herase ( jsw_hash_t *htab, void *key )
\r
217 unsigned h = htab->hash ( key ) % htab->capacity;
\r
218 jsw_node_t *save, *it;
\r
220 if ( htab->table[h] == NULL )
\r
223 it = htab->table[h]->first;
\r
225 /* Remove the first node in the chain? */
\r
226 if ( htab->cmp ( key, it->key ) == 0 ) {
\r
227 htab->table[h]->first = it->next;
\r
229 /* Release the node's memory */
\r
230 htab->keyrel ( it->key );
\r
231 htab->itemrel ( it->item );
\r
234 /* Remove the chain if it's empty */
\r
235 if ( htab->table[h]->first == NULL ) {
\r
236 free ( htab->table[h] );
\r
237 htab->table[h] = NULL;
\r
240 --htab->table[h]->size;
\r
243 /* Search for the node */
\r
244 while ( it->next != NULL ) {
\r
245 if ( htab->cmp ( key, it->next->key ) == 0 )
\r
252 if ( it->next == NULL )
\r
256 it->next = it->next->next;
\r
258 /* Release the node's memory */
\r
259 htab->keyrel ( save->key );
\r
260 htab->itemrel ( save->item );
\r
263 --htab->table[h]->size;
\r
266 /* Erasure invalidates traversal markers */
\r
267 jsw_hreset ( htab );
\r
275 Grow or shrink the table, this is a slow operation
\r
277 Returns: non-zero for success, zero for failure
\r
279 int jsw_hresize ( jsw_hash_t *htab, size_t new_size )
\r
281 jsw_hash_t *new_htab;
\r
285 /* Build a new hash table, then assign it to the old one */
\r
286 new_htab = jsw_hnew ( new_size, htab->hash, htab->cmp,
\r
287 htab->keydup, htab->itemdup, htab->keyrel, htab->itemrel );
\r
289 if ( new_htab == NULL )
\r
292 for ( i = 0; i < htab->capacity; i++ ) {
\r
293 if ( htab->table[i] == NULL )
\r
296 for ( it = htab->table[i]->first; it != NULL; it = it->next )
\r
297 jsw_hinsert ( new_htab, it->key, it->item );
\r
300 /* A hash table holds copies, so release the old table */
\r
301 jsw_hdelete ( htab );
\r
307 /* Reset the traversal markers to the beginning */
\r
308 void jsw_hreset ( jsw_hash_t *htab )
\r
313 htab->currl = NULL;
\r
315 /* Find the first non-empty bucket */
\r
316 for ( i = 0; i < htab->capacity; i++ ) {
\r
317 if ( htab->table[i] != NULL )
\r
323 /* Set the link marker if the table was not empty */
\r
324 if ( i != htab->capacity )
\r
325 htab->currl = htab->table[i]->first;
\r
328 /* Traverse forward by one key */
\r
329 int jsw_hnext ( jsw_hash_t *htab )
\r
331 if ( htab->currl != NULL ) {
\r
332 htab->currl = htab->currl->next;
\r
334 /* At the end of the chain? */
\r
335 if ( htab->currl == NULL ) {
\r
336 /* Find the next chain */
\r
337 while ( ++htab->curri < htab->capacity ) {
\r
338 if ( htab->table[htab->curri] != NULL )
\r
342 /* No more chains? */
\r
343 if ( htab->curri == htab->capacity )
\r
346 htab->currl = htab->table[htab->curri]->first;
\r
353 /* Get the current key */
\r
354 const void *jsw_hkey ( jsw_hash_t *htab )
\r
356 return htab->currl != NULL ? htab->currl->key : NULL;
\r
359 /* Get the current item */
\r
360 void *jsw_hitem ( jsw_hash_t *htab )
\r
362 return htab->currl != NULL ? htab->currl->item : NULL;
\r
365 /* Current number of items in the table */
\r
366 size_t jsw_hsize ( jsw_hash_t *htab )
\r
371 /* Total allowable number of items without resizing */
\r
372 size_t jsw_hcapacity ( jsw_hash_t *htab )
\r
374 return htab->capacity;
\r
377 /* Get statistics for the hash table */
\r
378 jsw_hstat_t *jsw_hstat ( jsw_hash_t *htab )
\r
381 double sum = 0, used = 0;
\r
384 /* No stats for an empty table */
\r
385 if ( htab->size == 0 )
\r
388 stat = (jsw_hstat_t *)malloc ( sizeof *stat );
\r
390 if ( stat == NULL )
\r
394 stat->schain = (size_t)-1;
\r
396 for ( i = 0; i < htab->capacity; i++ ) {
\r
397 if ( htab->table[i] != NULL ) {
\r
398 sum += htab->table[i]->size;
\r
400 ++used; /* Non-empty buckets */
\r
402 if ( htab->table[i]->size > stat->lchain )
\r
403 stat->lchain = htab->table[i]->size;
\r
405 if ( htab->table[i]->size < stat->schain )
\r
406 stat->schain = htab->table[i]->size;
\r
410 stat->load = used / htab->capacity;
\r
411 stat->achain = sum / used;
\r