2 Hash table library using separate chaining
4 > Created (Julienne Walker): August 7, 2005
5 > Modified (Julienne Walker): August 11, 2005
6 Added a cast for malloc to enable clean
16 typedef struct jsw_node {
17 void *key; /* Key used for searching */
18 void *item; /* Actual content of a node */
19 struct jsw_node *next; /* Next link in the chain */
22 typedef struct jsw_head {
23 jsw_node_t *first; /* First link in the chain */
24 size_t size; /* Length of the chain */
28 jsw_head_t **table; /* Dynamic chained hash table */
29 size_t size; /* Current item count */
30 size_t capacity; /* Current table size */
31 size_t curri; /* Current index for traversal */
32 jsw_node_t *currl; /* Current link for traversal */
33 hash_f hash; /* User defined key hash function */
34 cmp_f cmp; /* User defined key comparison function */
35 keydup_f keydup; /* User defined key copy function */
36 itemdup_f itemdup; /* User defined item copy function */
37 keyrel_f keyrel; /* User defined key delete function */
38 itemrel_f itemrel; /* User defined item delete function */
41 static unsigned int default_hash(const void *key) {
42 const unsigned char *p = key;
45 int len = strlen((char *)p);
47 for (i = 0; i < len; i++) {
60 static jsw_node_t *new_node ( void *key, void *item, jsw_node_t *next )
62 jsw_node_t *node = malloc ( sizeof *node );
74 static jsw_head_t *new_chain ( void )
76 jsw_head_t *chain = malloc ( sizeof *chain );
88 Create a new hash table with a capacity of size, and
89 user defined functions for handling keys and items.
91 Returns: An empty hash table, or NULL on failure.
93 jsw_hash_t *jsw_hnew ( size_t size, hash_f hash, cmp_f cmp,
94 keydup_f keydup, itemdup_f itemdup,
95 keyrel_f keyrel, itemrel_f itemrel )
97 jsw_hash_t *htab = malloc ( sizeof *htab );
103 htab->table = malloc ( size * sizeof *htab->table );
105 if ( htab->table == NULL ) {
110 /* Empty chains have no head */
111 for ( i = 0; i < size; i++ )
112 htab->table[i] = NULL;
115 htab->capacity = size;
118 htab->hash = hash ? hash : default_hash;
120 htab->keydup = keydup;
121 htab->itemdup = itemdup;
122 htab->keyrel = keyrel;
123 htab->itemrel = itemrel;
128 /* Release all memory used by the hash table */
129 void jsw_hdelete ( jsw_hash_t *htab )
133 /* Release each chain individually */
134 for ( i = 0; i < htab->capacity; i++ ) {
135 jsw_node_t *save, *it;
137 if ( htab->table[i] == NULL )
140 it = htab->table[i]->first;
142 for ( ; it != NULL; it = save ) {
144 htab->keyrel ( it->key );
145 htab->itemrel ( it->item );
149 free ( htab->table[i] );
152 /* Release the hash table */
153 free ( htab->table );
158 Find an item with the selected key
160 Returns: The item, or NULL if not found
162 void *jsw_hfind ( jsw_hash_t *htab, void *key )
164 unsigned h = htab->hash ( key ) % htab->capacity;
166 /* Search the chain only if it exists */
167 if ( htab->table[h] != NULL ) {
168 jsw_node_t *it = htab->table[h]->first;
170 for ( ; it != NULL; it = it->next ) {
171 if ( htab->cmp ( key, it->key ) == 0 )
179 static int insert_item(jsw_head_t **table, jsw_node_t *item, unsigned int chain) {
180 /* Create a chain if the bucket is empty */
181 if ( table[chain] == NULL ) {
182 table[chain] = new_chain();
185 if ( table[chain] == NULL ) {
189 /* Insert at the front of the chain */
190 item->next = table[chain]->first;
191 table[chain]->first = item;
193 ++table[chain]->size;
199 Insert an item with the selected key
201 Returns: non-zero for success, zero for failure
203 int jsw_hinsert ( jsw_hash_t *htab, void *key, void *item )
205 unsigned h = htab->hash ( key ) % htab->capacity;
206 void *dupkey, *dupitem;
207 jsw_node_t *new_item;
209 /* Disallow duplicate keys */
210 if ( jsw_hfind ( htab, key ) != NULL ) {
214 /* Attempt to create a new item */
215 dupkey = htab->keydup ( key );
216 dupitem = htab->itemdup ( item );
218 new_item = new_node ( dupkey, dupitem, NULL );
220 if ( new_item == NULL ) {
224 /* Create a chain if the bucket is empty */
225 if ( htab->table[h] == NULL ) {
226 htab->table[h] = new_chain();
228 if ( htab->table[h] == NULL ) {
229 htab->keyrel ( new_item->key );
230 htab->itemrel ( new_item->item );
236 /* Insert at the front of the chain */
237 new_item->next = htab->table[h]->first;
238 htab->table[h]->first = new_item;
240 ++htab->table[h]->size;
247 Remove an item with the selected key
249 Returns: non-zero for success, zero for failure
251 int jsw_herase ( jsw_hash_t *htab, void *key )
253 unsigned h = htab->hash ( key ) % htab->capacity;
254 jsw_node_t *save, *it;
256 if ( htab->table[h] == NULL )
259 it = htab->table[h]->first;
261 /* Remove the first node in the chain? */
262 if ( htab->cmp ( key, it->key ) == 0 ) {
263 htab->table[h]->first = it->next;
265 /* Release the node's memory */
266 htab->keyrel ( it->key );
267 htab->itemrel ( it->item );
270 /* Remove the chain if it's empty */
271 if ( htab->table[h]->first == NULL ) {
272 free ( htab->table[h] );
273 htab->table[h] = NULL;
276 --htab->table[h]->size;
279 /* Search for the node */
280 while ( it->next != NULL ) {
281 if ( htab->cmp ( key, it->next->key ) == 0 )
288 if ( it->next == NULL )
292 it->next = it->next->next;
294 /* Release the node's memory */
295 htab->keyrel ( save->key );
296 htab->itemrel ( save->item );
299 --htab->table[h]->size;
302 /* Erasure invalidates traversal markers */
311 Grow or shrink the table, this is a slow operation
313 Returns: non-zero for success, zero for failure
315 int jsw_hresize ( jsw_hash_t *htab, size_t new_size )
318 jsw_head_t **newtable;
322 if (new_size == htab->capacity) {
326 newtable = malloc(new_size * sizeof *htab->table);
332 for(i=0; i < new_size; i++) {
336 for ( i = 0; i < htab->capacity; i++ ) {
337 if ( htab->table[i] == NULL ) continue;
339 for ( it = htab->table[i]->first; it != NULL; it = it->next ) {
340 bucket = htab->hash(it->key) % new_size;
341 insert_item(newtable, it, bucket);
345 htab->capacity = new_size;
346 htab->table = newtable;
351 /* Reset the traversal markers to the beginning */
352 void jsw_hreset ( jsw_hash_t *htab )
359 /* Find the first non-empty bucket */
360 for ( i = 0; i < htab->capacity; i++ ) {
361 if ( htab->table[i] != NULL )
367 /* Set the link marker if the table was not empty */
368 if ( i != htab->capacity )
369 htab->currl = htab->table[i]->first;
372 /* Traverse forward by one key */
373 int jsw_hnext ( jsw_hash_t *htab )
375 if ( htab->currl != NULL ) {
376 htab->currl = htab->currl->next;
378 /* At the end of the chain? */
379 if ( htab->currl == NULL ) {
380 /* Find the next chain */
381 while ( ++htab->curri < htab->capacity ) {
382 if ( htab->table[htab->curri] != NULL )
386 /* No more chains? */
387 if ( htab->curri == htab->capacity )
390 htab->currl = htab->table[htab->curri]->first;
397 /* Get the current key */
398 const void *jsw_hkey ( jsw_hash_t *htab )
400 return htab->currl != NULL ? htab->currl->key : NULL;
403 /* Get the current item */
404 void *jsw_hitem ( jsw_hash_t *htab )
406 return htab->currl != NULL ? htab->currl->item : NULL;
409 /* Current number of items in the table */
410 size_t jsw_hsize ( jsw_hash_t *htab )
415 /* Total allowable number of items without resizing */
416 size_t jsw_hcapacity ( jsw_hash_t *htab )
418 return htab->capacity;
421 /* Get statistics for the hash table */
422 jsw_hstat_t *jsw_hstat ( jsw_hash_t *htab )
425 double sum = 0, used = 0;
428 /* No stats for an empty table */
429 if ( htab->size == 0 )
432 stat = malloc ( sizeof *stat );
438 stat->schain = (size_t)-1;
440 for ( i = 0; i < htab->capacity; i++ ) {
441 if ( htab->table[i] != NULL ) {
442 sum += htab->table[i]->size;
444 ++used; /* Non-empty buckets */
446 if ( htab->table[i]->size > stat->lchain )
447 stat->lchain = htab->table[i]->size;
449 if ( htab->table[i]->size < stat->schain )
450 stat->schain = htab->table[i]->size;
454 stat->load = used / htab->capacity;
455 stat->achain = sum / used;