libdict 1.0.7
Data Structure C Library
Loading...
Searching...
No Matches
hashtable2.c
Go to the documentation of this file.
1/*
2 * libdict -- open-addressing hash-table, with linear probing, 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
53struct hashtable2 {
54 size_t count;
58 unsigned size;
59 /* Bumped by every change that invalidates iterators: a resize, which
60 * rehashes every element into a different slot, a clear, and a removal,
61 * which moves other elements into the freed slots. Iterators record it
62 * when positioned and treat a mismatch as unpositioned. */
63 size_t gen;
64};
65
68 int slot;
69 size_t gen;
70};
71
72/* Vtable thunks. The vtable slot types take void*, and calling a function
73 * through a function-pointer type that is not compatible with the function's
74 * own type is undefined behaviour (C11 6.5.2.2p9), so each slot below gets a
75 * thunk with the exact slot signature instead of a cast. A cast would also
76 * hide a genuine signature mismatch. */
77static dict_itor* vt_hashtable2_dict_itor_new(void* obj) { return hashtable2_dict_itor_new(obj); }
78static size_t vt_hashtable2_free(void* obj, dict_delete_func delete_func) { return hashtable2_free(obj, delete_func); }
79static dict_insert_result vt_hashtable2_insert(void* obj, void* key) { return hashtable2_insert(obj, key); }
80static void** vt_hashtable2_search(void* obj, const void* key) { return hashtable2_search(obj, key); }
81static dict_remove_result vt_hashtable2_remove(void* obj, const void* key) { return hashtable2_remove(obj, key); }
82static size_t vt_hashtable2_clear(void* obj, dict_delete_func delete_func) { return hashtable2_clear(obj, delete_func); }
83static size_t vt_hashtable2_traverse(void* obj, dict_visit_func visit, void* user_data) { return hashtable2_traverse(obj, visit, user_data); }
84static size_t vt_hashtable2_count(const void* obj) { return hashtable2_count(obj); }
85static bool vt_hashtable2_verify(const void* obj) { return hashtable2_verify(obj); }
86static void vt_hashtable2_itor_free(void* itor) { hashtable2_itor_free(itor); }
87static bool vt_hashtable2_itor_valid(const void* itor) { return hashtable2_itor_valid(itor); }
88static void vt_hashtable2_itor_invalidate(void* itor) { hashtable2_itor_invalidate(itor); }
89static bool vt_hashtable2_itor_next(void* itor) { return hashtable2_itor_next(itor); }
90static bool vt_hashtable2_itor_prev(void* itor) { return hashtable2_itor_prev(itor); }
91static bool vt_hashtable2_itor_nextn(void* itor, size_t count) { return hashtable2_itor_nextn(itor, count); }
92static bool vt_hashtable2_itor_prevn(void* itor, size_t count) { return hashtable2_itor_prevn(itor, count); }
93static bool vt_hashtable2_itor_first(void* itor) { return hashtable2_itor_first(itor); }
94static bool vt_hashtable2_itor_last(void* itor) { return hashtable2_itor_last(itor); }
95static void* vt_hashtable2_itor_key(void* itor) { return dict_unconst(hashtable2_itor_key(itor)); }
96static void** vt_hashtable2_itor_datum(void* itor) { return hashtable2_itor_datum(itor); }
97static bool vt_hashtable2_itor_search(void* itor, const void* key) { return hashtable2_itor_search(itor, key); }
98static bool vt_hashtable2_itor_remove(void* itor) { return hashtable2_itor_remove(itor); }
99
100static const dict_vtable hashtable2_vtable = {
101 false,
102 vt_hashtable2_dict_itor_new,
103 vt_hashtable2_free,
104 vt_hashtable2_insert,
105 vt_hashtable2_search,
106 NULL,/* search_le: not supported */
107 NULL,/* search_lt: not supported */
108 NULL,/* search_ge: not supported */
109 NULL,/* search_gt: not supported */
110 vt_hashtable2_remove,
111 vt_hashtable2_clear,
112 vt_hashtable2_traverse,
113 NULL,
114 vt_hashtable2_count,
115 vt_hashtable2_verify,
116};
117
118static const itor_vtable hashtable2_itor_vtable = {
119 vt_hashtable2_itor_free,
120 vt_hashtable2_itor_valid,
121 vt_hashtable2_itor_invalidate,
122 vt_hashtable2_itor_next,
123 vt_hashtable2_itor_prev,
124 vt_hashtable2_itor_nextn,
125 vt_hashtable2_itor_prevn,
126 vt_hashtable2_itor_first,
127 vt_hashtable2_itor_last,
128 vt_hashtable2_itor_key,
129 vt_hashtable2_itor_datum,
130 vt_hashtable2_itor_search,
131 NULL,/* itor_search_le: not supported */
132 NULL,/* itor_search_lt: not supported */
133 NULL,/* itor_search_ge: not supported */
134 NULL,/* itor_search_gt: not supported */
135 vt_hashtable2_itor_remove,
136 NULL,/* hashtable2_itor_compare not implemented yet */
137};
138
140hashtable2_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
141{
142 ASSERT(cmp_func != NULL);
143 ASSERT(hash_func != NULL);
144 ASSERT(initial_size > 0);
145
146 hashtable2* table = MALLOC(sizeof(*table));
147 if (table) {
148 table->size = dict_prime_geq(initial_size);
149 table->table = dict_malloc_array(table->size, sizeof(hash_node));
150 if (!table->table) {
151 FREE(table);
152 return NULL;
153 }
154 memset(table->table, 0, table->size * sizeof(hash_node));
155 table->cmp_func = cmp_func;
156 table->hash_func = hash_func;
157 table->count = 0;
158 table->gen = 0;
159 }
160 return table;
161}
162
163dict*
164hashtable2_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
165{
166 ASSERT(cmp_func != NULL);
167 ASSERT(hash_func != NULL);
168 ASSERT(initial_size > 0);
169
170 dict* dct = MALLOC(sizeof(*dct));
171 if (dct) {
172 dct->_object = hashtable2_new(cmp_func, hash_func, initial_size);
173 if (!dct->_object) {
174 FREE(dct);
175 return NULL;
176 }
177 dct->_vtable = &hashtable2_vtable;
178 }
179 return dct;
180}
181
182size_t
184{
185 ASSERT(table != NULL);
186 size_t count = hashtable2_clear(table, delete_func);
187 FREE(table->table);
188 FREE(table);
189 return count;
190}
191
192static inline dict_insert_result
193insert(hashtable2* table, void *key, unsigned hash)
194{
195 hash_node* const first = table->table + (hash % table->size);
196 hash_node* const table_end = table->table + table->size;
197 hash_node* node = first;
198 do {
199 if (!node->hash) {
200 node->hash = hash;
201 node->key = key;
202 ASSERT(node->datum == NULL);
203 return (dict_insert_result) { &node->datum, true };
204 }
205
206 if (node->hash == hash && table->cmp_func(key, node->key) == 0)
207 return (dict_insert_result) { &node->datum, false };
208
209 if (++node == table_end)
210 node = table->table;
211 } while (node != first);
212 /* No room for new element! */
213 return (dict_insert_result) { NULL, false };
214}
215
216static inline unsigned
217nonzero_hash(dict_hash_func hash_func, const void *key)
218{
219 const unsigned hash = hash_func(key);
220 return hash ? hash : ~(unsigned)0;
221}
222
225{
226 if (LOADFACTOR_DENOMINATOR * table->count >= LOADFACTOR_NUMERATOR * table->size) {
227 /* Load factor too high: increase the table size. A resize invalidates
228 * iterators, so do not resize for a key that is already present:
229 * nothing is added. */
230 void** datum_ptr = hashtable2_search(table, key);
231 if (datum_ptr)
232 return (dict_insert_result) { datum_ptr, false };
233 if (!hashtable2_resize(table, table->size + 1)) {
234 /* No memory for a bigger table, but let the insert proceed anyway. */
235 }
236 }
237 const unsigned hash = nonzero_hash(table->hash_func, key);
238 dict_insert_result result = insert(table, key, hash);
239 if (result.inserted)
240 table->count++;
241 return result;
242}
243
244void**
245hashtable2_search(hashtable2* table, const void* key)
246{
247 const unsigned hash = nonzero_hash(table->hash_func, key);
248 hash_node* const first = table->table + (hash % table->size);
249 hash_node* const table_end = table->table + table->size;
250 hash_node* node = first;
251 do {
252 if (!node->hash) /* Not occupied. */
253 break;
254
255 if (node->hash == hash && table->cmp_func(key, node->key) == 0)
256 return &node->datum;
257
258 if (++node == table_end)
259 node = table->table;
260 } while (node != first);
261 return NULL;
262}
263
264static void
265remove_cleanup(hashtable2* table, hash_node* const first, hash_node* node)
266{
267 hash_node* const table_end = table->table + table->size;
268 do {
269 const unsigned hash = node->hash;
270 if (!hash) /* Not occupied. */
271 break;
272
273 void* const datum = node->datum;
274 node->datum = NULL;
275 node->hash = 0;
276
277 dict_insert_result result = insert(table, node->key, hash);
278 ASSERT(result.inserted);
279 ASSERT(result.datum_ptr != NULL);
280 ASSERT(*result.datum_ptr == NULL);
281 *result.datum_ptr = datum;
282
283 if (++node == table_end)
284 node = table->table;
285 } while (node != first);
286}
287
288static void
289remove_node(hashtable2* table, hash_node* first, hash_node* node)
290{
291 ASSERT(node->hash != 0);
292
293 node->key = node->datum = NULL;
294 node->hash = 0;
295 table->count--;
296
297 if (++node == table->table + table->size)
298 node = table->table;
299 remove_cleanup(table, first, node);
300}
301
303hashtable2_remove(hashtable2* table, const void* key)
304{
305 const unsigned hash = nonzero_hash(table->hash_func, key);
306 hash_node* const first = table->table + (hash % table->size);
307 hash_node* const table_end = table->table + table->size;
308 hash_node* node = first;
309 do {
310 if (!node->hash) /* Not occupied. */
311 break;
312
313 if (node->hash == hash && table->cmp_func(key, node->key) == 0) {
314 dict_remove_result result = { node->key, node->datum, true };
315 remove_node(table, first, node);
316 /* Bumped here rather than in remove_node(): there it made every
317 * removal ~8% slower (gcc 11, -O2). */
318 table->gen++;
319 return result;
320 }
321
322 if (++node == table_end)
323 node = table->table;
324 } while (node != first);
325 return (dict_remove_result) { NULL, NULL, false };
326}
327
328size_t
330{
331 hash_node *node = table->table;
332 hash_node *const end = table->table + table->size;
333 for (; node != end; ++node) {
334 if (node->hash) {
335 if (delete_func) delete_func(node->key, node->datum);
336 node->key = node->datum = NULL;
337 node->hash = 0;
338 }
339 }
340
341 const size_t count = table->count;
342 table->count = 0;
343 table->gen++;
344 return count;
345}
346
347size_t
348hashtable2_traverse(hashtable2* table, dict_visit_func visit, void* user_data)
349{
350 size_t count = 0;
351 hash_node *node = table->table;
352 for (hash_node *const end = table->table + table->size; node != end; ++node) {
353 if (node->hash) {
354 ++count;
355 if (!visit(node->key, node->datum, user_data)) break;
356 }
357 }
358 return count;
359}
360
361size_t
363{
364 return table->count;
365}
366
367size_t
369{
370 return table->size;
371}
372
373size_t
375{
376 return table->count;
377}
378
379bool
380hashtable2_resize(hashtable2* table, unsigned new_size)
381{
382 ASSERT(new_size > 0);
383
384 new_size = dict_prime_geq(new_size);
385 if (table->size == new_size)
386 return true;
387
388 if (table->count > new_size) {
389 /* The number of records already in hashtable will not fit (must be a reduction in size). */
390 return false;
391 }
392
393 const unsigned old_size = table->size;
394 const size_t old_count = table->count;
395 hash_node* const old_table = table->table;
396
397 table->table = dict_malloc_array(new_size, sizeof(hash_node));
398 if (!table->table) {
399 table->table = old_table;
400 return false;
401 }
402 memset(table->table, 0, new_size * sizeof(hash_node));
403 table->size = new_size;
404
405 for (unsigned i = 0; i < old_size; ++i) {
406 if (old_table[i].hash) {
407 dict_insert_result result =
408 insert(table, old_table[i].key, old_table[i].hash);
409 if (!result.inserted || !result.datum_ptr) {
410 FREE(table->table);
411 table->table = old_table;
412 table->size = old_size;
413 table->count = old_count;
414 return false;
415 }
416 *result.datum_ptr = old_table[i].datum;
417 }
418 }
419 FREE(old_table);
420 table->gen++;
421 return true;
422}
423
424bool
426{
427 size_t count = 0;
428 const hash_node *node = table->table;
429 const hash_node *end = table->table + table->size;
430 for (; node != end; ++node) {
431 if (node->hash) {
432 ++count;
433 } else {
434 VERIFY(node->datum == NULL);
435 }
436 }
437 VERIFY(table->count == count);
438 return true;
439}
440
443{
444 hashtable2_itor* itor = MALLOC(sizeof(*itor));
445 if (itor) {
446 itor->table = table;
447 itor->slot = -1;
448 itor->gen = table->gen;
449 }
450 return itor;
451}
452
455{
456 dict_itor* itor = MALLOC(sizeof(*itor));
457 if (itor) {
458 if (!(itor->_itor = hashtable2_itor_new(table))) {
459 FREE(itor);
460 return NULL;
461 }
462 itor->_vtable = &hashtable2_itor_vtable;
463 }
464 return itor;
465}
466
467void
469{
470 FREE(itor);
471}
472
473/* True if the table was resized, cleared or had an element removed since
474 * |itor| was positioned, in which case its slot must not be used. */
475static inline bool
476itor_stale(const hashtable2_itor* itor)
477{
478 return itor->gen != itor->table->gen;
479}
480
481/* Unpositions |itor| if it is stale, and returns whether it is positioned. */
482static inline bool
483itor_check(hashtable2_itor* itor)
484{
485 if (itor->slot >= 0 && itor_stale(itor))
486 itor->slot = -1;
487 return itor->slot >= 0;
488}
489
490bool
492{
493 if (itor->slot < 0 || itor_stale(itor))
494 return false;
495 /* Nothing that could empty or move the slot happened since. */
496 ASSERT(itor->table->table[itor->slot].hash != 0);
497 return true;
498}
499
500void
502{
503 itor->slot = -1;
504}
505
506bool
508{
509 if (!itor_check(itor))
510 return false;
511
512 while (++itor->slot < (int) itor->table->size) {
513 if (itor->table->table[itor->slot].hash)
514 return true;
515 }
516 itor->slot = -1;
517 return false;
518}
519
520bool
522{
523 if (!itor_check(itor))
524 return false;
525
526 while (itor->slot-- > 0) {
527 if (itor->table->table[itor->slot].hash)
528 return true;
529 }
530 ASSERT(itor->slot == -1);
531 return false;
532}
533
534bool
536{
537 while (count--)
538 if (!hashtable2_itor_next(itor))
539 return false;
540 return itor_check(itor);
541}
542
543bool
545{
546 while (count--)
547 if (!hashtable2_itor_prev(itor))
548 return false;
549 return itor_check(itor);
550}
551
552bool
554{
555 itor->gen = itor->table->gen;
556 for (unsigned slot = 0; slot < itor->table->size; ++slot) {
557 if (itor->table->table[slot].hash) {
558 itor->slot = (int) slot;
559 return true;
560 }
561 }
562 itor->slot = -1;
563 return false;
564}
565
566bool
568{
569 itor->gen = itor->table->gen;
570 for (unsigned slot = itor->table->size; slot > 0;) {
571 if (itor->table->table[--slot].hash) {
572 itor->slot = (int) slot;
573 return true;
574 }
575 }
576 itor->slot = -1;
577 return false;
578}
579
580bool
582{
583 itor->gen = itor->table->gen;
584 const unsigned hash = nonzero_hash(itor->table->hash_func, key);
585 const unsigned truncated_hash = hash % itor->table->size;
586 unsigned index = truncated_hash;
587 do {
588 hash_node *node = &itor->table->table[index];
589 if (!node->hash)
590 break;
591 if (node->hash == hash && itor->table->cmp_func(key, node->key) == 0) {
592 itor->slot = (int) index;
593 return true;
594 }
595 if (++index == itor->table->size)
596 index = 0;
597 } while (index != truncated_hash);
598 itor->slot = -1;
599 return false;
600}
601
602const void*
604{
605 return hashtable2_itor_valid(itor) ? itor->table->table[itor->slot].key : NULL;
606}
607
608void**
610{
611 return itor_check(itor) ? &itor->table->table[itor->slot].datum : NULL;
612}
613
614bool
616{
617 if (!itor_check(itor))
618 return false;
619 remove_node(itor->table,
620 itor->table->table + itor->table->table[itor->slot].hash % itor->table->size,
621 itor->table->table + itor->slot);
622 itor->table->gen++;
623 itor->slot = -1;
624 return true;
625}
626
int(* dict_compare_func)(const void *, const void *)
A pointer to a function that compares two keys.
Definition dict.h:59
void(* dict_delete_func)(void *, void *)
A pointer to a function that is called when a key-value pair gets removed from a dictionary.
Definition dict.h:62
unsigned(* dict_hash_func)(const void *)
A pointer to a function that returns the hash value of a key.
Definition dict.h:66
bool(* dict_visit_func)(const void *, void *, void *)
A pointer to a function used for iterating over dictionary contents.
Definition dict.h:64
#define FREE(p)
#define ASSERT(expr)
#define VERIFY(expr)
#define MALLOC(n)
bool hashtable2_verify(const hashtable2 *table)
Check the internal invariants of the table and return true if they hold, or print the first failed ch...
Definition hashtable2.c:425
hashtable2_itor * hashtable2_itor_new(hashtable2 *table)
Create an iterator over |table| that is not positioned on any element yet.
Definition hashtable2.c:442
dict_itor * hashtable2_dict_itor_new(hashtable2 *table)
Like hashtable2_itor_new(), but wrapped in a generic dict_itor for use with the dict_itor_* macros.
Definition hashtable2.c:454
void ** hashtable2_search(hashtable2 *table, const void *key)
Return the address of the datum stored for |key|, or NULL if |key| is not present.
Definition hashtable2.c:245
bool hashtable2_itor_nextn(hashtable2_itor *itor, size_t count)
Move |itor| |count| elements forward and return whether it is still positioned.
Definition hashtable2.c:535
#define LOADFACTOR_NUMERATOR
Definition hashtable2.c:39
size_t hashtable2_free(hashtable2 *table, dict_delete_func delete_func)
Remove every element, calling |delete_func|(key, datum) for each unless |delete_func| is NULL,...
Definition hashtable2.c:183
bool hashtable2_itor_valid(const hashtable2_itor *itor)
Return whether |itor| is positioned on an element.
Definition hashtable2.c:491
bool hashtable2_itor_prev(hashtable2_itor *itor)
Move |itor| to the previous element in table order (unspecified) and return true, or unposition it an...
Definition hashtable2.c:521
void ** hashtable2_itor_datum(hashtable2_itor *itor)
Return the address of the datum |itor| is positioned on, through which it can be read or replaced,...
Definition hashtable2.c:609
bool hashtable2_itor_remove(hashtable2_itor *itor)
Remove the element |itor| is positioned on and unposition |itor|; return false if it was not position...
Definition hashtable2.c:615
size_t hashtable2_clear(hashtable2 *table, dict_delete_func delete_func)
Remove every element, calling |delete_func|(key, datum) for each unless |delete_func| is NULL.
Definition hashtable2.c:329
#define LOADFACTOR_DENOMINATOR
Definition hashtable2.c:40
dict_insert_result hashtable2_insert(hashtable2 *table, void *key)
Insert |key| unless an equal key is present.
Definition hashtable2.c:224
void hashtable2_itor_invalidate(hashtable2_itor *itor)
Unposition |itor|, so that it is not positioned on any element.
Definition hashtable2.c:501
hashtable2 * hashtable2_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
Create an empty hash table that compares keys with |cmp_func| and hashes them with |hash_func| (neith...
Definition hashtable2.c:140
size_t hashtable2_count(const hashtable2 *table)
Return the number of elements.
Definition hashtable2.c:362
size_t hashtable2_slots_used(const hashtable2 *table)
Return the number of occupied slots, which for open addressing equals the number of elements.
Definition hashtable2.c:374
size_t hashtable2_size(const hashtable2 *table)
Return the current number of slots in the table.
Definition hashtable2.c:368
void hashtable2_itor_free(hashtable2_itor *itor)
Free an iterator.
Definition hashtable2.c:468
bool hashtable2_resize(hashtable2 *table, unsigned new_size)
Rehash the table into at least |size| slots, rounded up as in hashtable2_new().
Definition hashtable2.c:380
bool hashtable2_itor_prevn(hashtable2_itor *itor, size_t count)
Move |itor| |count| elements back and return whether it is still positioned.
Definition hashtable2.c:544
bool hashtable2_itor_first(hashtable2_itor *itor)
Position |itor| on the first element in table order (unspecified) and return true,...
Definition hashtable2.c:553
const void * hashtable2_itor_key(const hashtable2_itor *itor)
Return the key |itor| is positioned on, or NULL if it is not positioned.
Definition hashtable2.c:603
bool hashtable2_itor_next(hashtable2_itor *itor)
Move |itor| to the next element in table order (unspecified) and return true, or unposition it and re...
Definition hashtable2.c:507
dict_remove_result hashtable2_remove(hashtable2 *table, const void *key)
Remove the element with key |key|.
Definition hashtable2.c:303
size_t hashtable2_traverse(hashtable2 *table, dict_visit_func visit, void *user_data)
Call |visit|(key, datum, |user_data|) for each element in table order (unspecified),...
Definition hashtable2.c:348
bool hashtable2_itor_search(hashtable2_itor *itor, const void *key)
Position |itor| on |key| and return true, or unposition it and return false if |key| is not present.
Definition hashtable2.c:581
bool hashtable2_itor_last(hashtable2_itor *itor)
Position |itor| on the last element in table order (unspecified) and return true, or unposition it an...
Definition hashtable2.c:567
dict * hashtable2_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
Like hashtable2_new(), but wrapped in a generic dict for use with the dict_* macros.
Definition hashtable2.c:164
Hash table with open addressing and linear probing.
unsigned dict_prime_geq(unsigned n)
void ** datum_ptr
Definition dict.h:81
void * _itor
Definition dict.h:235
const itor_vtable * _vtable
Definition dict.h:236
The operations of one container type.
Definition dict.h:110
Definition dict.h:164
void * _object
Definition dict.h:165
const dict_vtable * _vtable
Definition dict.h:166
unsigned hash
Definition hashtable.c:54
void * key
Definition hashtable.c:48
void * datum
Definition hashtable.c:49
hashtable2 * table
Definition hashtable2.c:67
unsigned size
Definition hashtable2.c:58
dict_compare_func cmp_func
Definition hashtable2.c:55
size_t gen
Definition hashtable2.c:63
hash_node * table
Definition hashtable2.c:57
size_t count
Definition hashtable2.c:54
dict_hash_func hash_func
Definition hashtable2.c:56