5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
20 /* Implementation of collision resistant hash table. This is a hash table
21 that provides a reliable (what you add stays there, and duplicate keys
22 are allowed) with as fast reference to the key as possible. If duplicate
23 keys are a lot in the hash table the lookup gets slower of course.
24 However, this is reliable and no data is lost at any point. If you know
25 that you never have duplicate keys then this is as fast as any simple
29 #include "silcincludes.h"
30 #include "silchashtable.h"
32 /* Default size of the hash table (index to prime table) */
33 #define SILC_HASH_TABLE_SIZE 3
35 /* Produce the index by hashing the key */
36 #define SILC_HASH_TABLE_HASH \
37 (ht->hash(key, ht->hash_user_context) % primesize[ht->table_size])
38 #define SILC_HASH_TABLE_HASH_F(f, c) \
39 ((f)(key, (c)) % primesize[ht->table_size])
41 /* One entry in the hash table. Includes the key and the associated
42 context. The `next' pointer is non-NULL if two (or more) different
43 keys hashed to same value. The pointer is the pointer to the next
45 typedef struct SilcHashTableEntryStruct {
48 struct SilcHashTableEntryStruct *next;
49 } *SilcHashTableEntry;
52 struct SilcHashTableStruct {
53 SilcHashTableEntry *table;
56 SilcHashFunction hash;
57 SilcHashCompare compare;
58 SilcHashDestructor destructor;
59 void *hash_user_context;
60 void *compare_user_context;
61 void *destructor_user_context;
64 /* Prime sizes for the hash table. The size of the table will always
66 const uint32 primesize[42] =
68 1, 3, 5, 11, 17, 37, 67, 109, 131, 163, 257, 367, 521, 823, 1031,
69 1237, 2053, 2777, 4099, 6247, 8209, 14057, 16411, 21089, 32771, 47431,
70 65537, 106721, 131101, 262147, 360163, 524309, 810343, 1048583, 2097169,
71 4194319, 6153409, 8388617, 13845163, 16777259, 33554467, 67108879
74 /* Find appropriate size for the hash table. The size will be a prime. */
76 static uint32 silc_hash_table_primesize(uint32 size, uint32 *index)
80 for (i = 0; i < sizeof(primesize); i++)
81 if (primesize[i] >= size) {
87 return primesize[i - 1];
90 /* Internal routine to find entry in the hash table by `key'. Returns
91 the previous entry (if exists) as well. */
93 static inline SilcHashTableEntry *
94 silc_hash_table_find_internal(SilcHashTable ht, void *key,
95 SilcHashTableEntry *prev_entry)
97 SilcHashTableEntry *entry, prev = NULL;
99 entry = &ht->table[SILC_HASH_TABLE_HASH];
101 while (*entry && ht->compare((*entry)->key, key, ht->compare_user_context)
104 entry = &(*entry)->next;
107 while (*entry && (*entry)->key != key) {
109 entry = &(*entry)->next;
117 /* Internal routine to find entry in the hash table by `key' and `context'.
118 Returns the previous entry (if exists) as well. */
120 static inline SilcHashTableEntry *
121 silc_hash_table_find_internal_context(SilcHashTable ht, void *key,
123 SilcHashTableEntry *prev_entry)
125 SilcHashTableEntry *entry, prev = NULL;
127 entry = &ht->table[SILC_HASH_TABLE_HASH];
129 while (*entry && ht->compare((*entry)->key, key, ht->compare_user_context)
130 == FALSE && (*entry)->context != context) {
132 entry = &(*entry)->next;
135 while (*entry && (*entry)->key != key && (*entry)->context != context) {
137 entry = &(*entry)->next;
145 /* Internal routine to find entry in the hash table by `key'. */
147 static inline SilcHashTableEntry *
148 silc_hash_table_find_internal_simple(SilcHashTable ht, void *key,
149 SilcHashFunction hash,
150 void *hash_user_context,
151 SilcHashCompare compare,
152 void *compare_user_context)
154 SilcHashTableEntry *entry;
157 entry = &ht->table[SILC_HASH_TABLE_HASH_F(hash, hash_user_context)];
159 entry = &ht->table[SILC_HASH_TABLE_HASH];
161 while (*entry && !compare((*entry)->key, key, compare_user_context))
162 entry = &(*entry)->next;
163 } else if (ht->compare) {
164 while (*entry && !ht->compare((*entry)->key, key,
165 ht->compare_user_context))
166 entry = &(*entry)->next;
168 while (*entry && (*entry)->key != key)
169 entry = &(*entry)->next;
175 /* Internal routine to find all keys by `key'. This may return multiple
176 entries if multiple entries with same key exists. */
179 silc_hash_table_find_internal_all(SilcHashTable ht, void *key,
180 SilcHashTableEntry **entries,
183 SilcHashTableEntry *entry;
188 entry = &ht->table[SILC_HASH_TABLE_HASH];
191 if (ht->compare((*entry)->key, key, ht->compare_user_context)) {
192 *entries = silc_realloc(*entries, sizeof(**entries) * (*count + 1));
193 (*entries)[*count] = *entry;
196 entry = &(*entry)->next;
200 if ((*entry)->key == key) {
201 *entries = silc_realloc(*entries, sizeof(**entries) * (*count + 1));
202 (*entries)[*count] = *entry;
205 entry = &(*entry)->next;
209 return *entries ? TRUE : FALSE;
212 /* Internal routine to find all keys by `key'. This may return multiple
213 entries if multiple entries with same key exists. With specific
214 hash and comparison functions. */
217 silc_hash_table_find_internal_all_ext(SilcHashTable ht, void *key,
218 SilcHashTableEntry **entries,
220 SilcHashFunction hash,
221 void *hash_user_context,
222 SilcHashCompare compare,
223 void *compare_user_context)
225 SilcHashTableEntry *entry;
230 entry = &ht->table[SILC_HASH_TABLE_HASH];
233 if (compare((*entry)->key, key, compare_user_context)) {
234 *entries = silc_realloc(*entries, sizeof(**entries) * (*count + 1));
235 (*entries)[*count] = *entry;
238 entry = &(*entry)->next;
242 if ((*entry)->key == key) {
243 *entries = silc_realloc(*entries, sizeof(**entries) * (*count + 1));
244 (*entries)[*count] = *entry;
247 entry = &(*entry)->next;
251 return *entries ? TRUE : FALSE;
254 /* Internal routine to add new key to the hash table */
257 silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context,
258 SilcHashFunction hash,
259 void *hash_user_context)
261 SilcHashTableEntry *entry;
262 uint32 index = (hash ? SILC_HASH_TABLE_HASH :
263 SILC_HASH_TABLE_HASH_F(hash, hash_user_context));
265 entry = &ht->table[index];
267 /* The entry exists already. We have a collision, add it to the
268 list to avoid collision. */
269 SilcHashTableEntry e, tmp;
278 e->next = silc_calloc(1, sizeof(*e->next));
280 e->next->context = context;
284 *entry = silc_calloc(1, sizeof(**entry));
286 (*entry)->context = context;
291 /* Allocates new hash table and returns it. If the `table_size' is not
292 zero then the hash table size is the size provided. If zero then the
293 default size will be used. Note that if the `table_size' is provided
294 it should be a prime. The `hash', `compare' and `destructor' are
295 the hash function, the key comparison function and key and context
296 destructor function, respectively. The `hash' is mandatory, the others
299 SilcHashTable silc_hash_table_alloc(uint32 table_size,
300 SilcHashFunction hash,
301 void *hash_user_context,
302 SilcHashCompare compare,
303 void *compare_user_context,
304 SilcHashDestructor destructor,
305 void *destructor_user_context)
308 uint32 size_index = SILC_HASH_TABLE_SIZE;
313 ht = silc_calloc(1, sizeof(*ht));
314 ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size,
316 primesize[SILC_HASH_TABLE_SIZE],
318 ht->table_size = size_index;
320 ht->compare = compare;
321 ht->destructor = destructor;
322 ht->hash_user_context = hash_user_context;
323 ht->compare_user_context = compare_user_context;
324 ht->destructor_user_context = destructor_user_context;
329 /* Frees the hash table. The destructor function provided in the
330 silc_hash_table_alloc will be called for all keys in the hash table. */
332 void silc_hash_table_free(SilcHashTable ht)
334 SilcHashTableEntry e, tmp;
337 for (i = 0; i < primesize[ht->table_size]; i++) {
341 ht->destructor(e->key, e->context, ht->destructor_user_context);
348 silc_free(ht->table);
352 /* Returns the size of the hash table */
354 uint32 silc_hash_table_size(SilcHashTable ht)
356 return primesize[ht->table_size];
359 /* Returns the number of the entires in the hash table. If there is more
360 entries in the table thatn the size of the hash table calling the
361 silc_hash_table_rehash is recommended. */
363 uint32 silc_hash_table_count(SilcHashTable ht)
365 return ht->entry_count;
368 /* Adds new entry to the hash table. The `key' is hashed using the
369 hash function and the both `key' and `context' will be saved to the
370 hash table. This function quarantees that the entry is always added
371 to the hash table reliably (it is collision resistant). */
373 void silc_hash_table_add(SilcHashTable ht, void *key, void *context)
375 silc_hash_table_add_internal(ht, key, context, NULL, NULL);
378 /* Same as above but with specific hash function and user context. */
380 void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
381 SilcHashFunction hash, void *hash_user_context)
383 silc_hash_table_add_internal(ht, key, context, hash, hash_user_context);
386 /* Same as above but if the `key' already exists in the hash table
387 the old key and the old context will be replace with the `key' and
388 the `context. The destructor function will be called for the
389 replaced key and context. */
391 void silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
393 SilcHashTableEntry *entry;
394 uint32 index = SILC_HASH_TABLE_HASH;
396 entry = &ht->table[index];
398 /* The entry exists already. We have a collision, replace the old
401 ht->destructor((*entry)->key, (*entry)->context,
402 ht->destructor_user_context);
405 *entry = silc_calloc(1, sizeof(**entry));
410 (*entry)->context = context;
413 /* Removes the entry from the hash table by the provided `key'. This will
414 call the destructor funtion for the found entry. Return TRUE if the
415 entry was removed successfully and FALSE otherwise. */
417 bool silc_hash_table_del(SilcHashTable ht, void *key)
419 SilcHashTableEntry *entry, prev, e;
421 entry = silc_hash_table_find_internal(ht, key, &prev);
427 if (!prev && e->next)
429 if (!prev && e->next == NULL)
434 prev->next = e->next;
437 ht->destructor(e->key, e->context, ht->destructor_user_context);
445 /* Same as above but verifies that the context associated with the `key'
446 matches the `context'. This is handy to use with hash tables that may
447 have duplicate keys. In that case the `context' may be used to check
448 whether the correct entry is being deleted. */
450 bool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
453 SilcHashTableEntry *entry, prev, e;
455 entry = silc_hash_table_find_internal_context(ht, key, context, &prev);
461 if (!prev && e->next)
463 if (!prev && e->next == NULL)
468 prev->next = e->next;
471 ht->destructor(e->key, e->context, ht->destructor_user_context);
479 /* Finds the entry in the hash table by the provided `key' as fast as
480 possible. Return TRUE if the entry was found and FALSE otherwise.
481 The found entry is returned to the `ret_key' and `ret_context',
482 respectively. If the `ret_key and `ret_context' are NULL then this
483 maybe used only to check whether given key exists in the table. */
485 bool silc_hash_table_find(SilcHashTable ht, void *key,
486 void **ret_key, void **ret_context)
488 SilcHashTableEntry *entry;
490 entry = silc_hash_table_find_internal_simple(ht, key, NULL, NULL,
496 *ret_key = (*entry)->key;
498 *ret_context = (*entry)->context;
503 /* As the hash table is collision resistant it is possible to save duplicate
504 keys to the hash table. This function can be used to return all keys
505 and contexts from the hash table that are found using the `key'. */
507 bool silc_hash_table_find_all(SilcHashTable ht, void *key,
508 void ***ret_keys, void ***ret_contexts,
509 unsigned int *ret_count)
511 SilcHashTableEntry *entries;
515 if (!silc_hash_table_find_internal_all(ht, key, &entries, &count))
519 *ret_keys = silc_calloc(count, sizeof(**ret_keys));
521 *ret_contexts = silc_calloc(count, sizeof(**ret_contexts));
525 if (ret_keys && ret_count) {
526 for (i = 0; i < count; i++) {
527 (*ret_keys)[i] = entries[i]->key;
528 (*ret_contexts)[i] = entries[i]->context;
530 } else if (ret_keys && !ret_contexts) {
531 for (i = 0; i < count; i++)
532 (*ret_keys)[i] = entries[i]->key;
533 } else if (!ret_keys && ret_contexts) {
534 for (i = 0; i < count; i++)
535 (*ret_contexts)[i] = entries[i]->context;
543 /* Same as above but with specified hash and comparison functions. */
545 bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
546 void **ret_key, void **ret_context,
547 SilcHashFunction hash,
548 void *hash_user_context,
549 SilcHashCompare compare,
550 void *compare_user_context)
552 SilcHashTableEntry *entry;
554 entry = silc_hash_table_find_internal_simple(ht, key,
555 hash, hash_user_context,
556 compare, compare_user_context);
561 *ret_key = (*entry)->key;
563 *ret_context = (*entry)->context;
568 /* Same as above but with specified hash and comparison functions. */
570 bool silc_hash_table_find_all_ext(SilcHashTable ht, void *key,
571 void ***ret_keys, void ***ret_contexts,
572 unsigned int *ret_count,
573 SilcHashFunction hash,
574 void *hash_user_context,
575 SilcHashCompare compare,
576 void *compare_user_context)
578 SilcHashTableEntry *entries;
582 if (!silc_hash_table_find_internal_all_ext(ht, key, &entries, &count,
583 hash, hash_user_context,
584 compare, compare_user_context))
588 *ret_keys = silc_calloc(count, sizeof(**ret_keys));
590 *ret_contexts = silc_calloc(count, sizeof(**ret_contexts));
594 if (ret_keys && ret_count) {
595 for (i = 0; i < count; i++) {
596 (*ret_keys)[i] = entries[i]->key;
597 (*ret_contexts)[i] = entries[i]->context;
599 } else if (ret_keys && !ret_contexts) {
600 for (i = 0; i < count; i++)
601 (*ret_keys)[i] = entries[i]->key;
602 } else if (!ret_keys && ret_contexts) {
603 for (i = 0; i < count; i++)
604 (*ret_contexts)[i] = entries[i]->context;
612 /* Traverse all entrys in the hash table and call the `foreach' for
613 every entry with the `user_context' context. */
615 void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
618 SilcHashTableEntry e, tmp;
624 for (i = 0; i < primesize[ht->table_size]; i++) {
627 /* Entry may become invalid inside the `foreach' */
629 foreach(e->key, e->context, user_context);
635 /* Rehashs the hash table. The size of the new hash table is provided
636 as `new_size'. If the `new_size' is zero then this routine will make
637 the new table of a suitable size. Note that this operation may be
640 void silc_hash_table_rehash(SilcHashTable ht, uint32 new_size)
643 SilcHashTableEntry *table, e, tmp;
644 uint32 table_size, size_index;
646 /* Take old hash table */
648 table_size = ht->table_size;
650 /* Allocate new table */
651 ht->table = silc_calloc(new_size ? silc_hash_table_primesize(new_size,
653 silc_hash_table_primesize(ht->entry_count,
656 ht->table_size = size_index;
659 for (i = 0; i < primesize[table_size]; i++) {
662 silc_hash_table_add(ht, e->key, e->context);
666 /* Remove old entry */
671 /* Remove old table */