#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include "cache.h"
#define mix(a, b, c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<< 8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>> 5); \
a -= b; a -= c; a ^= (c>> 3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
typedef struct cache_node_s
{
char *key;
void *datum;
time_t best_before;
uint32_t ttl;
struct cache_node_s *next;
} cache_node_t;
typedef struct __cache_private
{
uint32_t bucket_count;
cache_node_t **bucket;
uint32_t hash_mask;
uint32_t flags;
cache_datum_callback release_callback;
cache_datum_callback retain_callback;
} cache_private_t;
void
cache_set_retain_callback(cache_t *c, cache_datum_callback cb)
{
if (c == NULL) return;
c->retain_callback = cb;
}
void
cache_set_release_callback(cache_t *c, cache_datum_callback cb)
{
if (c == NULL) return;
c->release_callback = cb;
}
cache_t *
cache_new(uint32_t size, uint32_t flags)
{
cache_private_t *c;
if (size == 0) return NULL;
c = (cache_t *)calloc(1, sizeof(cache_t));
if (c == NULL) return NULL;
c->bucket_count = size;
c->bucket = (cache_node_t **)calloc(c->bucket_count, sizeof(sizeof(cache_node_t *)));
if (c->bucket == NULL)
{
free(c);
return NULL;
}
c->hash_mask = size - 1;
c->flags = flags;
return (cache_t *)c;
}
static uint32_t
cache_hash(const char *k, uint32_t mask)
{
uint32_t a, b, c, l, len;
l = strlen(k);
len = l;
a = b = 0x9e3779b9;
c = 0;
while (len >= 12)
{
a += (k[0] + ((uint32_t)k[1]<<8) + ((uint32_t)k[ 2]<<16) + ((uint32_t)k[ 3]<<24));
b += (k[4] + ((uint32_t)k[5]<<8) + ((uint32_t)k[ 6]<<16) + ((uint32_t)k[ 7]<<24));
c += (k[8] + ((uint32_t)k[9]<<8) + ((uint32_t)k[10]<<16) + ((uint32_t)k[11]<<24));
mix(a, b, c);
k += 12;
len -= 12;
}
c += l;
switch(len)
{
case 11: c += ((uint32_t)k[10]<<24);
case 10: c += ((uint32_t)k[9]<<16);
case 9 : c += ((uint32_t)k[8]<<8);
case 8 : b += ((uint32_t)k[7]<<24);
case 7 : b += ((uint32_t)k[6]<<16);
case 6 : b += ((uint32_t)k[5]<<8);
case 5 : b += k[4];
case 4 : a += ((uint32_t)k[3]<<24);
case 3 : a += ((uint32_t)k[2]<<16);
case 2 : a += ((uint32_t)k[1]<<8);
case 1 : a += k[0];
}
mix(a, b, c);
return c & mask;
}
static void
cache_remove_node(cache_private_t *c, uint32_t b, cache_node_t *p, cache_node_t *n)
{
if (p == NULL) c->bucket[b] = n->next;
else p->next = n->next;
if (n->key != NULL) free(n->key);
if (c->release_callback != NULL) c->release_callback(n->datum);
free(n);
}
static void *
cache_find_internal(cache_t *cin, const char *key, uint32_t reset)
{
cache_private_t *c;
cache_node_t *p, *n;
uint32_t b;
time_t now;
if (cin == NULL) return NULL;
if (key == NULL) return NULL;
c = (cache_private_t *)cin;
b = cache_hash(key, c->hash_mask);
p = NULL;
for (n = c->bucket[b]; n != NULL; n = n->next)
{
if ((n->key != NULL) && (!strcmp(key, n->key)))
{
if (n->ttl != 0)
{
now = time(NULL);
if (now >= n->best_before)
{
cache_remove_node(c, b, p, n);
return NULL;
}
if (reset != 0) n->best_before = now + n->ttl;
}
return n->datum;
}
p = n;
}
return NULL;
}
void *
cache_find_reset(cache_t *cin, const char *key)
{
return cache_find_internal(cin, key, 1);
}
void *
cache_find(cache_t *cin, const char *key)
{
return cache_find_internal(cin, key, 0);
}
void
cache_insert_ttl_time(cache_t *cin, const char *key, void *datum, uint32_t ttl, uint32_t time)
{
cache_private_t *c;
cache_node_t *p, *n;
uint32_t b;
if (cin == NULL) return;
if (key == NULL) return;
if (datum == NULL) return;
c = (cache_private_t *)cin;
b = cache_hash(key, c->hash_mask);
p = NULL;
for (n = c->bucket[b]; n != NULL; n = n->next)
{
if ((n->key != NULL) && (!strcmp(key, n->key)))
{
if (c->flags & CACHE_FLAG_REPLACE)
{
cache_remove_node(c, b, p, n);
break;
}
else
{
n->best_before = time + ttl;
return;
}
}
p = n;
}
n = (cache_node_t *)malloc(sizeof(cache_node_t));
n->key = strdup(key);
if (c->retain_callback != NULL) c->retain_callback(datum);
n->datum = datum;
n->best_before = time + ttl;
n->ttl = ttl;
n->next = c->bucket[b];
c->bucket[b] = n;
}
void
cache_insert_ttl(cache_t *cin, const char *key, void *datum, uint32_t ttl)
{
return cache_insert_ttl_time(cin, key, datum, ttl, time(NULL));
}
void
cache_insert(cache_t *cin, const char *key, void *datum)
{
return cache_insert_ttl_time(cin, key, datum, 0, 0);
}
void
cache_delete(cache_t *cin, const char *key)
{
cache_private_t *c;
cache_node_t *n, *p;
uint32_t b;
if (cin == NULL) return;
if (key == NULL) return;
c = (cache_private_t *)cin;
b = cache_hash(key, c->hash_mask);
p = NULL;
for (n = c->bucket[b]; n != NULL; n = n->next)
{
if ((n->key != NULL) && (!strcmp(key, n->key)))
{
cache_remove_node(c, b, p, n);
return;
}
p = n;
}
}
void
cache_delete_datum(cache_t *cin, void *d)
{
cache_private_t *c;
cache_node_t *n, *p, *x;
uint32_t b;
if (cin == NULL) return;
if (d == NULL) return;
c = (cache_private_t *)cin;
for (b = 0; b < c->bucket_count; b++)
{
p = NULL;
x = NULL;
for (n = c->bucket[b]; n != NULL; n = x)
{
x = n->next;
if (n->datum == d) cache_remove_node(c, b, p, n);
else p = n;
}
}
}
int
cache_contains_datum(cache_t *cin, void *d)
{
cache_private_t *c;
cache_node_t *n;
uint32_t b;
if (cin == NULL) return 0;
if (d == NULL) return 0;
c = (cache_private_t *)cin;
for (b = 0; b < c->bucket_count; b++)
{
for (n = c->bucket[b]; n != NULL; n = n->next)
{
if (n->datum == d) return 1;
}
}
return 0;
}
void
cache_sweep(cache_t *cin)
{
cache_private_t *c;
cache_node_t *p, *n, *x;
uint32_t b;
time_t now;
if (cin == NULL) return;
now = time(NULL);
c = (cache_private_t *)cin;
for (b = 0; b < c->bucket_count; b++)
{
x = NULL;
p = NULL;
for (n = c->bucket[b]; n != NULL; n = x)
{
x = n->next;
if ((n->ttl != 0) && (now >= n->best_before)) cache_remove_node(c, b, p, n);
else p = n;
}
}
}
void
cache_free(cache_t *cin)
{
cache_private_t *c;
cache_node_t *n, *x;
uint32_t b;
if (cin == NULL) return;
c = (cache_private_t *)cin;
for (b = 0; b < c->bucket_count; b++)
{
x = NULL;
for (n = c->bucket[b]; n != NULL; n = x)
{
x = n->next;
free(n->key);
if (c->release_callback != NULL) c->release_callback(n->datum);
free(n);
}
}
free(c->bucket);
free(c);
}
void
cache_print(cache_t *cin, FILE *f)
{
cache_private_t *c;
cache_node_t *n;
uint32_t b;
if (cin == NULL) return;
c = (cache_private_t *)cin;
for (b = 0; b < c->bucket_count; b++)
{
n = c->bucket[b];
if (n != NULL) fprintf(f, "Bucket %d\n", b);
for (; n != NULL; n = n->next)
{
fprintf(f, "\t%s 0x%08x %u %s", n->key, (unsigned int)n->datum, (unsigned int)n->ttl, ctime((time_t *)&(n->best_before)));
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1