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;
72 unsigned int delete_id : 1;
73 unsigned int delete_name : 1;
79 This is returned when searching the cache. Enumeration functions are
80 provided to traverse the list; actually this is used as table not as
83 By default the found cache entries are saved into the static cache
84 table to provide access without reallocation. However, if the static
85 table is full, rest of the cache entries are dynamically allocated
86 into `cache_dyn' table. Traversing functions automatically handles
90 struct SilcIDCacheListStruct {
91 SilcIDCacheEntry cache[128];
92 SilcIDCacheEntry *cache_dyn;
93 SilcUInt32 cache_dyn_count;
94 SilcUInt32 cache_count;
99 /* Allocates new ID cache object. The initial amount of allocated entries
100 can be sent as argument. If `count' is 0 the system uses default values.
101 The `id_type' defines the types of the ID's that will be saved to the
104 SilcIDCache silc_idcache_alloc(SilcUInt32 count, SilcIdType id_type,
105 SilcIDCacheDestructor destructor,
106 void *destructor_context,
107 bool delete_id, bool delete_name)
111 SILC_LOG_DEBUG(("Allocating new cache"));
113 cache = silc_calloc(1, sizeof(*cache));
116 cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
117 SILC_32_TO_PTR(id_type),
118 silc_hash_id_compare,
119 SILC_32_TO_PTR(id_type),
120 silc_idcache_destructor,
122 cache->name_table = silc_hash_table_alloc(count, silc_hash_utf8_string, NULL,
123 silc_hash_utf8_compare, NULL,
125 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
126 NULL, NULL, NULL, NULL, TRUE);
127 cache->destructor = destructor;
128 cache->context = destructor_context;
129 cache->type = id_type;
130 cache->delete_id = delete_id;
131 cache->delete_name = delete_name;
133 if (!cache->id_table || !cache->name_table || !cache->context_table) {
135 silc_hash_table_free(cache->id_table);
136 if (cache->name_table)
137 silc_hash_table_free(cache->name_table);
138 if (cache->context_table)
139 silc_hash_table_free(cache->context_table);
147 /* Frees ID cache object and cache entries */
149 void silc_idcache_free(SilcIDCache cache)
152 silc_hash_table_free(cache->id_table);
153 silc_hash_table_free(cache->name_table);
154 silc_hash_table_free(cache->context_table);
159 /* Add new entry to the cache. Returns TRUE if the entry was added and
160 FALSE if it could not be added. The `name' is the name associated with
161 the ID, the `id' the actual ID and the `context' a used specific context.
162 If the `expire' is TRUE the entry expires in default time and if FALSE
163 the entry never expires from the cache. */
165 bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
166 void *context, int expire, SilcIDCacheEntry *ret)
170 SILC_LOG_DEBUG(("Adding cache entry"));
172 /* Allocate new cache entry */
173 c = silc_calloc(1, sizeof(*c));
179 c->context = context;
181 /* Add the new entry to the hash tables */
184 silc_hash_table_add(cache->id_table, id, c);
186 silc_hash_table_add(cache->name_table, name, c);
188 silc_hash_table_add(cache->context_table, context, c);
196 /* Destructor for the ID Cache entry */
198 static void silc_idcache_destructor(void *key, void *context,
201 SilcIDCacheEntry c = context;
203 SilcIDCache cache = user_context;
205 if (cache->delete_id)
207 if (cache->delete_name)
210 memset(c, 'F', sizeof(*c));
215 /* Delete cache entry from cache. */
217 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
221 SILC_LOG_DEBUG(("Deleting cache entry"));
224 ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
226 ret = silc_hash_table_del(cache->context_table, old->context);
228 ret = silc_hash_table_del(cache->id_table, old->id);
230 silc_idcache_destructor(NULL, old, NULL);
237 /* Deletes ID cache entry by ID. */
239 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
243 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
246 return silc_idcache_del(cache, c);
249 /* Same as above but with specific hash and comparison functions. If the
250 functions are NULL then default values are used. */
252 bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
253 SilcHashFunction hash,
255 SilcHashCompare compare,
256 void *compare_context)
261 SILC_LOG_DEBUG(("Deleting cache entry"));
263 if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
264 hash, hash_context, compare,
269 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
271 ret = silc_hash_table_del(cache->context_table, c->context);
273 ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
274 hash_context, compare, compare_context,
279 /* Deletes ID cache entry by context. */
281 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
286 SILC_LOG_DEBUG(("Deleting cache entry"));
288 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
292 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
294 ret = silc_hash_table_del(cache->context_table, c->context);
296 ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
298 silc_idcache_destructor(NULL, c, NULL);
305 /* Deletes all ID entries from cache. Free's memory as well. */
307 bool silc_idcache_del_all(SilcIDCache cache)
309 silc_hash_table_free(cache->id_table);
310 silc_hash_table_free(cache->name_table);
311 silc_hash_table_free(cache->context_table);
316 static void silc_idcache_destructor_dummy(void *key, void *context,
319 /* Dummy - nothing */
322 /* Foreach callback fro silc_idcache_purge. */
324 static void silc_idcache_purge_foreach(void *key, void *context,
327 SilcIDCache cache = (SilcIDCache)user_context;
328 SilcUInt32 curtime = time(NULL);
329 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
335 if (c->expire && c->expire < curtime) {
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,
348 /* Call the destructor */
349 if (cache->destructor)
350 cache->destructor(cache, c, cache->context);
352 /* Free the entry, it has been deleted from the hash tables */
353 silc_idcache_destructor(NULL, c, NULL);
358 /* Purges the cache by removing expired cache entires. Note that this
359 may be very slow operation. */
361 bool silc_idcache_purge(SilcIDCache cache)
363 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
367 /* Purges the specific entry by context. */
369 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
374 if (!silc_hash_table_find(cache->context_table, context, NULL,
378 /* Remove the entry from the hash tables */
380 ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
382 ret = silc_hash_table_del(cache->context_table, c->context);
385 silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
386 NULL, NULL, NULL, NULL,
387 silc_idcache_destructor_dummy, NULL);
389 /* Call the destructor */
390 if (cache->destructor)
391 cache->destructor(cache, c, cache->context);
393 /* Free the entry, it has been deleted from the hash tables */
394 silc_idcache_destructor(NULL, c, NULL);
400 /* Callback that is called by the hash table routine when traversing
401 entrys in the hash table. */
403 static void silc_idcache_get_all_foreach(void *key, void *context,
406 SilcIDCacheList list = (SilcIDCacheList)user_context;
409 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
412 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
414 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
416 SilcIDCacheList list;
421 list = silc_idcache_list_alloc();
424 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
426 if (silc_idcache_list_count(list) == 0) {
427 silc_idcache_list_free(list);
436 /* Find ID Cache entry by ID. May return multiple entries. */
438 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
439 SilcIDCacheList *ret)
441 SilcIDCacheList list;
443 list = silc_idcache_list_alloc();
450 silc_hash_table_find_foreach(cache->id_table, id,
451 silc_idcache_get_all_foreach, list);
453 if (silc_idcache_list_count(list) == 0) {
454 silc_idcache_list_free(list);
463 /* Find specific ID with specific hash function and comparison functions.
464 If `hash' is NULL then the default hash funtion is used and if `compare'
465 is NULL default comparison function is used. */
467 bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id,
468 SilcHashFunction hash,
470 SilcHashCompare compare,
471 void *compare_context,
472 SilcIDCacheEntry *ret)
474 return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
475 hash, hash_context, compare,
479 /* Find one specific ID entry. */
481 bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id,
482 SilcIDCacheEntry *ret)
484 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
487 /* Finds cache entry by context. */
489 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
490 SilcIDCacheEntry *ret)
492 return silc_hash_table_find(cache->context_table, context, NULL,
496 /* Find ID Cache entry by name. Returns list of cache entries. */
498 bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
499 SilcIDCacheList *ret)
501 SilcIDCacheList list;
503 list = silc_idcache_list_alloc();
510 silc_hash_table_find_foreach(cache->name_table, name,
511 silc_idcache_get_all_foreach, list);
513 if (silc_idcache_list_count(list) == 0) {
514 silc_idcache_list_free(list);
523 /* Find ID Cache entry by name. Returns one cache entry. */
525 bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
526 SilcIDCacheEntry *ret)
528 if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
534 /* Allocates ID cache list. */
536 static SilcIDCacheList silc_idcache_list_alloc()
538 SilcIDCacheList list;
540 list = silc_calloc(1, sizeof(*list));
547 /* Adds cache entry to the ID cache list. If needed reallocates memory
550 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
554 /* Try to add to static cache */
555 if (!list->cache_dyn_count)
556 for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
557 if (!list->cache[i]) {
558 list->cache[i] = cache;
564 /* Static cache is full, allocate dynamic cache */
565 for (i = 0; i < list->cache_dyn_count; i++) {
566 if (!list->cache_dyn[i]) {
567 list->cache_dyn[i] = cache;
573 if (i >= list->cache_dyn_count) {
576 i = list->cache_dyn_count;
577 list->cache_dyn = silc_realloc(list->cache_dyn,
578 sizeof(*list->cache_dyn) * (i + 5));
579 if (!list->cache_dyn)
582 /* NULL the reallocated area */
583 for (k = i; k < (i + 5); k++)
584 list->cache_dyn[k] = NULL;
586 list->cache_dyn[i] = cache;
588 list->cache_dyn_count += 5;
592 /* Returns number of cache entries in the ID cache list. */
594 int silc_idcache_list_count(SilcIDCacheList list)
596 return list->cache_count;
599 /* Returns first entry from the ID cache list. */
601 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
605 if (!list->cache[list->pos])
609 *ret = list->cache[list->pos];
614 /* Returns next entry from the ID cache list. */
616 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
621 list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
626 if (list->dyn && list->pos >= list->cache_dyn_count)
629 if (!list->dyn && !list->cache[list->pos])
632 if (list->dyn && !list->cache_dyn[list->pos])
637 *ret = list->cache[list->pos];
639 *ret = list->cache_dyn[list->pos];
645 /* Frees ID cache list. User must free the list object returned by
646 any of the searching functions. */
648 void silc_idcache_list_free(SilcIDCacheList list)
652 silc_free(list->cache_dyn);