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[128];
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, TRUE);
116 cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL,
117 silc_hash_string_compare, NULL,
119 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
120 NULL, NULL, NULL, NULL, TRUE);
121 cache->destructor = destructor;
122 cache->type = id_type;
124 if (!cache->id_table || !cache->name_table || !cache->context_table) {
126 silc_hash_table_free(cache->id_table);
127 if (cache->name_table)
128 silc_hash_table_free(cache->name_table);
129 if (cache->context_table)
130 silc_hash_table_free(cache->context_table);
138 /* Frees ID cache object and cache entries */
140 void silc_idcache_free(SilcIDCache cache)
143 silc_hash_table_free(cache->id_table);
144 silc_hash_table_free(cache->name_table);
145 silc_hash_table_free(cache->context_table);
150 /* Add new entry to the cache. Returns TRUE if the entry was added and
151 FALSE if it could not be added. The `name' is the name associated with
152 the ID, the `id' the actual ID and the `context' a used specific context.
153 If the `expire' is TRUE the entry expires in default time and if FALSE
154 the entry never expires from the cache. */
156 bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
157 void *context, int expire, SilcIDCacheEntry *ret)
161 SILC_LOG_DEBUG(("Adding cache entry"));
163 /* Allocate new cache entry */
164 c = silc_calloc(1, sizeof(*c));
170 c->context = context;
172 /* Add the new entry to the hash tables */
175 silc_hash_table_add(cache->id_table, id, c);
177 silc_hash_table_add(cache->name_table, name, c);
179 silc_hash_table_add(cache->context_table, context, c);
187 /* Destructor for the ID Cache entry */
189 static void silc_idcache_destructor(void *key, void *context,
192 SilcIDCacheEntry c = context;
194 memset(c, 'F', sizeof(*c));
199 /* Delete cache entry from cache. */
201 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
205 SILC_LOG_DEBUG(("Deleting cache entry"));
208 ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
210 ret = silc_hash_table_del(cache->context_table, old->context);
212 ret = silc_hash_table_del(cache->id_table, old->id);
214 silc_idcache_destructor(NULL, old, NULL);
221 /* Deletes ID cache entry by ID. */
223 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
227 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
230 return silc_idcache_del(cache, c);
233 /* Same as above but with specific hash and comparison functions. If the
234 functions are NULL then default values are used. */
236 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
237 SilcHashFunction hash,
239 SilcHashCompare compare,
240 void *compare_context)
245 SILC_LOG_DEBUG(("Deleting cache entry"));
247 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
248 hash, hash_context, compare,
253 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
255 ret = silc_hash_table_del(cache->context_table, c->context);
257 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
258 hash_context, compare, compare_context,
264 /* Deletes ID cache entry by context. */
266 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
271 SILC_LOG_DEBUG(("Deleting cache entry"));
273 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
277 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
279 ret = silc_hash_table_del(cache->context_table, c->context);
281 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
283 silc_idcache_destructor(NULL, c, NULL);
290 /* Deletes all ID entries from cache. Free's memory as well. */
292 bool silc_idcache_del_all(SilcIDCache cache)
294 silc_hash_table_free(cache->id_table);
295 silc_hash_table_free(cache->name_table);
296 silc_hash_table_free(cache->context_table);
301 static void silc_idcache_destructor_dummy(void *key, void *context,
304 /* Dummy - nothing */
307 /* Foreach callback fro silc_idcache_purge. */
309 static void silc_idcache_purge_foreach(void *key, void *context,
312 SilcIDCache cache = (SilcIDCache)user_context;
313 SilcUInt32 curtime = time(NULL);
314 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
320 if (c->expire && c->expire < curtime) {
321 /* Remove the entry from the hash tables */
323 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
325 ret = silc_hash_table_del(cache->context_table, c->context);
328 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
329 NULL, NULL, NULL, NULL,
330 silc_idcache_destructor_dummy,
333 /* Call the destructor */
334 if (cache->destructor)
335 cache->destructor(cache, c);
337 /* Free the entry, it has been deleted from the hash tables */
338 silc_idcache_destructor(NULL, c, NULL);
343 /* Purges the cache by removing expired cache entires. Note that this
344 may be very slow operation. */
346 bool silc_idcache_purge(SilcIDCache cache)
348 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
352 /* Purges the specific entry by context. */
354 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
359 if (!silc_hash_table_find(cache->context_table, context, NULL,
363 /* Remove the entry from the hash tables */
365 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
367 ret = silc_hash_table_del(cache->context_table, c->context);
370 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
371 NULL, NULL, NULL, NULL,
372 silc_idcache_destructor_dummy, NULL);
374 /* Call the destructor */
375 if (cache->destructor)
376 cache->destructor(cache, c);
378 /* Free the entry, it has been deleted from the hash tables */
379 silc_idcache_destructor(NULL, c, NULL);
385 /* Callback that is called by the hash table routine when traversing
386 entrys in the hash table. */
388 static void silc_idcache_get_all_foreach(void *key, void *context,
391 SilcIDCacheList list = (SilcIDCacheList)user_context;
394 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
397 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
399 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
401 SilcIDCacheList list;
406 list = silc_idcache_list_alloc();
409 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
411 if (silc_idcache_list_count(list) == 0) {
412 silc_idcache_list_free(list);
421 /* Find ID Cache entry by ID. May return multiple entries. */
423 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
424 SilcIDCacheList *ret)
426 SilcIDCacheList list;
428 list = silc_idcache_list_alloc();
435 silc_hash_table_find_foreach(cache->id_table, id,
436 silc_idcache_get_all_foreach, list);
438 if (silc_idcache_list_count(list) == 0) {
439 silc_idcache_list_free(list);
448 /* Find specific ID with specific hash function and comparison functions.
449 If `hash' is NULL then the default hash funtion is used and if `compare'
450 is NULL default comparison function is used. */
452 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
453 SilcHashFunction hash,
455 SilcHashCompare compare,
456 void *compare_context,
457 SilcIDCacheEntry *ret)
459 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
460 hash, hash_context, compare,
464 /* Find one specific ID entry. */
466 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
467 SilcIDCacheEntry *ret)
469 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
472 /* Finds cache entry by context. */
474 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
475 SilcIDCacheEntry *ret)
477 return silc_hash_table_find(cache->context_table, context, NULL,
481 /* Find ID Cache entry by name. Returns list of cache entries. */
483 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
484 SilcIDCacheList *ret)
486 SilcIDCacheList list;
488 list = silc_idcache_list_alloc();
495 silc_hash_table_find_foreach(cache->name_table, name,
496 silc_idcache_get_all_foreach, list);
498 if (silc_idcache_list_count(list) == 0) {
499 silc_idcache_list_free(list);
508 /* Find ID Cache entry by name. Returns one cache entry. */
510 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
511 SilcIDCacheEntry *ret)
513 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
519 /* Allocates ID cache list. */
521 static SilcIDCacheList silc_idcache_list_alloc()
523 SilcIDCacheList list;
525 list = silc_calloc(1, sizeof(*list));
532 /* Adds cache entry to the ID cache list. If needed reallocates memory
535 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
539 /* Try to add to static cache */
540 if (!list->cache_dyn_count)
541 for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
542 if (!list->cache[i]) {
543 list->cache[i] = cache;
549 /* Static cache is full, allocate dynamic cache */
550 for (i = 0; i < list->cache_dyn_count; i++) {
551 if (!list->cache_dyn[i]) {
552 list->cache_dyn[i] = cache;
558 if (i >= list->cache_dyn_count) {
561 i = list->cache_dyn_count;
562 list->cache_dyn = silc_realloc(list->cache_dyn,
563 sizeof(*list->cache_dyn) * (i + 5));
564 if (!list->cache_dyn)
567 /* NULL the reallocated area */
568 for (k = i; k < (i + 5); k++)
569 list->cache_dyn[k] = NULL;
571 list->cache_dyn[i] = cache;
573 list->cache_dyn_count += 5;
577 /* Returns number of cache entries in the ID cache list. */
579 int silc_idcache_list_count(SilcIDCacheList list)
581 return list->cache_count;
584 /* Returns first entry from the ID cache list. */
586 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
590 if (!list->cache[list->pos])
594 *ret = list->cache[list->pos];
599 /* Returns next entry from the ID cache list. */
601 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
606 list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
611 if (list->dyn && list->pos >= list->cache_dyn_count)
614 if (!list->dyn && !list->cache[list->pos])
617 if (list->dyn && !list->cache_dyn[list->pos])
622 *ret = list->cache[list->pos];
624 *ret = list->cache_dyn[list->pos];
630 /* Frees ID cache list. User must free the list object returned by
631 any of the searching functions. */
633 void silc_idcache_list_free(SilcIDCacheList list)
637 silc_free(list->cache_dyn);