5 Author: Pekka Riikonen <priikone@silcnet.org>
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 /* Define to 1 if you want hash table debug enabled */
33 #define SILC_HASH_TABLE_DEBUG 0
35 #if SILC_HASH_TABLE_DEBUG == 1
36 #define SILC_HT_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
38 #define SILC_HT_DEBUG(fmt)
41 /* Default size of the hash table (index to prime table) */
42 #define SILC_HASH_TABLE_SIZE 3
44 /* Produce the index by hashing the key */
45 #define SILC_HASH_TABLE_HASH(f, c) \
46 ((f)(key, (c)) % primesize[ht->table_size])
48 /* Check whether need to rehash */
49 #define SILC_HASH_REHASH_INC \
50 (ht->auto_rehash && (ht->entry_count / 2) > primesize[ht->table_size])
51 #define SILC_HASH_REHASH_DEC \
52 (ht->auto_rehash && (ht->entry_count * 2) < primesize[ht->table_size] && \
53 ht->entry_count > primesize[SILC_HASH_TABLE_SIZE])
55 /* One entry in the hash table. Includes the key and the associated
56 context. The `next' pointer is non-NULL if two (or more) different
57 keys hashed to same value. The pointer is the pointer to the next
59 typedef struct SilcHashTableEntryStruct {
62 struct SilcHashTableEntryStruct *next;
63 } *SilcHashTableEntry;
66 struct SilcHashTableStruct {
67 SilcHashTableEntry *table;
70 SilcHashFunction hash;
71 SilcHashCompare compare;
72 SilcHashDestructor destructor;
73 void *hash_user_context;
74 void *compare_user_context;
75 void *destructor_user_context;
79 /* Prime sizes for the hash table. The size of the table will always
81 const uint32 primesize[42] =
83 1, 3, 5, 11, 17, 37, 67, 109, 131, 163, 257, 367, 521, 823, 1031,
84 1237, 2053, 2777, 4099, 6247, 8209, 14057, 16411, 21089, 32771, 47431,
85 65537, 106721, 131101, 262147, 360163, 524309, 810343, 1048583, 2097169,
86 4194319, 6153409, 8388617, 13845163, 16777259, 33554467, 67108879
89 /* Find appropriate size for the hash table. The size will be a prime. */
91 static uint32 silc_hash_table_primesize(uint32 size, uint32 *index)
95 for (i = 0; i < sizeof(primesize); i++)
96 if (primesize[i] >= size) {
98 SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index]));
103 SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index]));
104 return primesize[i - 1];
107 /* Internal routine to find entry in the hash table by `key'. Returns
108 the previous entry (if exists) as well. */
110 static inline SilcHashTableEntry *
111 silc_hash_table_find_internal(SilcHashTable ht, void *key,
112 SilcHashTableEntry *prev_entry,
113 SilcHashFunction hash, void *hash_user_context,
114 SilcHashCompare compare,
115 void *compare_user_context)
117 SilcHashTableEntry *entry, prev = NULL;
118 uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
120 SILC_HT_DEBUG(("index %d key %p", i, key));
122 entry = &ht->table[i];
124 while (*entry && !compare((*entry)->key, key, compare_user_context)) {
126 entry = &(*entry)->next;
129 while (*entry && (*entry)->key != key) {
131 entry = &(*entry)->next;
139 /* Internal routine to find entry in the hash table by `key' and `context'.
140 Returns the previous entry (if exists) as well. */
142 static inline SilcHashTableEntry *
143 silc_hash_table_find_internal_context(SilcHashTable ht, void *key,
145 SilcHashTableEntry *prev_entry,
146 SilcHashFunction hash,
147 void *hash_user_context,
148 SilcHashCompare compare,
149 void *compare_user_context)
151 SilcHashTableEntry *entry, prev = NULL;
152 uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
154 SILC_HT_DEBUG(("index %d key %p context %p", i, key, context));
156 entry = &ht->table[i];
159 if (compare((*entry)->key, key, compare_user_context) &&
160 (*entry)->context == context)
163 entry = &(*entry)->next;
167 if ((*entry)->key == key && (*entry)->context == context)
170 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 uint32 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;
219 uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
221 SILC_HT_DEBUG(("index %d key %p", i, key));
223 auto_rehash = ht->auto_rehash;
224 ht->auto_rehash = FALSE;
226 entry = &ht->table[i];
229 if (compare((*entry)->key, key, compare_user_context))
230 foreach((*entry)->key, (*entry)->context, foreach_user_context);
231 entry = &(*entry)->next;
235 if ((*entry)->key == key)
236 foreach((*entry)->key, (*entry)->context, foreach_user_context);
237 entry = &(*entry)->next;
241 ht->auto_rehash = auto_rehash;
244 /* Internal routine to add new key to the hash table */
247 silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context,
248 SilcHashFunction hash,
249 void *hash_user_context)
251 SilcHashTableEntry *entry;
252 uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
254 SILC_HT_DEBUG(("index %d key %p", i, key));
256 entry = &ht->table[i];
258 /* The entry exists already. We have a collision, add it to the
259 list to avoid collision. */
260 SilcHashTableEntry e, tmp;
269 SILC_HT_DEBUG(("Collision; adding new key to list"));
271 e->next = silc_calloc(1, sizeof(*e->next));
273 e->next->context = context;
277 SILC_HT_DEBUG(("New key"));
278 *entry = silc_calloc(1, sizeof(**entry));
280 (*entry)->context = context;
284 if (SILC_HASH_REHASH_INC)
285 silc_hash_table_rehash(ht, 0);
288 /* Internal routine to replace old key with new one (if it exists) */
291 silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context,
292 SilcHashFunction hash,
293 void *hash_user_context)
295 SilcHashTableEntry *entry;
296 uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
298 SILC_HT_DEBUG(("index %d key %p", i, key));
300 entry = &ht->table[i];
302 /* The entry exists already. We have a collision, replace the old
305 ht->destructor((*entry)->key, (*entry)->context,
306 ht->destructor_user_context);
309 *entry = silc_calloc(1, sizeof(**entry));
314 (*entry)->context = context;
316 if (SILC_HASH_REHASH_INC)
317 silc_hash_table_rehash(ht, 0);
320 /* Allocates new hash table and returns it. If the `table_size' is not
321 zero then the hash table size is the size provided. If zero then the
322 default size will be used. Note that if the `table_size' is provided
323 it should be a prime. The `hash', `compare' and `destructor' are
324 the hash function, the key comparison function and key and context
325 destructor function, respectively. The `hash' is mandatory, the others
328 SilcHashTable silc_hash_table_alloc(uint32 table_size,
329 SilcHashFunction hash,
330 void *hash_user_context,
331 SilcHashCompare compare,
332 void *compare_user_context,
333 SilcHashDestructor destructor,
334 void *destructor_user_context,
338 uint32 size_index = SILC_HASH_TABLE_SIZE;
343 ht = silc_calloc(1, sizeof(*ht));
344 ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size,
346 primesize[SILC_HASH_TABLE_SIZE],
348 ht->table_size = size_index;
350 ht->compare = compare;
351 ht->destructor = destructor;
352 ht->hash_user_context = hash_user_context;
353 ht->compare_user_context = compare_user_context;
354 ht->destructor_user_context = destructor_user_context;
355 ht->auto_rehash = auto_rehash;
360 /* Frees the hash table. The destructor function provided in the
361 silc_hash_table_alloc will be called for all keys in the hash table. */
363 void silc_hash_table_free(SilcHashTable ht)
365 SilcHashTableEntry e, tmp;
368 for (i = 0; i < primesize[ht->table_size]; i++) {
372 ht->destructor(e->key, e->context, ht->destructor_user_context);
379 silc_free(ht->table);
383 /* Returns the size of the hash table */
385 uint32 silc_hash_table_size(SilcHashTable ht)
387 return primesize[ht->table_size];
390 /* Returns the number of the entires in the hash table. If there is more
391 entries in the table thatn the size of the hash table calling the
392 silc_hash_table_rehash is recommended. */
394 uint32 silc_hash_table_count(SilcHashTable ht)
396 return ht->entry_count;
399 /* Adds new entry to the hash table. The `key' is hashed using the
400 hash function and the both `key' and `context' will be saved to the
401 hash table. This function quarantees that the entry is always added
402 to the hash table reliably (it is collision resistant). */
404 void silc_hash_table_add(SilcHashTable ht, void *key, void *context)
406 silc_hash_table_add_internal(ht, key, context, ht->hash,
407 ht->hash_user_context);
410 /* Same as above but with specific hash function and user context. */
412 void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
413 SilcHashFunction hash, void *hash_user_context)
415 silc_hash_table_add_internal(ht, key, context, hash, hash_user_context);
418 /* Same as above but if the `key' already exists in the hash table
419 the old key and the old context will be replace with the `key' and
420 the `context. The destructor function will be called for the
421 replaced key and context. */
423 void silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
425 silc_hash_table_replace_internal(ht, key, context, ht->hash,
426 ht->hash_user_context);
429 /* Same as above but with specific hash function. */
431 void silc_hash_table_replace_ext(SilcHashTable ht, void *key, void *context,
432 SilcHashFunction hash,
433 void *hash_user_context)
435 silc_hash_table_replace_internal(ht, key, context, hash, hash_user_context);
438 /* Removes the entry from the hash table by the provided `key'. This will
439 call the destructor funtion for the found entry. Return TRUE if the
440 entry was removed successfully and FALSE otherwise. */
442 bool silc_hash_table_del(SilcHashTable ht, void *key)
444 SilcHashTableEntry *entry, prev, e;
446 entry = silc_hash_table_find_internal(ht, key, &prev,
447 ht->hash, ht->hash_user_context,
448 ht->compare, ht->compare_user_context);
454 if (!prev && e->next)
456 if (!prev && e->next == NULL)
461 prev->next = e->next;
464 ht->destructor(e->key, e->context, ht->destructor_user_context);
469 if (SILC_HASH_REHASH_DEC)
470 silc_hash_table_rehash(ht, 0);
475 /* Same as above but with specific hash and compare functions. */
477 bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
478 SilcHashFunction hash,
479 void *hash_user_context,
480 SilcHashCompare compare,
481 void *compare_user_context,
482 SilcHashDestructor destructor,
483 void *destructor_user_context)
485 SilcHashTableEntry *entry, prev, e;
487 entry = silc_hash_table_find_internal(ht, key, &prev,
488 hash ? hash : ht->hash,
489 hash_user_context ? hash_user_context :
490 ht->hash_user_context,
491 compare ? compare : ht->compare,
492 compare_user_context ?
493 compare_user_context :
494 ht->compare_user_context);
500 if (!prev && e->next)
502 if (!prev && e->next == NULL)
507 prev->next = e->next;
510 destructor(e->key, e->context, destructor_user_context);
513 ht->destructor(e->key, e->context, ht->destructor_user_context);
519 if (SILC_HASH_REHASH_DEC)
520 silc_hash_table_rehash(ht, 0);
525 /* Same as above but verifies that the context associated with the `key'
526 matches the `context'. This is handy to use with hash tables that may
527 have duplicate keys. In that case the `context' may be used to check
528 whether the correct entry is being deleted. */
530 bool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
533 SilcHashTableEntry *entry, prev, e;
535 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
537 ht->hash_user_context,
539 ht->compare_user_context);
545 if (!prev && e->next)
547 if (!prev && e->next == NULL)
552 prev->next = e->next;
555 ht->destructor(e->key, e->context, ht->destructor_user_context);
560 if (SILC_HASH_REHASH_DEC)
561 silc_hash_table_rehash(ht, 0);
566 /* Same as above but with specific hash and compare functions. */
568 bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key,
570 SilcHashFunction hash,
571 void *hash_user_context,
572 SilcHashCompare compare,
573 void *compare_user_context,
574 SilcHashDestructor destructor,
575 void *destructor_user_context)
577 SilcHashTableEntry *entry, prev, e;
579 entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
580 hash ? hash : ht->hash,
583 ht->hash_user_context,
585 compare : ht->compare,
586 compare_user_context ?
587 compare_user_context :
588 ht->compare_user_context);
594 if (!prev && e->next)
596 if (!prev && e->next == NULL)
601 prev->next = e->next;
604 destructor(e->key, e->context, destructor_user_context);
607 ht->destructor(e->key, e->context, ht->destructor_user_context);
613 if (SILC_HASH_REHASH_DEC)
614 silc_hash_table_rehash(ht, 0);
619 /* Finds the entry in the hash table by the provided `key' as fast as
620 possible. Return TRUE if the entry was found and FALSE otherwise.
621 The found entry is returned to the `ret_key' and `ret_context',
622 respectively. If the `ret_key and `ret_context' are NULL then this
623 maybe used only to check whether given key exists in the table. */
625 bool silc_hash_table_find(SilcHashTable ht, void *key,
626 void **ret_key, void **ret_context)
628 SilcHashTableEntry *entry;
630 entry = silc_hash_table_find_internal_simple(ht, key, ht->hash,
631 ht->hash_user_context,
633 ht->compare_user_context);
638 *ret_key = (*entry)->key;
640 *ret_context = (*entry)->context;
645 /* Same as above but with specified hash and comparison functions. */
647 bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
648 void **ret_key, void **ret_context,
649 SilcHashFunction hash,
650 void *hash_user_context,
651 SilcHashCompare compare,
652 void *compare_user_context)
654 SilcHashTableEntry *entry;
656 entry = silc_hash_table_find_internal_simple(ht, key,
657 hash ? hash : ht->hash,
660 ht->hash_user_context,
663 compare_user_context ?
664 compare_user_context :
665 ht->compare_user_context);
670 *ret_key = (*entry)->key;
672 *ret_context = (*entry)->context;
677 /* As the hash table is collision resistant it is possible to save duplicate
678 keys to the hash table. This function can be used to find all keys
679 and contexts from the hash table that are found using the `key'. The
680 `foreach' is called for every found key. */
682 void silc_hash_table_find_foreach(SilcHashTable ht, void *key,
683 SilcHashForeach foreach, void *user_context)
685 silc_hash_table_find_internal_all(ht, key, ht->hash, ht->hash_user_context,
686 ht->compare, ht->compare_user_context,
687 foreach, user_context);
690 /* Same as above but with specific hash and comparison functions. */
692 void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key,
693 SilcHashFunction hash,
694 void *hash_user_context,
695 SilcHashCompare compare,
696 void *compare_user_context,
697 SilcHashForeach foreach,
698 void *foreach_user_context)
700 silc_hash_table_find_internal_all(ht, key,
701 hash ? hash : ht->hash,
704 ht->hash_user_context,
707 compare_user_context ?
708 compare_user_context :
709 ht->compare_user_context,
710 foreach, foreach_user_context);
713 /* Traverse all entrys in the hash table and call the `foreach' for
714 every entry with the `user_context' context. */
716 void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
719 SilcHashTableEntry e, tmp;
726 auto_rehash = ht->auto_rehash;
727 ht->auto_rehash = FALSE;
728 for (i = 0; i < primesize[ht->table_size]; i++) {
731 /* Entry may become invalid inside the `foreach' */
733 foreach(e->key, e->context, user_context);
737 ht->auto_rehash = auto_rehash;
740 /* Rehashs the hash table. The size of the new hash table is provided
741 as `new_size'. If the `new_size' is zero then this routine will make
742 the new table of a suitable size. Note that this operation may be
745 void silc_hash_table_rehash(SilcHashTable ht, uint32 new_size)
748 SilcHashTableEntry *table, e, tmp;
749 uint32 table_size, size_index;
751 SILC_HT_DEBUG(("Start"));
754 silc_hash_table_primesize(new_size, &size_index);
756 silc_hash_table_primesize(ht->entry_count, &size_index);
758 if (size_index == ht->table_size)
761 SILC_HT_DEBUG(("Rehashing"));
763 /* Take old hash table */
765 table_size = ht->table_size;
767 /* Allocate new table */
768 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
769 ht->table_size = size_index;
773 for (i = 0; i < primesize[table_size]; i++) {
776 silc_hash_table_add(ht, e->key, e->context);
780 /* Remove old entry */
785 /* Remove old table */
789 /* Same as above but with specific hash function. */
791 void silc_hash_table_rehash_ext(SilcHashTable ht, uint32 new_size,
792 SilcHashFunction hash,
793 void *hash_user_context)
796 SilcHashTableEntry *table, e, tmp;
797 uint32 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;
815 /* Allocate new table */
816 ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
817 ht->table_size = size_index;
821 for (i = 0; i < primesize[table_size]; i++) {
824 silc_hash_table_add_ext(ht, e->key, e->context, hash,
829 /* Remove old entry */
834 /* Remove old table */
838 /* Prepares the `htl' list structure sent as argument to be used in the
839 hash table traversing with the silc_hash_table_get. Usage:
840 SilcHashTableList htl; silc_hash_table_list(ht, &htl); */
842 void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl)
847 htl->auto_rehash = ht->auto_rehash;
849 /* Disallow rehashing of the table while traversing the table */
850 ht->auto_rehash = FALSE;
853 /* Resets the `htl' SilcHashTableList. */
855 void silc_hash_table_list_reset(SilcHashTableList *htl)
857 /* Set back the original auto rehash value to the table */
858 htl->ht->auto_rehash = htl->auto_rehash;
861 /* Returns always the next entry in the hash table into the `key' and
862 `context' and TRUE. If this returns FALSE then there are no anymore
863 any entrys. Usage: while (silc_hash_table_get(&htl, &key, &context)) */
865 bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context)
867 SilcHashTableEntry entry = (SilcHashTableEntry)htl->entry;
869 if (!htl->ht->entry_count)
872 while (!entry && htl->index < primesize[htl->ht->table_size]) {
873 entry = htl->ht->table[htl->index];
880 htl->entry = entry->next;
885 *context = entry->context;