Files
boc/c-runtime/cache.c
T

221 lines
5.8 KiB
C

#include "cache.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
struct crt_cache {
crt_cache_entry_t** buckets;
size_t bucket_count;
size_t max_entries;
size_t current_entries;
time_t default_ttl;
pthread_rwlock_t lock;
};
static size_t hash_key(const char* key) {
size_t hash = 5381;
int c;
while ((c = *key++)) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
crt_cache_t* crt_cache_create(size_t max_entries, time_t default_ttl_sec) {
crt_cache_t* cache = calloc(1, sizeof(crt_cache_t));
if (!cache) return NULL;
cache->bucket_count = 1024;
cache->buckets = calloc(cache->bucket_count, sizeof(crt_cache_entry_t*));
if (!cache->buckets) {
free(cache);
return NULL;
}
cache->max_entries = max_entries;
cache->default_ttl = default_ttl_sec;
cache->current_entries = 0;
pthread_rwlock_init(&cache->lock, NULL);
return cache;
}
void crt_cache_destroy(crt_cache_t* cache) {
if (!cache) return;
crt_cache_clear(cache);
free(cache->buckets);
pthread_rwlock_destroy(&cache->lock);
free(cache);
}
bool crt_cache_set(crt_cache_t* cache, const char* key, const void* data, size_t len) {
return crt_cache_set_ttl(cache, key, data, len, cache->default_ttl);
}
bool crt_cache_set_ttl(crt_cache_t* cache, const char* key, const void* data, size_t len, time_t ttl_sec) {
if (!cache || !key || !data || len == 0) return false;
pthread_rwlock_wrlock(&cache->lock);
if (cache->current_entries >= cache->max_entries) {
crt_cache_evict_expired(cache);
}
size_t idx = hash_key(key) % cache->bucket_count;
crt_cache_entry_t* entry = cache->buckets[idx];
while (entry) {
if (strcmp(entry->key, key) == 0) {
free(entry->data);
entry->data = malloc(len);
if (!entry->data) {
pthread_rwlock_unlock(&cache->lock);
return false;
}
memcpy(entry->data, data, len);
entry->len = len;
entry->expires_at = time(NULL) + ttl_sec;
pthread_rwlock_unlock(&cache->lock);
return true;
}
entry = entry->next;
}
entry = malloc(sizeof(crt_cache_entry_t));
if (!entry) {
pthread_rwlock_unlock(&cache->lock);
return false;
}
strncpy(entry->key, key, sizeof(entry->key) - 1);
entry->data = malloc(len);
if (!entry->data) {
free(entry);
pthread_rwlock_unlock(&cache->lock);
return false;
}
memcpy(entry->data, data, len);
entry->len = len;
entry->expires_at = time(NULL) + ttl_sec;
entry->next = cache->buckets[idx];
cache->buckets[idx] = entry;
cache->current_entries++;
pthread_rwlock_unlock(&cache->lock);
return true;
}
bool crt_cache_get(crt_cache_t* cache, const char* key, void* out, size_t* len) {
if (!cache || !key || !out || !len) return false;
pthread_rwlock_rdlock(&cache->lock);
size_t idx = hash_key(key) % cache->bucket_count;
crt_cache_entry_t* entry = cache->buckets[idx];
time_t now = time(NULL);
while (entry) {
if (strcmp(entry->key, key) == 0) {
if (entry->expires_at < now) {
pthread_rwlock_unlock(&cache->lock);
crt_cache_delete(cache, key);
return false;
}
memcpy(out, entry->data, entry->len);
*len = entry->len;
pthread_rwlock_unlock(&cache->lock);
return true;
}
entry = entry->next;
}
pthread_rwlock_unlock(&cache->lock);
return false;
}
bool crt_cache_delete(crt_cache_t* cache, const char* key) {
if (!cache || !key) return false;
pthread_rwlock_wrlock(&cache->lock);
size_t idx = hash_key(key) % cache->bucket_count;
crt_cache_entry_t* entry = cache->buckets[idx];
crt_cache_entry_t* prev = NULL;
while (entry) {
if (strcmp(entry->key, key) == 0) {
if (prev) {
prev->next = entry->next;
} else {
cache->buckets[idx] = entry->next;
}
free(entry->data);
free(entry);
cache->current_entries--;
pthread_rwlock_unlock(&cache->lock);
return true;
}
prev = entry;
entry = entry->next;
}
pthread_rwlock_unlock(&cache->lock);
return false;
}
void crt_cache_clear(crt_cache_t* cache) {
if (!cache) return;
pthread_rwlock_wrlock(&cache->lock);
for (size_t i = 0; i < cache->bucket_count; i++) {
crt_cache_entry_t* entry = cache->buckets[i];
while (entry) {
crt_cache_entry_t* next = entry->next;
free(entry->data);
free(entry);
entry = next;
}
cache->buckets[i] = NULL;
}
cache->current_entries = 0;
pthread_rwlock_unlock(&cache->lock);
}
size_t crt_cache_size(crt_cache_t* cache) {
if (!cache) return 0;
pthread_rwlock_rdlock(&cache->lock);
size_t size = cache->current_entries;
pthread_rwlock_unlock(&cache->lock);
return size;
}
void crt_cache_evict_expired(crt_cache_t* cache) {
if (!cache) return;
time_t now = time(NULL);
for (size_t i = 0; i < cache->bucket_count; i++) {
crt_cache_entry_t* entry = cache->buckets[i];
crt_cache_entry_t* prev = NULL;
while (entry) {
crt_cache_entry_t* next = entry->next;
if (entry->expires_at < now) {
if (prev) {
prev->next = next;
} else {
cache->buckets[i] = next;
}
free(entry->data);
free(entry);
cache->current_entries--;
} else {
prev = entry;
}
entry = next;
}
}
}