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;
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);
237 entry = &(*entry)->next;
241 if ((*entry)->key == key) {
242 tmp = &(*entry)->next;
243 foreach((*entry)->key, (*entry)->context, foreach_user_context);
247 entry = &(*entry)->next;
251 ht->auto_rehash = auto_rehash;
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 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
264 SILC_HT_DEBUG(("index %d key %p", i, key));
266 entry = &ht->table[i];
268 /* The entry exists already. We have a collision, add it to the
269 list to avoid collision. */
270 SilcHashTableEntry e, tmp;
279 SILC_HT_DEBUG(("Collision; adding new key to list"));
281 e->next = silc_calloc(1, sizeof(*e->next));
283 e->next->context = context;
287 SILC_HT_DEBUG(("New key"));
288 *entry = silc_calloc(1, sizeof(**entry));
290 (*entry)->context = context;
294 if (SILC_HASH_REHASH_INC)
295 silc_hash_table_rehash(ht, 0);
298 /* Internal routine to replace old key with new one (if it exists) */
301 silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context,
302 SilcHashFunction hash,
303 void *hash_user_context)
305 SilcHashTableEntry *entry;
306 SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
308 SILC_HT_DEBUG(("index %d key %p", i, key));
310 entry = &ht->table[i];
312 /* The entry exists already. We have a collision, replace the old
315 ht->destructor((*entry)->key, (*entry)->context,
316 ht->destructor_user_context);
319 *entry = silc_calloc(1, sizeof(**entry));
324 (*entry)->context = context;
326 if (SILC_HASH_REHASH_INC)
327 silc_hash_table_rehash(ht, 0);
330 /* Allocates new hash table and returns it. If the `table_size' is not
331 zero then the hash table size is the size provided. If zero then the
332 default size will be used. Note that if the `table_size' is provided
333 it should be a prime. The `hash', `compare' and `destructor' are
334 the hash function, the key comparison function and key and context
335 destructor function, respectively. The `hash' is mandatory, the others
338 SilcHashTable silc_hash_table_alloc(SilcUInt32 table_size,
339 SilcHashFunction hash,
340 void *hash_user_context,
341 SilcHashCompare compare,
342 void *compare_user_context,
343 SilcHashDestructor destructor,
344 void *destructor_user_context,
348 SilcUInt32 size_index = SILC_HASH_TABLE_SIZE;
353 ht = silc_calloc(1, sizeof(*ht));
354 ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size,
356 primesize[SILC_HASH_TABLE_SIZE],
358 ht->table_size = size_index;
360 ht->compare = compare;
361 ht->destructor = destructor;
362 ht->hash_user_context = hash_user_context;
363 ht->compare_user_context = compare_user_context;
364 ht->destructor_user_context = destructor_user_context;
365 ht->auto_rehash = auto_rehash;
370 /* Frees the hash table. The destructor function provided in the
371 silc_hash_table_alloc will be called for all keys in the hash table. */
373 void silc_hash_table_free(SilcHashTable ht)
375 SilcHashTableEntry e, tmp;
378 for (i = 0; i < primesize[ht->table_size]; i++) {
382 ht->destructor(e->key, e->context, ht->destructor_user_context);
389 silc_free(ht->table);
393 /* Returns the size of the hash table */
395 SilcUInt32 silc_hash_table_size(SilcHashTable ht)
397 return primesize[ht->table_size];
400 /* Returns the number of the entires in the hash table. If there is more
401 entries in the table thatn the size of the hash table calling the
402 silc_hash_table_rehash is recommended. */
404 SilcUInt32 silc_hash_table_count(SilcHashTable ht)
406 return ht->entry_count;
409 /* Adds new entry to the hash table. The `key' is hashed using the
410 hash function and the both `key' and `context' will be saved to the
411 hash table. This function quarantees that the entry is always added
412 to the hash table reliably (it is collision resistant). */
414 void silc_hash_table_add(SilcHashTable ht, void *key, void *context)
416 silc_hash_table_add_internal(ht, key, context, ht->hash,
417 ht->hash_user_context);
420 /* Same as above but with specific hash function and user context. */
422 void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
423 SilcHashFunction hash, void *hash_user_context)
425 silc_hash_table_add_internal(ht, key, context, hash, hash_user_context);
428 /* Same as above but if the `key' already exists in the hash table
429 the old key and the old context will be replace with the `key' and
430 the `context. The destructor function will be called for the
431 replaced key and context. */
433 void silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
435 silc_hash_table_replace_internal(ht, key, context, ht->hash,
436 ht->hash_user_context);
439 /* Same as above but with specific hash function. */
441 void silc_hash_table_replace_ext(SilcHashTable ht, void *key, void *context,
442 SilcHashFunction hash,
443 void *hash_user_context)
445 silc_hash_table_replace_internal(ht, key, context, hash, hash_user_context);
448 /* Removes the entry from the hash table by the provided `key'. This will
449 call the destructor funtion for the found entry. Return TRUE if the
450 entry was removed successfully and FALSE otherwise. */
452 bool silc_hash_table_del(SilcHashTable ht, void *key)
454 SilcHashTableEntry *entry, prev, e;
456 entry = silc_hash_table_find_internal(ht, key, &prev,
457 ht->hash, ht->hash_user_context,
458 ht->compare, ht->compare_user_context);
464 if (!prev && e->next)
466 if (!prev && e->next == NULL)
471 prev->next = e->next;
474 ht->destructor(e->key, e->context, ht->destructor_user_context);
479 if (SILC_HASH_REHASH_DEC)
480 silc_hash_table_rehash(ht, 0);
485 /* Same as above but with specific hash and compare functions. */
487 bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
488 SilcHashFunction hash,
489 void *hash_user_context,
490 SilcHashCompare compare,
491 void *compare_user_context,
492 SilcHashDestructor destructor,
493 void *destructor_user_context)
495 SilcHashTableEntry *entry, prev, e;
497 entry = silc_hash_table_find_internal(ht, key, &prev,
498 hash ? hash : ht->hash,
499 hash_user_context ? hash_user_context :
500 ht->hash_user_context,
501 compare ? compare : ht->compare,
502 compare_user_context ?
503 compare_user_context :
504 ht->compare_user_context);
510 if (!prev && e->next)
512 if (!prev && e->next == NULL)
517 prev->next = e->next;
520 destructor(e->key, e->context, destructor_user_context);
523 ht->destructor(e->key, e->context, ht->destructor_user_context);
529 if (SILC_HASH_REHASH_DEC)
530 silc_hash_table_rehash(ht, 0);
535 /* Same as above but verifies that the context associated with the `key'
536 matches the `context'. This is handy to use with hash tables that may
537 have duplicate keys. In that case the `context' may be used to check
538 whether the correct entry is being deleted. */
540 bool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
543 SilcHashTableEntry *entry, prev, e;
545 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
547 ht->hash_user_context,
549 ht->compare_user_context);
555 if (!prev && e->next)
557 if (!prev && e->next == NULL)
562 prev->next = e->next;
565 ht->destructor(e->key, e->context, ht->destructor_user_context);
570 if (SILC_HASH_REHASH_DEC)
571 silc_hash_table_rehash(ht, 0);
576 /* Same as above but with specific hash and compare functions. */
578 bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key,
580 SilcHashFunction hash,
581 void *hash_user_context,
582 SilcHashCompare compare,
583 void *compare_user_context,
584 SilcHashDestructor destructor,
585 void *destructor_user_context)
587 SilcHashTableEntry *entry, prev, e;
589 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
590 hash ? hash : ht->hash,
593 ht->hash_user_context,
595 compare : ht->compare,
596 compare_user_context ?
597 compare_user_context :
598 ht->compare_user_context);
604 if (!prev && e->next)
606 if (!prev && e->next == NULL)
611 prev->next = e->next;
614 destructor(e->key, e->context, destructor_user_context);
617 ht->destructor(e->key, e->context, ht->destructor_user_context);
623 if (SILC_HASH_REHASH_DEC)
624 silc_hash_table_rehash(ht, 0);
629 /* Finds the entry in the hash table by the provided `key' as fast as
630 possible. Return TRUE if the entry was found and FALSE otherwise.
631 The found entry is returned to the `ret_key' and `ret_context',
632 respectively. If the `ret_key and `ret_context' are NULL then this
633 maybe used only to check whether given key exists in the table. */
635 bool silc_hash_table_find(SilcHashTable ht, void *key,
636 void **ret_key, void **ret_context)
638 SilcHashTableEntry *entry;
640 entry = silc_hash_table_find_internal_simple(ht, key, ht->hash,
641 ht->hash_user_context,
643 ht->compare_user_context);
648 *ret_key = (*entry)->key;
650 *ret_context = (*entry)->context;
655 /* Same as above but with specified hash and comparison functions. */
657 bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
658 void **ret_key, void **ret_context,
659 SilcHashFunction hash,
660 void *hash_user_context,
661 SilcHashCompare compare,
662 void *compare_user_context)
664 SilcHashTableEntry *entry;
666 entry = silc_hash_table_find_internal_simple(ht, key,
667 hash ? hash : ht->hash,
670 ht->hash_user_context,
673 compare_user_context ?
674 compare_user_context :
675 ht->compare_user_context);
680 *ret_key = (*entry)->key;
682 *ret_context = (*entry)->context;
687 /* Same as silc_hash_table_find but finds with specific context. */
689 bool silc_hash_table_find_by_context(SilcHashTable ht, void *key,
690 void *context, void **ret_key)
692 SilcHashTableEntry *entry;
694 entry = silc_hash_table_find_internal_context(ht, key, context, NULL,
696 ht->hash_user_context,
698 ht->compare_user_context);
699 if (!entry || !(*entry))
703 *ret_key = (*entry)->key;
708 /* As the hash table is collision resistant it is possible to save duplicate
709 keys to the hash table. This function can be used to find all keys
710 and contexts from the hash table that are found using the `key'. The
711 `foreach' is called for every found key. */
713 void silc_hash_table_find_foreach(SilcHashTable ht, void *key,
714 SilcHashForeach foreach, void *user_context)
716 silc_hash_table_find_internal_all(ht, key, ht->hash, ht->hash_user_context,
717 ht->compare, ht->compare_user_context,
718 foreach, user_context);
721 /* Same as above but with specific hash and comparison functions. */
723 void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key,
724 SilcHashFunction hash,
725 void *hash_user_context,
726 SilcHashCompare compare,
727 void *compare_user_context,
728 SilcHashForeach foreach,
729 void *foreach_user_context)
731 silc_hash_table_find_internal_all(ht, key,
732 hash ? hash : ht->hash,
735 ht->hash_user_context,
738 compare_user_context ?
739 compare_user_context :
740 ht->compare_user_context,
741 foreach, foreach_user_context);
744 /* Traverse all entrys in the hash table and call the `foreach' for
745 every entry with the `user_context' context. */
747 void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
750 SilcHashTableEntry e, tmp;
757 auto_rehash = ht->auto_rehash;
758 ht->auto_rehash = FALSE;
759 for (i = 0; i < primesize[ht->table_size]; i++) {
762 /* Entry may become invalid inside the `foreach' */
764 foreach(e->key, e->context, user_context);
768 ht->auto_rehash = auto_rehash;
771 /* Rehashs the hash table. The size of the new hash table is provided
772 as `new_size'. If the `new_size' is zero then this routine will make
773 the new table of a suitable size. Note that this operation may be
776 void silc_hash_table_rehash(SilcHashTable ht, SilcUInt32 new_size)
779 SilcHashTableEntry *table, e, tmp;
780 SilcUInt32 table_size, size_index;
782 SILC_HT_DEBUG(("Start"));
785 silc_hash_table_primesize(new_size, &size_index);
787 silc_hash_table_primesize(ht->entry_count, &size_index);
789 if (size_index == ht->table_size)
792 SILC_HT_DEBUG(("Rehashing"));
794 /* Take old hash table */
796 table_size = ht->table_size;
798 /* Allocate new table */
799 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
800 ht->table_size = size_index;
804 for (i = 0; i < primesize[table_size]; i++) {
807 silc_hash_table_add(ht, e->key, e->context);
811 /* Remove old entry */
816 /* Remove old table */
820 /* Same as above but with specific hash function. */
822 void silc_hash_table_rehash_ext(SilcHashTable ht, SilcUInt32 new_size,
823 SilcHashFunction hash,
824 void *hash_user_context)
827 SilcHashTableEntry *table, e, tmp;
828 SilcUInt32 table_size, size_index;
830 SILC_HT_DEBUG(("Start"));
833 silc_hash_table_primesize(new_size, &size_index);
835 silc_hash_table_primesize(ht->entry_count, &size_index);
837 if (size_index == ht->table_size)
840 SILC_HT_DEBUG(("Rehashing"));
842 /* Take old hash table */
844 table_size = ht->table_size;
846 /* Allocate new table */
847 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
848 ht->table_size = size_index;
852 for (i = 0; i < primesize[table_size]; i++) {
855 silc_hash_table_add_ext(ht, e->key, e->context, hash,
860 /* Remove old entry */
865 /* Remove old table */
869 /* Prepares the `htl' list structure sent as argument to be used in the
870 hash table traversing with the silc_hash_table_get. Usage:
871 SilcHashTableList htl; silc_hash_table_list(ht, &htl); */
873 void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl)
878 htl->auto_rehash = ht->auto_rehash;
880 /* Disallow rehashing of the table while traversing the table */
881 ht->auto_rehash = FALSE;
884 /* Resets the `htl' SilcHashTableList. */
886 void silc_hash_table_list_reset(SilcHashTableList *htl)
888 /* Set back the original auto rehash value to the table */
889 htl->ht->auto_rehash = htl->auto_rehash;
892 /* Returns always the next entry in the hash table into the `key' and
893 `context' and TRUE. If this returns FALSE then there are no anymore
894 any entrys. Usage: while (silc_hash_table_get(&htl, &key, &context)) */
896 bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context)
898 SilcHashTableEntry entry = (SilcHashTableEntry)htl->entry;
900 if (!htl->ht->entry_count)
903 while (!entry && htl->index < primesize[htl->ht->table_size]) {
904 entry = htl->ht->table[htl->index];
911 htl->entry = entry->next;
916 *context = entry->context;