5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2002 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; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
19 /* Implementation of collision resistant hash table. This is a hash table
20 that provides a reliable (what you add stays there, and duplicate keys
21 are allowed) with as fast reference to the key as possible. If duplicate
22 keys are a lot in the hash table the lookup gets slower of course.
23 However, this is reliable and no data is lost at any point. If you know
24 that you never have duplicate keys then this is as fast as any simple
28 #include "silcincludes.h"
29 #include "silchashtable.h"
31 /* Define to 1 if you want hash table debug enabled */
32 #define SILC_HASH_TABLE_DEBUG 0
34 #if SILC_HASH_TABLE_DEBUG == 1
35 #define SILC_HT_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
37 #define SILC_HT_DEBUG(fmt)
40 /* Default size of the hash table (index to prime table) */
41 #define SILC_HASH_TABLE_SIZE 3
43 /* Produce the index by hashing the key */
44 #define SILC_HASH_TABLE_HASH(f, c) \
45 ((f)(key, (c)) % primesize[ht->table_size])
47 /* Check whether need to rehash */
48 #define SILC_HASH_REHASH_INC \
49 (ht->auto_rehash && (ht->entry_count / 2) > primesize[ht->table_size])
50 #define SILC_HASH_REHASH_DEC \
51 (ht->auto_rehash && (ht->entry_count * 2) < primesize[ht->table_size] && \
52 ht->entry_count > primesize[SILC_HASH_TABLE_SIZE])
54 /* One entry in the hash table. Includes the key and the associated
55 context. The `next' pointer is non-NULL if two (or more) different
56 keys hashed to same value. The pointer is the pointer to the next
58 typedef struct SilcHashTableEntryStruct {
61 struct SilcHashTableEntryStruct *next;
62 } *SilcHashTableEntry;
65 struct SilcHashTableStruct {
66 SilcHashTableEntry *table;
67 SilcUInt32 table_size;
68 SilcUInt32 entry_count;
69 SilcHashFunction hash;
70 SilcHashCompare compare;
71 SilcHashDestructor destructor;
72 void *hash_user_context;
73 void *compare_user_context;
74 void *destructor_user_context;
78 /* Prime sizes for the hash table. The size of the table will always
80 const SilcUInt32 primesize[42] =
82 1, 3, 5, 11, 17, 37, 67, 109, 131, 163, 257, 367, 521, 823, 1031,
83 1237, 2053, 2777, 4099, 6247, 8209, 14057, 16411, 21089, 32771, 47431,
84 65537, 106721, 131101, 262147, 360163, 524309, 810343, 1048583, 2097169,
85 4194319, 6153409, 8388617, 13845163, 16777259, 33554467, 67108879
88 /* Find appropriate size for the hash table. The size will be a prime. */
90 static SilcUInt32 silc_hash_table_primesize(SilcUInt32 size, SilcUInt32 *index)
94 for (i = 0; i < sizeof(primesize); i++)
95 if (primesize[i] >= size) {
97 SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index]));
102 SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index]));
103 return primesize[i - 1];
106 /* Internal routine to find entry in the hash table by `key'. Returns
107 the previous entry (if exists) as well. */
109 static inline SilcHashTableEntry *
110 silc_hash_table_find_internal(SilcHashTable ht, void *key,
111 SilcHashTableEntry *prev_entry,
112 SilcHashFunction hash, void *hash_user_context,
113 SilcHashCompare compare,
114 void *compare_user_context)
116 SilcHashTableEntry *entry, prev = NULL;
117 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
119 SILC_HT_DEBUG(("index %d key %p", i, key));
121 entry = &ht->table[i];
123 while (*entry && !compare((*entry)->key, key, compare_user_context)) {
125 entry = &(*entry)->next;
128 while (*entry && (*entry)->key != key) {
130 entry = &(*entry)->next;
138 /* Internal routine to find entry in the hash table by `key' and `context'.
139 Returns the previous entry (if exists) as well to `prev_entry'. */
141 static inline SilcHashTableEntry *
142 silc_hash_table_find_internal_context(SilcHashTable ht, void *key,
144 SilcHashTableEntry *prev_entry,
145 SilcHashFunction hash,
146 void *hash_user_context,
147 SilcHashCompare compare,
148 void *compare_user_context)
150 SilcHashTableEntry *entry, prev = NULL;
151 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
153 SILC_HT_DEBUG(("index %d key %p context %p", i, key, context));
155 entry = &ht->table[i];
158 if (compare((*entry)->key, key, compare_user_context) &&
159 (*entry)->context == context)
162 entry = &(*entry)->next;
166 if ((*entry)->key == key && (*entry)->context == context)
169 entry = &(*entry)->next;
178 /* Internal routine to find entry in the hash table by `key'. */
180 static inline SilcHashTableEntry *
181 silc_hash_table_find_internal_simple(SilcHashTable ht, void *key,
182 SilcHashFunction hash,
183 void *hash_user_context,
184 SilcHashCompare compare,
185 void *compare_user_context)
187 SilcHashTableEntry *entry;
188 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
190 SILC_HT_DEBUG(("index %d key %p", i, key));
192 entry = &ht->table[i];
194 while (*entry && !compare((*entry)->key, key, compare_user_context))
195 entry = &(*entry)->next;
197 while (*entry && (*entry)->key != key)
198 entry = &(*entry)->next;
204 /* Internal routine to find all keys by `key'. This may return multiple
205 entries if multiple entries with same key exists. With specific
206 hash and comparison functions. */
209 silc_hash_table_find_internal_all(SilcHashTable ht, void *key,
210 SilcHashFunction hash,
211 void *hash_user_context,
212 SilcHashCompare compare,
213 void *compare_user_context,
214 SilcHashForeach foreach,
215 void *foreach_user_context)
217 SilcHashTableEntry *entry, *tmp;
218 bool auto_rehash, found = FALSE;
219 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
221 SILC_HT_DEBUG(("index %d key %p", i, key));
223 /* Disallow auto rehashing while going through the table since we call
224 the `foreach' function which could alter the table. */
225 auto_rehash = ht->auto_rehash;
226 ht->auto_rehash = FALSE;
228 entry = &ht->table[i];
231 if (compare((*entry)->key, key, compare_user_context)) {
232 tmp = &(*entry)->next;
233 foreach((*entry)->key, (*entry)->context, foreach_user_context);
238 entry = &(*entry)->next;
242 if ((*entry)->key == key) {
243 tmp = &(*entry)->next;
244 foreach((*entry)->key, (*entry)->context, foreach_user_context);
249 entry = &(*entry)->next;
253 /* If nothing was found call with NULL context the callback */
255 foreach(key, NULL, foreach_user_context);
257 ht->auto_rehash = auto_rehash;
260 /* Internal routine to add new key to the hash table */
263 silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context,
264 SilcHashFunction hash,
265 void *hash_user_context)
267 SilcHashTableEntry *entry;
268 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
270 SILC_HT_DEBUG(("index %d key %p", i, key));
272 entry = &ht->table[i];
274 /* The entry exists already. We have a collision, add it to the
275 list to avoid collision. */
276 SilcHashTableEntry e, tmp;
285 SILC_HT_DEBUG(("Collision; adding new key to list"));
287 e->next = silc_calloc(1, sizeof(*e->next));
289 e->next->context = context;
293 SILC_HT_DEBUG(("New key"));
294 *entry = silc_calloc(1, sizeof(**entry));
296 (*entry)->context = context;
300 if (SILC_HASH_REHASH_INC)
301 silc_hash_table_rehash(ht, 0);
304 /* Internal routine to replace old key with new one (if it exists) */
307 silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context,
308 SilcHashFunction hash,
309 void *hash_user_context)
311 SilcHashTableEntry *entry;
312 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
314 SILC_HT_DEBUG(("index %d key %p", i, key));
316 entry = &ht->table[i];
318 /* The entry exists already. We have a collision, replace the old
321 ht->destructor((*entry)->key, (*entry)->context,
322 ht->destructor_user_context);
325 *entry = silc_calloc(1, sizeof(**entry));
330 (*entry)->context = context;
332 if (SILC_HASH_REHASH_INC)
333 silc_hash_table_rehash(ht, 0);
336 /* Allocates new hash table and returns it. If the `table_size' is not
337 zero then the hash table size is the size provided. If zero then the
338 default size will be used. Note that if the `table_size' is provided
339 it should be a prime. The `hash', `compare' and `destructor' are
340 the hash function, the key comparison function and key and context
341 destructor function, respectively. The `hash' is mandatory, the others
344 SilcHashTable silc_hash_table_alloc(SilcUInt32 table_size,
345 SilcHashFunction hash,
346 void *hash_user_context,
347 SilcHashCompare compare,
348 void *compare_user_context,
349 SilcHashDestructor destructor,
350 void *destructor_user_context,
354 SilcUInt32 size_index = SILC_HASH_TABLE_SIZE;
359 ht = silc_calloc(1, sizeof(*ht));
360 ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size,
362 primesize[SILC_HASH_TABLE_SIZE],
364 ht->table_size = size_index;
366 ht->compare = compare;
367 ht->destructor = destructor;
368 ht->hash_user_context = hash_user_context;
369 ht->compare_user_context = compare_user_context;
370 ht->destructor_user_context = destructor_user_context;
371 ht->auto_rehash = auto_rehash;
376 /* Frees the hash table. The destructor function provided in the
377 silc_hash_table_alloc will be called for all keys in the hash table. */
379 void silc_hash_table_free(SilcHashTable ht)
381 SilcHashTableEntry e, tmp;
384 for (i = 0; i < primesize[ht->table_size]; i++) {
388 ht->destructor(e->key, e->context, ht->destructor_user_context);
395 silc_free(ht->table);
399 /* Returns the size of the hash table */
401 SilcUInt32 silc_hash_table_size(SilcHashTable ht)
403 return primesize[ht->table_size];
406 /* Returns the number of the entires in the hash table. If there is more
407 entries in the table thatn the size of the hash table calling the
408 silc_hash_table_rehash is recommended. */
410 SilcUInt32 silc_hash_table_count(SilcHashTable ht)
412 return ht->entry_count;
415 /* Adds new entry to the hash table. The `key' is hashed using the
416 hash function and the both `key' and `context' will be saved to the
417 hash table. This function quarantees that the entry is always added
418 to the hash table reliably (it is collision resistant). */
420 void silc_hash_table_add(SilcHashTable ht, void *key, void *context)
422 silc_hash_table_add_internal(ht, key, context, ht->hash,
423 ht->hash_user_context);
426 /* Same as above but with specific hash function and user context. */
428 void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
429 SilcHashFunction hash, void *hash_user_context)
431 silc_hash_table_add_internal(ht, key, context, hash, hash_user_context);
434 /* Same as above but if the `key' already exists in the hash table
435 the old key and the old context will be replace with the `key' and
436 the `context. The destructor function will be called for the
437 replaced key and context. */
439 void silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
441 silc_hash_table_replace_internal(ht, key, context, ht->hash,
442 ht->hash_user_context);
445 /* Same as above but with specific hash function. */
447 void silc_hash_table_replace_ext(SilcHashTable ht, void *key, void *context,
448 SilcHashFunction hash,
449 void *hash_user_context)
451 silc_hash_table_replace_internal(ht, key, context, hash, hash_user_context);
454 /* Removes the entry from the hash table by the provided `key'. This will
455 call the destructor funtion for the found entry. Return TRUE if the
456 entry was removed successfully and FALSE otherwise. */
458 bool silc_hash_table_del(SilcHashTable ht, void *key)
460 SilcHashTableEntry *entry, prev, e;
462 entry = silc_hash_table_find_internal(ht, key, &prev,
463 ht->hash, ht->hash_user_context,
464 ht->compare, ht->compare_user_context);
470 if (!prev && e->next)
472 if (!prev && e->next == NULL)
477 prev->next = e->next;
480 ht->destructor(e->key, e->context, ht->destructor_user_context);
485 if (SILC_HASH_REHASH_DEC)
486 silc_hash_table_rehash(ht, 0);
491 /* Same as above but with specific hash and compare functions. */
493 bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
494 SilcHashFunction hash,
495 void *hash_user_context,
496 SilcHashCompare compare,
497 void *compare_user_context,
498 SilcHashDestructor destructor,
499 void *destructor_user_context)
501 SilcHashTableEntry *entry, prev, e;
503 entry = silc_hash_table_find_internal(ht, key, &prev,
504 hash ? hash : ht->hash,
505 hash_user_context ? hash_user_context :
506 ht->hash_user_context,
507 compare ? compare : ht->compare,
508 compare_user_context ?
509 compare_user_context :
510 ht->compare_user_context);
516 if (!prev && e->next)
518 if (!prev && e->next == NULL)
523 prev->next = e->next;
526 destructor(e->key, e->context, destructor_user_context);
529 ht->destructor(e->key, e->context, ht->destructor_user_context);
535 if (SILC_HASH_REHASH_DEC)
536 silc_hash_table_rehash(ht, 0);
541 /* Same as above but verifies that the context associated with the `key'
542 matches the `context'. This is handy to use with hash tables that may
543 have duplicate keys. In that case the `context' may be used to check
544 whether the correct entry is being deleted. */
546 bool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
549 SilcHashTableEntry *entry, prev, e;
551 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
553 ht->hash_user_context,
555 ht->compare_user_context);
561 if (!prev && e->next)
563 if (!prev && e->next == NULL)
568 prev->next = e->next;
571 ht->destructor(e->key, e->context, ht->destructor_user_context);
576 if (SILC_HASH_REHASH_DEC)
577 silc_hash_table_rehash(ht, 0);
582 /* Same as above but with specific hash and compare functions. */
584 bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key,
586 SilcHashFunction hash,
587 void *hash_user_context,
588 SilcHashCompare compare,
589 void *compare_user_context,
590 SilcHashDestructor destructor,
591 void *destructor_user_context)
593 SilcHashTableEntry *entry, prev, e;
595 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
596 hash ? hash : ht->hash,
599 ht->hash_user_context,
601 compare : ht->compare,
602 compare_user_context ?
603 compare_user_context :
604 ht->compare_user_context);
610 if (!prev && e->next)
612 if (!prev && e->next == NULL)
617 prev->next = e->next;
620 destructor(e->key, e->context, destructor_user_context);
623 ht->destructor(e->key, e->context, ht->destructor_user_context);
629 if (SILC_HASH_REHASH_DEC)
630 silc_hash_table_rehash(ht, 0);
635 /* Finds the entry in the hash table by the provided `key' as fast as
636 possible. Return TRUE if the entry was found and FALSE otherwise.
637 The found entry is returned to the `ret_key' and `ret_context',
638 respectively. If the `ret_key and `ret_context' are NULL then this
639 maybe used only to check whether given key exists in the table. */
641 bool silc_hash_table_find(SilcHashTable ht, void *key,
642 void **ret_key, void **ret_context)
644 SilcHashTableEntry *entry;
646 entry = silc_hash_table_find_internal_simple(ht, key, ht->hash,
647 ht->hash_user_context,
649 ht->compare_user_context);
654 *ret_key = (*entry)->key;
656 *ret_context = (*entry)->context;
661 /* Same as above but with specified hash and comparison functions. */
663 bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
664 void **ret_key, void **ret_context,
665 SilcHashFunction hash,
666 void *hash_user_context,
667 SilcHashCompare compare,
668 void *compare_user_context)
670 SilcHashTableEntry *entry;
672 entry = silc_hash_table_find_internal_simple(ht, key,
673 hash ? hash : ht->hash,
676 ht->hash_user_context,
679 compare_user_context ?
680 compare_user_context :
681 ht->compare_user_context);
686 *ret_key = (*entry)->key;
688 *ret_context = (*entry)->context;
693 /* Same as silc_hash_table_find but finds with specific context. */
695 bool silc_hash_table_find_by_context(SilcHashTable ht, void *key,
696 void *context, void **ret_key)
698 SilcHashTableEntry *entry;
700 entry = silc_hash_table_find_internal_context(ht, key, context, NULL,
702 ht->hash_user_context,
704 ht->compare_user_context);
705 if (!entry || !(*entry))
709 *ret_key = (*entry)->key;
714 /* As the hash table is collision resistant it is possible to save duplicate
715 keys to the hash table. This function can be used to find all keys
716 and contexts from the hash table that are found using the `key'. The
717 `foreach' is called for every found key. */
719 void silc_hash_table_find_foreach(SilcHashTable ht, void *key,
720 SilcHashForeach foreach, void *user_context)
722 silc_hash_table_find_internal_all(ht, key, ht->hash, ht->hash_user_context,
723 ht->compare, ht->compare_user_context,
724 foreach, user_context);
727 /* Same as above but with specific hash and comparison functions. */
729 void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key,
730 SilcHashFunction hash,
731 void *hash_user_context,
732 SilcHashCompare compare,
733 void *compare_user_context,
734 SilcHashForeach foreach,
735 void *foreach_user_context)
737 silc_hash_table_find_internal_all(ht, key,
738 hash ? hash : ht->hash,
741 ht->hash_user_context,
744 compare_user_context ?
745 compare_user_context :
746 ht->compare_user_context,
747 foreach, foreach_user_context);
750 /* Traverse all entrys in the hash table and call the `foreach' for
751 every entry with the `user_context' context. */
753 void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
756 SilcHashTableEntry e, tmp;
763 auto_rehash = ht->auto_rehash;
764 ht->auto_rehash = FALSE;
765 for (i = 0; i < primesize[ht->table_size]; i++) {
768 /* Entry may become invalid inside the `foreach' */
770 foreach(e->key, e->context, user_context);
774 ht->auto_rehash = auto_rehash;
777 /* Rehashs the hash table. The size of the new hash table is provided
778 as `new_size'. If the `new_size' is zero then this routine will make
779 the new table of a suitable size. Note that this operation may be
782 void silc_hash_table_rehash(SilcHashTable ht, SilcUInt32 new_size)
785 SilcHashTableEntry *table, e, tmp;
786 SilcUInt32 table_size, size_index;
788 SILC_HT_DEBUG(("Start"));
791 silc_hash_table_primesize(new_size, &size_index);
793 silc_hash_table_primesize(ht->entry_count, &size_index);
795 if (size_index == ht->table_size)
798 SILC_HT_DEBUG(("Rehashing"));
800 /* Take old hash table */
802 table_size = ht->table_size;
804 /* Allocate new table */
805 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
806 ht->table_size = size_index;
810 for (i = 0; i < primesize[table_size]; i++) {
813 silc_hash_table_add(ht, e->key, e->context);
817 /* Remove old entry */
822 /* Remove old table */
826 /* Same as above but with specific hash function. */
828 void silc_hash_table_rehash_ext(SilcHashTable ht, SilcUInt32 new_size,
829 SilcHashFunction hash,
830 void *hash_user_context)
833 SilcHashTableEntry *table, e, tmp;
834 SilcUInt32 table_size, size_index;
836 SILC_HT_DEBUG(("Start"));
839 silc_hash_table_primesize(new_size, &size_index);
841 silc_hash_table_primesize(ht->entry_count, &size_index);
843 if (size_index == ht->table_size)
846 SILC_HT_DEBUG(("Rehashing"));
848 /* Take old hash table */
850 table_size = ht->table_size;
852 /* Allocate new table */
853 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
854 ht->table_size = size_index;
858 for (i = 0; i < primesize[table_size]; i++) {
861 silc_hash_table_add_ext(ht, e->key, e->context, hash,
866 /* Remove old entry */
871 /* Remove old table */
875 /* Prepares the `htl' list structure sent as argument to be used in the
876 hash table traversing with the silc_hash_table_get. Usage:
877 SilcHashTableList htl; silc_hash_table_list(ht, &htl); */
879 void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl)
884 htl->auto_rehash = ht->auto_rehash;
886 /* Disallow rehashing of the table while traversing the table */
887 ht->auto_rehash = FALSE;
890 /* Resets the `htl' SilcHashTableList. */
892 void silc_hash_table_list_reset(SilcHashTableList *htl)
894 /* Set back the original auto rehash value to the table */
895 htl->ht->auto_rehash = htl->auto_rehash;
898 /* Returns always the next entry in the hash table into the `key' and
899 `context' and TRUE. If this returns FALSE then there are no anymore
900 any entrys. Usage: while (silc_hash_table_get(&htl, &key, &context)) */
902 bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context)
904 SilcHashTableEntry entry = (SilcHashTableEntry)htl->entry;
906 if (!htl->ht->entry_count)
909 while (!entry && htl->index < primesize[htl->ht->table_size]) {
910 entry = htl->ht->table[htl->index];
917 htl->entry = entry->next;
922 *context = entry->context;