Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / ctable.c
blobb57039ce98e6ecfd839bd988c74ae0411dd5013b
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* ctable 3
6 /* SUMMARY
7 /* cache manager
8 /* SYNOPSIS
9 /* #include <ctable.h>
11 /* CTABLE *ctable_create(limit, create, delete, context)
12 /* int limit;
13 /* void *(*create)(const char *key, void *context);
14 /* void (*delete)(void *value, void *context);
15 /* void *context;
17 /* const void *ctable_locate(cache, key)
18 /* CTABLE *cache;
19 /* const char *key;
21 /* void ctable_free(cache)
22 /* CTABLE *cache;
24 /* void ctable_walk(cache, action)
25 /* CTABLE *cache;
26 /* void (*action)(const char *key, const void *value);
27 /* DESCRIPTION
28 /* This module maintains multiple caches. Cache items are purged
29 /* automatically when the number of items exceeds a configurable
30 /* limit. Caches never shrink. Each cache entry consists of a
31 /* string-valued lookup key and a generic data pointer value.
33 /* ctable_create() creates a cache with the specified size limit, and
34 /* returns a pointer to the result. The create and delete arguments
35 /* specify pointers to call-back functions that create a value, given
36 /* a key, and delete a given value, respectively. The context argument
37 /* is passed on to the call-back routines.
39 /* ctable_locate() looks up or generates the value that corresponds to
40 /* the specified key, and returns that value.
42 /* ctable_free() destroys the specified cache, including its contents.
44 /* ctable_walk() iterates over all elements in the cache, and invokes
45 /* the action function for each cache element with the corresponding
46 /* key and value as arguments. This function is useful mainly for
47 /* cache performance debugging.
48 /* DIAGNOSTICS
49 /* Fatal errors: out of memory. Panic: interface violation.
50 /* LICENSE
51 /* .ad
52 /* .fi
53 /* The Secure Mailer license must be distributed with this software.
54 /* AUTHOR(S)
55 /* Wietse Venema
56 /* IBM T.J. Watson Research
57 /* P.O. Box 704
58 /* Yorktown Heights, NY 10598, USA
59 /*--*/
61 /* System library. */
63 #include <sys_defs.h>
64 #include <stdlib.h>
65 #include <stddef.h>
67 /* Utility library. */
69 #include <msg.h>
70 #include <mymalloc.h>
71 #include <ring.h>
72 #include <htable.h>
73 #include <ctable.h>
76 * Cache entries are kept in most-recently used order. We use a hash table
77 * to quickly locate cache entries.
79 #define CTABLE_ENTRY struct ctable_entry
81 struct ctable_entry {
82 RING ring; /* MRU linkage */
83 const char *key; /* lookup key */
84 void *value; /* corresponding value */
87 #define RING_TO_CTABLE_ENTRY(ring_ptr) \
88 RING_TO_APPL(ring_ptr, CTABLE_ENTRY, ring)
89 #define RING_PTR_OF(x) (&((x)->ring))
91 struct ctable {
92 HTABLE *table; /* table with key, ctable_entry pairs */
93 unsigned limit; /* max nr of entries */
94 unsigned used; /* current nr of entries */
95 CTABLE_CREATE_FN create; /* constructor */
96 CTABLE_DELETE_FN delete; /* destructor */
97 RING ring; /* MRU linkage */
98 void *context; /* application context */
101 #define CTABLE_MIN_SIZE 5
103 /* ctable_create - create empty cache */
105 CTABLE *ctable_create(int limit, CTABLE_CREATE_FN create,
106 CTABLE_DELETE_FN delete, void *context)
108 CTABLE *cache = (CTABLE *) mymalloc(sizeof(CTABLE));
109 const char *myname = "ctable_create";
111 if (limit < 1)
112 msg_panic("%s: bad cache limit: %d", myname, limit);
114 cache->table = htable_create(limit);
115 cache->limit = (limit < CTABLE_MIN_SIZE ? CTABLE_MIN_SIZE : limit);
116 cache->used = 0;
117 cache->create = create;
118 cache->delete = delete;
119 ring_init(RING_PTR_OF(cache));
120 cache->context = context;
121 return (cache);
124 /* ctable_locate - look up or create cache item */
126 const void *ctable_locate(CTABLE *cache, const char *key)
128 const char *myname = "ctable_locate";
129 CTABLE_ENTRY *entry;
132 * If the entry is not in the cache, make sure there is room for a new
133 * entry and install it at the front of the MRU chain. Otherwise, move
134 * the entry to the front of the MRU chain if it is not already there.
135 * All this means that the cache never shrinks.
137 if ((entry = (CTABLE_ENTRY *) htable_find(cache->table, key)) == 0) {
138 if (cache->used >= cache->limit) {
139 entry = RING_TO_CTABLE_ENTRY(ring_pred(RING_PTR_OF(cache)));
140 if (msg_verbose)
141 msg_info("%s: purge entry key %s", myname, entry->key);
142 ring_detach(RING_PTR_OF(entry));
143 cache->delete(entry->value, cache->context);
144 htable_delete(cache->table, entry->key, (void (*) (char *)) 0);
145 } else {
146 entry = (CTABLE_ENTRY *) mymalloc(sizeof(CTABLE_ENTRY));
147 cache->used++;
149 entry->value = cache->create(key, cache->context);
150 entry->key = htable_enter(cache->table, key, (char *) entry)->key;
151 ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
152 if (msg_verbose)
153 msg_info("%s: install entry key %s", myname, entry->key);
154 } else if (entry == RING_TO_CTABLE_ENTRY(ring_succ(RING_PTR_OF(cache)))) {
155 if (msg_verbose)
156 msg_info("%s: leave existing entry key %s", myname, entry->key);
157 } else {
158 ring_detach(RING_PTR_OF(entry));
159 ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
160 if (msg_verbose)
161 msg_info("%s: move existing entry key %s", myname, entry->key);
163 return (entry->value);
166 static CTABLE *ctable_free_cache;
168 /* ctable_free_callback - callback function */
170 static void ctable_free_callback(char *ptr)
172 CTABLE_ENTRY *entry = (CTABLE_ENTRY *) ptr;
174 ctable_free_cache->delete(entry->value, ctable_free_cache->context);
175 myfree((char *) entry);
178 /* ctable_free - destroy cache and contents */
180 void ctable_free(CTABLE *cache)
182 CTABLE *saved_cache = ctable_free_cache;
185 * XXX the hash table does not pass application context so we have to
186 * store it in a global variable.
188 ctable_free_cache = cache;
189 htable_free(cache->table, ctable_free_callback);
190 myfree((char *) cache);
191 ctable_free_cache = saved_cache;
194 /* ctable_walk - iterate over all cache entries */
196 void ctable_walk(CTABLE *cache, void (*action) (const char *, const void *))
198 RING *entry = RING_PTR_OF(cache);
200 /* Walking down the MRU chain is less work than using ht_walk(). */
202 while ((entry = ring_succ(entry)) != RING_PTR_OF(cache))
203 action((RING_TO_CTABLE_ENTRY(entry)->key),
204 (RING_TO_CTABLE_ENTRY(entry)->value));
207 #ifdef TEST
210 * Proof-of-concept test program. Read keys from stdin, ask for values not
211 * in cache.
213 #include <unistd.h>
214 #include <vstream.h>
215 #include <vstring.h>
216 #include <vstring_vstream.h>
217 #include <msg_vstream.h>
219 #define STR(x) vstring_str(x)
221 static void *ask(const char *key, void *context)
223 VSTRING *data_buf = (VSTRING *) context;
225 vstream_printf("ask: %s = ", key);
226 vstream_fflush(VSTREAM_OUT);
227 if (vstring_get_nonl(data_buf, VSTREAM_IN) == VSTREAM_EOF)
228 vstream_longjmp(VSTREAM_IN, 1);
229 if (!isatty(0)) {
230 vstream_printf("%s\n", STR(data_buf));
231 vstream_fflush(VSTREAM_OUT);
233 return (mystrdup(STR(data_buf)));
236 static void drop(void *data, void *unused_context)
238 myfree(data);
241 int main(int unused_argc, char **argv)
243 VSTRING *key_buf;
244 VSTRING *data_buf;
245 CTABLE *cache;
246 const char *value;
248 msg_vstream_init(argv[0], VSTREAM_ERR);
249 key_buf = vstring_alloc(100);
250 data_buf = vstring_alloc(100);
251 cache = ctable_create(1, ask, drop, (void *) data_buf);
252 msg_verbose = 1;
253 vstream_control(VSTREAM_IN, VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END);
255 if (vstream_setjmp(VSTREAM_IN) == 0) {
256 for (;;) {
257 vstream_printf("key = ");
258 vstream_fflush(VSTREAM_OUT);
259 if (vstring_get_nonl(key_buf, VSTREAM_IN) == VSTREAM_EOF)
260 vstream_longjmp(VSTREAM_IN, 1);
261 if (!isatty(0)) {
262 vstream_printf("%s\n", STR(key_buf));
263 vstream_fflush(VSTREAM_OUT);
265 value = ctable_locate(cache, STR(key_buf));
266 vstream_printf("result: %s\n", value);
269 ctable_free(cache);
270 vstring_free(key_buf);
271 vstring_free(data_buf);
272 return (0);
275 #endif