Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
- Copyright (C) 2000 - 2001 Pekka Riikonen
+ Copyright (C) 2000 - 2006 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; either version 2 of the License, or
- (at your option) any later version.
-
+ 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
*/
/* $Id$ */
-#include "silcincludes.h"
+#include "silc.h"
#include "silcidcache.h"
-/* Static prototypes */
-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,
- SilcIDCacheEntry cache);
-
-/*
- SILC ID Cache object.
-
- This is context for the ID cache system. This includes all the cache
- entries and other internal data. This is read-only object and not
- visible outside this cache system.
-
- Fields are as follows:
-
- SilcHashTable id_table
+/************************** Types and definitions ***************************/
- Hash table using the ID as the key.
-
- SilcHashTable name_table
+/* ID Cache context */
+struct SilcIDCacheStruct {
+ SilcHashTable id_table; /* ID hash table */
+ SilcHashTable name_table; /* Name hash table */
+ SilcHashTable context_table; /* Context hash table */
+ SilcIDCacheDestructor destructor; /* Entry destructor */
+ void *context; /* Destructor context */
+ SilcIdType id_type; /* Type of ID cache */
+};
- Hash table using the name as the key.
- SilcHashTable context_table
+/************************ Static utility functions **************************/
- Hash table using the context as the key.
+/* Callback that is called by the hash table routine when traversing
+ entries in the hash table. */
- SilcIDCacheDestructor destructor
+static void silc_idcache_get_all_foreach(void *key, void *context,
+ void *user_context)
+{
+ SilcList *list = user_context;
+ if (!context)
+ return;
+ silc_list_add(*list, context);
+}
- Destructor callback that is called when an cache entry expires or is
- purged from the ID cache. The application must not free cache entry
- because the library will do it automatically. The appliation, however,
- is responsible of freeing any data in the entry.
+/* Cache entry destructor */
- SilcIdType id_type
+static void silc_idcache_destructor(SilcIDCache cache,
+ SilcIDCacheEntry entry,
+ void *app_context)
+{
+ if (cache->destructor)
+ cache->destructor(cache, entry, cache->destructor, app_context);
- Indicates the type of the ID's this cache holds.
+ memset(entry, 'F', sizeof(*entry));
+ silc_free(entry);
+}
-*/
-struct SilcIDCacheStruct {
- SilcHashTable id_table;
- SilcHashTable name_table;
- SilcHashTable context_table;
- SilcIDCacheDestructor destructor;
- 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. :)
+/****************************** Public API **********************************/
- By default the found cache entries are saved into the static cache
- table to provide access without reallocation. However, if the static
- table is full, rest of the cache entries are dynamically allocated
- into `cache_dyn' table. Traversing functions automatically handles
- these situations.
+/* Allocates new ID cache object. */
-*/
-struct SilcIDCacheListStruct {
- SilcIDCacheEntry cache[64];
- SilcIDCacheEntry *cache_dyn;
- uint32 cache_dyn_count;
- uint32 cache_count;
- uint32 pos;
-};
-
-/* 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.
- 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,
- SilcIDCacheDestructor destructor)
+SilcIDCache silc_idcache_alloc(SilcUInt32 count, SilcIdType id_type,
+ SilcIDCacheDestructor destructor,
+ void *destructor_context)
{
SilcIDCache cache;
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);
- cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL,
- silc_hash_string_compare, NULL,
- NULL, NULL, FALSE);
+ if (!cache)
+ return NULL;
+
+ cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
+ SILC_32_TO_PTR(id_type),
+ silc_hash_id_compare,
+ SILC_32_TO_PTR(id_type),
+ NULL, NULL, TRUE);
+ cache->name_table = silc_hash_table_alloc(count, silc_hash_utf8_string, NULL,
+ silc_hash_utf8_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;
+ cache->context = destructor_context;
+ cache->id_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;
}
void silc_idcache_free(SilcIDCache cache)
{
- if (cache) {
- silc_hash_table_free(cache->id_table);
- silc_hash_table_free(cache->name_table);
- silc_hash_table_free(cache->context_table);
- silc_free(cache);
- }
+ silc_hash_table_free(cache->id_table);
+ silc_hash_table_free(cache->name_table);
+ silc_hash_table_free(cache->context_table);
+ silc_free(cache);
}
-/* Add new entry to the cache. Returns TRUE if the entry was added and
- FALSE if it could not be added. The `name' is the name associated with
- the ID, the `id' the actual ID and the `context' a used specific context.
- If the `expire' is TRUE the entry expires in default time and if FALSE
- the entry never expires from the cache. */
+/* Add new entry to cache */
-bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
- void *context, int expire)
+SilcIDCacheEntry
+silc_idcache_add(SilcIDCache cache, char *name, void *id, void *context)
{
SilcIDCacheEntry c;
- uint32 curtime = time(NULL);
- SILC_LOG_DEBUG(("Adding cache entry"));
+ if (!id)
+ return NULL;
/* Allocate new cache entry */
c = silc_calloc(1, sizeof(*c));
+ if (!c)
+ return NULL;
+
c->id = id;
c->name = name;
- c->expire = (expire ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
c->context = context;
+ SILC_LOG_DEBUG(("Adding cache entry %p", c));
+
/* Add the new entry to the hash tables */
- if (id)
- silc_hash_table_add(cache->id_table, id, c);
+ if (id) {
+ if (silc_idcache_find_by_id_one(cache, id, NULL)) {
+ SILC_LOG_ERROR(("Attempted to add same ID twice to ID Cache"));
+ goto err;
+ }
+ if (!silc_hash_table_add(cache->id_table, id, c))
+ goto err;
+ }
if (name)
- silc_hash_table_add(cache->name_table, name, c);
+ if (!silc_hash_table_add(cache->name_table, name, c))
+ goto err;
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 (!silc_hash_table_add(cache->context_table, context, c))
+ goto err;
- return TRUE;
-}
+ return c;
-/* Destructor for the ID Cache entry */
+ err:
+ if (c->name)
+ silc_hash_table_del_by_context(cache->name_table, c->name, c);
+ if (c->context)
+ silc_hash_table_del_by_context(cache->context_table, c->context, c);
+ if (c->id)
+ silc_hash_table_del_by_context(cache->id_table, c->id, c);
+ silc_free(c);
-static void silc_idcache_destructor(void *key, void *context,
- void *user_context)
-{
- silc_free(context);
+ return NULL;
}
/* Delete cache entry from cache. */
-bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
+SilcBool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry entry,
+ void *app_context)
{
- bool ret = FALSE;
+ SilcBool ret = FALSE;
- SILC_LOG_DEBUG(("Deleting cache entry"));
+ SILC_LOG_DEBUG(("Deleting cache entry %p", entry));
- if (old->name)
- ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
- if (old->context)
- ret = silc_hash_table_del(cache->context_table, old->context);
- if (old->id)
- ret = silc_hash_table_del(cache->id_table, old->id);
+ if (entry->name)
+ ret = silc_hash_table_del_by_context(cache->name_table, entry->name,
+ entry);
+ if (entry->context)
+ ret = silc_hash_table_del_by_context(cache->context_table, entry->context,
+ entry);
+ if (entry->id)
+ ret = silc_hash_table_del_by_context(cache->id_table, entry->id,
+ entry);
+
+ if (ret)
+ silc_idcache_destructor(cache, entry, app_context);
return ret;
}
/* Deletes ID cache entry by ID. */
-bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
+SilcBool silc_idcache_del_by_id(SilcIDCache cache, void *id,
+ void *app_context)
{
SilcIDCacheEntry c;
- if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
+ if (!silc_hash_table_find(cache->id_table, id, NULL, (void **)&c))
return FALSE;
- return silc_idcache_del(cache, c);
-}
-
-/* Same as above but with specific hash and comparison functions. If the
- functions are NULL then default values are used. */
-
-bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
- SilcHashFunction hash,
- void *hash_context,
- SilcHashCompare compare,
- void *compare_context)
-{
- SilcIDCacheEntry c;
- bool ret = FALSE;
-
- SILC_LOG_DEBUG(("Deleting cache entry"));
-
- if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
- hash, hash_context, compare,
- compare_context))
- return FALSE;
-
- 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_ext(cache->id_table, c->id, hash,
- hash_context, compare, compare_context,
- NULL, NULL);
-
- return ret;
+ return silc_idcache_del(cache, c, app_context);
}
/* Deletes ID cache entry by context. */
-bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
+SilcBool silc_idcache_del_by_context(SilcIDCache cache, void *context,
+ void *app_context)
{
SilcIDCacheEntry c;
- bool ret = FALSE;
- SILC_LOG_DEBUG(("Deleting cache entry"));
-
- if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
+ if (!silc_hash_table_find(cache->context_table, context, NULL, (void **)&c))
return FALSE;
- 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(cache->id_table, c->id, c);
-
- return ret;
+ return silc_idcache_del(cache, c, app_context);
}
-/* Deletes all ID entries from cache. Free's memory as well. */
+/* Update entry */
-bool silc_idcache_del_all(SilcIDCache cache)
+SilcBool silc_idcache_update(SilcIDCache cache, SilcIDCacheEntry entry,
+ void *old_id, void *new_id,
+ char *old_name, char *new_name)
{
- silc_hash_table_free(cache->id_table);
- silc_hash_table_free(cache->name_table);
- silc_hash_table_free(cache->context_table);
-
- return TRUE;
-}
+ if (old_id && new_id) {
+ if (!silc_hash_table_del_by_context(cache->id_table, old_id, entry))
+ return FALSE;
+
+ if (cache->id_type == SILC_ID_CLIENT)
+ *(SilcClientID *)entry->id = *(SilcClientID *)new_id;
+ if (cache->id_type == SILC_ID_SERVER)
+ *(SilcServerID *)entry->id = *(SilcServerID *)new_id;
+ if (cache->id_type == SILC_ID_CHANNEL)
+ *(SilcChannelID *)entry->id = *(SilcChannelID *)new_id;
+
+ if (!silc_hash_table_add(cache->id_table, entry->id, entry))
+ return FALSE;
+ }
-static void silc_idcache_destructor_dummy(void *key, void *context,
- void *user_context)
-{
- /* Dummy - nothing */
-}
+ if (old_name && new_name) {
+ if (!silc_hash_table_del_by_context(cache->name_table, old_name, entry))
+ return FALSE;
-/* Foreach callback fro silc_idcache_purge. */
+ entry->name = new_name;
-static void silc_idcache_purge_foreach(void *key, void *context,
- void *user_context)
-{
- SilcIDCache cache = (SilcIDCache)user_context;
- uint32 curtime = time(NULL);
- SilcIDCacheEntry c = (SilcIDCacheEntry)context;
-
- if (c->expire && c->expire < curtime) {
- /* Remove the entry from the hash tables */
- if (c->name)
- silc_hash_table_del_by_context(cache->name_table, c->name, c);
- if (c->context)
- silc_hash_table_del(cache->context_table, c->context);
- if (c->id)
- silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
- NULL, NULL, NULL, NULL,
- silc_idcache_destructor_dummy, NULL);
-
- /* Call the destructor */
- if (cache->destructor)
- cache->destructor(cache, c);
-
- /* Free the entry, it has been deleted from the hash tables */
- silc_free(c);
+ if (!silc_hash_table_add(cache->name_table, entry->name, entry))
+ return FALSE;
}
-}
-
-/* Purges the cache by removing expired cache entires. Note that this
- may be very slow operation. */
-bool silc_idcache_purge(SilcIDCache cache)
-{
- silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
return TRUE;
}
-/* Purges the specific entry by context. */
-
-bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
-{
- SilcIDCacheEntry c;
- bool ret = FALSE;
-
- if (!silc_hash_table_find(cache->context_table, context, NULL,
- (void *)&c))
- return FALSE;
-
- /* 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);
-
- /* Call the destructor */
- if (cache->destructor)
- cache->destructor(cache, c);
-
- /* Free the entry, it has been deleted from the hash tables */
- silc_free(c);
-
- return ret;
-}
-
-/* Callback that is called by the hash table routine when traversing
- entrys in the hash table. */
-
-static void silc_idcache_get_all_foreach(void *key, void *context,
- void *user_context)
-{
- SilcIDCacheList list = (SilcIDCacheList)user_context;
- silc_idcache_list_add(list, (SilcIDCacheEntry)context);
-}
-
/* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
-bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
+SilcBool silc_idcache_get_all(SilcIDCache cache, SilcList *ret_list)
{
- SilcIDCacheList list;
+ if (!ret_list)
+ return FALSE;
- if (!ret)
- return TRUE;
+ if (!silc_hash_table_count(cache->id_table))
+ return FALSE;
- list = silc_idcache_list_alloc();
- silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
+ silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach,
+ ret_list);
- if (silc_idcache_list_count(list) == 0) {
- silc_idcache_list_free(list);
+ if (!silc_list_count(*ret_list))
return FALSE;
- }
-
- *ret = list;
return TRUE;
}
/* Find ID Cache entry by ID. May return multiple entries. */
-bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
- SilcIDCacheList *ret)
+SilcBool silc_idcache_find_by_id(SilcIDCache cache, void *id,
+ SilcList *ret_list)
{
- SilcIDCacheList list;
-
- list = silc_idcache_list_alloc();
+ if (!ret_list)
+ return FALSE;
- if (!ret)
- return TRUE;
+ if (!silc_hash_table_count(cache->id_table))
+ return FALSE;
silc_hash_table_find_foreach(cache->id_table, id,
- silc_idcache_get_all_foreach, list);
+ silc_idcache_get_all_foreach, ret_list);
- if (silc_idcache_list_count(list) == 0) {
- silc_idcache_list_free(list);
+ if (!silc_list_count(*ret_list))
return FALSE;
- }
-
- *ret = list;
return TRUE;
}
-/* Find specific ID with specific hash function and comparison functions.
- If `hash' is NULL then the default hash funtion is used and if `compare'
- is NULL default comparison function is used. */
+/* Find one specific ID entry. Compare full IDs */
-bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
- SilcHashFunction hash,
- void *hash_context,
- SilcHashCompare compare,
- void *compare_context,
+SilcBool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
SilcIDCacheEntry *ret)
{
return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
- hash, hash_context, compare,
- compare_context);
-}
-
-/* Find one specific ID entry. */
-
-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);
+ NULL, NULL,
+ silc_hash_id_compare_full,
+ SILC_32_TO_PTR(cache->id_type));
}
/* Finds cache entry by context. */
-bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
- SilcIDCacheEntry *ret)
+SilcBool 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);
}
/* Find ID Cache entry by name. Returns list of cache entries. */
-bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
- SilcIDCacheList *ret)
+SilcBool silc_idcache_find_by_name(SilcIDCache cache, char *name,
+ SilcList *ret_list)
{
- SilcIDCacheList list;
-
- list = silc_idcache_list_alloc();
-
- if (!ret)
- return TRUE;
-
- silc_hash_table_find_foreach(cache->name_table, name,
- silc_idcache_get_all_foreach, list);
-
- if (silc_idcache_list_count(list) == 0) {
- silc_idcache_list_free(list);
+ if (!ret_list)
return FALSE;
- }
-
- *ret = list;
-
- return TRUE;
-}
-/* Find ID Cache entry by name. Returns one cache entry. */
-
-bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
- SilcIDCacheEntry *ret)
-{
- if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
+ if (!silc_hash_table_count(cache->name_table))
return FALSE;
- return TRUE;
-}
+ silc_hash_table_find_foreach(cache->name_table, name,
+ silc_idcache_get_all_foreach, ret_list);
-/* Allocates ID cache list. */
-
-static SilcIDCacheList silc_idcache_list_alloc()
-{
- SilcIDCacheList list;
-
- list = silc_calloc(1, sizeof(*list));
-
- return list;
-}
-
-/* Adds cache entry to the ID cache list. If needed reallocates memory
- for the list. */
-
-static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
-{
- int i;
-
- /* Try to add to static cache */
- if (!list->cache_dyn_count)
- for (i = 0; i < sizeof(list->cache); i++) {
- if (!list->cache[i]) {
- list->cache[i] = cache;
- list->cache_count++;
- return;
- }
- }
-
- /* Static cache is full, allocate dynamic cache */
- for (i = 0; i < list->cache_dyn_count; i++) {
- if (!list->cache_dyn[i]) {
- list->cache_dyn[i] = cache;
- list->cache_count++;
- break;
- }
- }
-
- if (i >= list->cache_dyn_count) {
- int k;
-
- i += 5;
- list->cache_dyn = silc_realloc(list->cache_dyn,
- sizeof(*list->cache) * (i));
-
- /* NULL the reallocated area */
- for (k = list->cache_dyn_count; k < i; k++)
- list->cache_dyn[k] = NULL;
-
- list->cache_dyn[list->cache_dyn_count] = cache;
- list->cache_dyn_count = i;
- list->cache_count++;
- }
-}
-
-/* Returns number of cache entries in the ID cache list. */
-
-int silc_idcache_list_count(SilcIDCacheList list)
-{
- return list->cache_count;
-}
-
-/* Returns first entry from the ID cache list. */
-
-bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
-{
- list->pos = 0;
-
- if (!list->cache[list->pos])
+ if (!silc_list_count(*ret_list))
return FALSE;
-
- if (ret)
- *ret = list->cache[list->pos];
return TRUE;
}
-/* Returns next entry from the ID cache list. */
-
-bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
-{
- int dyn = FALSE;
- list->pos++;
-
- if (list->pos >= sizeof(list->cache)) {
- list->pos = 0;
- dyn = TRUE;
- }
-
- if (dyn && list->pos >= list->cache_dyn_count)
- return FALSE;
-
- if (!dyn && !list->cache[list->pos])
- return FALSE;
-
- if (dyn && !list->cache_dyn[list->pos])
- return FALSE;
-
- if (ret) {
- if (!dyn)
- *ret = list->cache[list->pos];
- else
- *ret = list->cache_dyn[list->pos];
- }
-
- return TRUE;
-}
-
-/* Frees ID cache list. User must free the list object returned by
- any of the searching functions. */
+/* Find ID Cache entry by name. Returns one cache entry. */
-void silc_idcache_list_free(SilcIDCacheList list)
+SilcBool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
+ SilcIDCacheEntry *ret)
{
- if (list) {
- if (list->cache_dyn)
- silc_free(list->cache_dyn);
- silc_free(list);
- }
+ return silc_hash_table_find(cache->name_table, name, NULL, (void *)ret);
}