5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2003 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;
75 unsigned int auto_rehash : 1;
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) / sizeof(primesize[0]); 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 e, 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;
232 if (compare(e->key, key, compare_user_context)) {
234 foreach(e->key, e->context, foreach_user_context);
243 foreach(e->key, e->context, foreach_user_context);
249 /* If nothing was found call with NULL context the callback */
251 foreach(key, NULL, foreach_user_context);
253 ht->auto_rehash = auto_rehash;
256 /* Internal routine to add new key to the hash table */
259 silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context,
260 SilcHashFunction hash,
261 void *hash_user_context)
263 SilcHashTableEntry *entry;
264 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
266 SILC_HT_DEBUG(("index %d key %p", i, key));
268 entry = &ht->table[i];
270 /* The entry exists already. We have a collision, add it to the
271 list to avoid collision. */
272 SilcHashTableEntry e, tmp;
281 SILC_HT_DEBUG(("Collision; adding new key to list"));
283 e->next = silc_calloc(1, sizeof(*e->next));
287 e->next->context = context;
291 SILC_HT_DEBUG(("New key"));
292 *entry = silc_calloc(1, sizeof(**entry));
296 (*entry)->context = context;
300 if (SILC_HASH_REHASH_INC)
301 silc_hash_table_rehash(ht, 0);
306 /* Internal routine to replace old key with new one (if it exists) */
309 silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context,
310 SilcHashFunction hash,
311 void *hash_user_context)
313 SilcHashTableEntry *entry;
314 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
316 SILC_HT_DEBUG(("index %d key %p", i, key));
318 entry = &ht->table[i];
320 /* The entry exists already. We have a collision, replace the old
323 ht->destructor((*entry)->key, (*entry)->context,
324 ht->destructor_user_context);
327 *entry = silc_calloc(1, sizeof(**entry));
334 (*entry)->context = context;
336 if (SILC_HASH_REHASH_INC)
337 silc_hash_table_rehash(ht, 0);
342 /* Allocates new hash table and returns it. If the `table_size' is not
343 zero then the hash table size is the size provided. If zero then the
344 default size will be used. Note that if the `table_size' is provided
345 it should be a prime. The `hash', `compare' and `destructor' are
346 the hash function, the key comparison function and key and context
347 destructor function, respectively. The `hash' is mandatory, the others
350 SilcHashTable silc_hash_table_alloc(SilcUInt32 table_size,
351 SilcHashFunction hash,
352 void *hash_user_context,
353 SilcHashCompare compare,
354 void *compare_user_context,
355 SilcHashDestructor destructor,
356 void *destructor_user_context,
360 SilcUInt32 size_index = SILC_HASH_TABLE_SIZE;
365 ht = silc_calloc(1, sizeof(*ht));
368 ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size,
370 primesize[SILC_HASH_TABLE_SIZE],
374 ht->table_size = size_index;
376 ht->compare = compare;
377 ht->destructor = destructor;
378 ht->hash_user_context = hash_user_context;
379 ht->compare_user_context = compare_user_context;
380 ht->destructor_user_context = destructor_user_context;
381 ht->auto_rehash = auto_rehash;
386 /* Frees the hash table. The destructor function provided in the
387 silc_hash_table_alloc will be called for all keys in the hash table. */
389 void silc_hash_table_free(SilcHashTable ht)
391 SilcHashTableEntry e, tmp;
394 for (i = 0; i < primesize[ht->table_size]; i++) {
398 ht->destructor(e->key, e->context, ht->destructor_user_context);
405 silc_free(ht->table);
409 /* Returns the size of the hash table */
411 SilcUInt32 silc_hash_table_size(SilcHashTable ht)
413 return primesize[ht->table_size];
416 /* Returns the number of the entires in the hash table. If there is more
417 entries in the table thatn the size of the hash table calling the
418 silc_hash_table_rehash is recommended. */
420 SilcUInt32 silc_hash_table_count(SilcHashTable ht)
422 return ht->entry_count;
425 /* Adds new entry to the hash table. The `key' is hashed using the
426 hash function and the both `key' and `context' will be saved to the
427 hash table. This function quarantees that the entry is always added
428 to the hash table reliably (it is collision resistant). */
430 void silc_hash_table_add(SilcHashTable ht, void *key, void *context)
432 silc_hash_table_add_internal(ht, key, context, ht->hash,
433 ht->hash_user_context);
436 /* Same as above but with specific hash function and user context. */
438 void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
439 SilcHashFunction hash, void *hash_user_context)
441 silc_hash_table_add_internal(ht, key, context, hash, hash_user_context);
444 /* Same as above but if the `key' already exists in the hash table
445 the old key and the old context will be replace with the `key' and
446 the `context. The destructor function will be called for the
447 replaced key and context. */
449 void silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
451 silc_hash_table_replace_internal(ht, key, context, ht->hash,
452 ht->hash_user_context);
455 /* Same as above but with specific hash function. */
457 void silc_hash_table_replace_ext(SilcHashTable ht, void *key, void *context,
458 SilcHashFunction hash,
459 void *hash_user_context)
461 silc_hash_table_replace_internal(ht, key, context, hash, hash_user_context);
464 /* Removes the entry from the hash table by the provided `key'. This will
465 call the destructor funtion for the found entry. Return TRUE if the
466 entry was removed successfully and FALSE otherwise. */
468 bool silc_hash_table_del(SilcHashTable ht, void *key)
470 SilcHashTableEntry *entry, prev, e;
472 entry = silc_hash_table_find_internal(ht, key, &prev,
473 ht->hash, ht->hash_user_context,
474 ht->compare, ht->compare_user_context);
480 if (!prev && e->next)
482 if (!prev && e->next == NULL)
487 prev->next = e->next;
490 ht->destructor(e->key, e->context, ht->destructor_user_context);
495 if (SILC_HASH_REHASH_DEC)
496 silc_hash_table_rehash(ht, 0);
501 /* Same as above but with specific hash and compare functions. */
503 bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
504 SilcHashFunction hash,
505 void *hash_user_context,
506 SilcHashCompare compare,
507 void *compare_user_context,
508 SilcHashDestructor destructor,
509 void *destructor_user_context)
511 SilcHashTableEntry *entry, prev, e;
513 entry = silc_hash_table_find_internal(ht, key, &prev,
514 hash ? hash : ht->hash,
515 hash_user_context ? hash_user_context :
516 ht->hash_user_context,
517 compare ? compare : ht->compare,
518 compare_user_context ?
519 compare_user_context :
520 ht->compare_user_context);
526 if (!prev && e->next)
528 if (!prev && e->next == NULL)
533 prev->next = e->next;
536 destructor(e->key, e->context, destructor_user_context);
539 ht->destructor(e->key, e->context, ht->destructor_user_context);
545 if (SILC_HASH_REHASH_DEC)
546 silc_hash_table_rehash(ht, 0);
551 /* Same as above but verifies that the context associated with the `key'
552 matches the `context'. This is handy to use with hash tables that may
553 have duplicate keys. In that case the `context' may be used to check
554 whether the correct entry is being deleted. */
556 bool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
559 SilcHashTableEntry *entry, prev, e;
561 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
563 ht->hash_user_context,
565 ht->compare_user_context);
571 if (!prev && e->next)
573 if (!prev && e->next == NULL)
578 prev->next = e->next;
581 ht->destructor(e->key, e->context, ht->destructor_user_context);
586 if (SILC_HASH_REHASH_DEC)
587 silc_hash_table_rehash(ht, 0);
592 /* Same as above but with specific hash and compare functions. */
594 bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key,
596 SilcHashFunction hash,
597 void *hash_user_context,
598 SilcHashCompare compare,
599 void *compare_user_context,
600 SilcHashDestructor destructor,
601 void *destructor_user_context)
603 SilcHashTableEntry *entry, prev, e;
605 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
606 hash ? hash : ht->hash,
609 ht->hash_user_context,
611 compare : ht->compare,
612 compare_user_context ?
613 compare_user_context :
614 ht->compare_user_context);
620 if (!prev && e->next)
622 if (!prev && e->next == NULL)
627 prev->next = e->next;
630 destructor(e->key, e->context, destructor_user_context);
633 ht->destructor(e->key, e->context, ht->destructor_user_context);
639 if (SILC_HASH_REHASH_DEC)
640 silc_hash_table_rehash(ht, 0);
645 /* Finds the entry in the hash table by the provided `key' as fast as
646 possible. Return TRUE if the entry was found and FALSE otherwise.
647 The found entry is returned to the `ret_key' and `ret_context',
648 respectively. If the `ret_key and `ret_context' are NULL then this
649 maybe used only to check whether given key exists in the table. */
651 bool silc_hash_table_find(SilcHashTable ht, void *key,
652 void **ret_key, void **ret_context)
654 SilcHashTableEntry *entry;
656 entry = silc_hash_table_find_internal_simple(ht, key, ht->hash,
657 ht->hash_user_context,
659 ht->compare_user_context);
664 *ret_key = (*entry)->key;
666 *ret_context = (*entry)->context;
671 /* Same as above but with specified hash and comparison functions. */
673 bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
674 void **ret_key, void **ret_context,
675 SilcHashFunction hash,
676 void *hash_user_context,
677 SilcHashCompare compare,
678 void *compare_user_context)
680 SilcHashTableEntry *entry;
682 entry = silc_hash_table_find_internal_simple(ht, key,
683 hash ? hash : ht->hash,
686 ht->hash_user_context,
689 compare_user_context ?
690 compare_user_context :
691 ht->compare_user_context);
696 *ret_key = (*entry)->key;
698 *ret_context = (*entry)->context;
703 /* Same as silc_hash_table_find but finds with specific context. */
705 bool silc_hash_table_find_by_context(SilcHashTable ht, void *key,
706 void *context, void **ret_key)
708 SilcHashTableEntry *entry;
710 entry = silc_hash_table_find_internal_context(ht, key, context, NULL,
712 ht->hash_user_context,
714 ht->compare_user_context);
715 if (!entry || !(*entry))
719 *ret_key = (*entry)->key;
724 /* As the hash table is collision resistant it is possible to save duplicate
725 keys to the hash table. This function can be used to find all keys
726 and contexts from the hash table that are found using the `key'. The
727 `foreach' is called for every found key. */
729 void silc_hash_table_find_foreach(SilcHashTable ht, void *key,
730 SilcHashForeach foreach, void *user_context)
732 silc_hash_table_find_internal_all(ht, key, ht->hash, ht->hash_user_context,
733 ht->compare, ht->compare_user_context,
734 foreach, user_context);
737 /* Same as above but with specific hash and comparison functions. */
739 void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key,
740 SilcHashFunction hash,
741 void *hash_user_context,
742 SilcHashCompare compare,
743 void *compare_user_context,
744 SilcHashForeach foreach,
745 void *foreach_user_context)
747 silc_hash_table_find_internal_all(ht, key,
748 hash ? hash : ht->hash,
751 ht->hash_user_context,
754 compare_user_context ?
755 compare_user_context :
756 ht->compare_user_context,
757 foreach, foreach_user_context);
760 /* Traverse all entrys in the hash table and call the `foreach' for
761 every entry with the `user_context' context. */
763 void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
766 SilcHashTableEntry e, tmp;
773 auto_rehash = ht->auto_rehash;
774 ht->auto_rehash = FALSE;
775 for (i = 0; i < primesize[ht->table_size]; i++) {
778 /* Entry may become invalid inside the `foreach' */
780 foreach(e->key, e->context, user_context);
784 ht->auto_rehash = auto_rehash;
787 /* Rehashs the hash table. The size of the new hash table is provided
788 as `new_size'. If the `new_size' is zero then this routine will make
789 the new table of a suitable size. Note that this operation may be
792 void silc_hash_table_rehash(SilcHashTable ht, SilcUInt32 new_size)
795 SilcHashTableEntry *table, e, tmp;
796 SilcUInt32 table_size, size_index;
799 SILC_HT_DEBUG(("Start"));
802 silc_hash_table_primesize(new_size, &size_index);
804 silc_hash_table_primesize(ht->entry_count, &size_index);
806 if (size_index == ht->table_size)
809 SILC_HT_DEBUG(("Rehashing"));
811 /* Take old hash table */
813 table_size = ht->table_size;
814 auto_rehash = ht->auto_rehash;
815 ht->auto_rehash = FALSE;
817 /* Allocate new table */
818 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
821 ht->table_size = size_index;
825 for (i = 0; i < primesize[table_size]; i++) {
828 silc_hash_table_add(ht, e->key, e->context);
832 /* Remove old entry */
837 ht->auto_rehash = auto_rehash;
839 /* Remove old table */
843 /* Same as above but with specific hash function. */
845 void silc_hash_table_rehash_ext(SilcHashTable ht, SilcUInt32 new_size,
846 SilcHashFunction hash,
847 void *hash_user_context)
850 SilcHashTableEntry *table, e, tmp;
851 SilcUInt32 table_size, size_index;
854 SILC_HT_DEBUG(("Start"));
857 silc_hash_table_primesize(new_size, &size_index);
859 silc_hash_table_primesize(ht->entry_count, &size_index);
861 if (size_index == ht->table_size)
864 SILC_HT_DEBUG(("Rehashing"));
866 /* Take old hash table */
868 table_size = ht->table_size;
869 auto_rehash = ht->auto_rehash;
870 ht->auto_rehash = FALSE;
872 /* Allocate new table */
873 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
876 ht->table_size = size_index;
880 for (i = 0; i < primesize[table_size]; i++) {
883 silc_hash_table_add_ext(ht, e->key, e->context, hash,
888 /* Remove old entry */
893 ht->auto_rehash = auto_rehash;
895 /* Remove old table */
899 /* Prepares the `htl' list structure sent as argument to be used in the
900 hash table traversing with the silc_hash_table_get. Usage:
901 SilcHashTableList htl; silc_hash_table_list(ht, &htl); */
903 void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl)
908 htl->auto_rehash = ht->auto_rehash;
910 /* Disallow rehashing of the table while traversing the table */
911 ht->auto_rehash = FALSE;
914 /* Resets the `htl' SilcHashTableList. */
916 void silc_hash_table_list_reset(SilcHashTableList *htl)
918 /* Set back the original auto rehash value to the table */
919 htl->ht->auto_rehash = htl->auto_rehash;
922 /* Returns always the next entry in the hash table into the `key' and
923 `context' and TRUE. If this returns FALSE then there are no anymore
924 any entrys. Usage: while (silc_hash_table_get(&htl, &key, &context)) */
926 bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context)
928 SilcHashTableEntry entry = (SilcHashTableEntry)htl->entry;
930 if (!htl->ht->entry_count)
933 while (!entry && htl->index < primesize[htl->ht->table_size]) {
934 entry = htl->ht->table[htl->index];
941 htl->entry = entry->next;
946 *context = entry->context;