aboutsummaryrefslogtreecommitdiff
path: root/tests/lru_cache/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lru_cache/cache.c')
-rw-r--r--tests/lru_cache/cache.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/tests/lru_cache/cache.c b/tests/lru_cache/cache.c
new file mode 100644
index 000000000..632c55862
--- /dev/null
+++ b/tests/lru_cache/cache.c
@@ -0,0 +1,221 @@
+/*
+ * =====================================================================================
+ *
+ * Filename: cache.c
+ *
+ * Description: A simple cache
+ *
+ * Version: 1.0
+ * Created: 04/11/2013 02:31:02 PM
+ * Revision: none
+ * Compiler: gcc
+ *
+ * Author: Oliver Lorenz (ol), olli@olorenz.org
+ * Company: https://olorenz.org
+ * License: This is licensed under the same terms as uthash itself
+ *
+ * =====================================================================================
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include "cache.h"
+#include "uthash.h"
+
+/**
+ * A cache entry
+ */
+struct foo_cache_entry {
+ char *key; /**<The key */
+ void *data; /**<Payload */
+ UT_hash_handle hh; /**<Hash Handle for uthash */
+};
+#define KEY_MAX_LENGTH 32
+
+/**
+ * A cache object
+ */
+struct foo_cache {
+ size_t max_entries; /**<Amount of entries this cache object can hold */
+ pthread_rwlock_t cache_lock; /**<A lock for concurrent access */
+ struct foo_cache_entry *entries; /**<Head pointer for uthash */
+ void (*free_cb) (void *element);/**<Callback function to free cache entries */
+};
+
+/** Creates a new cache object
+
+ @param dst
+ Where the newly allocated cache object will be stored in
+
+ @param capacity
+ The maximum number of elements this cache object can hold
+
+ @return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise
+*/
+int foo_cache_create(struct foo_cache **dst, const size_t capacity,
+ void (*free_cb) (void *element))
+{
+ struct foo_cache *new = NULL;
+ int rv;
+
+ if (!dst)
+ return EINVAL;
+
+ if ((new = malloc(sizeof(*new))) == NULL)
+ return ENOMEM;
+
+ if ((rv = pthread_rwlock_init(&(new->cache_lock), NULL)) != 0)
+ goto err_out;
+
+ new->max_entries = capacity;
+ new->entries = NULL;
+ new->free_cb = free_cb;
+ *dst = new;
+ return 0;
+
+err_out:
+ if (new)
+ free(new);
+ return rv;
+}
+
+/** Frees an allocated cache object
+
+ @param cache
+ The cache object to free
+
+ @param keep_data
+ Whether to free contained data or just delete references to it
+
+ @return EINVAL if cache is NULL, 0 otherwise
+*/
+int foo_cache_delete(struct foo_cache *cache, int keep_data)
+{
+ struct foo_cache_entry *entry, *tmp;
+ int rv;
+
+ if (!cache)
+ return EINVAL;
+
+ rv = pthread_rwlock_wrlock(&(cache->cache_lock));
+ if (rv)
+ return rv;
+
+ if (keep_data) {
+ HASH_CLEAR(hh, cache->entries);
+ } else {
+ HASH_ITER(hh, cache->entries, entry, tmp) {
+ HASH_DEL(cache->entries, entry);
+ if (cache->free_cb)
+ cache->free_cb(entry->data);
+ free(entry);
+ }
+ }
+ (void)pthread_rwlock_unlock(&(cache->cache_lock));
+ (void)pthread_rwlock_destroy(&(cache->cache_lock));
+ free(cache);
+ cache = NULL;
+ return 0;
+}
+
+/** Checks if a given key is in the cache
+
+ @param cache
+ The cache object
+
+ @param key
+ The key to look-up
+
+ @param result
+ Where to store the result if key is found.
+
+ A warning: Even though result is just a pointer,
+ you have to call this function with a **ptr,
+ otherwise this will blow up in your face.
+
+ @return EINVAL if cache is NULL, 0 otherwise
+*/
+int foo_cache_lookup(struct foo_cache *cache, char *key, void *result)
+{
+ int rv;
+ struct foo_cache_entry *tmp = NULL;
+ char **dirty_hack = result;
+
+ if (!cache || !key || !result)
+ return EINVAL;
+
+ rv = pthread_rwlock_wrlock(&(cache->cache_lock));
+ if (rv)
+ return rv;
+
+ HASH_FIND_STR(cache->entries, key, tmp);
+ if (tmp) {
+ size_t key_len = strnlen(tmp->key, KEY_MAX_LENGTH);
+ HASH_DELETE(hh, cache->entries, tmp);
+ HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
+ *dirty_hack = tmp->data;
+ } else {
+ *dirty_hack = result = NULL;
+ }
+ rv = pthread_rwlock_unlock(&(cache->cache_lock));
+ return rv;
+}
+
+/** Inserts a given <key, value> pair into the cache
+
+ @param cache
+ The cache object
+
+ @param key
+ The key that identifies <value>
+
+ @param data
+ Data associated with <key>
+
+ @return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise
+*/
+int foo_cache_insert(struct foo_cache *cache, char *key, void *data)
+{
+ struct foo_cache_entry *entry = NULL;
+ struct foo_cache_entry *tmp_entry = NULL;
+ size_t key_len = 0;
+ int rv;
+
+ if (!cache || !data)
+ return EINVAL;
+
+ if ((entry = malloc(sizeof(*entry))) == NULL)
+ return ENOMEM;
+
+ if ((rv = pthread_rwlock_wrlock(&(cache->cache_lock))) != 0)
+ goto err_out;
+
+ entry->key = key;
+ entry->data = data;
+ key_len = strnlen(entry->key, KEY_MAX_LENGTH);
+ HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry);
+
+ if (HASH_COUNT(cache->entries) >= cache->max_entries) {
+ HASH_ITER(hh, cache->entries, entry, tmp_entry) {
+ HASH_DELETE(hh, cache->entries, entry);
+ if (cache->free_cb)
+ cache->free_cb(entry->data);
+ else
+ free(entry->data);
+ /* free(key->key) if data has been copied */
+ free(entry);
+ break;
+ }
+ }
+
+ rv = pthread_rwlock_unlock(&(cache->cache_lock));
+ return rv;
+
+err_out:
+ if (entry)
+ free(entry);
+ (void)pthread_rwlock_unlock(&(cache->cache_lock));
+ return rv;
+
+}