Added SILC Thread Queue API
[silc.git] / lib / silcutil / silchashtable.c
index b166644b287c1df06a5bf3f97957b4f436033b16..13c655e792160a6af32414ee3c2d65784fc6848e 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 - 2006 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
@@ -63,6 +63,7 @@ typedef struct SilcHashTableEntryStruct {
 
 /* Hash table. */
 struct SilcHashTableStruct {
+  SilcStack stack;
   SilcHashTableEntry *table;
   SilcUInt32 table_size;
   SilcUInt32 entry_count;
@@ -281,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;
@@ -290,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;
@@ -325,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++;
@@ -348,7 +349,8 @@ 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,
@@ -360,20 +362,30 @@ SilcHashTable silc_hash_table_alloc(SilcUInt32 table_size,
   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;
@@ -391,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;
 
@@ -401,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 */
@@ -451,7 +465,7 @@ SilcBool 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. */
 
-SilcBool silc_hash_table_replace(SilcHashTable ht, void *key, void *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);
@@ -459,10 +473,10 @@ SilcBool silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
 
 /* Same as above but with specific hash function. */
 
-SilcBool silc_hash_table_replace_ext(SilcHashTable ht, void *key,
-                                    void *context,
-                                    SilcHashFunction hash,
-                                    void *hash_user_context)
+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);
@@ -479,8 +493,10 @@ SilcBool silc_hash_table_del(SilcHashTable ht, void *key)
   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;
 
@@ -495,7 +511,7 @@ SilcBool 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--;
 
@@ -508,12 +524,12 @@ SilcBool silc_hash_table_del(SilcHashTable ht, void *key)
 /* 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)
+                                SilcHashFunction hash,
+                                void *hash_user_context,
+                                SilcHashCompare compare,
+                                void *compare_user_context,
+                                SilcHashDestructor destructor,
+                                void *destructor_user_context)
 {
   SilcHashTableEntry *entry, prev, e;
 
@@ -525,8 +541,10 @@ SilcBool 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;
 
@@ -545,7 +563,7 @@ SilcBool 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--;
 
@@ -561,7 +579,7 @@ SilcBool silc_hash_table_del_ext(SilcHashTable ht, void *key,
    whether the correct entry is being deleted. */
 
 SilcBool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
-                                   void *context)
+                                       void *context)
 {
   SilcHashTableEntry *entry, prev, e;
 
@@ -570,8 +588,10 @@ SilcBool 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;
 
@@ -586,7 +606,7 @@ SilcBool 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--;
 
@@ -599,13 +619,13 @@ SilcBool silc_hash_table_del_by_context(SilcHashTable ht, void *key,
 /* 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)
+                                           void *context,
+                                           SilcHashFunction hash,
+                                           void *hash_user_context,
+                                           SilcHashCompare compare,
+                                           void *compare_user_context,
+                                           SilcHashDestructor destructor,
+                                           void *destructor_user_context)
 {
   SilcHashTableEntry *entry, prev, e;
 
@@ -619,8 +639,10 @@ SilcBool 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;
 
@@ -639,7 +661,7 @@ SilcBool 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--;
 
@@ -656,7 +678,7 @@ SilcBool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key,
    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)
+                             void **ret_key, void **ret_context)
 {
   return silc_hash_table_find_ext(ht, key, ret_key, ret_context,
                                  NULL, NULL, NULL, NULL);
@@ -665,11 +687,11 @@ SilcBool silc_hash_table_find(SilcHashTable ht, void *key,
 /* 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)
+                                 void **ret_key, void **ret_context,
+                                 SilcHashFunction hash,
+                                 void *hash_user_context,
+                                 SilcHashCompare compare,
+                                 void *compare_user_context)
 {
   SilcHashTableEntry *entry;
 
@@ -683,8 +705,10 @@ SilcBool 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;
@@ -697,7 +721,7 @@ SilcBool silc_hash_table_find_ext(SilcHashTable ht, void *key,
 /* 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)
+                                        void *context, void **ret_key)
 {
   return silc_hash_table_find_by_context_ext(ht, key, context, ret_key,
                                             NULL, NULL, NULL, NULL);
@@ -706,11 +730,11 @@ SilcBool silc_hash_table_find_by_context(SilcHashTable ht, void *key,
 /* 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)
+                                            void *context, void **ret_key,
+                                            SilcHashFunction hash,
+                                            void *hash_user_context,
+                                            SilcHashCompare compare,
+                                            void *compare_user_context)
 {
   SilcHashTableEntry *entry;
 
@@ -724,8 +748,10 @@ SilcBool 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;
@@ -827,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;
@@ -842,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. */
@@ -882,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;
@@ -898,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
@@ -960,3 +988,136 @@ SilcBool silc_hash_table_get(SilcHashTableList *htl, void **key,
 
   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);
+}