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; 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;
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 bool delete_id, bool delete_name)
109 SILC_LOG_DEBUG(("Allocating new cache"));
111 cache = silc_calloc(1, sizeof(*cache));
114 cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
115 SILC_32_TO_PTR(id_type),
116 silc_hash_id_compare,
117 SILC_32_TO_PTR(id_type),
118 silc_idcache_destructor,
120 cache->name_table = silc_hash_table_alloc(count, silc_hash_utf8_string, NULL,
121 silc_hash_utf8_compare, NULL,
123 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
124 NULL, NULL, NULL, NULL, TRUE);
125 cache->destructor = destructor;
126 cache->type = id_type;
127 cache->delete_id = delete_id;
128 cache->delete_name = delete_name;
130 if (!cache->id_table || !cache->name_table || !cache->context_table) {
132 silc_hash_table_free(cache->id_table);
133 if (cache->name_table)
134 silc_hash_table_free(cache->name_table);
135 if (cache->context_table)
136 silc_hash_table_free(cache->context_table);
144 /* Frees ID cache object and cache entries */
146 void silc_idcache_free(SilcIDCache cache)
149 silc_hash_table_free(cache->id_table);
150 silc_hash_table_free(cache->name_table);
151 silc_hash_table_free(cache->context_table);
156 /* Add new entry to the cache. Returns TRUE if the entry was added and
157 FALSE if it could not be added. The `name' is the name associated with
158 the ID, the `id' the actual ID and the `context' a used specific context.
159 If the `expire' is TRUE the entry expires in default time and if FALSE
160 the entry never expires from the cache. */
162 bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
163 void *context, int expire, SilcIDCacheEntry *ret)
167 SILC_LOG_DEBUG(("Adding cache entry"));
169 /* Allocate new cache entry */
170 c = silc_calloc(1, sizeof(*c));
176 c->context = context;
178 /* Add the new entry to the hash tables */
181 silc_hash_table_add(cache->id_table, id, c);
183 silc_hash_table_add(cache->name_table, name, c);
185 silc_hash_table_add(cache->context_table, context, c);
193 /* Destructor for the ID Cache entry */
195 static void silc_idcache_destructor(void *key, void *context,
198 SilcIDCacheEntry c = context;
200 SilcIDCache cache = user_context;
202 if (cache->delete_id)
204 if (cache->delete_name)
207 memset(c, 'F', sizeof(*c));
212 /* Delete cache entry from cache. */
214 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
218 SILC_LOG_DEBUG(("Deleting cache entry"));
221 ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
223 ret = silc_hash_table_del(cache->context_table, old->context);
225 ret = silc_hash_table_del(cache->id_table, old->id);
227 silc_idcache_destructor(NULL, old, NULL);
234 /* Deletes ID cache entry by ID. */
236 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
240 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
243 return silc_idcache_del(cache, c);
246 /* Same as above but with specific hash and comparison functions. If the
247 functions are NULL then default values are used. */
249 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
250 SilcHashFunction hash,
252 SilcHashCompare compare,
253 void *compare_context)
258 SILC_LOG_DEBUG(("Deleting cache entry"));
260 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
261 hash, hash_context, compare,
266 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
268 ret = silc_hash_table_del(cache->context_table, c->context);
270 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
271 hash_context, compare, compare_context,
276 /* Deletes ID cache entry by context. */
278 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
283 SILC_LOG_DEBUG(("Deleting cache entry"));
285 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
289 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
291 ret = silc_hash_table_del(cache->context_table, c->context);
293 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
295 silc_idcache_destructor(NULL, c, NULL);
302 /* Deletes all ID entries from cache. Free's memory as well. */
304 bool silc_idcache_del_all(SilcIDCache cache)
306 silc_hash_table_free(cache->id_table);
307 silc_hash_table_free(cache->name_table);
308 silc_hash_table_free(cache->context_table);
313 static void silc_idcache_destructor_dummy(void *key, void *context,
316 /* Dummy - nothing */
319 /* Foreach callback fro silc_idcache_purge. */
321 static void silc_idcache_purge_foreach(void *key, void *context,
324 SilcIDCache cache = (SilcIDCache)user_context;
325 SilcUInt32 curtime = time(NULL);
326 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
332 if (c->expire && c->expire < curtime) {
333 /* Remove the entry from the hash tables */
335 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
337 ret = silc_hash_table_del(cache->context_table, c->context);
340 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
341 NULL, NULL, NULL, NULL,
342 silc_idcache_destructor_dummy,
345 /* Call the destructor */
346 if (cache->destructor)
347 cache->destructor(cache, c);
349 /* Free the entry, it has been deleted from the hash tables */
350 silc_idcache_destructor(NULL, c, NULL);
355 /* Purges the cache by removing expired cache entires. Note that this
356 may be very slow operation. */
358 bool silc_idcache_purge(SilcIDCache cache)
360 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
364 /* Purges the specific entry by context. */
366 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
371 if (!silc_hash_table_find(cache->context_table, context, NULL,
375 /* Remove the entry from the hash tables */
377 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
379 ret = silc_hash_table_del(cache->context_table, c->context);
382 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
383 NULL, NULL, NULL, NULL,
384 silc_idcache_destructor_dummy, NULL);
386 /* Call the destructor */
387 if (cache->destructor)
388 cache->destructor(cache, c);
390 /* Free the entry, it has been deleted from the hash tables */
391 silc_idcache_destructor(NULL, c, NULL);
397 /* Callback that is called by the hash table routine when traversing
398 entrys in the hash table. */
400 static void silc_idcache_get_all_foreach(void *key, void *context,
403 SilcIDCacheList list = (SilcIDCacheList)user_context;
406 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
409 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
411 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
413 SilcIDCacheList list;
418 list = silc_idcache_list_alloc();
421 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
423 if (silc_idcache_list_count(list) == 0) {
424 silc_idcache_list_free(list);
433 /* Find ID Cache entry by ID. May return multiple entries. */
435 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
436 SilcIDCacheList *ret)
438 SilcIDCacheList list;
440 list = silc_idcache_list_alloc();
447 silc_hash_table_find_foreach(cache->id_table, id,
448 silc_idcache_get_all_foreach, list);
450 if (silc_idcache_list_count(list) == 0) {
451 silc_idcache_list_free(list);
460 /* Find specific ID with specific hash function and comparison functions.
461 If `hash' is NULL then the default hash funtion is used and if `compare'
462 is NULL default comparison function is used. */
464 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
465 SilcHashFunction hash,
467 SilcHashCompare compare,
468 void *compare_context,
469 SilcIDCacheEntry *ret)
471 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
472 hash, hash_context, compare,
476 /* Find one specific ID entry. */
478 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
479 SilcIDCacheEntry *ret)
481 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
484 /* Finds cache entry by context. */
486 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
487 SilcIDCacheEntry *ret)
489 return silc_hash_table_find(cache->context_table, context, NULL,
493 /* Find ID Cache entry by name. Returns list of cache entries. */
495 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
496 SilcIDCacheList *ret)
498 SilcIDCacheList list;
500 list = silc_idcache_list_alloc();
507 silc_hash_table_find_foreach(cache->name_table, name,
508 silc_idcache_get_all_foreach, list);
510 if (silc_idcache_list_count(list) == 0) {
511 silc_idcache_list_free(list);
520 /* Find ID Cache entry by name. Returns one cache entry. */
522 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
523 SilcIDCacheEntry *ret)
525 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
531 /* Allocates ID cache list. */
533 static SilcIDCacheList silc_idcache_list_alloc()
535 SilcIDCacheList list;
537 list = silc_calloc(1, sizeof(*list));
544 /* Adds cache entry to the ID cache list. If needed reallocates memory
547 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
551 /* Try to add to static cache */
552 if (!list->cache_dyn_count)
553 for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
554 if (!list->cache[i]) {
555 list->cache[i] = cache;
561 /* Static cache is full, allocate dynamic cache */
562 for (i = 0; i < list->cache_dyn_count; i++) {
563 if (!list->cache_dyn[i]) {
564 list->cache_dyn[i] = cache;
570 if (i >= list->cache_dyn_count) {
573 i = list->cache_dyn_count;
574 list->cache_dyn = silc_realloc(list->cache_dyn,
575 sizeof(*list->cache_dyn) * (i + 5));
576 if (!list->cache_dyn)
579 /* NULL the reallocated area */
580 for (k = i; k < (i + 5); k++)
581 list->cache_dyn[k] = NULL;
583 list->cache_dyn[i] = cache;
585 list->cache_dyn_count += 5;
589 /* Returns number of cache entries in the ID cache list. */
591 int silc_idcache_list_count(SilcIDCacheList list)
593 return list->cache_count;
596 /* Returns first entry from the ID cache list. */
598 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
602 if (!list->cache[list->pos])
606 *ret = list->cache[list->pos];
611 /* Returns next entry from the ID cache list. */
613 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
618 list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
623 if (list->dyn && list->pos >= list->cache_dyn_count)
626 if (!list->dyn && !list->cache[list->pos])
629 if (list->dyn && !list->cache_dyn[list->pos])
634 *ret = list->cache[list->pos];
636 *ret = list->cache_dyn[list->pos];
642 /* Frees ID cache list. User must free the list object returned by
643 any of the searching functions. */
645 void silc_idcache_list_free(SilcIDCacheList list)
649 silc_free(list->cache_dyn);