#include "cache.h" #include #include #include 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; } } }