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 SilcUInt32 cache_dyn_count;
91 SilcUInt32 cache_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(SilcUInt32 count, SilcIdType id_type,
102 SilcIDCacheDestructor destructor)
106 SILC_LOG_DEBUG(("Allocating new cache"));
108 cache = silc_calloc(1, sizeof(*cache));
111 cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
112 (void *)(SilcUInt32)id_type,
113 silc_hash_id_compare,
114 (void *)(SilcUInt32)id_type,
115 silc_idcache_destructor, NULL,
117 cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL,
118 silc_hash_string_compare, NULL,
120 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
121 NULL, NULL, NULL, NULL, FALSE);
122 cache->destructor = destructor;
123 cache->type = id_type;
125 if (!cache->id_table || !cache->name_table || !cache->context_table) {
127 silc_hash_table_free(cache->id_table);
128 if (cache->name_table)
129 silc_hash_table_free(cache->name_table);
130 if (cache->context_table)
131 silc_hash_table_free(cache->context_table);
139 /* Frees ID cache object and cache entries */
141 void silc_idcache_free(SilcIDCache cache)
144 silc_hash_table_free(cache->id_table);
145 silc_hash_table_free(cache->name_table);
146 silc_hash_table_free(cache->context_table);
151 /* Add new entry to the cache. Returns TRUE if the entry was added and
152 FALSE if it could not be added. The `name' is the name associated with
153 the ID, the `id' the actual ID and the `context' a used specific context.
154 If the `expire' is TRUE the entry expires in default time and if FALSE
155 the entry never expires from the cache. */
157 bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
158 void *context, int expire, SilcIDCacheEntry *ret)
162 SILC_LOG_DEBUG(("Adding cache entry"));
164 /* Allocate new cache entry */
165 c = silc_calloc(1, sizeof(*c));
171 c->context = context;
173 /* Add the new entry to the hash tables */
176 silc_hash_table_add(cache->id_table, id, c);
178 silc_hash_table_add(cache->name_table, name, c);
180 silc_hash_table_add(cache->context_table, context, c);
182 /* See whether we have time to rehash the tables */
183 if ((silc_hash_table_count(cache->id_table) / 2) >
184 silc_hash_table_size(cache->id_table)) {
185 silc_hash_table_rehash(cache->id_table, 0);
186 silc_hash_table_rehash(cache->name_table, 0);
187 silc_hash_table_rehash(cache->context_table, 0);
196 /* Destructor for the ID Cache entry */
198 static void silc_idcache_destructor(void *key, void *context,
204 /* Delete cache entry from cache. */
206 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
210 SILC_LOG_DEBUG(("Deleting cache entry"));
213 ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
215 ret = silc_hash_table_del(cache->context_table, old->context);
217 ret = silc_hash_table_del(cache->id_table, old->id);
224 /* Deletes ID cache entry by ID. */
226 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
230 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
233 return silc_idcache_del(cache, c);
236 /* Same as above but with specific hash and comparison functions. If the
237 functions are NULL then default values are used. */
239 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
240 SilcHashFunction hash,
242 SilcHashCompare compare,
243 void *compare_context)
248 SILC_LOG_DEBUG(("Deleting cache entry"));
250 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
251 hash, hash_context, compare,
256 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
258 ret = silc_hash_table_del(cache->context_table, c->context);
260 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
261 hash_context, compare, compare_context,
267 /* Deletes ID cache entry by context. */
269 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
274 SILC_LOG_DEBUG(("Deleting cache entry"));
276 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
280 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
282 ret = silc_hash_table_del(cache->context_table, c->context);
284 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
291 /* Deletes all ID entries from cache. Free's memory as well. */
293 bool silc_idcache_del_all(SilcIDCache cache)
295 silc_hash_table_free(cache->id_table);
296 silc_hash_table_free(cache->name_table);
297 silc_hash_table_free(cache->context_table);
302 static void silc_idcache_destructor_dummy(void *key, void *context,
305 /* Dummy - nothing */
308 /* Foreach callback fro silc_idcache_purge. */
310 static void silc_idcache_purge_foreach(void *key, void *context,
313 SilcIDCache cache = (SilcIDCache)user_context;
314 SilcUInt32 curtime = time(NULL);
315 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
317 if (c->expire && c->expire < curtime) {
318 /* Remove the entry from the hash tables */
320 silc_hash_table_del_by_context(cache->name_table, c->name, c);
322 silc_hash_table_del(cache->context_table, c->context);
324 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
325 NULL, NULL, NULL, NULL,
326 silc_idcache_destructor_dummy, NULL);
328 /* Call the destructor */
329 if (cache->destructor)
330 cache->destructor(cache, c);
332 /* Free the entry, it has been deleted from the hash tables */
337 /* Purges the cache by removing expired cache entires. Note that this
338 may be very slow operation. */
340 bool silc_idcache_purge(SilcIDCache cache)
342 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
346 /* Purges the specific entry by context. */
348 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
353 if (!silc_hash_table_find(cache->context_table, context, NULL,
357 /* Remove the entry from the hash tables */
359 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
361 ret = silc_hash_table_del(cache->context_table, c->context);
364 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
365 NULL, NULL, NULL, NULL,
366 silc_idcache_destructor_dummy, NULL);
368 /* Call the destructor */
369 if (cache->destructor)
370 cache->destructor(cache, c);
372 /* Free the entry, it has been deleted from the hash tables */
378 /* Callback that is called by the hash table routine when traversing
379 entrys in the hash table. */
381 static void silc_idcache_get_all_foreach(void *key, void *context,
384 SilcIDCacheList list = (SilcIDCacheList)user_context;
385 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
388 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
390 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
392 SilcIDCacheList list;
397 list = silc_idcache_list_alloc();
400 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
402 if (silc_idcache_list_count(list) == 0) {
403 silc_idcache_list_free(list);
412 /* Find ID Cache entry by ID. May return multiple entries. */
414 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
415 SilcIDCacheList *ret)
417 SilcIDCacheList list;
419 list = silc_idcache_list_alloc();
426 silc_hash_table_find_foreach(cache->id_table, id,
427 silc_idcache_get_all_foreach, list);
429 if (silc_idcache_list_count(list) == 0) {
430 silc_idcache_list_free(list);
439 /* Find specific ID with specific hash function and comparison functions.
440 If `hash' is NULL then the default hash funtion is used and if `compare'
441 is NULL default comparison function is used. */
443 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
444 SilcHashFunction hash,
446 SilcHashCompare compare,
447 void *compare_context,
448 SilcIDCacheEntry *ret)
450 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
451 hash, hash_context, compare,
455 /* Find one specific ID entry. */
457 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
458 SilcIDCacheEntry *ret)
460 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
463 /* Finds cache entry by context. */
465 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
466 SilcIDCacheEntry *ret)
468 return silc_hash_table_find(cache->context_table, context, NULL,
472 /* Find ID Cache entry by name. Returns list of cache entries. */
474 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
475 SilcIDCacheList *ret)
477 SilcIDCacheList list;
479 list = silc_idcache_list_alloc();
486 silc_hash_table_find_foreach(cache->name_table, name,
487 silc_idcache_get_all_foreach, list);
489 if (silc_idcache_list_count(list) == 0) {
490 silc_idcache_list_free(list);
499 /* Find ID Cache entry by name. Returns one cache entry. */
501 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
502 SilcIDCacheEntry *ret)
504 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
510 /* Allocates ID cache list. */
512 static SilcIDCacheList silc_idcache_list_alloc()
514 SilcIDCacheList list;
516 list = silc_calloc(1, sizeof(*list));
523 /* Adds cache entry to the ID cache list. If needed reallocates memory
526 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
530 /* Try to add to static cache */
531 if (!list->cache_dyn_count)
532 for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
533 if (!list->cache[i]) {
534 list->cache[i] = cache;
540 /* Static cache is full, allocate dynamic cache */
541 for (i = 0; i < list->cache_dyn_count; i++) {
542 if (!list->cache_dyn[i]) {
543 list->cache_dyn[i] = cache;
549 if (i >= list->cache_dyn_count) {
552 i = list->cache_dyn_count;
553 list->cache_dyn = silc_realloc(list->cache_dyn,
554 sizeof(*list->cache_dyn) * (i + 5));
555 if (!list->cache_dyn)
558 /* NULL the reallocated area */
559 for (k = i; k < (i + 5); k++)
560 list->cache_dyn[k] = NULL;
562 list->cache_dyn[i] = cache;
564 list->cache_dyn_count += 5;
568 /* Returns number of cache entries in the ID cache list. */
570 int silc_idcache_list_count(SilcIDCacheList list)
572 return list->cache_count;
575 /* Returns first entry from the ID cache list. */
577 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
581 if (!list->cache[list->pos])
585 *ret = list->cache[list->pos];
590 /* Returns next entry from the ID cache list. */
592 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
597 list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
602 if (list->dyn && list->pos >= list->cache_dyn_count)
605 if (!list->dyn && !list->cache[list->pos])
608 if (list->dyn && !list->cache_dyn[list->pos])
613 *ret = list->cache[list->pos];
615 *ret = list->cache_dyn[list->pos];
621 /* Frees ID cache list. User must free the list object returned by
622 any of the searching functions. */
624 void silc_idcache_list_free(SilcIDCacheList list)
628 silc_free(list->cache_dyn);