/* silchashtable.c Author: Pekka Riikonen Copyright (C) 2001 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ /* Implementation of collision resistant hash table. This is a hash table that provides a reliable (what you add stays there, and duplicate keys are allowed) with as fast reference to the key as possible. If duplicate keys are a lot in the hash table the lookup gets slower of course. However, this is reliable and no data is lost at any point. If you know that you never have duplicate keys then this is as fast as any simple hash table. */ /* $Id$ */ #include "silc.h" #include "silchashtable.h" /* Define to 1 if you want hash table debug enabled */ #define SILC_HASH_TABLE_DEBUG 0 #if SILC_HASH_TABLE_DEBUG == 1 #define SILC_HT_DEBUG(fmt) SILC_LOG_DEBUG(fmt) #else #define SILC_HT_DEBUG(fmt) #endif /* Default size of the hash table (index to prime table) */ #define SILC_HASH_TABLE_SIZE 2 /* Produce the index by hashing the key */ #define SILC_HASH_TABLE_HASH(f, c) \ ((f)(key, (c)) % primesize[ht->table_size]) /* Check whether need to rehash */ #define SILC_HASH_REHASH_INC \ (ht->auto_rehash && (ht->entry_count / 2) > primesize[ht->table_size]) #define SILC_HASH_REHASH_DEC \ (ht->auto_rehash && (ht->entry_count * 2) < primesize[ht->table_size] && \ ht->entry_count > primesize[SILC_HASH_TABLE_SIZE]) /* One entry in the hash table. Includes the key and the associated context. The `next' pointer is non-NULL if two (or more) different keys hashed to same value. The pointer is the pointer to the next entry. */ typedef struct SilcHashTableEntryStruct { void *key; void *context; struct SilcHashTableEntryStruct *next; } *SilcHashTableEntry; /* Hash table. */ struct SilcHashTableStruct { SilcStack stack; SilcHashTableEntry *table; SilcUInt32 table_size; SilcUInt32 entry_count; SilcHashFunction hash; SilcHashCompare compare; SilcHashDestructor destructor; void *hash_user_context; void *compare_user_context; void *destructor_user_context; unsigned int auto_rehash : 1; }; /* Prime sizes for the hash table. The size of the table will always be one of these. */ const SilcUInt32 primesize[] = { 3, 5, 11, 17, 37, 67, 109, 131, 163, 257, 367, 521, 823, 1031, 1237, 1447, 2053, 2389, 2777, 3323, 4099, 5059, 6247, 7001, 8209, 10993, 14057, 16411, 19181, 21089, 25033, 32771, 40009, 47431, 65537, 106721, 131101, 262147, 360163, 524309, 810343, 1048583, 2097169, 4194319, 6153409, 8388617, 13845163, 16777259, 33554467, 67108879 }; /* Find appropriate size for the hash table. The size will be a prime. */ static SilcUInt32 silc_hash_table_primesize(SilcUInt32 size, SilcUInt32 *index) { int i; for (i = 0; i < sizeof(primesize) / sizeof(primesize[0]); i++) if (primesize[i] >= size) { *index = i; SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index])); return primesize[i]; } *index = i - 1; SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index])); return primesize[i - 1]; } /* Internal routine to find entry in the hash table by `key'. Returns the previous entry (if exists) as well. */ static inline SilcHashTableEntry * silc_hash_table_find_internal(SilcHashTable ht, void *key, SilcHashTableEntry *prev_entry, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context) { SilcHashTableEntry *entry, prev = NULL; SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context); SILC_HT_DEBUG(("index %d key %p", i, key)); entry = &ht->table[i]; if (compare) { while (*entry && !compare((*entry)->key, key, compare_user_context)) { prev = *entry; entry = &(*entry)->next; } } else { while (*entry && (*entry)->key != key) { prev = *entry; entry = &(*entry)->next; } } *prev_entry = prev; return entry; } /* Internal routine to find entry in the hash table by `key' and `context'. Returns the previous entry (if exists) as well to `prev_entry'. */ static inline SilcHashTableEntry * silc_hash_table_find_internal_context(SilcHashTable ht, void *key, void *context, SilcHashTableEntry *prev_entry, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context) { SilcHashTableEntry *entry, prev = NULL; SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context); SILC_HT_DEBUG(("index %d key %p context %p", i, key, context)); entry = &ht->table[i]; if (ht->compare) { while (*entry) { if (compare((*entry)->key, key, compare_user_context) && (*entry)->context == context) break; prev = *entry; entry = &(*entry)->next; } } else { while (*entry) { if ((*entry)->key == key && (*entry)->context == context) break; prev = *entry; entry = &(*entry)->next; } } if (prev_entry) *prev_entry = prev; return entry; } /* Internal routine to find entry in the hash table by `key'. */ static inline SilcHashTableEntry * silc_hash_table_find_internal_simple(SilcHashTable ht, void *key, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context) { SilcHashTableEntry *entry; SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context); SILC_HT_DEBUG(("index %d key %p", i, key)); entry = &ht->table[i]; if (compare) { while (*entry && !compare((*entry)->key, key, compare_user_context)) entry = &(*entry)->next; } else { while (*entry && (*entry)->key != key) entry = &(*entry)->next; } return entry; } /* Internal routine to find all keys by `key'. This may return multiple entries if multiple entries with same key exists. With specific hash and comparison functions. */ static inline void silc_hash_table_find_internal_all(SilcHashTable ht, void *key, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context, SilcHashForeach foreach, void *foreach_user_context) { SilcHashTableEntry e, tmp; SilcBool auto_rehash, found = FALSE; SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context); SILC_HT_DEBUG(("index %d key %p", i, key)); /* Disallow auto rehashing while going through the table since we call the `foreach' function which could alter the table. */ auto_rehash = ht->auto_rehash; ht->auto_rehash = FALSE; e = ht->table[i]; if (compare) { while (e) { tmp = e->next; if (compare(e->key, key, compare_user_context)) { found = TRUE; foreach(e->key, e->context, foreach_user_context); } e = tmp; } } else { while (e) { tmp = e->next; if (e->key == key) { found = TRUE; foreach(e->key, e->context, foreach_user_context); } e = tmp; } } /* If nothing was found call with NULL context the callback */ if (!found) foreach(key, NULL, foreach_user_context); ht->auto_rehash = auto_rehash; } /* Internal routine to add new key to the hash table */ static inline SilcBool silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context, SilcHashFunction hash, void *hash_user_context) { SilcHashTableEntry *entry; SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context); SILC_HT_DEBUG(("index %d key %p", i, key)); entry = &ht->table[i]; if (*entry) { /* The entry exists already. We have a collision, add it to the list to avoid collision. */ SilcHashTableEntry e, tmp; e = *entry; tmp = e->next; while (tmp) { e = tmp; tmp = tmp->next; } SILC_HT_DEBUG(("Collision; adding new key to list")); e->next = silc_scalloc(ht->stack, 1, sizeof(*e->next)); if (!e->next) return FALSE; e->next->key = key; e->next->context = context; ht->entry_count++; } else { /* New key */ SILC_HT_DEBUG(("New key")); *entry = silc_scalloc(ht->stack, 1, sizeof(**entry)); if (!(*entry)) return FALSE; (*entry)->key = key; (*entry)->context = context; ht->entry_count++; } if (SILC_HASH_REHASH_INC) silc_hash_table_rehash(ht, 0); return TRUE; } /* Internal routine to replace old key with new one (if it exists) */ static inline SilcBool silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context, SilcHashFunction hash, void *hash_user_context) { SilcHashTableEntry *entry; SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context); SILC_HT_DEBUG(("index %d key %p", i, key)); entry = &ht->table[i]; if (*entry) { /* The entry exists already. We have a collision, replace the old key and context. */ if (ht->destructor) ht->destructor((*entry)->key, (*entry)->context, ht->destructor_user_context); } else { /* New key */ *entry = silc_scalloc(ht->stack, 1, sizeof(**entry)); if (!(*entry)) return FALSE; ht->entry_count++; } (*entry)->key = key; (*entry)->context = context; if (SILC_HASH_REHASH_INC) silc_hash_table_rehash(ht, 0); return TRUE; } /* Allocates new hash table and returns it. If the `table_size' is not zero then the hash table size is the size provided. If zero then the default size will be used. Note that if the `table_size' is provided it should be a prime. The `hash', `compare' and `destructor' are the hash function, the key comparison function and key and context destructor function, respectively. The `hash' is mandatory, the others are optional. */ SilcHashTable silc_hash_table_alloc(SilcStack stack, SilcUInt32 table_size, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context, SilcHashDestructor destructor, void *destructor_user_context, SilcBool auto_rehash) { SilcHashTable ht; SilcUInt32 size_index = SILC_HASH_TABLE_SIZE; if (!hash) { silc_set_errno(SILC_ERR_INVALID_ARGUMENT); return NULL; } if (stack) stack = silc_stack_alloc(0, stack); ht = silc_scalloc(stack, 1, sizeof(*ht)); if (!ht) { silc_stack_free(stack); return NULL; } ht->table = silc_scalloc(stack, table_size ? silc_hash_table_primesize(table_size, &size_index) : primesize[SILC_HASH_TABLE_SIZE], sizeof(*ht->table)); if (!ht->table) { silc_stack_free(stack); silc_sfree(stack, ht); return NULL; } ht->stack = stack; ht->table_size = size_index; ht->hash = hash; ht->compare = compare; ht->destructor = destructor; ht->hash_user_context = hash_user_context; ht->compare_user_context = compare_user_context; ht->destructor_user_context = destructor_user_context; ht->auto_rehash = auto_rehash; return ht; } /* Frees the hash table. The destructor function provided in the silc_hash_table_alloc will be called for all keys in the hash table. */ void silc_hash_table_free(SilcHashTable ht) { SilcStack stack = ht->stack; SilcHashTableEntry e, tmp; int i; for (i = 0; i < primesize[ht->table_size]; i++) { e = ht->table[i]; while (e) { if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); tmp = e; e = e->next; silc_sfree(stack, tmp); } } silc_sfree(stack, ht->table); silc_sfree(stack, ht); silc_stack_free(stack); } /* Returns the size of the hash table */ SilcUInt32 silc_hash_table_size(SilcHashTable ht) { return primesize[ht->table_size]; } /* Returns the number of the entires in the hash table. If there is more entries in the table thatn the size of the hash table calling the silc_hash_table_rehash is recommended. */ SilcUInt32 silc_hash_table_count(SilcHashTable ht) { return ht->entry_count; } /* Adds new entry to the hash table. The `key' is hashed using the hash function and the both `key' and `context' will be saved to the hash table. This function quarantees that the entry is always added to the hash table reliably (it is collision resistant). */ SilcBool silc_hash_table_add(SilcHashTable ht, void *key, void *context) { return silc_hash_table_add_internal(ht, key, context, ht->hash, ht->hash_user_context); } /* Same as above but with specific hash function and user context. */ SilcBool silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context, SilcHashFunction hash, void *hash_user_context) { return silc_hash_table_add_internal(ht, key, context, hash, hash_user_context); } /* Same as above but if the `key' already exists in the hash table the old key and the old context will be replace with the `key' and the `context. The destructor function will be called for the replaced key and context. */ SilcBool silc_hash_table_set(SilcHashTable ht, void *key, void *context) { return silc_hash_table_replace_internal(ht, key, context, ht->hash, ht->hash_user_context); } /* Same as above but with specific hash function. */ SilcBool silc_hash_table_set_ext(SilcHashTable ht, void *key, void *context, SilcHashFunction hash, void *hash_user_context) { return silc_hash_table_replace_internal(ht, key, context, hash, hash_user_context); } /* Removes the entry from the hash table by the provided `key'. This will call the destructor funtion for the found entry. Return TRUE if the entry was removed successfully and FALSE otherwise. */ SilcBool silc_hash_table_del(SilcHashTable ht, void *key) { SilcHashTableEntry *entry, prev, e; entry = silc_hash_table_find_internal(ht, key, &prev, ht->hash, ht->hash_user_context, ht->compare, ht->compare_user_context); if (*entry == NULL) { silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; } e = *entry; if (!prev && e->next) *entry = e->next; if (!prev && e->next == NULL) *entry = NULL; if (prev) prev->next = NULL; if (prev && e->next) prev->next = e->next; if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); silc_sfree(ht->stack, e); ht->entry_count--; if (SILC_HASH_REHASH_DEC) silc_hash_table_rehash(ht, 0); return TRUE; } /* Same as above but with specific hash and compare functions. */ SilcBool silc_hash_table_del_ext(SilcHashTable ht, void *key, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context, SilcHashDestructor destructor, void *destructor_user_context) { SilcHashTableEntry *entry, prev, e; entry = silc_hash_table_find_internal(ht, key, &prev, hash ? hash : ht->hash, hash_user_context ? hash_user_context : ht->hash_user_context, compare ? compare : ht->compare, compare_user_context ? compare_user_context : ht->compare_user_context); if (*entry == NULL) { silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; } e = *entry; if (!prev && e->next) *entry = e->next; if (!prev && e->next == NULL) *entry = NULL; if (prev) prev->next = NULL; if (prev && e->next) prev->next = e->next; if (destructor) { destructor(e->key, e->context, destructor_user_context); } else { if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); } silc_sfree(ht->stack, e); ht->entry_count--; if (SILC_HASH_REHASH_DEC) silc_hash_table_rehash(ht, 0); return TRUE; } /* Same as above but verifies that the context associated with the `key' matches the `context'. This is handy to use with hash tables that may have duplicate keys. In that case the `context' may be used to check whether the correct entry is being deleted. */ SilcBool silc_hash_table_del_by_context(SilcHashTable ht, void *key, void *context) { SilcHashTableEntry *entry, prev, e; entry = silc_hash_table_find_internal_context(ht, key, context, &prev, ht->hash, ht->hash_user_context, ht->compare, ht->compare_user_context); if (*entry == NULL) { silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; } e = *entry; if (!prev && e->next) *entry = e->next; if (!prev && e->next == NULL) *entry = NULL; if (prev) prev->next = NULL; if (prev && e->next) prev->next = e->next; if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); silc_sfree(ht->stack, e); ht->entry_count--; if (SILC_HASH_REHASH_DEC) silc_hash_table_rehash(ht, 0); return TRUE; } /* Same as above but with specific hash and compare functions. */ SilcBool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key, void *context, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context, SilcHashDestructor destructor, void *destructor_user_context) { SilcHashTableEntry *entry, prev, e; entry = silc_hash_table_find_internal_context(ht, key, context, &prev, hash ? hash : ht->hash, hash_user_context ? hash_user_context : ht->hash_user_context, compare ? compare : ht->compare, compare_user_context ? compare_user_context : ht->compare_user_context); if (*entry == NULL) { silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; } e = *entry; if (!prev && e->next) *entry = e->next; if (!prev && e->next == NULL) *entry = NULL; if (prev) prev->next = NULL; if (prev && e->next) prev->next = e->next; if (destructor) { destructor(e->key, e->context, destructor_user_context); } else { if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); } silc_sfree(ht->stack, e); ht->entry_count--; if (SILC_HASH_REHASH_DEC) silc_hash_table_rehash(ht, 0); return TRUE; } /* Finds the entry in the hash table by the provided `key' as fast as possible. Return TRUE if the entry was found and FALSE otherwise. The found entry is returned to the `ret_key' and `ret_context', respectively. If the `ret_key and `ret_context' are NULL then this maybe used only to check whether given key exists in the table. */ SilcBool silc_hash_table_find(SilcHashTable ht, void *key, void **ret_key, void **ret_context) { return silc_hash_table_find_ext(ht, key, ret_key, ret_context, NULL, NULL, NULL, NULL); } /* Same as above but with specified hash and comparison functions. */ SilcBool silc_hash_table_find_ext(SilcHashTable ht, void *key, void **ret_key, void **ret_context, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context) { SilcHashTableEntry *entry; entry = silc_hash_table_find_internal_simple(ht, key, hash ? hash : ht->hash, hash_user_context ? hash_user_context : ht->hash_user_context, compare ? compare : ht->compare, compare_user_context ? compare_user_context : ht->compare_user_context); if (*entry == NULL) { silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; } if (ret_key) *ret_key = (*entry)->key; if (ret_context) *ret_context = (*entry)->context; return TRUE; } /* Same as silc_hash_table_find but finds with specific context. */ SilcBool silc_hash_table_find_by_context(SilcHashTable ht, void *key, void *context, void **ret_key) { return silc_hash_table_find_by_context_ext(ht, key, context, ret_key, NULL, NULL, NULL, NULL); } /* Same as above but with specified hash and comparison functions. */ SilcBool silc_hash_table_find_by_context_ext(SilcHashTable ht, void *key, void *context, void **ret_key, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context) { SilcHashTableEntry *entry; entry = silc_hash_table_find_internal_context(ht, key, context, NULL, hash ? hash : ht->hash, hash_user_context ? hash_user_context : ht->hash_user_context, compare ? compare : ht->compare, compare_user_context ? compare_user_context : ht->compare_user_context); if (!entry || !(*entry)) { silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; } if (ret_key) *ret_key = (*entry)->key; return TRUE; } /* As the hash table is collision resistant it is possible to save duplicate keys to the hash table. This function can be used to find all keys and contexts from the hash table that are found using the `key'. The `foreach' is called for every found key. */ void silc_hash_table_find_foreach(SilcHashTable ht, void *key, SilcHashForeach foreach, void *user_context) { silc_hash_table_find_internal_all(ht, key, ht->hash, ht->hash_user_context, ht->compare, ht->compare_user_context, foreach, user_context); } /* Same as above but with specific hash and comparison functions. */ void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key, SilcHashFunction hash, void *hash_user_context, SilcHashCompare compare, void *compare_user_context, SilcHashForeach foreach, void *foreach_user_context) { silc_hash_table_find_internal_all(ht, key, hash ? hash : ht->hash, hash_user_context ? hash_user_context : ht->hash_user_context, compare ? compare : ht->compare, compare_user_context ? compare_user_context : ht->compare_user_context, foreach, foreach_user_context); } /* Traverse all entrys in the hash table and call the `foreach' for every entry with the `user_context' context. */ void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach, void *user_context) { SilcHashTableEntry e, tmp; int i; SilcBool auto_rehash; if (!foreach) return; auto_rehash = ht->auto_rehash; ht->auto_rehash = FALSE; for (i = 0; i < primesize[ht->table_size]; i++) { e = ht->table[i]; while (e) { /* Entry may become invalid inside the `foreach' */ tmp = e->next; foreach(e->key, e->context, user_context); e = tmp; } } ht->auto_rehash = auto_rehash; } /* Rehashs the hash table. The size of the new hash table is provided as `new_size'. If the `new_size' is zero then this routine will make the new table of a suitable size. Note that this operation may be very slow. */ void silc_hash_table_rehash(SilcHashTable ht, SilcUInt32 new_size) { int i; SilcHashTableEntry *table, e, tmp; SilcUInt32 table_size, size_index; SilcBool auto_rehash; SILC_HT_DEBUG(("Start")); if (new_size) silc_hash_table_primesize(new_size, &size_index); else silc_hash_table_primesize(ht->entry_count, &size_index); if (size_index == ht->table_size) return; SILC_HT_DEBUG(("Rehashing")); /* Take old hash table */ table = ht->table; table_size = ht->table_size; auto_rehash = ht->auto_rehash; ht->auto_rehash = FALSE; /* Allocate new table */ ht->table = silc_scalloc(ht->stack, primesize[size_index], sizeof(*ht->table)); if (!ht->table) return; ht->table_size = size_index; ht->entry_count = 0; /* Rehash */ for (i = 0; i < primesize[table_size]; i++) { e = table[i]; while (e) { silc_hash_table_add(ht, e->key, e->context); tmp = e; e = e->next; /* Remove old entry */ silc_sfree(ht->stack, tmp); } } ht->auto_rehash = auto_rehash; /* Remove old table */ silc_sfree(ht->stack, table); } /* Same as above but with specific hash function. */ void silc_hash_table_rehash_ext(SilcHashTable ht, SilcUInt32 new_size, SilcHashFunction hash, void *hash_user_context) { int i; SilcHashTableEntry *table, e, tmp; SilcUInt32 table_size, size_index; SilcBool auto_rehash; SILC_HT_DEBUG(("Start")); if (new_size) silc_hash_table_primesize(new_size, &size_index); else silc_hash_table_primesize(ht->entry_count, &size_index); if (size_index == ht->table_size) return; SILC_HT_DEBUG(("Rehashing")); /* Take old hash table */ table = ht->table; table_size = ht->table_size; auto_rehash = ht->auto_rehash; ht->auto_rehash = FALSE; /* Allocate new table */ ht->table = silc_scalloc(ht->stack, primesize[size_index], sizeof(*ht->table)); if (!ht->table) return; ht->table_size = size_index; ht->entry_count = 0; /* Rehash */ for (i = 0; i < primesize[table_size]; i++) { e = table[i]; while (e) { silc_hash_table_add_ext(ht, e->key, e->context, hash, hash_user_context); tmp = e; e = e->next; /* Remove old entry */ silc_sfree(ht->stack, tmp); } } ht->auto_rehash = auto_rehash; /* Remove old table */ silc_sfree(ht->stack, table); } /* Prepares the `htl' list structure sent as argument to be used in the hash table traversing with the silc_hash_table_get. Usage: SilcHashTableList htl; silc_hash_table_list(ht, &htl); */ void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl) { htl->ht = ht; htl->entry = NULL; htl->index = 0; htl->auto_rehash = ht->auto_rehash; /* Disallow rehashing of the table while traversing the table */ ht->auto_rehash = FALSE; } /* Resets the `htl' SilcHashTableList. */ void silc_hash_table_list_reset(SilcHashTableList *htl) { /* Set back the original auto rehash value to the table */ htl->ht->auto_rehash = htl->auto_rehash; } /* Returns always the next entry in the hash table into the `key' and `context' and TRUE. If this returns FALSE then there are no anymore any entrys. Usage: while (silc_hash_table_get(&htl, &key, &context)) */ SilcBool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context) { SilcHashTableEntry entry = (SilcHashTableEntry)htl->entry; if (!htl->ht->entry_count) return FALSE; while (!entry && htl->index < primesize[htl->ht->table_size]) { entry = htl->ht->table[htl->index]; htl->index++; } if (!entry) return FALSE; htl->entry = entry->next; if (key) *key = entry->key; if (context) *context = entry->context; return TRUE; } /**************************** Utility functions *****************************/ /* Case sensitive hashing */ SilcUInt32 silc_hash_string(void *key, void *user_context) { char *s = (char *)key; SilcUInt32 h = 0; while (*s != '\0') { h += *s++; h += (h << 10); h ^= (h >> 6); } h += (h << 3); h ^= (h >> 11); h += (h << 15); return h; } /* Case-insensitive hashing */ SilcUInt32 silc_hash_string_case(void *key, void *user_context) { char *s = (char *)key; SilcUInt32 h = 0; while (*s != '\0') { h += tolower((int)*s); h += (h << 10); h ^= (h >> 6); s++; } h += (h << 3); h ^= (h >> 11); h += (h << 15); return h; } /* Hash UTF-8 string */ SilcUInt32 silc_hash_utf8_string(void *key, void *user_context) { char *s = (char *)key; SilcUInt32 h = 0; while (*s != '\0') { h += *s++; h += (h << 10); h ^= (h >> 6); } h += (h << 3); h ^= (h >> 11); h += (h << 15); return h; } /* Basic hash function to hash integers. */ SilcUInt32 silc_hash_uint(void *key, void *user_context) { return SILC_PTR_TO_32(key); } /* Basic hash funtion to hash pointers. */ SilcUInt32 silc_hash_ptr(void *key, void *user_context) { return SILC_PTR_TO_32(key); } /* Hash binary data. The `user_context' is the data length. */ SilcUInt32 silc_hash_data(void *key, void *user_context) { SilcUInt32 len = SILC_PTR_TO_32(user_context), h, i; unsigned char *data = (char *)key; h = (data[0] * data[len - 1] + 1) * len; for (i = 0; i < len; i++) { h += data[i]; h += (h << 10); h ^= (h >> 6); } h += (h << 3); h ^= (h >> 11); h += (h << 15); return h; } /* Compares two strings. */ SilcBool silc_hash_string_compare(void *key1, void *key2, void *user_context) { return !strcmp((char *)key1, (char *)key2); } /* Compares two strings, ignores case. */ SilcBool silc_hash_string_case_compare(void *key1, void *key2, void *user_context) { return !strcasecmp((char *)key1, (char *)key2); } /* Compares binary data. */ SilcBool silc_hash_data_compare(void *key1, void *key2, void *user_context) { SilcUInt32 len = SILC_PTR_TO_32(user_context); return !memcmp(key1, key2, len); } /* Compares UTF-8 string. */ SilcBool silc_hash_utf8_compare(void *key1, void *key2, void *user_context) { int l1 = strlen((char *)key1); int l2 = strlen((char *)key2); if (l1 != l2) return FALSE; return !memcmp(key1, key2, l2); }