Merged silc_1_0_branch to trunk.
[silc.git] / lib / silccore / silcidcache.c
index 02ed07e07ba4fe39ce16b76a8685e507d8b9ae3f..fb284c22237fc333807f9a90b56e4deb264e6d67 100644 (file)
@@ -10,7 +10,7 @@
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
+
   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
@@ -26,7 +26,7 @@
 static void silc_idcache_destructor(void *key, void *context,
                                    void *user_context);
 static SilcIDCacheList silc_idcache_list_alloc();
-static void silc_idcache_list_add(SilcIDCacheList list, 
+static void silc_idcache_list_add(SilcIDCacheList list,
                                  SilcIDCacheEntry cache);
 
 /*
@@ -70,9 +70,9 @@ struct SilcIDCacheStruct {
   SilcIdType type;
 };
 
-/* 
+/*
    ID Cache list.
-   
+
    This is returned when searching the cache. Enumeration functions are
    provided to traverse the list; actually this is used as table not as
    list. :)
@@ -85,19 +85,20 @@ struct SilcIDCacheStruct {
 
 */
 struct SilcIDCacheListStruct {
-  SilcIDCacheEntry cache[64];
+  SilcIDCacheEntry cache[128];
   SilcIDCacheEntry *cache_dyn;
-  uint32 cache_dyn_count;
-  uint32 cache_count;
-  uint32 pos;
+  SilcUInt32 cache_dyn_count;
+  SilcUInt32 cache_count;
+  SilcUInt32 pos;
+  bool dyn;
 };
 
 /* Allocates new ID cache object. The initial amount of allocated entries
-   can be sent as argument. If `count' is 0 the system uses default values. 
+   can be sent as argument. If `count' is 0 the system uses default values.
    The `id_type' defines the types of the ID's that will be saved to the
    cache. */
 
-SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
+SilcIDCache silc_idcache_alloc(SilcUInt32 count, SilcIdType id_type,
                               SilcIDCacheDestructor destructor)
 {
   SilcIDCache cache;
@@ -105,20 +106,32 @@ SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
   SILC_LOG_DEBUG(("Allocating new cache"));
 
   cache = silc_calloc(1, sizeof(*cache));
-  cache->id_table = silc_hash_table_alloc(count, silc_hash_id, 
-                                         (void *)(uint32)id_type,
-                                         silc_hash_id_compare, 
-                                         (void *)(uint32)id_type, 
-                                         silc_idcache_destructor, NULL, 
-                                         FALSE);
+  if (!cache)
+    return NULL;
+  cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
+                                         (void *)(SilcUInt32)id_type,
+                                         silc_hash_id_compare,
+                                         (void *)(SilcUInt32)id_type,
+                                         silc_idcache_destructor, NULL, TRUE);
   cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL,
-                                           silc_hash_string_compare, NULL, 
-                                           NULL, NULL, FALSE);
+                                           silc_hash_string_compare, NULL,
+                                           NULL, NULL, TRUE);
   cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
-                                              NULL, NULL, NULL, NULL, FALSE);
+                                              NULL, NULL, NULL, NULL, TRUE);
   cache->destructor = destructor;
   cache->type = id_type;
 
+  if (!cache->id_table || !cache->name_table || !cache->context_table) {
+    if (cache->id_table)
+      silc_hash_table_free(cache->id_table);
+    if (cache->name_table)
+      silc_hash_table_free(cache->name_table);
+    if (cache->context_table)
+      silc_hash_table_free(cache->context_table);
+    silc_free(cache);
+    return NULL;
+  }
+
   return cache;
 }
 
