5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2000 - 2005 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; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcincludes.h"
22 #include "silcidcache.h"
24 /* Static prototypes */
25 static void silc_idcache_destructor(void *key, void *context,
27 static SilcIDCacheList silc_idcache_list_alloc();
28 static void silc_idcache_list_add(SilcIDCacheList list,
29 SilcIDCacheEntry cache);
34 This is context for the ID cache system. This includes all the cache
35 entries and other internal data. This is read-only object and not
36 visible outside this cache system.
38 Fields are as follows:
40 SilcHashTable id_table
42 Hash table using the ID as the key.
44 SilcHashTable name_table
46 Hash table using the name as the key.
48 SilcHashTable context_table
50 Hash table using the context as the key.
52 SilcIDCacheDestructor destructor
54 Destructor callback that is called when an cache entry expires or is
55 purged from the ID cache. The application must not free cache entry
56 because the library will do it automatically. The appliation, however,
57 is responsible of freeing any data in the entry.
61 Indicates the type of the ID's this cache holds.
64 struct SilcIDCacheStruct {
65 SilcHashTable id_table;
66 SilcHashTable name_table;
67 SilcHashTable context_table;
68 SilcIDCacheDestructor destructor;
71 unsigned int delete_id : 1;
72 unsigned int delete_name : 1;
78 This is returned when searching the cache. Enumeration functions are
79 provided to traverse the list; actually this is used as table not as
82 By default the found cache entries are saved into the static cache
83 table to provide access without reallocation. However, if the static
84 table is full, rest of the cache entries are dynamically allocated
85 into `cache_dyn' table. Traversing functions automatically handles
89 struct SilcIDCacheListStruct {
90 SilcIDCacheEntry cache[128];
91 SilcIDCacheEntry *cache_dyn;
92 SilcUInt32 cache_dyn_count;
93 SilcUInt32 cache_count;
98 /* Allocates new ID cache object. The initial amount of allocated entries
99 can be sent as argument. If `count' is 0 the system uses default values.
100 The `id_type' defines the types of the ID's that will be saved to the
103 SilcIDCache silc_idcache_alloc(SilcUInt32 count, SilcIdType id_type,
104 SilcIDCacheDestructor destructor,
105 void *destructor_context,
106 bool delete_id, bool delete_name)
110 SILC_LOG_DEBUG(("Allocating new cache"));
112 cache = silc_calloc(1, sizeof(*cache));
115 cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
116 SILC_32_TO_PTR(id_type),
117 silc_hash_id_compare,
118 SILC_32_TO_PTR(id_type),
119 silc_idcache_destructor,
121 cache->name_table = silc_hash_table_alloc(count, silc_hash_utf8_string, NULL,
122 silc_hash_utf8_compare, NULL,
124 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
125 NULL, NULL, NULL, NULL, TRUE);
126 cache->destructor = destructor;
127 cache->context = destructor_context;
128 cache->type = id_type;
129 cache->delete_id = delete_id;
130 cache->delete_name = delete_name;
132 if (!cache->id_table || !cache->name_table || !cache->context_table) {
134 silc_hash_table_free(cache->id_table);
135 if (cache->name_table)
136 silc_hash_table_free(cache->name_table);
137 if (cache->context_table)
138 silc_hash_table_free(cache->context_table);
146 /* Frees ID cache object and cache entries */
148 void silc_idcache_free(SilcIDCache cache)
151 silc_hash_table_free(cache->id_table);
152 silc_hash_table_free(cache->name_table);
153 silc_hash_table_free(cache->context_table);
158 /* Add new entry to the cache. Returns TRUE if the entry was added and
159 FALSE if it could not be added. The `name' is the name associated with
160 the ID, the `id' the actual ID and the `context' a used specific context.
161 If the `expire' is TRUE the entry expires in default time and if FALSE
162 the entry never expires from the cache. */
164 bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
165 void *context, int expire, SilcIDCacheEntry *ret)
169 SILC_LOG_DEBUG(("Adding cache entry"));
171 /* Allocate new cache entry */
172 c = silc_calloc(1, sizeof(*c));
178 c->context = context;
180 /* Add the new entry to the hash tables */
183 silc_hash_table_add(cache->id_table, id, c);
185 silc_hash_table_add(cache->name_table, name, c);
187 silc_hash_table_add(cache->context_table, context, c);
195 /* Destructor for the ID Cache entry */
197 static void silc_idcache_destructor(void *key, void *context,
200 SilcIDCacheEntry c = context;
202 SilcIDCache cache = user_context;
204 if (cache->delete_id)
206 if (cache->delete_name)
209 memset(c, 'F', sizeof(*c));
214 /* Delete cache entry from cache. */
216 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
220 SILC_LOG_DEBUG(("Deleting cache entry"));
223 ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
225 ret = silc_hash_table_del(cache->context_table, old->context);
227 ret = silc_hash_table_del(cache->id_table, old->id);
229 silc_idcache_destructor(NULL, old, NULL);
236 /* Deletes ID cache entry by ID. */
238 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
242 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
245 return silc_idcache_del(cache, c);
248 /* Same as above but with specific hash and comparison functions. If the
249 functions are NULL then default values are used. */
251 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
252 SilcHashFunction hash,
254 SilcHashCompare compare,
255 void *compare_context)
260 SILC_LOG_DEBUG(("Deleting cache entry"));
262 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
263 hash, hash_context, compare,
268 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
270 ret = silc_hash_table_del(cache->context_table, c->context);
272 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
273 hash_context, compare, compare_context,
278 /* Deletes ID cache entry by context. */
280 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
285 SILC_LOG_DEBUG(("Deleting cache entry"));
287 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
291 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
293 ret = silc_hash_table_del(cache->context_table, c->context);
295 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
297 silc_idcache_destructor(NULL, c, NULL);
304 /* Deletes all ID entries from cache. Free's memory as well. */
306 bool silc_idcache_del_all(SilcIDCache cache)
308 silc_hash_table_free(cache->id_table);
309 silc_hash_table_free(cache->name_table);
310 silc_hash_table_free(cache->context_table);
315 static void silc_idcache_destructor_dummy(void *key, void *context,
318 /* Dummy - nothing */
321 /* Foreach callback fro silc_idcache_purge. */
323 static void silc_idcache_purge_foreach(void *key, void *context,
326 SilcIDCache cache = (SilcIDCache)user_context;
327 SilcUInt32 curtime = time(NULL);
328 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
334 if (c->expire && c->expire < curtime) {
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,
347 /* Call the destructor */
348 if (cache->destructor)
349 cache->destructor(cache, c, cache->context);
351 /* Free the entry, it has been deleted from the hash tables */
352 silc_idcache_destructor(NULL, c, NULL);
357 /* Purges the cache by removing expired cache entires. Note that this
358 may be very slow operation. */
360 bool silc_idcache_purge(SilcIDCache cache)
362 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
366 /* Purges the specific entry by context. */
368 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
373 if (!silc_hash_table_find(cache->context_table, context, NULL,
377 /* Remove the entry from the hash tables */
379 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
381 ret = silc_hash_table_del(cache->context_table, c->context);
384 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
385 NULL, NULL, NULL, NULL,
386 silc_idcache_destructor_dummy, NULL);
388 /* Call the destructor */
389 if (cache->destructor)
390 cache->destructor(cache, c, cache->context);
392 /* Free the entry, it has been deleted from the hash tables */
393 silc_idcache_destructor(NULL, c, NULL);
399 /* Callback that is called by the hash table routine when traversing
400 entrys in the hash table. */
402 static void silc_idcache_get_all_foreach(void *key, void *context,
405 SilcIDCacheList list = (SilcIDCacheList)user_context;
408 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
411 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
413 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
415 SilcIDCacheList list;
420 list = silc_idcache_list_alloc();
423 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
425 if (silc_idcache_list_count(list) == 0) {
426 silc_idcache_list_free(list);
435 /* Find ID Cache entry by ID. May return multiple entries. */
437 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
438 SilcIDCacheList *ret)
440 SilcIDCacheList list;
442 list = silc_idcache_list_alloc();
449 silc_hash_table_find_foreach(cache->id_table, id,
450 silc_idcache_get_all_foreach, list);
452 if (silc_idcache_list_count(list) == 0) {
453 silc_idcache_list_free(list);
462 /* Find specific ID with specific hash function and comparison functions.
463 If `hash' is NULL then the default hash funtion is used and if `compare'
464 is NULL default comparison function is used. */
466 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
467 SilcHashFunction hash,
469 SilcHashCompare compare,
470 void *compare_context,
471 SilcIDCacheEntry *ret)
473 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
474 hash, hash_context, compare,
478 /* Find one specific ID entry. */
480 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
481 SilcIDCacheEntry *ret)
483 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
486 /* Finds cache entry by context. */
488 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
489 SilcIDCacheEntry *ret)
491 return silc_hash_table_find(cache->context_table, context, NULL,
495 /* Find ID Cache entry by name. Returns list of cache entries. */
497 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
498 SilcIDCacheList *ret)
500 SilcIDCacheList list;
502 list = silc_idcache_list_alloc();
509 silc_hash_table_find_foreach(cache->name_table, name,
510 silc_idcache_get_all_foreach, list);
512 if (silc_idcache_list_count(list) == 0) {
513 silc_idcache_list_free(list);
522 /* Find ID Cache entry by name. Returns one cache entry. */
524 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
525 SilcIDCacheEntry *ret)
527 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
533 /* Allocates ID cache list. */
535 static SilcIDCacheList silc_idcache_list_alloc()
537 SilcIDCacheList list;
539 list = silc_calloc(1, sizeof(*list));
546 /* Adds cache entry to the ID cache list. If needed reallocates memory
549 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
553 /* Try to add to static cache */
554 if (!list->cache_dyn_count)
555 for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
556 if (!list->cache[i]) {
557 list->cache[i] = cache;
563 /* Static cache is full, allocate dynamic cache */
564 for (i = 0; i < list->cache_dyn_count; i++) {
565 if (!list->cache_dyn[i]) {
566 list->cache_dyn[i] = cache;
572 if (i >= list->cache_dyn_count) {
575 i = list->cache_dyn_count;
576 list->cache_dyn = silc_realloc(list->cache_dyn,
577 sizeof(*list->cache_dyn) * (i + 5));
578 if (!list->cache_dyn)
581 /* NULL the reallocated area */
582 for (k = i; k < (i + 5); k++)
583 list->cache_dyn[k] = NULL;
585 list->cache_dyn[i] = cache;
587 list->cache_dyn_count += 5;
591 /* Returns number of cache entries in the ID cache list. */
593 int silc_idcache_list_count(SilcIDCacheList list)
595 return list->cache_count;
598 /* Returns first entry from the ID cache list. */
600 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
604 if (!list->cache[list->pos])
608 *ret = list->cache[list->pos];
613 /* Returns next entry from the ID cache list. */
615 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
620 list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
625 if (list->dyn && list->pos >= list->cache_dyn_count)
628 if (!list->dyn && !list->cache[list->pos])
631 if (list->dyn && !list->cache_dyn[list->pos])
636 *ret = list->cache[list->pos];
638 *ret = list->cache_dyn[list->pos];
644 /* Frees ID cache list. User must free the list object returned by
645 any of the searching functions. */
647 void silc_idcache_list_free(SilcIDCacheList list)
651 silc_free(list->cache_dyn);