5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2000 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 int silc_idcache_sorter(const void *a, const void *b);
27 static SilcIDCacheList silc_idcache_list_alloc();
28 static void silc_idcache_list_add(SilcIDCacheList list,
29 SilcIDCacheEntry cache);
34 This is context for the ID cache system. This includes all the cache
35 entries and other internal data. This is read-only object and not
36 visible outside this cache system.
38 Fields are as follows:
40 SilcIDCacheEntry cache
42 Table of the cache entries allocated by silc_idcache_add function.
43 This table is reallocated when new entry is added into the cache.
47 Number of cache entries in the cache.
51 Boolean value to indicate whether the cache is sorted or not. If
52 cache is not sorted the fast access (described next) cannot be used.
53 Sorting can be done by calling sorting function or when adding new
58 Table to provide fast access into the cache by index. When searching
59 by data this table is used to get the index to the first occurence
60 of that data (or part of the data) in the cache. Purpose of this
61 is to provide faster access to the cache when searching by data.
62 This is updated by silc_idcache_add and sorting functions.
64 SilcIDCacheDestructor destructor
66 Destructor callback that is called when an cache entry expires or is
67 purged from the ID cache. The application must not free cache entry
68 because the library will do it automatically. The appliation, however,
69 is responsible of freeing any data in the entry.
72 struct SilcIDCacheStruct {
73 SilcIDCacheEntry cache;
77 SilcIDCacheDestructor destructor;
83 This is returned when searching the cache. Enumeration functions are
84 provided to traverse the list; actually this is used as table not as
87 By default the found cache entries are saved into the static cache
88 table to provide access without reallocation. However, if the static
89 table is full, rest of the cache entries are dynamically allocated
90 into `cache_dyn' table. Traversing functions automatically handles
94 struct SilcIDCacheListStruct {
95 SilcIDCacheEntry cache[64];
96 SilcIDCacheEntry *cache_dyn;
97 uint32 cache_dyn_count;
102 /* Allocates new ID cache object. The initial amount of allocated entries
103 can be sent as argument. If `count' is 0 the system uses default values. */
105 SilcIDCache silc_idcache_alloc(uint32 count,
106 SilcIDCacheDestructor destructor)
110 SILC_LOG_DEBUG(("Allocating new cache"));
112 cache = silc_calloc(1, sizeof(*cache));
113 cache->cache = silc_calloc(count ? count : 5, sizeof(*cache->cache));
114 cache->cache_count = count ? count : 5;
115 memset(cache->fast_access, -1, sizeof(cache->fast_access));
116 cache->destructor = destructor;
121 /* Free's ID cache object and cache entries */
123 void silc_idcache_free(SilcIDCache cache)
126 silc_free(cache->cache);
131 /* qsort() sorter function. */
133 static int silc_idcache_sorter(const void *a, const void *b)
135 SilcIDCacheEntry a1, b1;
137 a1 = (SilcIDCacheEntry)a;
138 b1 = (SilcIDCacheEntry)b;
140 if (!a1->data && !b1->data)
149 return a1->data[0] - b1->data[0];
152 /* Sorts given cache by data. After sorting this updates the fast access
153 table that can be used to access the cache faster. */
155 void silc_idcache_sort_by_data(SilcIDCache cache)
159 qsort(cache->cache, cache->cache_count,
160 sizeof(*cache->cache), silc_idcache_sorter);
162 memset(cache->fast_access, -1, sizeof(cache->fast_access));
164 /* Update the fast access table (this of course takes its own time when
165 the cache is very large). */
166 for (i = 0; i < cache->cache_count; i++) {
167 if (cache->cache[i].data &&
168 cache->fast_access[(int)cache->cache[i].data[0]] == -1)
169 cache->fast_access[(int)cache->cache[i].data[0]] = i;
172 cache->sorted = TRUE;
175 /* Find ID Cache entry by data. The data maybe anything that must
176 match exactly. Returns list of cache entries. */
178 int silc_idcache_find_by_data(SilcIDCache cache, unsigned char *data,
179 SilcIDCacheList *ret)
182 SilcIDCacheList list;
184 if (!cache || !cache->cache || !data)
187 list = silc_idcache_list_alloc();
190 i = cache->fast_access[(int)data[0]];
197 for (i = i; i < cache->cache_count; i++) {
198 if (cache->sorted && cache->cache[i].data &&
199 cache->cache[i].data[0] != data[0])
202 if (cache->cache[i].data &&
203 !memcmp(cache->cache[i].data, data, cache->cache[i].data_len))
204 silc_idcache_list_add(list, &(cache->cache[i]));
207 if (!silc_idcache_list_count(list)) {
208 silc_idcache_list_free(list);
215 silc_idcache_list_free(list);
220 /* Find ID Cache entry by data. The data maybe anything that must
221 match exactly. Returns one cache entry. */
223 int silc_idcache_find_by_data_one(SilcIDCache cache, unsigned char *data,
224 SilcIDCacheEntry *ret)
228 if (!cache || !cache->cache || !data)
232 i = cache->fast_access[(int)data[0]];
239 for (i = i; i < cache->cache_count; i++)
240 if (cache->cache[i].data &&
241 !memcmp(cache->cache[i].data, data, cache->cache[i].data_len)) {
243 *ret = &(cache->cache[i]);
250 /* Find ID Cache entry by data, loosely. The data don't have to be 100%
251 match. This ignores data case-sensitivity when searching with this
252 function. Returns list of cache entries. */
254 int silc_idcache_find_by_data_loose(SilcIDCache cache, unsigned char *data,
255 SilcIDCacheList *ret)
258 SilcIDCacheList list;
260 if (!cache || !cache->cache || !data)
263 list = silc_idcache_list_alloc();
265 c = tolower((int)data[0]);
268 i = cache->fast_access[c];
275 for (i = i; i < cache->cache_count; i++) {
276 if (cache->sorted && cache->cache[i].data &&
277 cache->cache[i].data[0] != (char)c)
280 if (cache->cache[i].data &&
281 !strcasecmp(cache->cache[i].data, data))
282 silc_idcache_list_add(list, &(cache->cache[i]));
286 c = toupper((int)data[0]);
287 i = cache->fast_access[c];
289 for (i = i; i < cache->cache_count; i++) {
290 if (cache->sorted && cache->cache[i].data &&
291 cache->cache[i].data[0] != (char)c)
294 if (cache->cache[i].data &&
295 !strcasecmp(cache->cache[i].data, data))
296 silc_idcache_list_add(list, &(cache->cache[i]));
300 if (!silc_idcache_list_count(list)) {
301 silc_idcache_list_free(list);
308 silc_idcache_list_free(list);
313 /* Find ID Cache entry by ID. Returns list of cache entries. If `id' is
314 SILC_ID_CACHE_ANY this returns all ID's of type `type'. */
316 int silc_idcache_find_by_id(SilcIDCache cache, void *id, SilcIdType type,
317 SilcIDCacheList *ret)
320 SilcIDCacheList list;
322 if (!cache || !cache->cache || !id)
325 id_len = silc_id_get_len(type);
327 list = silc_idcache_list_alloc();
329 if (id != SILC_ID_CACHE_ANY) {
330 for (i = 0; i < cache->cache_count; i++)
331 if (cache->cache[i].id && !memcmp(cache->cache[i].id, id, id_len))
332 silc_idcache_list_add(list, &(cache->cache[i]));
334 for (i = 0; i < cache->cache_count; i++)
335 if (cache->cache[i].id && cache->cache[i].type == type)
336 silc_idcache_list_add(list, &(cache->cache[i]));
339 if (!silc_idcache_list_count(list)) {
340 silc_idcache_list_free(list);
347 silc_idcache_list_free(list);
352 /* Find ID Cache entry by ID. Returns one cache entry. */
354 int silc_idcache_find_by_id_one(SilcIDCache cache, void *id, SilcIdType type,
355 SilcIDCacheEntry *ret)
359 if (!cache || !cache->cache || !id)
362 id_len = silc_id_get_len(type);
364 for (i = 0; i < cache->cache_count; i++)
365 if (cache->cache[i].id && !memcmp(cache->cache[i].id, id, id_len)) {
367 *ret = &(cache->cache[i]);
374 /* Finds cache entry by context. */
376 int silc_idcache_find_by_context(SilcIDCache cache, void *context,
377 SilcIDCacheEntry *ret)
381 if (!cache || !cache->cache || !context)
384 for (i = 0; i < cache->cache_count; i++)
385 if (cache->cache[i].context && cache->cache[i].context == context) {
387 *ret = &(cache->cache[i]);
394 /* Add new entry to the cache. Returns TRUE or FALSE. If `sort' is TRUE
395 then the cache is sorted after the new entry has been added. The
396 cache must be sorted in order for the fast access feature to work,
397 however, it is not mandatory. */
399 int silc_idcache_add(SilcIDCache cache, unsigned char *data,
400 uint32 data_len, SilcIdType id_type, void *id,
401 void *context, int sort, int expire)
405 uint32 curtime = time(NULL);
408 if (!cache || !cache->cache)
411 SILC_LOG_DEBUG(("Adding cache entry"));
414 count = cache->cache_count;
417 c = silc_calloc(5, sizeof(*c));
421 /* See if it exists already */
422 /* XXX this slows down this function. */
423 if (silc_idcache_find_by_id(cache, id, id_type, NULL))
426 for (i = 0; i < count; i++) {
427 if (c[i].data == NULL && c[i].id == NULL) {
429 c[i].data_len = data_len;
432 c[i].expire = (expire ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
433 c[i].context = context;
439 c = silc_realloc(c, sizeof(*c) * (count + 5));
440 for (i = count; i < count + 5; i++) {
444 c[count].data = data;
445 c[count].data_len = data_len;
446 c[count].type = id_type;
448 c[count].expire = (expire ? (curtime + SILC_ID_CACHE_EXPIRE) : 0);
449 c[count].context = context;
454 cache->cache_count = count;
455 cache->sorted = sort;
458 silc_idcache_sort_by_data(cache);
463 /* Delete cache entry from cache. */
466 int silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
474 int silc_idcache_del_by_data(SilcIDCache cache, unsigned char *data)
480 /* Deletes ID cache entry by ID. */
482 int silc_idcache_del_by_id(SilcIDCache cache, SilcIdType type, void *id)
486 if (!cache || !cache->cache || !id)
489 id_len = silc_id_get_len(type);
491 for (i = 0; i < cache->cache_count; i++)
492 if (cache->cache[i].id && !memcmp(cache->cache[i].id, id, id_len)) {
493 cache->cache[i].id = NULL;
494 cache->cache[i].data = NULL;
495 cache->cache[i].type = 0;
496 cache->cache[i].context = NULL;
503 /* Deletes all ID entries from cache. Free's memory as well. */
505 int silc_idcache_del_all(SilcIDCache cache)
507 if (!cache || !cache->cache)
510 silc_free(cache->cache);
512 cache->cache_count = 0;
517 /* Purges the cache by removing expired cache entires. This does not
518 free any memory though. */
520 int silc_idcache_purge(SilcIDCache cache)
523 uint32 curtime = time(NULL);
526 if (!cache || !cache->cache)
531 for (i = 0; i < cache->cache_count; i++) {
532 if (c[i].data && c[i].expire && c[i].expire < curtime) {
534 /* Call the destructor */
535 if (cache->destructor)
536 cache->destructor(cache, &c[i]);
549 /* Purges the specific entry by context. */
551 int silc_idcache_purge_by_context(SilcIDCache cache, void *context)
553 SilcIDCacheEntry entry;
555 if (!silc_idcache_find_by_context(cache, context, &entry))
558 /* Call the destructor */
559 if (cache->destructor)
560 cache->destructor(cache, entry);
566 entry->context = NULL;
570 /* Allocates ID cache list. */
572 static SilcIDCacheList silc_idcache_list_alloc()
574 SilcIDCacheList list;
576 list = silc_calloc(1, sizeof(*list));
581 /* Adds cache entry to the ID cache list. If needed reallocates memory
584 static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
588 /* Try to add to static cache */
589 if (!list->cache_dyn_count)
590 for (i = 0; i < sizeof(list->cache); i++) {
591 if (!list->cache[i]) {
592 list->cache[i] = cache;
598 /* Static cache is full, allocate dynamic cache */
599 for (i = 0; i < list->cache_dyn_count; i++) {
600 if (!list->cache_dyn[i]) {
601 list->cache_dyn[i] = cache;
607 if (i >= list->cache_dyn_count) {
611 list->cache_dyn = silc_realloc(list->cache_dyn,
612 sizeof(*list->cache) * (i));
614 /* NULL the reallocated area */
615 for (k = list->cache_dyn_count; k < i; k++)
616 list->cache_dyn[k] = NULL;
618 list->cache_dyn[list->cache_dyn_count] = cache;
619 list->cache_dyn_count = i;
624 /* Returns number of cache entries in the ID cache list. */
626 int silc_idcache_list_count(SilcIDCacheList list)
628 return list->cache_count;
631 /* Returns first entry from the ID cache list. */
633 int silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
637 if (!list->cache[list->pos])
641 *ret = list->cache[list->pos];
646 /* Returns next entry from the ID cache list. */
648 int silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
653 if (list->pos >= sizeof(list->cache)) {
658 if (dyn && list->pos >= list->cache_dyn_count)
661 if (!dyn && !list->cache[list->pos])
664 if (dyn && !list->cache_dyn[list->pos])
669 *ret = list->cache[list->pos];
671 *ret = list->cache_dyn[list->pos];
677 /* Free's ID cache list. User must free the list object returned by
678 any of the searching functions. */
680 void silc_idcache_list_free(SilcIDCacheList list)
684 silc_free(list->cache_dyn);