5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2000 - 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "silcincludes.h"
23 #include "silcidcache.h"
25 /* Static prototypes */
26 static void silc_idcache_destructor(void *key, void *context,
28 static SilcIDCacheList silc_idcache_list_alloc();
29 static void silc_idcache_list_add(SilcIDCacheList list,
30 SilcIDCacheEntry cache);
35 This is context for the ID cache system. This includes all the cache
36 entries and other internal data. This is read-only object and not
37 visible outside this cache system.
39 Fields are as follows:
41 SilcHashTable id_table
43 Hash table using the ID as the key.
45 SilcHashTable name_table
47 Hash table using the name as the key.
49 SilcHashTable context_table
51 Hash table using the context as the key.
53 SilcIDCacheDestructor destructor
55 Destructor callback that is called when an cache entry expires or is
56 purged from the ID cache. The application must not free cache entry
57 because the library will do it automatically. The appliation, however,
58 is responsible of freeing any data in the entry.
62 Indicates the type of the ID's this cache holds.
65 struct SilcIDCacheStruct {
66 SilcHashTable id_table;
67 SilcHashTable name_table;
68 SilcHashTable context_table;
69 SilcIDCacheDestructor destructor;
76 This is returned when searching the cache. Enumeration functions are
77 provided to traverse the list; actually this is used as table not as
80 By default the found cache entries are saved into the static cache
81 table to provide access without reallocation. However, if the static
82 table is full, rest of the cache entries are dynamically allocated
83 into `cache_dyn' table. Traversing functions automatically handles
87 struct SilcIDCacheListStruct {
88 SilcIDCacheEntry cache[64];
89 SilcIDCacheEntry *cache_dyn;
90 uint32 cache_dyn_count;
95 /* Allocates new ID cache object. The initial amount of allocated entries
96 can be sent as argument. If `count' is 0 the system uses default values.
97 The `id_type' defines the types of the ID's that will be saved to the
100 SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
101 SilcIDCacheDestructor destructor)
105 SILC_LOG_DEBUG(("Allocating new cache"));
107 cache = silc_calloc(1, sizeof(*cache));
108 cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
109 (void *)(uint32)id_type,
110 silc_hash_id_compare,
111 (void *)(uint32)id_type,
112 silc_idcache_destructor, NULL,
114 cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL,
115 silc_hash_string_compare, NULL,
117 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
118 NULL, NULL, NULL, NULL, FALSE);
119 cache->destructor = destructor;
120 cache->type = id_type;
125 /* Frees ID cache object and cache entries */
127 void silc_idcache_free(SilcIDCache cache)
130 silc_hash_table_free(cache->id_table);
131 silc_hash_table_free(cache->name_table);
132 silc_hash_table_free(cache->context_table);
137 /* Add new entry to the cache. Returns TRUE if the entry was added and
138 FALSE if it could not be added. The `name' is the name associated with
139 the ID, the `id' the actual ID and the `context' a used specific context.
140 If the `expire' is TRUE the entry expires in default time and if FALSE
141 the entry never expires from the cache. */
143 bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
144 void *context, int expire)
147 uint32 curtime = time(NULL);
149 SILC_LOG_DEBUG(("Adding cache entry"));
151 /* Allocate new cache entry */
152 c = silc_calloc(1, sizeof(*c));
155 c->expire = (expire ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
156 c->context = context;
158 /* Add the new entry to the hash tables */
161 silc_hash_table_add(cache->id_table, id, c);
163 silc_hash_table_add(cache->name_table, name, c);
165 silc_hash_table_add(cache->context_table, context, c);
167 /* See whether we have time to rehash the tables */
168 if ((silc_hash_table_count(cache->id_table) / 2) >
169 silc_hash_table_size(cache->id_table)) {
170 silc_hash_table_rehash(cache->id_table, 0);
171 silc_hash_table_rehash(cache->name_table, 0);
172 silc_hash_table_rehash(cache->context_table, 0);
178 /* Destructor for the ID Cache entry */
180 static void silc_idcache_destructor(void *key, void *context,
186 /* Delete cache entry from cache. */
188 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
192 SILC_LOG_DEBUG(("Deleting cache entry"));
195 ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
197 ret = silc_hash_table_del(cache->context_table, old->context);
199 ret = silc_hash_table_del(cache->id_table, old->id);
204 /* Deletes ID cache entry by ID. */
206 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
210 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
213 return silc_idcache_del(cache, c);
216 /* Same as above but with specific hash and comparison functions. If the
217 functions are NULL then default values are used. */
219 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
220 SilcHashFunction hash,
222 SilcHashCompare compare,
223 void *compare_context)
228 SILC_LOG_DEBUG(("Deleting cache entry"));
230 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
231 hash, hash_context, compare,
236 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
238 ret = silc_hash_table_del(cache->context_table, c->context);
240 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
241 hash_context, compare, compare_context,
247 /* Deletes ID cache entry by context. */
249 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
254 SILC_LOG_DEBUG(("Deleting cache entry"));
256 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
260 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
262 ret = silc_hash_table_del(cache->context_table, c->context);
264 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
269 /* Deletes all ID entries from cache. Free's memory as well. */
271 bool silc_idcache_del_all(SilcIDCache cache)
273 silc_hash_table_free(cache->id_table);
274 silc_hash_table_free(cache->name_table);
275 silc_hash_table_free(cache->context_table);
280 static void silc_idcache_destructor_dummy(void *key, void *context,
283 /* Dummy - nothing */
286 /* Foreach callback fro silc_idcache_purge. */
288 static void silc_idcache_purge_foreach(void *key, void *context,
291 SilcIDCache cache = (SilcIDCache)user_context;
292 uint32 curtime = time(NULL);
293 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
295 if (c->expire && c->expire < curtime) {
296 /* Remove the entry from the hash tables */
298 silc_hash_table_del_by_context(cache->name_table, c->name, c);
300 silc_hash_table_del(cache->context_table, c->context);
302 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
303 NULL, NULL, NULL, NULL,
304 silc_idcache_destructor_dummy, NULL);
306 /* Call the destructor */
307 if (cache->destructor)
308 cache->destructor(cache, c);
310 /* Free the entry, it has been deleted from the hash tables */
315 /* Purges the cache by removing expired cache entires. Note that this
316 may be very slow operation. */
318 bool silc_idcache_purge(SilcIDCache cache)
320 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
324 /* Purges the specific entry by context. */
326 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
331 if (!silc_hash_table_find(cache->context_table, context, NULL,
335 /* Remove the entry from the hash tables */
337 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
339 ret = silc_hash_table_del(cache->context_table, c->context);
342 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
343 NULL, NULL, NULL, NULL,
344 silc_idcache_destructor_dummy, NULL);
346 /* Call the destructor */
347 if (cache->destructor)
348 cache->destructor(cache, c);
350 /* Free the entry, it has been deleted from the hash tables */
356 /* Callback that is called by the hash table routine when traversing
357 entrys in the hash table. */
359 static void silc_idcache_get_all_foreach(void *key, void *context,
362 SilcIDCacheList list = (SilcIDCacheList)user_context;
363 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
366 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
368 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
370 SilcIDCacheList list;
375 list = silc_idcache_list_alloc();
376 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
378 if (silc_idcache_list_count(list) == 0) {
379 silc_idcache_list_free(list);
388 /* Find ID Cache entry by ID. May return multiple entries. */
390 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
391 SilcIDCacheList *ret)
393 SilcIDCacheList list;
395 list = silc_idcache_list_alloc();
400 silc_hash_table_find_foreach(cache->id_table, id,
401 silc_idcache_get_all_foreach, list);
403 if (silc_idcache_list_count(list) == 0) {
404 silc_idcache_list_free(list);
413 /* Find specific ID with specific hash function and comparison functions.
414 If `hash' is NULL then the default hash funtion is used and if `compare'
415 is NULL default comparison function is used. */
417 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
418 SilcHashFunction hash,
420 SilcHashCompare compare,
421 void *compare_context,
422 SilcIDCacheEntry *ret)
424 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
425 hash, hash_context, compare,
429 /* Find one specific ID entry. */
431 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
432 SilcIDCacheEntry *ret)
434 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
437 /* Finds cache entry by context. */
439 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
440 SilcIDCacheEntry *ret)
442 return silc_hash_table_find(cache->context_table, context, NULL,
446 /* Find ID Cache entry by name. Returns list of cache entries. */
448 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
449 SilcIDCacheList *ret)
451 SilcIDCacheList list;
453 list = silc_idcache_list_alloc();
458 silc_hash_table_find_foreach(cache->name_table, name,
459 silc_idcache_get_all_foreach, list);
461 if (silc_idcache_list_count(list) == 0) {
462 silc_idcache_list_free(list);
471 /* Find ID Cache entry by name. Returns one cache entry. */
473 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
474 SilcIDCacheEntry *ret)
476 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
482 /* Allocates ID cache list. */
484 static SilcIDCacheList silc_idcache_list_alloc()
486 SilcIDCacheList list;
488 list = silc_calloc(1, sizeof(*list));
493 /* Adds cache entry to the ID cache list. If needed reallocates memory
496 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
500 /* Try to add to static cache */
501 if (!list->cache_dyn_count)
502 for (i = 0; i < sizeof(list->cache); i++) {
503 if (!list->cache[i]) {
504 list->cache[i] = cache;
510 /* Static cache is full, allocate dynamic cache */
511 for (i = 0; i < list->cache_dyn_count; i++) {
512 if (!list->cache_dyn[i]) {
513 list->cache_dyn[i] = cache;
519 if (i >= list->cache_dyn_count) {
523 list->cache_dyn = silc_realloc(list->cache_dyn,
524 sizeof(*list->cache) * (i));
526 /* NULL the reallocated area */
527 for (k = list->cache_dyn_count; k < i; k++)
528 list->cache_dyn[k] = NULL;
530 list->cache_dyn[list->cache_dyn_count] = cache;
531 list->cache_dyn_count = i;
536 /* Returns number of cache entries in the ID cache list. */
538 int silc_idcache_list_count(SilcIDCacheList list)
540 return list->cache_count;
543 /* Returns first entry from the ID cache list. */
545 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
549 if (!list->cache[list->pos])
553 *ret = list->cache[list->pos];
558 /* Returns next entry from the ID cache list. */
560 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
565 if (list->pos >= sizeof(list->cache)) {
570 if (dyn && list->pos >= list->cache_dyn_count)
573 if (!dyn && !list->cache[list->pos])
576 if (dyn && !list->cache_dyn[list->pos])
581 *ret = list->cache[list->pos];
583 *ret = list->cache_dyn[list->pos];
589 /* Frees ID cache list. User must free the list object returned by
590 any of the searching functions. */
592 void silc_idcache_list_free(SilcIDCacheList list)
596 silc_free(list->cache_dyn);