X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilchashtable.c;h=13c655e792160a6af32414ee3c2d65784fc6848e;hb=96d69ecd5b1e5090db05efee7c992e2b2b1e3062;hp=ab5a25a51f1cf28e057eedcf47e2f019fb62bceb;hpb=c257b555225193e54d85daf541d29578b3c93882;p=silc.git diff --git a/lib/silcutil/silchashtable.c b/lib/silcutil/silchashtable.c index ab5a25a5..13c655e7 100644 --- a/lib/silcutil/silchashtable.c +++ b/lib/silcutil/silchashtable.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2001 - 2003 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 @@ -25,7 +25,7 @@ hash table. */ /* $Id$ */ -#include "silcincludes.h" +#include "silc.h" #include "silchashtable.h" /* Define to 1 if you want hash table debug enabled */ @@ -63,6 +63,7 @@ typedef struct SilcHashTableEntryStruct { /* Hash table. */ struct SilcHashTableStruct { + SilcStack stack; SilcHashTableEntry *table; SilcUInt32 table_size; SilcUInt32 entry_count; @@ -77,12 +78,13 @@ struct SilcHashTableStruct { /* Prime sizes for the hash table. The size of the table will always be one of these. */ -const SilcUInt32 primesize[42] = +const SilcUInt32 primesize[] = { 3, 5, 11, 17, 37, 67, 109, 131, 163, 257, 367, 521, 823, 1031, - 1237, 2053, 2777, 4099, 6247, 8209, 14057, 16411, 21089, 32771, 47431, - 65537, 106721, 131101, 262147, 360163, 524309, 810343, 1048583, 2097169, - 4194319, 6153409, 8388617, 13845163, 16777259, 33554467, 67108879 + 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. */ @@ -215,7 +217,7 @@ silc_hash_table_find_internal_all(SilcHashTable ht, void *key, void *foreach_user_context) { SilcHashTableEntry e, tmp; - bool auto_rehash, found = FALSE; + SilcBool auto_rehash, found = FALSE; SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context); SILC_HT_DEBUG(("index %d key %p", i, key)); @@ -255,7 +257,7 @@ silc_hash_table_find_internal_all(SilcHashTable ht, void *key, /* Internal routine to add new key to the hash table */ -static inline bool +static inline SilcBool silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context, SilcHashFunction hash, void *hash_user_context) @@ -280,7 +282,7 @@ silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context, SILC_HT_DEBUG(("Collision; adding new key to list")); - e->next = silc_calloc(1, sizeof(*e->next)); + e->next = silc_scalloc(ht->stack, 1, sizeof(*e->next)); if (!e->next) return FALSE; e->next->key = key; @@ -289,7 +291,7 @@ silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context, } else { /* New key */ SILC_HT_DEBUG(("New key")); - *entry = silc_calloc(1, sizeof(**entry)); + *entry = silc_scalloc(ht->stack, 1, sizeof(**entry)); if (!(*entry)) return FALSE; (*entry)->key = key; @@ -305,7 +307,7 @@ silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context, /* Internal routine to replace old key with new one (if it exists) */ -static inline bool +static inline SilcBool silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context, SilcHashFunction hash, void *hash_user_context) @@ -324,7 +326,7 @@ silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context, ht->destructor_user_context); } else { /* New key */ - *entry = silc_calloc(1, sizeof(**entry)); + *entry = silc_scalloc(ht->stack, 1, sizeof(**entry)); if (!(*entry)) return FALSE; ht->entry_count++; @@ -347,32 +349,43 @@ silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context, destructor function, respectively. The `hash' is mandatory, the others are optional. */ -SilcHashTable silc_hash_table_alloc(SilcUInt32 table_size, +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, - bool auto_rehash) + SilcBool auto_rehash) { SilcHashTable ht; SilcUInt32 size_index = SILC_HASH_TABLE_SIZE; - if (!hash) + if (!hash) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); return NULL; + } + + if (stack) + stack = silc_stack_alloc(0, stack); - ht = silc_calloc(1, sizeof(*ht)); - if (!ht) + ht = silc_scalloc(stack, 1, sizeof(*ht)); + if (!ht) { + silc_stack_free(stack); return NULL; - ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size, - &size_index) : - primesize[SILC_HASH_TABLE_SIZE], - sizeof(*ht->table)); + } + 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_free(ht); + silc_stack_free(stack); + silc_sfree(stack, ht); return NULL; } + ht->stack = stack; ht->table_size = size_index; ht->hash = hash; ht->compare = compare; @@ -390,6 +403,7 @@ SilcHashTable silc_hash_table_alloc(SilcUInt32 table_size, void silc_hash_table_free(SilcHashTable ht) { + SilcStack stack = ht->stack; SilcHashTableEntry e, tmp; int i; @@ -400,12 +414,13 @@ void silc_hash_table_free(SilcHashTable ht) ht->destructor(e->key, e->context, ht->destructor_user_context); tmp = e; e = e->next; - silc_free(tmp); + silc_sfree(stack, tmp); } } - silc_free(ht->table); - silc_free(ht); + silc_sfree(stack, ht->table); + silc_sfree(stack, ht); + silc_stack_free(stack); } /* Returns the size of the hash table */ @@ -429,18 +444,20 @@ SilcUInt32 silc_hash_table_count(SilcHashTable ht) hash table. This function quarantees that the entry is always added to the hash table reliably (it is collision resistant). */ -void silc_hash_table_add(SilcHashTable ht, void *key, void *context) +SilcBool silc_hash_table_add(SilcHashTable ht, void *key, void *context) { - silc_hash_table_add_internal(ht, key, context, ht->hash, - ht->hash_user_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. */ -void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context, - SilcHashFunction hash, void *hash_user_context) +SilcBool silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context, + SilcHashFunction hash, + void *hash_user_context) { - silc_hash_table_add_internal(ht, key, context, hash, 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 @@ -448,34 +465,38 @@ void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context, the `context. The destructor function will be called for the replaced key and context. */ -void silc_hash_table_replace(SilcHashTable ht, void *key, void *context) +SilcBool silc_hash_table_set(SilcHashTable ht, void *key, void *context) { - silc_hash_table_replace_internal(ht, key, context, ht->hash, - ht->hash_user_context); + return silc_hash_table_replace_internal(ht, key, context, ht->hash, + ht->hash_user_context); } /* Same as above but with specific hash function. */ -void silc_hash_table_replace_ext(SilcHashTable ht, void *key, void *context, +SilcBool silc_hash_table_set_ext(SilcHashTable ht, void *key, + void *context, SilcHashFunction hash, void *hash_user_context) { - silc_hash_table_replace_internal(ht, key, context, hash, 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. */ -bool silc_hash_table_del(SilcHashTable ht, void *key) +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) + if (*entry == NULL) { + silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; + } e = *entry; @@ -490,7 +511,7 @@ bool silc_hash_table_del(SilcHashTable ht, void *key) if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); - silc_free(e); + silc_sfree(ht->stack, e); ht->entry_count--; @@ -502,13 +523,13 @@ bool silc_hash_table_del(SilcHashTable ht, void *key) /* Same as above but with specific hash and compare functions. */ -bool 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) +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; @@ -520,8 +541,10 @@ bool silc_hash_table_del_ext(SilcHashTable ht, void *key, compare_user_context ? compare_user_context : ht->compare_user_context); - if (*entry == NULL) + if (*entry == NULL) { + silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; + } e = *entry; @@ -540,7 +563,7 @@ bool silc_hash_table_del_ext(SilcHashTable ht, void *key, if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); } - silc_free(e); + silc_sfree(ht->stack, e); ht->entry_count--; @@ -555,8 +578,8 @@ bool silc_hash_table_del_ext(SilcHashTable ht, void *key, have duplicate keys. In that case the `context' may be used to check whether the correct entry is being deleted. */ -bool silc_hash_table_del_by_context(SilcHashTable ht, void *key, - void *context) +SilcBool silc_hash_table_del_by_context(SilcHashTable ht, void *key, + void *context) { SilcHashTableEntry *entry, prev, e; @@ -565,8 +588,10 @@ bool silc_hash_table_del_by_context(SilcHashTable ht, void *key, ht->hash_user_context, ht->compare, ht->compare_user_context); - if (*entry == NULL) + if (*entry == NULL) { + silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; + } e = *entry; @@ -581,7 +606,7 @@ bool silc_hash_table_del_by_context(SilcHashTable ht, void *key, if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); - silc_free(e); + silc_sfree(ht->stack, e); ht->entry_count--; @@ -593,14 +618,14 @@ bool silc_hash_table_del_by_context(SilcHashTable ht, void *key, /* Same as above but with specific hash and compare functions. */ -bool 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) +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; @@ -614,8 +639,10 @@ bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key, compare_user_context ? compare_user_context : ht->compare_user_context); - if (*entry == NULL) + if (*entry == NULL) { + silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; + } e = *entry; @@ -634,7 +661,7 @@ bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key, if (ht->destructor) ht->destructor(e->key, e->context, ht->destructor_user_context); } - silc_free(e); + silc_sfree(ht->stack, e); ht->entry_count--; @@ -650,8 +677,8 @@ bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key, respectively. If the `ret_key and `ret_context' are NULL then this maybe used only to check whether given key exists in the table. */ -bool silc_hash_table_find(SilcHashTable ht, void *key, - void **ret_key, void **ret_context) +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); @@ -659,12 +686,12 @@ bool silc_hash_table_find(SilcHashTable ht, void *key, /* Same as above but with specified hash and comparison functions. */ -bool 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) +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; @@ -678,8 +705,10 @@ bool silc_hash_table_find_ext(SilcHashTable ht, void *key, compare_user_context ? compare_user_context : ht->compare_user_context); - if (*entry == NULL) + if (*entry == NULL) { + silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; + } if (ret_key) *ret_key = (*entry)->key; @@ -691,8 +720,8 @@ bool silc_hash_table_find_ext(SilcHashTable ht, void *key, /* Same as silc_hash_table_find but finds with specific context. */ -bool silc_hash_table_find_by_context(SilcHashTable ht, void *key, - void *context, void **ret_key) +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); @@ -700,12 +729,12 @@ bool silc_hash_table_find_by_context(SilcHashTable ht, void *key, /* Same as above but with specified hash and comparison functions. */ -bool 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) +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; @@ -719,8 +748,10 @@ bool silc_hash_table_find_by_context_ext(SilcHashTable ht, void *key, compare_user_context ? compare_user_context : ht->compare_user_context); - if (!entry || !(*entry)) + if (!entry || !(*entry)) { + silc_set_errno(SILC_ERR_NOT_FOUND); return FALSE; + } if (ret_key) *ret_key = (*entry)->key; @@ -772,7 +803,7 @@ void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach, { SilcHashTableEntry e, tmp; int i; - bool auto_rehash; + SilcBool auto_rehash; if (!foreach) return; @@ -801,7 +832,7 @@ void silc_hash_table_rehash(SilcHashTable ht, SilcUInt32 new_size) int i; SilcHashTableEntry *table, e, tmp; SilcUInt32 table_size, size_index; - bool auto_rehash; + SilcBool auto_rehash; SILC_HT_DEBUG(("Start")); @@ -822,7 +853,8 @@ void silc_hash_table_rehash(SilcHashTable ht, SilcUInt32 new_size) ht->auto_rehash = FALSE; /* Allocate new table */ - ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table)); + ht->table = silc_scalloc(ht->stack, + primesize[size_index], sizeof(*ht->table)); if (!ht->table) return; ht->table_size = size_index; @@ -837,14 +869,14 @@ void silc_hash_table_rehash(SilcHashTable ht, SilcUInt32 new_size) e = e->next; /* Remove old entry */ - silc_free(tmp); + silc_sfree(ht->stack, tmp); } } ht->auto_rehash = auto_rehash; /* Remove old table */ - silc_free(table); + silc_sfree(ht->stack, table); } /* Same as above but with specific hash function. */ @@ -856,7 +888,7 @@ void silc_hash_table_rehash_ext(SilcHashTable ht, SilcUInt32 new_size, int i; SilcHashTableEntry *table, e, tmp; SilcUInt32 table_size, size_index; - bool auto_rehash; + SilcBool auto_rehash; SILC_HT_DEBUG(("Start")); @@ -877,7 +909,8 @@ void silc_hash_table_rehash_ext(SilcHashTable ht, SilcUInt32 new_size, ht->auto_rehash = FALSE; /* Allocate new table */ - ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table)); + ht->table = silc_scalloc(ht->stack, + primesize[size_index], sizeof(*ht->table)); if (!ht->table) return; ht->table_size = size_index; @@ -893,14 +926,14 @@ void silc_hash_table_rehash_ext(SilcHashTable ht, SilcUInt32 new_size, e = e->next; /* Remove old entry */ - silc_free(tmp); + silc_sfree(ht->stack, tmp); } } ht->auto_rehash = auto_rehash; /* Remove old table */ - silc_free(table); + silc_sfree(ht->stack, table); } /* Prepares the `htl' list structure sent as argument to be used in the @@ -930,7 +963,8 @@ void silc_hash_table_list_reset(SilcHashTableList *htl) `context' and TRUE. If this returns FALSE then there are no anymore any entrys. Usage: while (silc_hash_table_get(&htl, &key, &context)) */ -bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context) +SilcBool silc_hash_table_get(SilcHashTableList *htl, void **key, + void **context) { SilcHashTableEntry entry = (SilcHashTableEntry)htl->entry; @@ -954,3 +988,136 @@ bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **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); +}