#include #include #include #include #include #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))); } } }