@@ -140,19 +153,20 @@ void silc_idcache_free(SilcIDCache cache)
    If the `expire' is TRUE the entry expires in default time and if FALSE
    the entry never expires from the cache. */
 
-bool silc_idcache_add(SilcIDCache cache, char *name, void *id, 
-                     void *context, int expire)
+bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
+                     void *context, int expire, SilcIDCacheEntry *ret)
 {
   SilcIDCacheEntry c;
-  uint32 curtime = time(NULL);
 
   SILC_LOG_DEBUG(("Adding cache entry"));
 
   /* Allocate new cache entry */
   c = silc_calloc(1, sizeof(*c));
+  if (!c)
+    return FALSE;
   c->id = id;
   c->name = name;
-  c->expire = (expire ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
+  c->expire = expire;
   c->context = context;
 
   /* Add the new entry to the hash tables */
@@ -164,13 +178,8 @@ bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
   if (context)
     silc_hash_table_add(cache->context_table, context, c);
 
-  /* See whether we have time to rehash the tables */
-  if ((silc_hash_table_count(cache->id_table) / 2) >
-      silc_hash_table_size(cache->id_table)) {
-    silc_hash_table_rehash(cache->id_table, 0);
-    silc_hash_table_rehash(cache->name_table, 0);
-    silc_hash_table_rehash(cache->context_table, 0);
-  }
+  if (ret)
+    *ret = c;
 
   return TRUE;
 }
@@ -180,7 +189,11 @@ bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
 static void silc_idcache_destructor(void *key, void *context,
                                    void *user_context)
 {
-  silc_free(context);
+  SilcIDCacheEntry c = context;
+  if (c) {
+    memset(c, 'F', sizeof(*c));
+    silc_free(c);
+  }
 }
 
 /* Delete cache entry from cache. */
@@ -197,6 +210,10 @@ bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
     ret = silc_hash_table_del(cache->context_table, old->context);
   if (old->id)
     ret = silc_hash_table_del(cache->id_table, old->id);
+  else {
+    silc_idcache_destructor(NULL, old, NULL);
+    ret = TRUE;
+  }
 
   return ret;
 }
@@ -217,9 +234,9 @@ bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
    functions are NULL then default values are used. */
 
 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
-                               SilcHashFunction hash, 
+                               SilcHashFunction hash,
                                void *hash_context,
-                               SilcHashCompare compare, 
+                               SilcHashCompare compare,
                                void *compare_context)
 {
   SilcIDCacheEntry c;
@@ -228,7 +245,7 @@ bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
   SILC_LOG_DEBUG(("Deleting cache entry"));
 
   if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
-                               hash, hash_context, compare, 
+                               hash, hash_context, compare,
                                compare_context))
     return FALSE;
 
@@ -238,7 +255,8 @@ bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
     ret = silc_hash_table_del(cache->context_table, c->context);
   if (c->id)
     ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
-                                 hash_context, compare, compare_context);
+                                 hash_context, compare, compare_context,
+                                 NULL, NULL);
 
   return ret;
 }
@@ -261,6 +279,10 @@ bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
     ret = silc_hash_table_del(cache->context_table, c->context);
   if (c->id)
     ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
+  else {
+    silc_idcache_destructor(NULL, c, NULL);
+    ret = TRUE;
+  }
 
   return ret;
 }
@@ -276,22 +298,45 @@ bool silc_idcache_del_all(SilcIDCache cache)
   return TRUE;
 }
 
+static void silc_idcache_destructor_dummy(void *key, void *context,
+                                         void *user_context)
+{
+  /* Dummy - nothing */
+}
+
 /* Foreach callback fro silc_idcache_purge. */
 
 static void silc_idcache_purge_foreach(void *key, void *context,
                                       void *user_context)
 {
   SilcIDCache cache = (SilcIDCache)user_context;
-  uint32 curtime = time(NULL);
+  SilcUInt32 curtime = time(NULL);
   SilcIDCacheEntry c = (SilcIDCacheEntry)context;
+  bool ret = FALSE;
 
-  if (c->expire && c->expire < curtime) {
-    /* Call the destructor */
-    if (cache->destructor)
-      cache->destructor(cache, c);
+  if (!context)
+    return;
 
-    /* Delete the entry */
-    silc_idcache_del(cache, c);
+  if (c->expire && c->expire < curtime) {
+    /* Remove the entry from the hash tables */
+    if (c->name)
+      ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
+    if (c->context)
+      ret = silc_hash_table_del(cache->context_table, c->context);
+    if (c->id)
+      ret =
+       silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
+                                          NULL, NULL, NULL, NULL,
+                                          silc_idcache_destructor_dummy,
+                                          NULL);
+    if (ret == TRUE) {
+      /* Call the destructor */
+      if (cache->destructor)
+       cache->destructor(cache, c);
+
+      /* Free the entry, it has been deleted from the hash tables */
+      silc_idcache_destructor(NULL, c, NULL);
+    }
   }
 }
 
@@ -308,24 +353,31 @@ bool silc_idcache_purge(SilcIDCache cache)
 
 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
 {
-  SilcIDCacheEntry entry;
+  SilcIDCacheEntry c;
   bool ret = FALSE;
 
-  if (!silc_hash_table_find(cache->context_table, context, NULL, 
-                           (void *)&entry))
+  if (!silc_hash_table_find(cache->context_table, context, NULL,
+                           (void *)&c))
     return FALSE;
 
-  /* Call the destructor */
-  if (cache->destructor)
-    cache->destructor(cache, entry);
-  
-  if (entry->name)
-    ret = silc_hash_table_del_by_context(cache->name_table, entry->name, 
-                                        entry);
-  if (entry->context)
-    ret = silc_hash_table_del(cache->context_table, entry->context);
-  if (entry->id)
-    ret = silc_hash_table_del_by_context(cache->id_table, entry->id, entry);
+    /* Remove the entry from the hash tables */
+  if (c->name)
+    ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
+  if (c->context)
+    ret = silc_hash_table_del(cache->context_table, c->context);
+  if (c->id)
+    ret =
+      silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
+                                        NULL, NULL, NULL, NULL,
+                                        silc_idcache_destructor_dummy, NULL);
+  if (ret == TRUE) {
+    /* Call the destructor */
+    if (cache->destructor)
+      cache->destructor(cache, c);
+
+    /* Free the entry, it has been deleted from the hash tables */
+    silc_idcache_destructor(NULL, c, NULL);
+  }
 
   return ret;
 }
@@ -337,6 +389,8 @@ static void silc_idcache_get_all_foreach(void *key, void *context,
                                         void *user_context)
 {
   SilcIDCacheList list = (SilcIDCacheList)user_context;
+  if (!context)
+    return;
   silc_idcache_list_add(list, (SilcIDCacheEntry)context);
 }
 
@@ -350,6 +404,8 @@ bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
     return TRUE;
 
   list = silc_idcache_list_alloc();
+  if (!list)
+    return FALSE;
   silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
 
   if (silc_idcache_list_count(list) == 0) {
@@ -364,12 +420,14 @@ bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
 
 /* Find ID Cache entry by ID. May return multiple entries. */
 
-bool silc_idcache_find_by_id(SilcIDCache cache, void *id, 
+bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
                             SilcIDCacheList *ret)
 {
   SilcIDCacheList list;
 
   list = silc_idcache_list_alloc();
+  if (!list)
+    return FALSE;
 
   if (!ret)
     return TRUE;
@@ -391,21 +449,21 @@ bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
    If `hash' is NULL then the default hash funtion is used and if `compare'
    is NULL default comparison function is used. */
 
-bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id, 
-                                    SilcHashFunction hash, 
+bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
+                                    SilcHashFunction hash,
                                     void *hash_context,
-                                    SilcHashCompare compare, 
+                                    SilcHashCompare compare,
                                     void *compare_context,
                                     SilcIDCacheEntry *ret)
 {
   return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
-                                 hash, hash_context, compare, 
+                                 hash, hash_context, compare,
                                  compare_context);
 }
 
 /* Find one specific ID entry. */
 
-bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id, 
+bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
                                 SilcIDCacheEntry *ret)
 {
   return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
@@ -413,10 +471,10 @@ bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
 
 /* Finds cache entry by context. */
 
-bool silc_idcache_find_by_context(SilcIDCache cache, void *context, 
+bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
                                  SilcIDCacheEntry *ret)
 {
-  return silc_hash_table_find(cache->context_table, context, NULL, 
+  return silc_hash_table_find(cache->context_table, context, NULL,
                              (void *)ret);
 }
 
@@ -428,11 +486,13 @@ bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
   SilcIDCacheList list;
 
   list = silc_idcache_list_alloc();
+  if (!list)
+    return FALSE;
 
   if (!ret)
     return TRUE;
 
-  silc_hash_table_find_foreach(cache->name_table, name, 
+  silc_hash_table_find_foreach(cache->name_table, name,
                               silc_idcache_get_all_foreach, list);
 
   if (silc_idcache_list_count(list) == 0) {
@@ -463,6 +523,8 @@ static SilcIDCacheList silc_idcache_list_alloc()
   SilcIDCacheList list;
 
   list = silc_calloc(1, sizeof(*list));
+  if (!list)
+    return FALSE;
 
   return list;
 }
@@ -476,7 +538,7 @@ static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
 
   /* Try to add to static cache */
   if (!list->cache_dyn_count)
-    for (i = 0; i < sizeof(list->cache); i++) {
+    for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
       if (!list->cache[i]) {
        list->cache[i] = cache;
        list->cache_count++;
@@ -496,17 +558,19 @@ static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
   if (i >= list->cache_dyn_count) {
     int k;
 
-    i += 5;
-    list->cache_dyn = silc_realloc(list->cache_dyn, 
-                                  sizeof(*list->cache) * (i));
+    i = list->cache_dyn_count;
+    list->cache_dyn = silc_realloc(list->cache_dyn,
+                                  sizeof(*list->cache_dyn) * (i + 5));
+    if (!list->cache_dyn)
+      return;
 
     /* NULL the reallocated area */
-    for (k = list->cache_dyn_count; k < i; k++)
+    for (k = i; k < (i + 5); k++)
       list->cache_dyn[k] = NULL;
 
-    list->cache_dyn[list->cache_dyn_count] = cache;
-    list->cache_dyn_count = i;
+    list->cache_dyn[i] = cache;
     list->cache_count++;
+    list->cache_dyn_count += 5;
   }
 }
 
@@ -525,7 +589,7 @@ bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
 
   if (!list->cache[list->pos])
     return FALSE;
-  
+
   if (ret)
     *ret = list->cache[list->pos];
 
@@ -536,30 +600,30 @@ bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
 
 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
 {
-  int dyn = FALSE;
   list->pos++;
 
-  if (list->pos >= sizeof(list->cache)) {
+  if (!list->dyn &&
+      list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
     list->pos = 0;
-    dyn = TRUE;
+    list->dyn = TRUE;
   }
 
-  if (dyn && list->pos >= list->cache_dyn_count)
+  if (list->dyn && list->pos >= list->cache_dyn_count)
     return FALSE;
 
-  if (!dyn && !list->cache[list->pos])
+  if (!list->dyn && !list->cache[list->pos])
     return FALSE;
-  
-  if (dyn && !list->cache_dyn[list->pos])
+
+  if (list->dyn && !list->cache_dyn[list->pos])
     return FALSE;
-  
+
   if (ret) {
-    if (!dyn)
+    if (!list->dyn)
       *ret = list->cache[list->pos];
     else
       *ret = list->cache_dyn[list->pos];
   }
-  
+
   return TRUE;
 }