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);
205 /* Deletes ID cache entry by ID. */
207 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
211 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
214 return silc_idcache_del(cache, c);
217 /* Same as above but with specific hash and comparison functions. If the
218 functions are NULL then default values are used. */
220 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
221 SilcHashFunction hash,
223 SilcHashCompare compare,
224 void *compare_context)
229 SILC_LOG_DEBUG(("Deleting cache entry"));
231 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
232 hash, hash_context, compare,
237 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
239 ret = silc_hash_table_del(cache->context_table, c->context);
241 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
242 hash_context, compare, compare_context,
248 /* Deletes ID cache entry by context. */
250 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
255 SILC_LOG_DEBUG(("Deleting cache entry"));
257 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
261 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
263 ret = silc_hash_table_del(cache->context_table, c->context);
265 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
270 /* Deletes all ID entries from cache. Free's memory as well. */
272 bool silc_idcache_del_all(SilcIDCache cache)
274 silc_hash_table_free(cache->id_table);
275 silc_hash_table_free(cache->name_table);
276 silc_hash_table_free(cache->context_table);
281 static void silc_idcache_destructor_dummy(void *key, void *context,
284 /* Dummy - nothing */
287 /* Foreach callback fro silc_idcache_purge. */
289 static void silc_idcache_purge_foreach(void *key, void *context,
292 SilcIDCache cache = (SilcIDCache)user_context;
293 uint32 curtime = time(NULL);
294 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
296 if (c->expire && c->expire < curtime) {
297 /* Remove the entry from the hash tables */
299 silc_hash_table_del_by_context(cache->name_table, c->name, c);
301 silc_hash_table_del(cache->context_table, c->context);
303 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
304 NULL, NULL, NULL, NULL,
305 silc_idcache_destructor_dummy, NULL);
307 /* Call the destructor */
308 if (cache->destructor)
309 cache->destructor(cache, c);
311 /* Free the entry, it has been deleted from the hash tables */
316 /* Purges the cache by removing expired cache entires. Note that this
317 may be very slow operation. */
319 bool silc_idcache_purge(SilcIDCache cache)
321 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
325 /* Purges the specific entry by context. */
327 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
332 if (!silc_hash_table_find(cache->context_table, context, NULL,
336 /* Remove the entry from the hash tables */
338 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
340 ret = silc_hash_table_del(cache->context_table, c->context);
343 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
344 NULL, NULL, NULL, NULL,
345 silc_idcache_destructor_dummy, NULL);
347 /* Call the destructor */
348 if (cache->destructor)
349 cache->destructor(cache, c);
351 /* Free the entry, it has been deleted from the hash tables */
357 /* Callback that is called by the hash table routine when traversing
358 entrys in the hash table. */
360 static void silc_idcache_get_all_foreach(void *key, void *context,
363 SilcIDCacheList list = (SilcIDCacheList)user_context;
364 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
367 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
369 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
371 SilcIDCacheList list;
376 list = silc_idcache_list_alloc();
377 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
379 if (silc_idcache_list_count(list) == 0) {
380 silc_idcache_list_free(list);
389 /* Find ID Cache entry by ID. May return multiple entries. */
391 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
392 SilcIDCacheList *ret)
394 SilcIDCacheList list;
396 list = silc_idcache_list_alloc();
401 silc_hash_table_find_foreach(cache->id_table, id,
402 silc_idcache_get_all_foreach, list);
404 if (silc_idcache_list_count(list) == 0) {
405 silc_idcache_list_free(list);
414 /* Find specific ID with specific hash function and comparison functions.
415 If `hash' is NULL then the default hash funtion is used and if `compare'
416 is NULL default comparison function is used. */
418 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
419 SilcHashFunction hash,
421 SilcHashCompare compare,
422 void *compare_context,
423 SilcIDCacheEntry *ret)
425 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
426 hash, hash_context, compare,
430 /* Find one specific ID entry. */
432 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
433 SilcIDCacheEntry *ret)
435 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
438 /* Finds cache entry by context. */
440 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
441 SilcIDCacheEntry *ret)
443 return silc_hash_table_find(cache->context_table, context, NULL,
447 /* Find ID Cache entry by name. Returns list of cache entries. */
449 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
450 SilcIDCacheList *ret)
452 SilcIDCacheList list;
454 list = silc_idcache_list_alloc();
459 silc_hash_table_find_foreach(cache->name_table, name,
460 silc_idcache_get_all_foreach, list);
462 if (silc_idcache_list_count(list) == 0) {
463 silc_idcache_list_free(list);
472 /* Find ID Cache entry by name. Returns one cache entry. */
474 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
475 SilcIDCacheEntry *ret)
477 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
483 /* Allocates ID cache list. */
485 static SilcIDCacheList silc_idcache_list_alloc()
487 SilcIDCacheList list;
489 list = silc_calloc(1, sizeof(*list));
494 /* Adds cache entry to the ID cache list. If needed reallocates memory
497 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
501 /* Try to add to static cache */
502 if (!list->cache_dyn_count)
503 for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
504 if (!list->cache[i]) {
505 list->cache[i] = cache;
511 /* Static cache is full, allocate dynamic cache */
512 for (i = 0; i < list->cache_dyn_count; i++) {
513 if (!list->cache_dyn[i]) {
514 list->cache_dyn[i] = cache;
520 if (i >= list->cache_dyn_count) {
523 i = list->cache_dyn_count;
524 list->cache_dyn = silc_realloc(list->cache_dyn,
525 sizeof(*list->cache_dyn) * (i + 5));
527 /* NULL the reallocated area */
528 for (k = i; k < (i + 5); k++)
529 list->cache_dyn[k] = NULL;
531 list->cache_dyn[i] = cache;
533 list->cache_dyn_count += 5;
537 /* Returns number of cache entries in the ID cache list. */
539 int silc_idcache_list_count(SilcIDCacheList list)
541 return list->cache_count;
544 /* Returns first entry from the ID cache list. */
546 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
550 if (!list->cache[list->pos])
554 *ret = list->cache[list->pos];
559 /* Returns next entry from the ID cache list. */
561 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
566 list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
571 if (list->dyn && list->pos >= list->cache_dyn_count)
574 if (!list->dyn && !list->cache[list->pos])
577 if (list->dyn && !list->cache_dyn[list->pos])
582 *ret = list->cache[list->pos];
584 *ret = list->cache_dyn[list->pos];
590 /* Frees ID cache list. User must free the list object returned by
591 any of the searching functions. */
593 void silc_idcache_list_free(SilcIDCacheList list)
597 silc_free(list->cache_dyn);