libdict
Data Structure C Library
Loading...
Searching...
No Matches
hashtable.c
Go to the documentation of this file.
1/*
2 * libdict -- chained hash-table, with chains sorted by hash, implementation.
3 *
4 * Copyright (c) 2001-2014, Farooq Mela
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/*
29 * cf. [Gonnet 1984], [Knuth 1998]
30 */
31
32#include "hashtable.h"
33
34#include <string.h> /* For memset() */
35#include "dict_private.h"
36#include "hashtable_common.h"
37
38/* TODO: make this configurable in the constructor methods */
39#define LOADFACTOR_NUMERATOR 2
40#define LOADFACTOR_DENOMINATOR 3
41#if LOADFACTOR_NUMERATOR > LOADFACTOR_DENOMINATOR
42# error LOADFACTOR_NUMERATOR must be less than LOADFACTOR_DENOMINATOR
43#endif
44
45typedef struct hash_node hash_node;
46
47struct hash_node {
48 void* key;
49 void* datum;
51
52 /* Only because iterators are bidirectional: */
54 unsigned hash; /* Untruncated hash value. */
55};
56
64
70
71static const dict_vtable hashtable_vtable = {
72 false,
77 (dict_search_func) NULL,/* search_le: not supported */
78 (dict_search_func) NULL,/* search_lt: not supported */
79 (dict_search_func) NULL,/* search_ge: not supported */
80 (dict_search_func) NULL,/* search_gt: not supported */
84 (dict_select_func) NULL,
87};
88
89static const itor_vtable hashtable_itor_vtable = {
102 (dict_isearch_func) NULL,/* itor_search_le: not supported */
103 (dict_isearch_func) NULL,/* itor_search_lt: not supported */
104 (dict_isearch_func) NULL,/* itor_search_ge: not supported */
105 (dict_isearch_func) NULL,/* itor_search_gt: not supported */
106 (dict_iremove_func) hashtable_itor_remove,/* hashtable_itor_remove not implemented yet */
107 (dict_icompare_func) NULL,/* hashtable_itor_compare not implemented yet */
108};
109
111hashtable_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size)
112{
113 ASSERT(cmp_func != NULL);
114 ASSERT(hash_func != NULL);
115 ASSERT(size > 0);
116
117 hashtable* table = MALLOC(sizeof(*table));
118 if (table) {
119 table->size = dict_prime_geq(size);
120 table->table = MALLOC(table->size * sizeof(hash_node*));
121 if (!table->table) {
122 FREE(table);
123 return NULL;
124 }
125 memset(table->table, 0, table->size * sizeof(hash_node*));
126 table->cmp_func = cmp_func;
127 table->hash_func = hash_func;
128 table->count = 0;
129 }
130 return table;
131}
132
133dict*
134hashtable_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size)
135{
136 ASSERT(hash_func != NULL);
137 ASSERT(size > 0);
138
139 dict* dct = MALLOC(sizeof(*dct));
140 if (dct) {
141 dct->_object = hashtable_new(cmp_func, hash_func, size);
142 if (!dct->_object) {
143 FREE(dct);
144 return NULL;
145 }
146 dct->_vtable = &hashtable_vtable;
147 }
148 return dct;
149}
150
151size_t
153{
154 ASSERT(table != NULL);
155
156 size_t count = hashtable_clear(table, delete_func);
157 FREE(table->table);
158 FREE(table);
159 return count;
160}
161
163hashtable_insert(hashtable* table, void* key)
164{
165 if (LOADFACTOR_DENOMINATOR * table->count >= LOADFACTOR_NUMERATOR * table->size) {
166 /* Load factor too high. */
167 hashtable_resize(table, table->size + 1);
168 }
169
170 const unsigned hash = table->hash_func(key);
171 const unsigned mhash = hash % table->size;
172 hash_node* node = table->table[mhash];
173 hash_node* prev = NULL;
174 while (node && hash >= node->hash) {
175 if (hash == node->hash && table->cmp_func(key, node->key) == 0)
176 return (dict_insert_result) { &node->datum, false };
177 prev = node;
178 node = node->next;
179 }
180
181 hash_node* add = MALLOC(sizeof(*add));
182 if (!add)
183 return (dict_insert_result) { NULL, false };
184
185 add->key = key;
186 add->datum = NULL;
187 add->hash = hash;
188 add->prev = prev;
189
190 if (prev)
191 prev->next = add;
192 else
193 table->table[mhash] = add;
194
195 add->next = node;
196 if (node)
197 node->prev = add;
198
199 table->count++;
200 return (dict_insert_result) { &add->datum, true };
201}
202
203void**
204hashtable_search(hashtable* table, const void* key)
205{
206 const unsigned hash = table->hash_func(key);
207 hash_node* node = table->table[hash % table->size];
208 while (node && hash >= node->hash) {
209 if (hash == node->hash && table->cmp_func(key, node->key) == 0)
210 return &node->datum;
211
212 node = node->next;
213 }
214 return NULL;
215}
216
217static void
218remove_node(hashtable* table, hash_node* node, unsigned mhash)
219{
220 if (node->prev)
221 node->prev->next = node->next;
222 else
223 table->table[mhash] = node->next;
224
225 if (node->next)
226 node->next->prev = node->prev;
227
228 FREE(node);
229 table->count--;
230}
231
233hashtable_remove(hashtable* table, const void* key)
234{
235 const unsigned hash = table->hash_func(key);
236 const unsigned mhash = hash % table->size;
237
238 hash_node* node = table->table[mhash];
239 while (node && hash >= node->hash) {
240 if (hash == node->hash && table->cmp_func(key, node->key) == 0) {
241 dict_remove_result result = { node->key, node->datum, true };
242 remove_node(table, node, mhash);
243 return result;
244 }
245 node = node->next;
246 }
247 return (dict_remove_result) { NULL, NULL, false };
248}
249
250size_t
252{
253 for (unsigned slot = 0; slot < table->size; slot++) {
254 hash_node* node = table->table[slot];
255 while (node != NULL) {
256 hash_node* next = node->next;
257 if (delete_func) delete_func(node->key, node->datum);
258 FREE(node);
259 node = next;
260 }
261 table->table[slot] = NULL;
262 }
263
264 const size_t count = table->count;
265 table->count = 0;
266 return count;
267}
268
269size_t
270hashtable_traverse(hashtable* table, dict_visit_func visit, void* user_data)
271{
272 size_t count = 0;
273 for (unsigned i = 0; i < table->size; i++) {
274 for (hash_node* node = table->table[i]; node; node = node->next) {
275 ++count;
276 if (!visit(node->key, node->datum, user_data)) return count;
277 }
278 }
279 return count;
280}
281
282size_t
284{
285 return table->count;
286}
287
288size_t
290{
291 return table->size;
292}
293
294size_t
296{
297 size_t count = 0;
298 for (unsigned i = 0; i < table->size; i++)
299 if (table->table[i])
300 count++;
301 return count;
302}
303
304bool
305hashtable_resize(hashtable* table, unsigned new_size)
306{
307 ASSERT(new_size > 0);
308
309 new_size = dict_prime_geq(new_size);
310 if (table->size == new_size) return true;
311
312 /* TODO: investigate whether using realloc would be advantageous. */
313 hash_node** ntable = MALLOC(new_size * sizeof(hash_node*));
314 if (!ntable) return false;
315
316 memset(ntable, 0, new_size * sizeof(hash_node*));
317
318 for (unsigned i = 0; i < table->size; i++) {
319 for (hash_node* node = table->table[i]; node;) {
320 hash_node* const next = node->next;
321 const unsigned mhash = node->hash % new_size;
322 hash_node* search = ntable[mhash];
323 hash_node* prev = NULL;
324 while (search && node->hash >= search->hash) {
325 prev = search;
326 search = search->next;
327 }
328 if ((node->next = search) != NULL)
329 search->prev = node;
330 if ((node->prev = prev) != NULL)
331 prev->next = node;
332 else
333 ntable[mhash] = node;
334 node = next;
335 }
336 }
337
338 FREE(table->table);
339 table->table = ntable;
340 table->size = new_size;
341 return true;
342}
343
344bool
346{
347 for (unsigned slot = 0; slot < table->size; ++slot) {
348 for (hash_node* n = table->table[slot]; n; n = n->next) {
349 if (n == table->table[slot]) {
350 VERIFY(n->prev == NULL);
351 } else {
352 VERIFY(n->prev != NULL);
353 VERIFY(n->prev->next == n);
354 }
355 if (n->next) {
356 VERIFY(n->next->prev == n);
357 VERIFY(n->hash <= n->next->hash);
358 }
359 VERIFY(n->hash % table->size == slot);
360 }
361 }
362 return true;
363}
364
367{
368 hashtable_itor* itor = MALLOC(sizeof(*itor));
369 if (itor) {
370 itor->table = table;
371 itor->node = NULL;
372 itor->slot = 0;
373 }
374 return itor;
375}
376
379{
380 dict_itor* itor = MALLOC(sizeof(*itor));
381 if (itor) {
382 if (!(itor->_itor = hashtable_itor_new(table))) {
383 FREE(itor);
384 return NULL;
385 }
386 itor->_vtable = &hashtable_itor_vtable;
387 }
388 return itor;
389}
390
391void
393{
394 FREE(itor);
395}
396
397bool
399{
400 return itor->node != NULL;
401}
402
403void
405{
406 itor->node = NULL;
407 itor->slot = 0;
408}
409
410bool
412{
413 if (!itor->node)
414 return false;
415
416 if ((itor->node = itor->node->next) != NULL)
417 return true;
418
419 unsigned slot = itor->slot;
420 while (++slot < itor->table->size) {
421 if (itor->table->table[slot]) {
422 itor->node = itor->table->table[slot];
423 itor->slot = slot;
424 return true;
425 }
426 }
427 itor->node = NULL;
428 itor->slot = 0;
429 return itor->node != NULL;
430}
431
432bool
434{
435 if (!itor->node)
436 return false;
437
438 if ((itor->node = itor->node->prev) != NULL)
439 return true;
440
441 unsigned slot = itor->slot;
442 while (slot > 0) {
443 hash_node* node = itor->table->table[--slot];
444 if (node) {
445 while (node->next)
446 node = node->next;
447 itor->node = node;
448 itor->slot = slot;
449 return true;
450 }
451 }
452 itor->node = NULL;
453 itor->slot = 0;
454 return false;
455}
456
457bool
459{
460 while (count--) {
461 if (!hashtable_itor_next(itor))
462 return false;
463 }
464 return itor->node != NULL;
465}
466
467bool
469{
470 while (count--) {
471 if (!hashtable_itor_prev(itor))
472 return false;
473 }
474 return itor->node != NULL;
475}
476
477bool
479{
480 for (unsigned slot = 0; slot < itor->table->size; ++slot) {
481 if (itor->table->table[slot]) {
482 itor->node = itor->table->table[slot];
483 itor->slot = slot;
484 return true;
485 }
486 }
487 itor->node = NULL;
488 itor->slot = 0;
489 return false;
490}
491
492bool
494{
495 for (unsigned slot = itor->table->size; slot > 0;) {
496 if (itor->table->table[--slot]) {
497 hash_node* node = itor->table->table[slot];
498 while (node->next)
499 node = node->next;
500 itor->node = node;
501 itor->slot = slot;
502 return true;
503 }
504 }
505 itor->node = NULL;
506 itor->slot = 0;
507 return false;
508}
509
510bool
512{
513 const unsigned hash = itor->table->hash_func(key);
514 const unsigned mhash = hash % itor->table->size;
515 hash_node* node = itor->table->table[mhash];
516 while (node && hash >= node->hash) {
517 if (hash == node->hash && itor->table->cmp_func(key, node->key) == 0) {
518 itor->node = node;
519 itor->slot = mhash;
520 return true;
521 }
522 node = node->next;
523 }
524 itor->node = NULL;
525 itor->slot = 0;
526 return false;
527}
528
529const void*
531{
532 return itor->node ? itor->node->key : NULL;
533}
534
535void**
537{
538 return itor->node ? &itor->node->datum : NULL;
539}
540
541bool
543{
544 if (!itor->node)
545 return false;
546 remove_node(itor->table, itor->node, itor->node->hash % itor->table->size);
547 itor->node = NULL;
548 return true;
549}
dict_remove_result(* dict_remove_func)(void *obj, const void *key)
Definition dict.h:90
int(* dict_compare_func)(const void *, const void *)
Definition dict.h:54
bool(* dict_select_func)(void *obj, size_t n, const void **key, void **datum)
Definition dict.h:93
void(* dict_invalidate_func)(void *itor)
Definition dict.h:117
size_t(* dict_traverse_func)(void *obj, dict_visit_func visit, void *user_data)
Definition dict.h:92
dict_insert_result(* dict_insert_func)(void *obj, void *key)
Definition dict.h:87
void(* dict_delete_func)(void *, void *)
Definition dict.h:57
int(* dict_icompare_func)(void *itor1, void *itor2)
Definition dict.h:128
bool(* dict_nextn_func)(void *itor, size_t count)
Definition dict.h:120
bool(* dict_valid_func)(const void *itor)
Definition dict.h:116
size_t(* dict_count_func)(const void *obj)
Definition dict.h:94
bool(* dict_first_func)(void *itor)
Definition dict.h:122
bool(* dict_next_func)(void *itor)
Definition dict.h:118
void *(* dict_key_func)(void *itor)
Definition dict.h:124
unsigned(* dict_hash_func)(const void *)
Definition dict.h:61
size_t(* dict_dfree_func)(void *obj, dict_delete_func delete_func)
Definition dict.h:85
void **(* dict_datum_func)(void *itor)
Definition dict.h:125
bool(* dict_prevn_func)(void *itor, size_t count)
Definition dict.h:121
void **(* dict_search_func)(void *obj, const void *key)
Definition dict.h:88
bool(* dict_isearch_func)(void *itor, const void *key)
Definition dict.h:126
bool(* dict_iremove_func)(void *itor)
Definition dict.h:127
bool(* dict_prev_func)(void *itor)
Definition dict.h:119
dict_itor *(* dict_inew_func)(void *obj)
Definition dict.h:84
bool(* dict_last_func)(void *itor)
Definition dict.h:123
bool(* dict_verify_func)(const void *obj)
Definition dict.h:95
bool(* dict_visit_func)(const void *, void *, void *)
Definition dict.h:59
size_t(* dict_clear_func)(void *obj, dict_delete_func delete_func)
Definition dict.h:91
void(* dict_ifree_func)(void *itor)
Definition dict.h:115
#define FREE(p)
#define ASSERT(expr)
#define VERIFY(expr)
#define MALLOC(n)
size_t hashtable_free(hashtable *table, dict_delete_func delete_func)
Definition hashtable.c:152
#define LOADFACTOR_NUMERATOR
Definition hashtable.c:39
bool hashtable_itor_search(hashtable_itor *itor, const void *key)
Definition hashtable.c:511
dict_remove_result hashtable_remove(hashtable *table, const void *key)
Definition hashtable.c:233
dict_itor * hashtable_dict_itor_new(hashtable *table)
Definition hashtable.c:378
hashtable_itor * hashtable_itor_new(hashtable *table)
Definition hashtable.c:366
bool hashtable_verify(const hashtable *table)
Definition hashtable.c:345
bool hashtable_itor_last(hashtable_itor *itor)
Definition hashtable.c:493
bool hashtable_itor_next(hashtable_itor *itor)
Definition hashtable.c:411
bool hashtable_itor_first(hashtable_itor *itor)
Definition hashtable.c:478
size_t hashtable_count(const hashtable *table)
Definition hashtable.c:283
bool hashtable_itor_prevn(hashtable_itor *itor, size_t count)
Definition hashtable.c:468
#define LOADFACTOR_DENOMINATOR
Definition hashtable.c:40
dict_insert_result hashtable_insert(hashtable *table, void *key)
Definition hashtable.c:163
void ** hashtable_itor_datum(hashtable_itor *itor)
Definition hashtable.c:536
bool hashtable_itor_remove(hashtable_itor *itor)
Definition hashtable.c:542
void hashtable_itor_free(hashtable_itor *itor)
Definition hashtable.c:392
bool hashtable_resize(hashtable *table, unsigned new_size)
Definition hashtable.c:305
bool hashtable_itor_nextn(hashtable_itor *itor, size_t count)
Definition hashtable.c:458
size_t hashtable_clear(hashtable *table, dict_delete_func delete_func)
Definition hashtable.c:251
bool hashtable_itor_prev(hashtable_itor *itor)
Definition hashtable.c:433
void hashtable_itor_invalidate(hashtable_itor *itor)
Definition hashtable.c:404
size_t hashtable_traverse(hashtable *table, dict_visit_func visit, void *user_data)
Definition hashtable.c:270
dict * hashtable_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size)
Definition hashtable.c:134
bool hashtable_itor_valid(const hashtable_itor *itor)
Definition hashtable.c:398
void ** hashtable_search(hashtable *table, const void *key)
Definition hashtable.c:204
size_t hashtable_size(const hashtable *table)
Definition hashtable.c:289
size_t hashtable_slots_used(const hashtable *table)
Definition hashtable.c:295
const void * hashtable_itor_key(const hashtable_itor *itor)
Definition hashtable.c:530
hashtable * hashtable_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size)
Definition hashtable.c:111
unsigned dict_prime_geq(unsigned n)
void * _itor
Definition dict.h:175
const itor_vtable * _vtable
Definition dict.h:176
Definition dict.h:151
void * _object
Definition dict.h:152
const dict_vtable * _vtable
Definition dict.h:153
unsigned hash
Definition hashtable.c:54
void * key
Definition hashtable.c:48
hash_node * next
Definition hashtable.c:50
hash_node * prev
Definition hashtable.c:53
void * datum
Definition hashtable.c:49
unsigned slot
Definition hashtable.c:68
hashtable * table
Definition hashtable.c:66
hash_node * node
Definition hashtable.c:67
unsigned size
Definition hashtable.c:62
dict_compare_func cmp_func
Definition hashtable.c:59
size_t count
Definition hashtable.c:61
hash_node ** table
Definition hashtable.c:58
dict_hash_func hash_func
Definition hashtable.c:60