libdict
Data Structure C Library
Loading...
Searching...
No Matches
hashtable2.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 "hashtable2.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;
50 unsigned hash; /* Untruncated hash value; 0 iff unoccupied. */
51};
52
60
65
66static const dict_vtable hashtable2_vtable = {
67 false,
72 (dict_search_func) NULL,/* search_le: not supported */
73 (dict_search_func) NULL,/* search_lt: not supported */
74 (dict_search_func) NULL,/* search_ge: not supported */
75 (dict_search_func) NULL,/* search_gt: not supported */
79 (dict_select_func) NULL,
82};
83
84static const itor_vtable hashtable2_itor_vtable = {
97 (dict_isearch_func) NULL,/* itor_search_le: not supported */
98 (dict_isearch_func) NULL,/* itor_search_lt: not supported */
99 (dict_isearch_func) NULL,/* itor_search_ge: not supported */
100 (dict_isearch_func) NULL,/* itor_search_gt: not supported */
102 (dict_icompare_func) NULL,/* hashtable2_itor_compare not implemented yet */
103};
104
106hashtable2_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
107{
108 ASSERT(cmp_func != NULL);
109 ASSERT(hash_func != NULL);
110 ASSERT(initial_size > 0);
111
112 hashtable2* table = MALLOC(sizeof(*table));
113 if (table) {
114 table->size = dict_prime_geq(initial_size);
115 table->table = MALLOC(table->size * sizeof(hash_node));
116 if (!table->table) {
117 FREE(table);
118 return NULL;
119 }
120 memset(table->table, 0, table->size * sizeof(hash_node));
121 table->cmp_func = cmp_func;
122 table->hash_func = hash_func;
123 table->count = 0;
124 }
125 return table;
126}
127
128dict*
129hashtable2_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
130{
131 ASSERT(hash_func != NULL);
132 ASSERT(initial_size > 0);
133
134 dict* dct = MALLOC(sizeof(*dct));
135 if (dct) {
136 dct->_object = hashtable2_new(cmp_func, hash_func, initial_size);
137 if (!dct->_object) {
138 FREE(dct);
139 return NULL;
140 }
141 dct->_vtable = &hashtable2_vtable;
142 }
143 return dct;
144}
145
146size_t
148{
149 size_t count = hashtable2_clear(table, delete_func);
150 FREE(table->table);
151 FREE(table);
152 return count;
153}
154
155static inline dict_insert_result
156insert(hashtable2* table, void *key, unsigned hash)
157{
158 hash_node* const first = table->table + (hash % table->size);
159 hash_node* const table_end = table->table + table->size;
160 hash_node* node = first;
161 do {
162 if (!node->hash) {
163 node->hash = hash;
164 node->key = key;
165 ASSERT(node->datum == NULL);
166 return (dict_insert_result) { &node->datum, true };
167 }
168
169 if (node->hash == hash && table->cmp_func(key, node->key) == 0)
170 return (dict_insert_result) { &node->datum, false };
171
172 if (++node == table_end)
173 node = table->table;
174 } while (node != first);
175 /* No room for new element! */
176 return (dict_insert_result) { NULL, false };
177}
178
179static inline unsigned
180nonzero_hash(dict_hash_func hash_func, const void *key)
181{
182 const unsigned hash = hash_func(key);
183 return hash ? hash : ~(unsigned)0;
184}
185
188{
189 if (LOADFACTOR_DENOMINATOR * table->count >= LOADFACTOR_NUMERATOR * table->size) {
190 /* Load factor too high: increase the table size. */
191 if (!hashtable2_resize(table, table->size + 1)) {
192 /* No memory for a bigger table, but let the insert proceed anyway. */
193 }
194 }
195 const unsigned hash = nonzero_hash(table->hash_func, key);
196 dict_insert_result result = insert(table, key, hash);
197 if (result.inserted)
198 table->count++;
199 return result;
200}
201
202void**
203hashtable2_search(hashtable2* table, const void* key)
204{
205 const unsigned hash = nonzero_hash(table->hash_func, key);
206 hash_node* const first = table->table + (hash % table->size);
207 hash_node* const table_end = table->table + table->size;
208 hash_node* node = first;
209 do {
210 if (!node->hash) /* Not occupied. */
211 break;
212
213 if (node->hash == hash && table->cmp_func(key, node->key) == 0)
214 return &node->datum;
215
216 if (++node == table_end)
217 node = table->table;
218 } while (node != first);
219 return NULL;
220}
221
222#if 0
223static int
224index_of_node_to_shift(hashtable2* table, unsigned truncated_hash, unsigned index)
225{
226 int last_index = -1;
227 do {
228 hash_node* node = &table->table[index];
229 if (!node->hash) {
230 break;
231 }
232 if (node->hash % table->size == truncated_hash) {
233 last_index = index;
234 }
235 if (++index == table->size) {
236 index = 0;
237 }
238 } while (index != truncated_hash);
239 return last_index;
240}
241#endif
242
243static void
244remove_cleanup(hashtable2* table, hash_node* const first, hash_node* node)
245{
246 hash_node* const table_end = table->table + table->size;
247 do {
248 const unsigned hash = node->hash;
249 if (!hash) /* Not occupied. */
250 break;
251
252 void* const datum = node->datum;
253 node->datum = NULL;
254 node->hash = 0;
255
256 dict_insert_result result = insert(table, node->key, hash);
257 ASSERT(result.inserted);
258 ASSERT(result.datum_ptr != NULL);
259 ASSERT(*result.datum_ptr == NULL);
260 *result.datum_ptr = datum;
261
262 if (++node == table_end)
263 node = table->table;
264 } while (node != first);
265}
266
267static void
268remove_node(hashtable2* table, hash_node* first, hash_node* node)
269{
270 ASSERT(node->hash != 0);
271
272 node->key = node->datum = NULL;
273 node->hash = 0;
274 table->count--;
275
276 if (++node == table->table + table->size)
277 node = table->table;
278 remove_cleanup(table, first, node);
279}
280
282hashtable2_remove(hashtable2* table, const void* key)
283{
284 const unsigned hash = nonzero_hash(table->hash_func, key);
285 hash_node* const first = table->table + (hash % table->size);
286 hash_node* const table_end = table->table + table->size;
287 hash_node* node = first;
288 do {
289 if (!node->hash) /* Not occupied. */
290 break;
291
292 if (node->hash == hash && table->cmp_func(key, node->key) == 0) {
293 dict_remove_result result = { node->key, node->datum, true };
294 remove_node(table, first, node);
295 return result;
296 }
297
298 if (++node == table_end)
299 node = table->table;
300 } while (node != first);
301 return (dict_remove_result) { NULL, NULL, false };
302}
303
304size_t
306{
307 hash_node *node = table->table;
308 hash_node *const end = table->table + table->size;
309 for (; node != end; ++node) {
310 if (node->hash) {
311 if (delete_func) delete_func(node->key, node->datum);
312 node->key = node->datum = NULL;
313 node->hash = 0;
314 }
315 }
316
317 const size_t count = table->count;
318 table->count = 0;
319 return count;
320}
321
322size_t
323hashtable2_traverse(hashtable2* table, dict_visit_func visit, void* user_data)
324{
325 size_t count = 0;
326 hash_node *node = table->table;
327 for (hash_node *const end = table->table + table->size; node != end; ++node) {
328 if (node->hash) {
329 ++count;
330 if (!visit(node->key, node->datum, user_data)) break;
331 }
332 }
333 return count;
334}
335
336size_t
338{
339 return table->count;
340}
341
342size_t
344{
345 return table->size;
346}
347
348size_t
350{
351 return table->count;
352}
353
354bool
355hashtable2_resize(hashtable2* table, unsigned new_size)
356{
357 ASSERT(new_size > 0);
358
359 new_size = dict_prime_geq(new_size);
360 if (table->size == new_size)
361 return true;
362
363 if (table->count > new_size) {
364 /* The number of records already in hashtable will not fit (must be a reduction in size). */
365 return false;
366 }
367
368 const unsigned old_size = table->size;
369 const size_t old_count = table->count;
370 hash_node* const old_table = table->table;
371
372 table->table = MALLOC(new_size * sizeof(hash_node));
373 if (!table->table) {
374 table->table = old_table;
375 return false;
376 }
377 memset(table->table, 0, new_size * sizeof(hash_node));
378 table->size = new_size;
379
380 for (unsigned i = 0; i < old_size; ++i) {
381 if (old_table[i].hash) {
382 dict_insert_result result =
383 insert(table, old_table[i].key, old_table[i].hash);
384 if (!result.inserted || !result.datum_ptr) {
385 FREE(table->table);
386 table->table = old_table;
387 table->size = old_size;
388 table->count = old_count;
389 return false;
390 }
391 *result.datum_ptr = old_table[i].datum;
392 }
393 }
394 FREE(old_table);
395 return true;
396}
397
398bool
400{
401 size_t count = 0;
402 const hash_node *node = table->table;
403 const hash_node *end = table->table + table->size;
404 for (; node != end; ++node) {
405 if (node->hash) {
406 ++count;
407 } else {
408 VERIFY(node->datum == NULL);
409 }
410 }
411 VERIFY(table->count == count);
412 return true;
413}
414
417{
418 hashtable2_itor* itor = MALLOC(sizeof(*itor));
419 if (itor) {
420 itor->table = table;
421 itor->slot = -1;
422 }
423 return itor;
424}
425
428{
429 dict_itor* itor = MALLOC(sizeof(*itor));
430 if (itor) {
431 if (!(itor->_itor = hashtable2_itor_new(table))) {
432 FREE(itor);
433 return NULL;
434 }
435 itor->_vtable = &hashtable2_itor_vtable;
436 }
437 return itor;
438}
439
440void
442{
443 FREE(itor);
444}
445
446bool
448{
449 if (itor->slot < 0)
450 return false;
451 ASSERT(itor->table->table[itor->slot].hash != 0);
452 return true;
453}
454
455void
457{
458 itor->slot = -1;
459}
460
461bool
463{
464 if (itor->slot < 0)
465 return false;
466
467 while (++itor->slot < (int) itor->table->size) {
468 if (itor->table->table[itor->slot].hash)
469 return true;
470 }
471 itor->slot = -1;
472 return false;
473}
474
475bool
477{
478 if (itor->slot < 0)
479 return false;
480
481 while (itor->slot-- > 0) {
482 if (itor->table->table[itor->slot].hash)
483 return true;
484 }
485 ASSERT(itor->slot == -1);
486 return false;
487}
488
489bool
491{
492 while (count--)
493 if (!hashtable2_itor_next(itor))
494 return false;
495 return itor->slot >= 0;
496}
497
498bool
500{
501 while (count--)
502 if (!hashtable2_itor_prev(itor))
503 return false;
504 return itor->slot >= 0;
505}
506
507bool
509{
510 for (unsigned slot = 0; slot < itor->table->size; ++slot) {
511 if (itor->table->table[slot].hash) {
512 itor->slot = (int) slot;
513 return true;
514 }
515 }
516 itor->slot = -1;
517 return false;
518}
519
520bool
522{
523 for (unsigned slot = itor->table->size; slot > 0;) {
524 if (itor->table->table[--slot].hash) {
525 itor->slot = (int) slot;
526 return true;
527 }
528 }
529 itor->slot = -1;
530 return false;
531}
532
533bool
535{
536 const unsigned hash = nonzero_hash(itor->table->hash_func, key);
537 const unsigned truncated_hash = hash % itor->table->size;
538 unsigned index = truncated_hash;
539 do {
540 hash_node *node = &itor->table->table[index];
541 if (!node->hash)
542 break;
543 if (node->hash == hash && itor->table->cmp_func(key, node->key) == 0) {
544 itor->slot = (int) index;
545 return true;
546 }
547 if (++index == itor->table->size)
548 index = 0;
549 } while (index != truncated_hash);
550 itor->slot = -1;
551 return NULL;
552}
553
554const void*
556{
557 return (itor->slot >= 0) ? itor->table->table[itor->slot].key : NULL;
558}
559
560void**
562{
563 return (itor->slot >= 0) ? &itor->table->table[itor->slot].datum : NULL;
564}
565
566bool
568{
569 if (itor->slot < 0)
570 return false;
571 remove_node(itor->table,
572 itor->table->table + itor->table->table[itor->slot].hash % itor->table->size,
573 itor->table->table + itor->slot);
574 itor->slot = -1;
575 return true;
576}
577
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)
bool hashtable2_verify(const hashtable2 *table)
Definition hashtable2.c:399
hashtable2_itor * hashtable2_itor_new(hashtable2 *table)
Definition hashtable2.c:416
dict_itor * hashtable2_dict_itor_new(hashtable2 *table)
Definition hashtable2.c:427
void ** hashtable2_search(hashtable2 *table, const void *key)
Definition hashtable2.c:203
bool hashtable2_itor_nextn(hashtable2_itor *itor, size_t count)
Definition hashtable2.c:490
#define LOADFACTOR_NUMERATOR
Definition hashtable2.c:39
size_t hashtable2_free(hashtable2 *table, dict_delete_func delete_func)
Definition hashtable2.c:147
bool hashtable2_itor_valid(const hashtable2_itor *itor)
Definition hashtable2.c:447
bool hashtable2_itor_prev(hashtable2_itor *itor)
Definition hashtable2.c:476
void ** hashtable2_itor_datum(hashtable2_itor *itor)
Definition hashtable2.c:561
bool hashtable2_itor_remove(hashtable2_itor *itor)
Definition hashtable2.c:567
size_t hashtable2_clear(hashtable2 *table, dict_delete_func delete_func)
Definition hashtable2.c:305
#define LOADFACTOR_DENOMINATOR
Definition hashtable2.c:40
dict_insert_result hashtable2_insert(hashtable2 *table, void *key)
Definition hashtable2.c:187
void hashtable2_itor_invalidate(hashtable2_itor *itor)
Definition hashtable2.c:456
hashtable2 * hashtable2_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
Definition hashtable2.c:106
size_t hashtable2_count(const hashtable2 *table)
Definition hashtable2.c:337
size_t hashtable2_slots_used(const hashtable2 *table)
Definition hashtable2.c:349
size_t hashtable2_size(const hashtable2 *table)
Definition hashtable2.c:343
void hashtable2_itor_free(hashtable2_itor *itor)
Definition hashtable2.c:441
bool hashtable2_resize(hashtable2 *table, unsigned new_size)
Definition hashtable2.c:355
bool hashtable2_itor_prevn(hashtable2_itor *itor, size_t count)
Definition hashtable2.c:499
bool hashtable2_itor_first(hashtable2_itor *itor)
Definition hashtable2.c:508
const void * hashtable2_itor_key(const hashtable2_itor *itor)
Definition hashtable2.c:555
bool hashtable2_itor_next(hashtable2_itor *itor)
Definition hashtable2.c:462
dict_remove_result hashtable2_remove(hashtable2 *table, const void *key)
Definition hashtable2.c:282
size_t hashtable2_traverse(hashtable2 *table, dict_visit_func visit, void *user_data)
Definition hashtable2.c:323
bool hashtable2_itor_search(hashtable2_itor *itor, const void *key)
Definition hashtable2.c:534
bool hashtable2_itor_last(hashtable2_itor *itor)
Definition hashtable2.c:521
dict * hashtable2_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
Definition hashtable2.c:129
unsigned dict_prime_geq(unsigned n)
void ** datum_ptr
Definition dict.h:74
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
void * datum
Definition hashtable.c:49
hashtable2 * table
Definition hashtable2.c:62
unsigned size
Definition hashtable2.c:58
dict_compare_func cmp_func
Definition hashtable2.c:55
hash_node * table
Definition hashtable2.c:57
size_t count
Definition hashtable2.c:54
dict_hash_func hash_func
Definition hashtable2.c:56