libdatatypes 0.3.2
Abstract datatypes for C.
Loading...
Searching...
No Matches
hashtable.c
Go to the documentation of this file.
1/***************************************************************************
2 begin........: May 2012
3 copyright....: Sebastian Fedrau
4 email........: sebastian.fedrau@gmail.com
5 ***************************************************************************/
6
7/***************************************************************************
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License v3 as published by
10 the Free Software Foundation.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License v3 for more details.
16 ***************************************************************************/
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <assert.h>
26
27#include "hashtable.h"
28
31/* Memory pool block size. */
32#define HASHTABLE_BUCKET_POOL_BLOCK_SIZE 128
33
34/* Initial hashtable size when resizing automatically. */
35#define HASHTABLE_INITIAL_SIZE 257
36
37/* Prime numbers used for table size. */
38static const size_t PRIMES[] =
39{
40 257,
41 1031,
42 4127,
43 16519,
44 66083,
45 264343,
46 1057387,
47 4229573,
48 8459173,
49 16918357,
50 33836729,
51 67673503,
52 135347029,
53 270694063,
54 541388129,
55 1082776297,
56 2165552617,
57 #if SIZE_MAX == 18446744073709551615UL
58 4331105239,
59 8662210517,
60 17324420957,
61 34648841923,
62 69297683827,
63 138595367657,
64 277190735317,
65 554381470637,
66 110876294129,
67 221752588259,
68 443505176519,
69 887010353059,
70 1774020706189,
71 3548041412411,
72 7096082824837,
73 14192165649709,
74 28384331299451,
75 56768662598903,
76 113537325197831,
77 227074650395669,
78 454149300791371,
79 908298601582769,
80 1816597203165593,
81 3633194406331187,
82 7266388812662387,
83 14532777625324787,
84 29065555250649649,
85 58131110501299349,
86 116262221002598761,
87 232524442005197556,
88 465048884010395089,
89 930097768020790181,
90 #endif
91 0
92};
93
94/* Calculates table index of a key. */
95#define HASHTABLE_INDEX(table, key) table->hash(key) % table->size
96
100hashtable_new(size_t size, HashFunc hash_func, EqualFunc compare_keys, FreeFunc free_key, FreeFunc free_value)
101{
102 assert(hash_func != NULL);
103 assert(compare_keys != NULL);
104 assert(size <= SIZE_MAX / sizeof(struct _Bucket *));
105
106 HashTable *table = (HashTable *)malloc(sizeof(HashTable));
107
108 if(!table)
109 {
110 perror("malloc()");
111 abort();
112 }
113
114 hashtable_init(table, size, hash_func, compare_keys, free_key, free_value);
115
116 return table;
117}
118
119void
120hashtable_init(HashTable *table, size_t size, HashFunc hash_func, EqualFunc compare_keys, FreeFunc free_key, FreeFunc free_value)
121{
122 assert(table != NULL);
123 assert(hash_func != NULL);
124 assert(compare_keys != NULL);
125 assert(size <= SIZE_MAX / sizeof(struct _Bucket *));
126
127 size_t table_size = HASHTABLE_INITIAL_SIZE;
128
129 if(size != HASHTABLE_AUTO_RESIZE)
130 {
131 table_size = size;
132 }
133
134 table->buckets = (struct _Bucket **)calloc(table_size, sizeof(struct _Bucket *));
135
136 if(!table->buckets)
137 {
138 perror("calloc()");
139 abort();
140 }
141
142 table->pool = (Pool *)memory_pool_new(sizeof(struct _Bucket), HASHTABLE_BUCKET_POOL_BLOCK_SIZE);
143
144 table->compare_keys = compare_keys;
145 table->free_key = free_key;
146 table->free_value = free_value;
147 table->size = table_size;
148
149 if(size == HASHTABLE_AUTO_RESIZE)
150 {
151 table->grow = true;
152 table->sizeptr = PRIMES;
153 }
154 else
155 {
156 table->grow = false;
157 table->sizeptr = NULL;
158 }
159
160 table->hash = hash_func;
161 table->count = 0;
162 table->pair.free_value = free_value;
163 table->pair.bucket = NULL;
164}
165
166void
168{
169 assert(table != NULL);
170
171 hashtable_free(table);
172 free(table);
173}
174
175static void
176_hashtable_free_memory(HashTable *table)
177{
178 assert(table != NULL);
179
180 if(table->free_key || table->free_value)
181 {
182 for(size_t i = 0; i < table->size; ++i)
183 {
184 struct _Bucket *iter;
185
186 if((iter = table->buckets[i]))
187 {
188 while(iter)
189 {
190 if(table->free_key)
191 {
192 table->free_key(iter->key);
193 }
194
195 if(table->free_value && iter->data)
196 {
197 table->free_value(iter->data);
198 }
199
200 iter = iter->next;
201 }
202 }
203 }
204 }
205}
206
207void
209{
210 assert(table != NULL);
211
212 _hashtable_free_memory(table);
214 free(table->buckets);
215}
216
217void
219{
220 assert(table != NULL);
221
222 table->count = 0;
223
224 _hashtable_free_memory(table);
225 memset(table->buckets, 0, sizeof(struct _Bucket *) * table->size);
226
228 table->pool = (Pool *)memory_pool_new(sizeof(struct _Bucket), HASHTABLE_BUCKET_POOL_BLOCK_SIZE);
229}
230
231static void
232_hashtable_resize(HashTable *table)
233{
234 assert(table != NULL);
235 assert(table->size > 0);
236
237 size_t old_size = table->size;
238
239 table->sizeptr++;
240 table->size = *table->sizeptr;
241
242 if(!table->size || table->size > SIZE_MAX / sizeof(struct _Bucket *))
243 {
244 fprintf(stderr, "%s(): integer overflow.\n", __func__);
245 abort();
246 }
247
248 struct _Bucket **buckets = table->buckets;
249
250 table->buckets = (struct _Bucket **)calloc(table->size, sizeof(struct _Bucket *));
251
252 if(!table->buckets)
253 {
254 perror("calloc()");
255 abort();
256 }
257
258 for(size_t i = 0; i < old_size; ++i)
259 {
260 struct _Bucket *iter = buckets[i];
261
262 while(iter)
263 {
264 struct _Bucket *next = iter->next;
265 size_t index = HASHTABLE_INDEX(table, iter->key);
266
267 if(table->buckets[index])
268 {
269 iter->next = table->buckets[index];
270 table->buckets[index] = iter;
271 }
272 else
273 {
274 iter->next = NULL;
275 table->buckets[index] = iter;
276 }
277
278 iter = next;
279 }
280 }
281
282 free(buckets);
283}
284
285static struct _Bucket *
286_hashtable_find_bucket(const HashTable *table, struct _Bucket *head, const void *key)
287{
288 assert(table != NULL);
289 assert(key != NULL);
290
291 struct _Bucket *iter = head;
292 struct _Bucket *item = NULL;
293
294 while(iter && !item)
295 {
296 if(table->compare_keys(key, iter->key))
297 {
298 item = iter;
299 }
300
301 iter = iter->next;
302 }
303
304 return item;
305}
306
308hashtable_set(HashTable *table, void *key, void *value, bool overwrite_key)
309{
311
312 assert(table != NULL);
313 assert(key != NULL);
314
315 if(table->grow && table->count > table->size)
316 {
317 _hashtable_resize(table);
318 }
319
320 size_t index = HASHTABLE_INDEX(table, key);
321 struct _Bucket *head = table->buckets[index];
322 struct _Bucket *item = _hashtable_find_bucket(table, head, key);
323
324 if(item)
325 {
327
328 if(overwrite_key)
329 {
330 if(table->free_key)
331 {
332 table->free_key(item->key);
333 }
334
335 item->key = key;
336 }
337
338 if(table->free_value && item->data)
339 {
340 table->free_value(item->data);
341 }
342
343 item->data = value;
344 }
345 else if(table->count < SIZE_MAX)
346 {
348
349 item = (struct _Bucket *)table->pool->alloc(table->pool);
350
351 item->key = key;
352 item->data = value;
353 table->buckets[index] = item;
354 item->next = head;
355
356 ++table->count;
357 }
358 else
359 {
360 fprintf(stderr, "%s(): integer overflow.\n", __func__);
361 }
362
363 return result;
364}
365
366void
367hashtable_remove(HashTable *table, const void *key)
368{
369 assert(table != NULL);
370 assert(key != NULL);
371
372 struct _Bucket *iter;
373 size_t index = HASHTABLE_INDEX(table, key);
374
375 if((iter = table->buckets[index]))
376 {
377 struct _Bucket *prev = NULL;
378 bool removed = false;
379
380 while(iter && !removed)
381 {
382 if(table->compare_keys(iter->key, key))
383 {
384 if(table->free_key)
385 {
386 table->free_key(iter->key);
387 }
388
389 if(table->free_value && iter->data)
390 {
391 table->free_value(iter->data);
392 }
393
394 if(prev)
395 {
396 prev->next = iter->next;
397 }
398 else
399 {
400 table->buckets[index] = iter->next;
401 }
402
403 table->pool->free(table->pool, iter);
404 --table->count;
405
406 removed = true;
407 }
408
409 prev = iter;
410 iter = iter->next;
411 }
412 }
413}
414
416hashtable_lookup(HashTable *table, const void *key)
417{
418 assert(table != NULL);
419 assert(key != NULL);
420
421 struct _Bucket *head = table->buckets[HASHTABLE_INDEX(table, key)];
422 struct _Bucket *item = _hashtable_find_bucket(table, head, key);
423
424 if(item)
425 {
426 table->pair.bucket = item;
427
428 return &table->pair;
429 }
430
431 return NULL;
432}
433
434void *
436{
437 assert(pair != NULL);
438 assert(pair->bucket != NULL);
439 assert(pair->bucket->key != NULL);
440
441 return pair->bucket->key;
442}
443
444void *
446{
447 assert(pair != NULL);
448 assert(pair->bucket != NULL);
449 assert(pair->bucket->key != NULL);
450
451 return pair->bucket->data;
452}
453
454void
456{
457 assert(pair != NULL);
458 assert(pair->bucket != NULL);
459 assert(pair->bucket->key != NULL);
460
461 if(pair->free_value && pair->bucket->data)
462 {
463 pair->free_value(pair->bucket->data);
464 }
465
466 pair->bucket->data = value;
467}
468
469bool
470hashtable_key_exists(const HashTable *table, const void *key)
471{
472 assert(table != NULL);
473 assert(key != NULL);
474
475 struct _Bucket *head = table->buckets[HASHTABLE_INDEX(table, key)];
476
477 return _hashtable_find_bucket(table, head, key) != NULL;
478}
479
480size_t
482{
483 assert(table != NULL);
484
485 return table->count;
486}
487
488void
490{
491 assert(table != NULL);
492
493 memset(iter, 0, sizeof(HashTableIter));
494 iter->table = table;
495}
496
497static bool
498_hashtable_iter_get_next_bucket(HashTableIter *iter)
499{
500 assert(iter != NULL);
501
502 iter->liter = NULL;
503
504 while(!iter->liter && iter->offset < iter->table->size)
505 {
506 iter->liter = iter->table->buckets[iter->offset++];
507 }
508
509 return iter->liter != NULL;
510}
511
512bool
514{
515 assert(iter != NULL);
516
517 bool success = false;
518
519 while(!success && !iter->finished)
520 {
521 if(iter->liter)
522 {
523 iter->liter = iter->liter->next;
524 success = iter->liter != NULL;
525 }
526 else
527 {
528 success = _hashtable_iter_get_next_bucket(iter);
529 iter->finished = !success;
530 }
531 }
532
533 return success;
534}
535
536void *
538{
539 assert(iter != NULL);
540
541 return iter->liter->key;
542}
543
544void *
546{
547 assert(iter != NULL);
548
549 return iter->liter->data;
550}
551
bool(* EqualFunc)(const void *a, const void *b)
Definition compare.h:33
void(* FreeFunc)(void *p)
Definition datatypes.h:33
uint32_t(* HashFunc)(const void *ptr)
Definition hash.h:28
void hashtable_clear(HashTable *table)
Definition hashtable.c:218
void * hashtable_pair_get_key(const HashTablePair *pair)
Definition hashtable.c:435
void * hashtable_pair_get_value(const HashTablePair *pair)
Definition hashtable.c:445
void hashtable_iter_init(const HashTable *table, HashTableIter *iter)
Definition hashtable.c:489
void * hashtable_iter_get_value(const HashTableIter *iter)
Definition hashtable.c:545
size_t hashtable_count(const HashTable *table)
Definition hashtable.c:481
void * hashtable_iter_get_key(const HashTableIter *iter)
Definition hashtable.c:537
HashTable * hashtable_new(size_t size, HashFunc hash_func, EqualFunc compare_keys, FreeFunc free_key, FreeFunc free_value)
Definition hashtable.c:100
bool hashtable_key_exists(const HashTable *table, const void *key)
Definition hashtable.c:470
void hashtable_remove(HashTable *table, const void *key)
Definition hashtable.c:367
void hashtable_pair_set_value(HashTablePair *pair, void *value)
Definition hashtable.c:455
bool hashtable_iter_next(HashTableIter *iter)
Definition hashtable.c:513
void hashtable_init(HashTable *table, size_t size, HashFunc hash_func, EqualFunc compare_keys, FreeFunc free_key, FreeFunc free_value)
Definition hashtable.c:120
void hashtable_free(HashTable *table)
Definition hashtable.c:208
void hashtable_destroy(HashTable *table)
Definition hashtable.c:167
HashTableInsertResult hashtable_set(HashTable *table, void *key, void *value, bool overwrite_key)
Definition hashtable.c:308
HashTablePair * hashtable_lookup(HashTable *table, const void *key)
Definition hashtable.c:416
Generic hashtable.
HashFunc hash
Definition hashtable.h:47
const HashTable * table
Definition hashtable.h:100
FreeFunc free_key
Definition hashtable.h:43
FreeFunc free_value
Definition hashtable.h:45
struct _Bucket * next
Definition hashtable.h:68
bool grow
Definition hashtable.h:53
struct HashTable::_Bucket ** buckets
Array containing pointers to buckets.
size_t size
Definition hashtable.h:49
size_t count
Definition hashtable.h:71
#define HASHTABLE_AUTO_RESIZE
Definition hashtable.h:32
struct HashTable::_HashTablePair pair
Last found key-value pair.
struct _Bucket * bucket
Definition hashtable.h:86
HashTableInsertResult
result of hashtable_set() method.
Definition hashtable.h:114
@ HASHTABLE_INSERT_RESULT_NEW
Definition hashtable.h:116
@ HASHTABLE_INSERT_RESULT_REPLACED
Definition hashtable.h:118
@ HASHTABLE_INSERT_RESULT_FAILED
Definition hashtable.h:120
struct _HashTablePair HashTablePair
Definition hashtable.h:91
EqualFunc compare_keys
Definition hashtable.h:41
size_t offset
Definition hashtable.h:102
struct _Bucket * liter
Definition hashtable.h:104
Pool * pool
Definition hashtable.h:73
const size_t * sizeptr
Definition hashtable.h:51
Table containing lists of buckets to create associations between keys and values.
Definition hashtable.h:39
Structure to iterate over the elements of a HashTable.
Definition hashtable.h:98
void memory_pool_destroy(MemoryPool *pool)
Definition pool.c:202
MemoryPool * memory_pool_new(size_t item_size, size_t block_size)
Definition pool.c:175
This memory pool allocates blocks of memory and grows automatically.
Definition pool.h:45
Allocate groups of equal-sized chunks of memory.
Definition pool.h:33
void(* free)(struct _Pool *pool, void *ptr)
Definition pool.h:37
void *(* alloc)(struct _Pool *pool)
Definition pool.h:35