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"
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 data_table
47 Hash table using the data 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 data_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 uint32 cache_dyn_count;
95 /* Allocates new ID cache object. The initial amount of allocated entries
96 can be sent as argument. If `count' is 0 the system uses default values.
97 The `id_type' defines the types of the ID's that will be saved to the
100 SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
101 SilcIDCacheDestructor destructor)
105 SILC_LOG_DEBUG(("Allocating new cache"));
107 cache = silc_calloc(1, sizeof(*cache));
108 cache->id_table = silc_hash_table_alloc(count, silc_hash_id,
109 (void *)(uint32)id_type,
110 silc_hash_id_compare,
111 (void *)(uint32)id_type,
112 silc_idcache_destructor, NULL);
113 cache->data_table = silc_hash_table_alloc(count, silc_hash_data, NULL,
114 silc_hash_data_compare, NULL,
116 cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
117 NULL, NULL, NULL, NULL);
118 cache->destructor = destructor;
119 cache->type = id_type;
124 /* Free's ID cache object and cache entries */
126 void silc_idcache_free(SilcIDCache cache)
129 silc_hash_table_free(cache->id_table);
130 silc_hash_table_free(cache->data_table);
131 silc_hash_table_free(cache->context_table);
136 /* Add new entry to the cache. */
138 bool silc_idcache_add(SilcIDCache cache, unsigned char *data,
139 uint32 data_len, void *id, void *context, int expire)
142 uint32 curtime = time(NULL);
144 SILC_LOG_DEBUG(("Adding cache entry"));
146 /* See if entry with this ID already exists. */
147 if (silc_hash_table_find(cache->id_table, id, NULL, NULL))
150 /* Allocate new cache entry */
151 c = silc_calloc(1, sizeof(*c));
153 c->data_len = data_len;
155 c->expire = (expire ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
156 c->context = context;
158 /* Add the new entry to the hash tables */
160 silc_hash_table_add(cache->id_table, id, c);
162 silc_hash_table_add_ext(cache->data_table, data, c,
163 silc_hash_data, (void *)data_len);
165 silc_hash_table_add(cache->context_table, context, c);
167 /* See whether we have time to rehash the tables */
168 if ((silc_hash_table_count(cache->id_table) / 2) >
169 silc_hash_table_size(cache->id_table)) {
170 silc_hash_table_rehash(cache->id_table, 0);
171 silc_hash_table_rehash(cache->data_table, 0);
172 silc_hash_table_rehash(cache->context_table, 0);
178 /* Destructor for the ID Cache entry */
180 static void silc_idcache_destructor(void *key, void *context,
186 /* Delete cache entry from cache. */
188 bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
192 ret = silc_hash_table_del_by_context_ext(cache->data_table, old->data, old,
194 (void *)old->data_len,
195 silc_hash_data_compare,
196 (void *)old->data_len);
197 ret = silc_hash_table_del(cache->context_table, old->context);
198 ret = silc_hash_table_del(cache->id_table, old->id);
203 /* Deletes ID cache entry by ID. */
205 bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
209 if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
212 return silc_idcache_del(cache, c);
215 /* Deletes ID cache entry by context. */
217 bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
221 if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
224 return silc_idcache_del(cache, c);
227 /* Deletes all ID entries from cache. Free's memory as well. */
229 bool silc_idcache_del_all(SilcIDCache cache)
231 silc_hash_table_free(cache->id_table);
232 silc_hash_table_free(cache->data_table);
233 silc_hash_table_free(cache->context_table);
238 /* Foreach callback fro silc_idcache_purge. */
240 static void silc_idcache_purge_foreach(void *key, void *context,
243 SilcIDCache cache = (SilcIDCache)user_context;
244 uint32 curtime = time(NULL);
245 SilcIDCacheEntry c = (SilcIDCacheEntry)context;
247 if (c->expire && c->expire < curtime) {
248 /* Call the destructor */
249 if (cache->destructor)
250 cache->destructor(cache, c);
252 /* Delete the entry */
253 silc_idcache_del(cache, c);
257 /* Purges the cache by removing expired cache entires. Note that this
258 may be very slow operation. */
260 bool silc_idcache_purge(SilcIDCache cache)
262 silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
266 /* Purges the specific entry by context. */
268 bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
270 SilcIDCacheEntry entry;
272 if (!silc_hash_table_find(cache->context_table, context, NULL,
276 /* Call the destructor */
277 if (cache->destructor)
278 cache->destructor(cache, entry);
280 return silc_idcache_del(cache, entry);
283 /* Callback that is called by the hash table routine when traversing
284 entrys in the hash table. */
286 static void silc_idcache_get_all_foreach(void *key, void *context,
289 SilcIDCacheList list = (SilcIDCacheList)user_context;
290 silc_idcache_list_add(list, (SilcIDCacheEntry)context);
293 /* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
295 bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
297 SilcIDCacheList list;
302 list = silc_idcache_list_alloc();
303 silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
305 if (silc_idcache_list_count(list) == 0) {
306 silc_idcache_list_free(list);
315 /* Find ID Cache entry by ID. */
317 bool silc_idcache_find_by_id(SilcIDCache cache, void *id,
318 SilcIDCacheEntry *ret)
320 return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
323 /* Finds cache entry by context. */
325 bool silc_idcache_find_by_context(SilcIDCache cache, void *context,
326 SilcIDCacheEntry *ret)
328 return silc_hash_table_find(cache->context_table, context, NULL,
332 /* Find ID Cache entry by data. The data maybe anything that must
333 match exactly. Returns list of cache entries. */
335 bool silc_idcache_find_by_data(SilcIDCache cache, unsigned char *data,
336 unsigned int data_len, SilcIDCacheList *ret)
338 SilcIDCacheList list;
340 list = silc_idcache_list_alloc();
345 silc_hash_table_find_foreach_ext(cache->data_table, data,
346 silc_hash_data, (void *)data_len,
347 silc_hash_data_compare, (void *)data_len,
348 silc_idcache_get_all_foreach, list);
350 if (silc_idcache_list_count(list) == 0) {
351 silc_idcache_list_free(list);
360 /* Find ID Cache entry by data. The data maybe anything that must
361 match exactly. Returns one cache entry. */
363 bool silc_idcache_find_by_data_one(SilcIDCache cache, unsigned char *data,
364 unsigned int data_len,
365 SilcIDCacheEntry *ret)
369 if (!silc_hash_table_find_ext(cache->data_table, data, NULL, (void *)&c,
370 silc_hash_data, (void *)data_len,
371 silc_hash_data_compare, (void *)data_len))
380 /* Allocates ID cache list. */
382 static SilcIDCacheList silc_idcache_list_alloc()
384 SilcIDCacheList list;
386 list = silc_calloc(1, sizeof(*list));
391 /* Adds cache entry to the ID cache list. If needed reallocates memory
394 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
398 /* Try to add to static cache */
399 if (!list->cache_dyn_count)
400 for (i = 0; i < sizeof(list->cache); i++) {
401 if (!list->cache[i]) {
402 list->cache[i] = cache;
408 /* Static cache is full, allocate dynamic cache */
409 for (i = 0; i < list->cache_dyn_count; i++) {
410 if (!list->cache_dyn[i]) {
411 list->cache_dyn[i] = cache;
417 if (i >= list->cache_dyn_count) {
421 list->cache_dyn = silc_realloc(list->cache_dyn,
422 sizeof(*list->cache) * (i));
424 /* NULL the reallocated area */
425 for (k = list->cache_dyn_count; k < i; k++)
426 list->cache_dyn[k] = NULL;
428 list->cache_dyn[list->cache_dyn_count] = cache;
429 list->cache_dyn_count = i;
434 /* Returns number of cache entries in the ID cache list. */
436 int silc_idcache_list_count(SilcIDCacheList list)
438 return list->cache_count;
441 /* Returns first entry from the ID cache list. */
443 bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
447 if (!list->cache[list->pos])
451 *ret = list->cache[list->pos];
456 /* Returns next entry from the ID cache list. */
458 bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
463 if (list->pos >= sizeof(list->cache)) {
468 if (dyn && list->pos >= list->cache_dyn_count)
471 if (!dyn && !list->cache[list->pos])
474 if (dyn && !list->cache_dyn[list->pos])
479 *ret = list->cache[list->pos];
481 *ret = list->cache_dyn[list->pos];
487 /* Free's ID cache list. User must free the list object returned by
488 any of the searching functions. */
490 void silc_idcache_list_free(SilcIDCacheList list)
494 silc_free(list->cache_dyn);