a99a6040 |
struct cache_key {
char digest[16];
uint32_t size; /* 0 is used to mark an empty hash slot! */
struct cache_key *lru_next, *lru_prev;
};
struct cache_set {
struct cache_key *data;
size_t capacity;
size_t maxelements;/* considering load factor */
size_t elements;
size_t version;
struct cache_key *lru_head, *lru_tail;
pthread_mutex_t mutex;
};
#define CACHE_INVALID_VERSION ~0u
/* size must be power of 2! */
static int cacheset_init(struct cache_set* map, size_t maxsize, uint8_t loadfactor)
{
map->data = cli_calloc(maxsize, sizeof(*map->data));
if (!map->data)
return CL_EMEM;
map->capacity = maxsize;
map->maxelements = loadfactor*maxsize / 100;
map->elements = 0;
map->version = CACHE_INVALID_VERSION;
map->lru_head = map->lru_tail = NULL;
if (pthread_mutex_init(&map->mutex, NULL)) {
cli_errmsg("mutex init fail\n");
return CL_EMEM;
}
}
static void cacheset_destroy(struct cache_set *map)
{
pthread_mutex_destroy(&map->mutex);
free(map->data);
}
static void cacheset_acquire(struct cache_set *map)
{
pthread_mutex_lock(&map->mutex);
}
static void cache_setversion(struct cache_set* map, uint32_t version)
{
unsigned i;
if (map->version == version)
return;
map->version = version;
map->elements = 0;/* all elements have expired now */
for (i=0;i<map->capacity;i++)
map->data[i].size = 0;
map->lru_head = map->lru_tail = NULL;
}
static void cacheset_lru_remove(struct cache_set *map, size_t howmany)
{
while (howmany--) {
struct cache_key *old;
assert(map->lru_head);
assert(!old->lru_prev);
// Remove a key from the head of the list
old = map->lru_head;
map->lru_head = old->lru_next;
old->size = 0; /* this slot is now empty */
if (old == map->lru_tail)
map->lru_tail = 0;
}
}
static inline uint32_t hash32shift(uint32_t key)
{
key = ~key + (key << 15);
key = key ^ (key >> 12);
key = key + (key << 2);
key = key ^ (key >> 4);
key = (key + (key << 3)) + (key << 11);
key = key ^ (key >> 16);
return key;
}
static inline size_t hash(const unsigned char* k,const size_t len,const size_t SIZE)
{
size_t Hash = 1;
size_t i;
for(i=0;i<len;i++) {
/* a simple add is good, because we use the mixing function below */
Hash += k[i];
/* mixing function */
Hash = hash32shift(Hash);
}
/* SIZE is power of 2 */
return Hash & (SIZE - 1);
}
int cacheset_lookup_internal(struct cache_set *map, const struct cache_key *key,
uint32_t *insert_pos)
{
uint32_t idx = hash((const unsigned char*)key, sizeof(*key), map->capacity);
uint32_t tries = 0;
struct cache_key *k = &map->data[idx];
while (k->size) {
if (k->size == key->size &&
!memcmp(k->digest, key, 16)) {
/* found key */
*insert_pos = idx;
return 1;
}
idx = (idx + tries++)&(map->capacity-1);
k = &map->data[idx];
}
/* found empty pos */
*insert_pos = idx;
return 0;
}
static inline void lru_remove(struct cache_set *map, struct cache_key *newkey)
{
if (newkey->lru_next)
newkey->lru_next->lru_prev = newkey->lru_prev;
if (newkey->lru_prev)
newkey->lru_prev->lru_next = newkey->lru_next;
if (newkey == map->lru_head)
map->lru_head = newkey->lru_next;
}
static inline void lru_addtail(struct cache_set *map, struct cache_key *newkey)
{
if (!map->lru_head)
map->lru_head = newkey;
if (map->lru_tail)
map->lru_tail->lru_next = newkey;
newkey->lru_next = NULL;
newkey->lru_prev = map->lru_tail;
map->lru_tail = newkey;
}
static void cacheset_add(struct cache_set *map, const struct cache_key *key)
{
int ret;
uint32_t pos;
struct cache_key *newkey;
if (map->elements >= map->maxelements)
cacheset_lru_remove(map, 1);
assert(map->elements < map->maxelements);
ret = cacheset_lookup_internal(map, key, &pos);
newkey = &map->data[pos];
if (ret) {
/* was already added, remove from LRU list */
lru_remove(map, newkey);
}
/* add new key to tail of LRU list */
lru_addtail(map, newkey);
map->elements++;
assert(pos < map->maxelements);
memcpy(&map->data[pos], key, sizeof(*key));
}
static int cacheset_lookup(struct cache_set *map, const struct cache_key *key)
{
struct cache_key *newkey;
int ret;
uint32_t pos;
ret = cacheset_lookup_internal(map, key, &pos);
if (!ret)
return CACHE_INVALID_VERSION;
newkey = &map->data[pos];
/* update LRU position: move to tail */
lru_remove(map, newkey);
lru_addtail(map, newkey);
return map->version;
}
static void cacheset_release(struct cache_set *map)
{
pthread_mutex_unlock(&map->mutex);
}
#if 0
int main(int argc, char **argv)
{
struct cache_key key;
struct cache_set map;
cacheset_init(&map, 256, 80);
cacheset_acquire(&map);
cache_setversion(&map, 10);
key.size = 1024;
memcpy(key.digest, "1234567890123456", 16);
cacheset_add(&map, &key);
memcpy(key.digest, "1234567890123457", 16);
cacheset_add(&map, &key);
memcpy(key.digest, "0123456789012345", 16);
cacheset_add(&map, &key);
key.size = 1024;
memcpy(key.digest, "1234567890123456", 16);
if (cacheset_lookup(&map, &key) != 10)
abort();
memcpy(key.digest, "1234567890123456", 16);
if (cacheset_lookup(&map, &key) != 10)
abort();
memcpy(key.digest, "1234567890123457", 16);
if (cacheset_lookup(&map, &key) != 10)
abort();
memcpy(key.digest, "0123456789012345", 16);
if (cacheset_lookup(&map, &key) != 10)
abort();
memcpy(key.digest, "0123456789012346", 16);
if (cacheset_lookup(&map, &key) == 10)
abort();
cache_setversion(&map, 1);
memcpy(key.digest, "1234567890123456", 16);
if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION)
abort();
memcpy(key.digest, "1234567890123456", 16);
if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION)
abort();
memcpy(key.digest, "1234567890123457", 16);
if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION)
abort();
memcpy(key.digest, "0123456789012345", 16);
if (cacheset_lookup(&map, &key) != CACHE_INVALID_VERSION)
abort();
cacheset_release(&map);
cacheset_destroy(&map);
cacheset_init(&map, 8, 50);
cacheset_acquire(&map);
cache_setversion(&map, 10);
key.size = 416;
memcpy(key.digest, "1234567890123456", 16);
cacheset_add(&map, &key);
memcpy(key.digest, "1234567890123457", 16);
cacheset_add(&map, &key);
memcpy(key.digest, "1234567890123459", 16);
cacheset_add(&map, &key);
key.size = 400;
memcpy(key.digest, "1234567890123450", 16);
cacheset_add(&map, &key);
key.size = 416;
memcpy(key.digest, "1234567890123456", 16);
if (cacheset_lookup(&map, &key) != 10)
abort();
if (cacheset_lookup(&map, &key) != 10)
abort();
if (cacheset_lookup(&map, &key) != 10)
abort();
key.size = 500;
cacheset_add(&map, &key);
memcpy(key.digest, "1234567890123457", 16);
if (cacheset_lookup(&map, &key) == 10)
abort();
cacheset_release(&map);
cacheset_destroy(&map);
return 0;
}
#endif |