#include <sys_defs.h>
#include <stdlib.h>
#include <stddef.h>
#include <msg.h>
#include <mymalloc.h>
#include <ring.h>
#include <htable.h>
#include <ctable.h>
#define CTABLE_ENTRY struct ctable_entry
struct ctable_entry {
RING ring;
const char *key;
void *value;
};
#define RING_TO_CTABLE_ENTRY(ring_ptr) \
RING_TO_APPL(ring_ptr, CTABLE_ENTRY, ring)
#define RING_PTR_OF(x) (&((x)->ring))
struct ctable {
HTABLE *table;
unsigned limit;
unsigned used;
CTABLE_CREATE_FN create;
CTABLE_DELETE_FN delete;
RING ring;
void *context;
};
#define CTABLE_MIN_SIZE 5
CTABLE *ctable_create(int limit, CTABLE_CREATE_FN create,
CTABLE_DELETE_FN delete, void *context)
{
CTABLE *cache = (CTABLE *) mymalloc(sizeof(CTABLE));
const char *myname = "ctable_create";
if (limit < 1)
msg_panic("%s: bad cache limit: %d", myname, limit);
cache->table = htable_create(limit);
cache->limit = (limit < CTABLE_MIN_SIZE ? CTABLE_MIN_SIZE : limit);
cache->used = 0;
cache->create = create;
cache->delete = delete;
ring_init(RING_PTR_OF(cache));
cache->context = context;
return (cache);
}
const void *ctable_locate(CTABLE *cache, const char *key)
{
const char *myname = "ctable_locate";
CTABLE_ENTRY *entry;
if ((entry = (CTABLE_ENTRY *) htable_find(cache->table, key)) == 0) {
if (cache->used >= cache->limit) {
entry = RING_TO_CTABLE_ENTRY(ring_pred(RING_PTR_OF(cache)));
if (msg_verbose)
msg_info("%s: purge entry key %s", myname, entry->key);
ring_detach(RING_PTR_OF(entry));
cache->delete(entry->value, cache->context);
htable_delete(cache->table, entry->key, (void (*) (char *)) 0);
} else {
entry = (CTABLE_ENTRY *) mymalloc(sizeof(CTABLE_ENTRY));
cache->used++;
}
entry->value = cache->create(key, cache->context);
entry->key = htable_enter(cache->table, key, (char *) entry)->key;
ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
if (msg_verbose)
msg_info("%s: install entry key %s", myname, entry->key);
} else if (entry == RING_TO_CTABLE_ENTRY(ring_succ(RING_PTR_OF(cache)))) {
if (msg_verbose)
msg_info("%s: leave existing entry key %s", myname, entry->key);
} else {
ring_detach(RING_PTR_OF(entry));
ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
if (msg_verbose)
msg_info("%s: move existing entry key %s", myname, entry->key);
}
return (entry->value);
}
static CTABLE *ctable_free_cache;
static void ctable_free_callback(char *ptr)
{
CTABLE_ENTRY *entry = (CTABLE_ENTRY *) ptr;
ctable_free_cache->delete(entry->value, ctable_free_cache->context);
myfree((char *) entry);
}
void ctable_free(CTABLE *cache)
{
CTABLE *saved_cache = ctable_free_cache;
ctable_free_cache = cache;
htable_free(cache->table, ctable_free_callback);
myfree((char *) cache);
ctable_free_cache = saved_cache;
}
void ctable_walk(CTABLE *cache, void (*action) (const char *, const void *))
{
RING *entry = RING_PTR_OF(cache);
while ((entry = ring_succ(entry)) != RING_PTR_OF(cache))
action((RING_TO_CTABLE_ENTRY(entry)->key),
(RING_TO_CTABLE_ENTRY(entry)->value));
}
#ifdef TEST
#include <unistd.h>
#include <vstream.h>
#include <vstring.h>
#include <vstring_vstream.h>
#include <msg_vstream.h>
#define STR(x) vstring_str(x)
static void *ask(const char *key, void *context)
{
VSTRING *data_buf = (VSTRING *) context;
vstream_printf("ask: %s = ", key);
vstream_fflush(VSTREAM_OUT);
if (vstring_get_nonl(data_buf, VSTREAM_IN) == VSTREAM_EOF)
vstream_longjmp(VSTREAM_IN, 1);
if (!isatty(0)) {
vstream_printf("%s\n", STR(data_buf));
vstream_fflush(VSTREAM_OUT);
}
return (mystrdup(STR(data_buf)));
}
static void drop(void *data, void *unused_context)
{
myfree(data);
}
int main(int unused_argc, char **argv)
{
VSTRING *key_buf;
VSTRING *data_buf;
CTABLE *cache;
const char *value;
msg_vstream_init(argv[0], VSTREAM_ERR);
key_buf = vstring_alloc(100);
data_buf = vstring_alloc(100);
cache = ctable_create(1, ask, drop, (void *) data_buf);
msg_verbose = 1;
vstream_control(VSTREAM_IN, VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END);
if (vstream_setjmp(VSTREAM_IN) == 0) {
for (;;) {
vstream_printf("key = ");
vstream_fflush(VSTREAM_OUT);
if (vstring_get_nonl(key_buf, VSTREAM_IN) == VSTREAM_EOF)
vstream_longjmp(VSTREAM_IN, 1);
if (!isatty(0)) {
vstream_printf("%s\n", STR(key_buf));
vstream_fflush(VSTREAM_OUT);
}
value = ctable_locate(cache, STR(key_buf));
vstream_printf("result: %s\n", value);
}
}
ctable_free(cache);
vstring_free(key_buf);
vstring_free(data_buf);
return (0);
}
#endif