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;
96 /* Allocates new ID cache object. The initial amount of allocated entries
97 can be sent as argument. If `count' is 0 the system uses default values.
98 The `id_type' defines the types of the ID's that will be saved to the
101 SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
102 SilcIDCacheDestructor destructor)
106 SILC_LOG_DEBUG(("Allocating new cache"));
108 cache = silc_calloc(1, sizeof(*cache));
109 cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
110 (void *)(uint32)id_type,
111 silc_hash_id_compare,
112 (void *)(uint32)id_type,
113 silc_idcache_destructor, NULL,
115 cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL,
116 silc_hash_string_compare, NULL,
118 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
119 NULL, NULL, NULL, NULL, FALSE);
120 cache->destructor = destructor;
121 cache->type = id_type;
126 /* Frees ID cache object and cache entries */
128 void silc_idcache_free(SilcIDCache cache)
131 silc_hash_table_free(cache->id_table);
132 silc_hash_table_free(cache->name_table);
133 silc_hash_table_free(cache->context_table);
138 /* Add new entry to the cache. Returns TRUE if the entry was added and
139 FALSE if it could not be added. The `name' is the name associated with
140 the ID, the `id' the actual ID and the `context' a used specific context.
141 If the `expire' is TRUE the entry expires in default time and if FALSE
142 the entry never expires from the cache. */
144 bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
145 void *context, int expire)
148 uint32 curtime = time(NULL);
150 SILC_LOG_DEBUG(("Adding cache entry"));
152 /* Allocate new cache entry */
153 c = silc_calloc(1, sizeof(*c));
156 c->expire = (expire ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
157 c->context = context;
159 /* Add the new entry to the hash tables */
162 silc_hash_table_add(cache->id_table, id, c);
164 silc_hash_table_add(cache->name_table, name, c);
166 silc_hash_table_add(cache->context_table, context, c);
168 /* See whether we have time to rehash the tables */
169 if ((silc_hash_table_count(cache->id_table) / 2) >
170 silc_hash_table_size(cache->id_table)) {
171 silc_hash_table_rehash(cache->id_table, 0);
172 silc_hash_table_rehash(cache->name_table, 0);
173 silc_hash_table_rehash(cache->context_table, 0);
179 /* Destructor for the ID Cache entry */
181 static void silc_idcache_destructor(void *key, void *context,
187 /* Delete cache entry from cache. */
189 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
193 SILC_LOG_DEBUG(("Deleting cache entry"));
196 ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
198 ret = silc_hash_table_del(cache->context_table, old->context);
200 ret = silc_hash_table_del(cache->id_table, old->id);
207 /* Deletes ID cache entry by ID. */
209 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
213 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
216 return silc_idcache_del(cache, c);
219 /* Same as above but with specific hash and comparison functions. If the
220 functions are NULL then default values are used. */
222 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
223 SilcHashFunction hash,
225 SilcHashCompare compare,
226 void *compare_context)
231 SILC_LOG_DEBUG(("Deleting cache entry"));
233 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
234 hash, hash_context, compare,
239 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
241 ret = silc_hash_table_del(cache->context_table, c->context);
243 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
244 hash_context, compare, compare_context,
250 /* Deletes ID cache entry by context. */
252 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
257 SILC_LOG_DEBUG(("Deleting cache entry"));
259 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
263 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
265 ret = silc_hash_table_del(cache->context_table, c->context);
267 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
274 /* Deletes all ID entries from cache. Free's memory as well. */
276 bool silc_idcache_del_all(SilcIDCache cache)
278 silc_hash_table_free(cache->id_table);
279 silc_hash_table_free(cache->name_table);
280 silc_hash_table_free(cache->context_table);
285 static void silc_idcache_destructor_dummy(void *key, void *context,
288 /* Dummy - nothing */
291 /* Foreach callback fro silc_idcache_purge. */
293 static void silc_idcache_purge_foreach(void *key, void *context,
296 SilcIDCache cache = (SilcIDCache)user_context;
297 uint32 curtime = time(NULL);
298 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
300 if (c->expire && c->expire < curtime) {
301 /* Remove the entry from the hash tables */
303 silc_hash_table_del_by_context(cache->name_table, c->name, c);
305 silc_hash_table_del(cache->context_table, c->context);
307 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
308 NULL, NULL, NULL, NULL,
309 silc_idcache_destructor_dummy, NULL);
311 /* Call the destructor */
312 if (cache->destructor)
313 cache->destructor(cache, c);
315 /* Free the entry, it has been deleted from the hash tables */
320 /* Purges the cache by removing expired cache entires. Note that this
321 may be very slow operation. */
323 bool silc_idcache_purge(SilcIDCache cache)
325 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
329 /* Purges the specific entry by context. */
331 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
336 if (!silc_hash_table_find(cache->context_table, context, NULL,
340 /* Remove the entry from the hash tables */
342 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
344 ret = silc_hash_table_del(cache->context_table, c->context);
347 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
348 NULL, NULL, NULL, NULL,
349 silc_idcache_destructor_dummy, NULL);
351 /* Call the destructor */
352 if (cache->destructor)
353 cache->destructor(cache, c);
355 /* Free the entry, it has been deleted from the hash tables */
361 /* Callback that is called by the hash table routine when traversing
362 entrys in the hash table. */
364 static void silc_idcache_get_all_foreach(void *key, void *context,
367 SilcIDCacheList list = (SilcIDCacheList)user_context;
368 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
371 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
373 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
375 SilcIDCacheList list;
380 list = silc_idcache_list_alloc();
381 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
383 if (silc_idcache_list_count(list) == 0) {
384 silc_idcache_list_free(list);
393 /* Find ID Cache entry by ID. May return multiple entries. */
395 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
396 SilcIDCacheList *ret)
398 SilcIDCacheList list;
400 list = silc_idcache_list_alloc();
405 silc_hash_table_find_foreach(cache->id_table, id,
406 silc_idcache_get_all_foreach, list);
408 if (silc_idcache_list_count(list) == 0) {
409 silc_idcache_list_free(list);
418 /* Find specific ID with specific hash function and comparison functions.
419 If `hash' is NULL then the default hash funtion is used and if `compare'
420 is NULL default comparison function is used. */
422 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
423 SilcHashFunction hash,
425 SilcHashCompare compare,
426 void *compare_context,
427 SilcIDCacheEntry *ret)
429 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
430 hash, hash_context, compare,
434 /* Find one specific ID entry. */
436 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
437 SilcIDCacheEntry *ret)
439 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
442 /* Finds cache entry by context. */
444 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
445 SilcIDCacheEntry *ret)
447 return silc_hash_table_find(cache->context_table, context, NULL,
451 /* Find ID Cache entry by name. Returns list of cache entries. */
453 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
454 SilcIDCacheList *ret)
456 SilcIDCacheList list;
458 list = silc_idcache_list_alloc();
463 silc_hash_table_find_foreach(cache->name_table, name,
464 silc_idcache_get_all_foreach, list);
466 if (silc_idcache_list_count(list) == 0) {
467 silc_idcache_list_free(list);
476 /* Find ID Cache entry by name. Returns one cache entry. */
478 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
479 SilcIDCacheEntry *ret)
481 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
487 /* Allocates ID cache list. */
489 static SilcIDCacheList silc_idcache_list_alloc()
491 SilcIDCacheList list;
493 list = silc_calloc(1, sizeof(*list));
498 /* Adds cache entry to the ID cache list. If needed reallocates memory
501 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
505 /* Try to add to static cache */
506 if (!list->cache_dyn_count)
507 for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
508 if (!list->cache[i]) {
509 list->cache[i] = cache;
515 /* Static cache is full, allocate dynamic cache */
516 for (i = 0; i < list->cache_dyn_count; i++) {
517 if (!list->cache_dyn[i]) {
518 list->cache_dyn[i] = cache;
524 if (i >= list->cache_dyn_count) {
527 i = list->cache_dyn_count;
528 list->cache_dyn = silc_realloc(list->cache_dyn,
529 sizeof(*list->cache_dyn) * (i + 5));
531 /* NULL the reallocated area */
532 for (k = i; k < (i + 5); k++)
533 list->cache_dyn[k] = NULL;
535 list->cache_dyn[i] = cache;
537 list->cache_dyn_count += 5;
541 /* Returns number of cache entries in the ID cache list. */
543 int silc_idcache_list_count(SilcIDCacheList list)
545 return list->cache_count;
548 /* Returns first entry from the ID cache list. */
550 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
554 if (!list->cache[list->pos])
558 *ret = list->cache[list->pos];
563 /* Returns next entry from the ID cache list. */
565 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
570 list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
575 if (list->dyn && list->pos >= list->cache_dyn_count)
578 if (!list->dyn && !list->cache[list->pos])
581 if (list->dyn && !list->cache_dyn[list->pos])
586 *ret = list->cache[list->pos];
588 *ret = list->cache_dyn[list->pos];
594 /* Frees ID cache list. User must free the list object returned by
595 any of the searching functions. */
597 void silc_idcache_list_free(SilcIDCacheList list)
601 silc_free(list->cache_dyn);