summaryrefslogtreecommitdiff
path: root/lib/lvgl/src/misc/lv_lru.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lvgl/src/misc/lv_lru.c')
m---------lib/lvgl0
-rw-r--r--lib/lvgl/src/misc/lv_lru.c349
2 files changed, 349 insertions, 0 deletions
diff --git a/lib/lvgl b/lib/lvgl
deleted file mode 160000
-Subproject 0732400e7b564dd0e7dc4a924619d8e19c5b23a
diff --git a/lib/lvgl/src/misc/lv_lru.c b/lib/lvgl/src/misc/lv_lru.c
new file mode 100644
index 00000000..6ff83903
--- /dev/null
+++ b/lib/lvgl/src/misc/lv_lru.c
@@ -0,0 +1,349 @@
+/**
+ * @file lv_lru.c
+ *
+ * @see https://github.com/willcannings/C-LRU-Cache
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "lv_lru.h"
+#include "lv_math.h"
+#include "lv_mem.h"
+#include "lv_assert.h"
+#include "lv_log.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_lru_item_t {
+ void * value;
+ void * key;
+ size_t value_length;
+ size_t key_length;
+ uint64_t access_count;
+ struct _lv_lru_item_t * next;
+};
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**
+ * MurmurHash2
+ * @author Austin Appleby
+ * @see http://sites.google.com/site/murmurhash/
+ */
+static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length);
+
+/** compare a key against an existing item's key */
+static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_length);
+
+/** remove an item and push it to the free items queue */
+static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index);
+
+/** pop an existing item off the free queue, or create a new one */
+static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/* error helpers */
+#define error_for(conditions, error) if(conditions) {return error;}
+#define test_for_missing_cache() error_for(!cache, LV_LRU_MISSING_CACHE)
+#define test_for_missing_key() error_for(!key, LV_LRU_MISSING_KEY)
+#define test_for_missing_value() error_for(!value || value_length == 0, LV_LRU_MISSING_VALUE)
+#define test_for_value_too_large() error_for(value_length > cache->total_memory, LV_LRU_VALUE_TOO_LARGE)
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_lru_t * lv_lru_create(size_t cache_size, size_t average_length, lv_lru_free_t * value_free,
+ lv_lru_free_t * key_free)
+{
+ // create the cache
+ lv_lru_t * cache = (lv_lru_t *) lv_mem_alloc(sizeof(lv_lru_t));
+ lv_memset_00(cache, sizeof(lv_lru_t));
+ if(!cache) {
+ LV_LOG_WARN("LRU Cache unable to create cache object");
+ return NULL;
+ }
+ cache->hash_table_size = cache_size / average_length;
+ cache->average_item_length = average_length;
+ cache->free_memory = cache_size;
+ cache->total_memory = cache_size;
+ cache->seed = lv_rand(1, UINT32_MAX);
+ cache->value_free = value_free ? value_free : lv_mem_free;
+ cache->key_free = key_free ? key_free : lv_mem_free;
+
+ // size the hash table to a guestimate of the number of slots required (assuming a perfect hash)
+ cache->items = (lv_lru_item_t **) lv_mem_alloc(sizeof(lv_lru_item_t *) * cache->hash_table_size);
+ lv_memset_00(cache->items, sizeof(lv_lru_item_t *) * cache->hash_table_size);
+ if(!cache->items) {
+ LV_LOG_WARN("LRU Cache unable to create cache hash table");
+ lv_mem_free(cache);
+ return NULL;
+ }
+ return cache;
+}
+
+
+void lv_lru_del(lv_lru_t * cache)
+{
+ LV_ASSERT_NULL(cache);
+
+ // free each of the cached items, and the hash table
+ lv_lru_item_t * item = NULL, *next = NULL;
+ uint32_t i = 0;
+ if(cache->items) {
+ for(; i < cache->hash_table_size; i++) {
+ item = cache->items[i];
+ while(item) {
+ next = (lv_lru_item_t *) item->next;
+ cache->value_free(item->value);
+ cache->key_free(item->key);
+ cache->free_memory += item->value_length;
+ lv_mem_free(item);
+ item = next;
+ }
+ }
+ lv_mem_free(cache->items);
+ }
+
+ if(cache->free_items) {
+ item = cache->free_items;
+ while(item) {
+ next = (lv_lru_item_t *) item->next;
+ lv_mem_free(item);
+ item = next;
+ }
+ }
+
+ // free the cache
+ lv_mem_free(cache);
+}
+
+
+lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, void * value, size_t value_length)
+{
+ test_for_missing_cache();
+ test_for_missing_key();
+ test_for_missing_value();
+ test_for_value_too_large();
+
+ // see if the key already exists
+ uint32_t hash_index = lv_lru_hash(cache, key, key_length);
+ int required = 0;
+ lv_lru_item_t * item = NULL, *prev = NULL;
+ item = cache->items[hash_index];
+
+ while(item && lv_lru_cmp_keys(item, key, key_length)) {
+ prev = item;
+ item = (lv_lru_item_t *) item->next;
+ }
+
+ if(item) {
+ // update the value and value_lengths
+ required = (int)(value_length - item->value_length);
+ cache->value_free(item->value);
+ item->value = value;
+ item->value_length = value_length;
+
+ }
+ else {
+ // insert a new item
+ item = lv_lru_pop_or_create_item(cache);
+ item->value = value;
+ item->key = lv_mem_alloc(key_length);
+ memcpy(item->key, key, key_length);
+ item->value_length = value_length;
+ item->key_length = key_length;
+ required = (int) value_length;
+
+ if(prev)
+ prev->next = item;
+ else
+ cache->items[hash_index] = item;
+ }
+ item->access_count = ++cache->access_count;
+
+ // remove as many items as necessary to free enough space
+ if(required > 0 && (size_t) required > cache->free_memory) {
+ while(cache->free_memory < (size_t) required)
+ lv_lru_remove_lru_item(cache);
+ }
+ cache->free_memory -= required;
+ return LV_LRU_OK;
+}
+
+
+lv_lru_res_t lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, void ** value)
+{
+ test_for_missing_cache();
+ test_for_missing_key();
+
+ // loop until we find the item, or hit the end of a chain
+ uint32_t hash_index = lv_lru_hash(cache, key, key_size);
+ lv_lru_item_t * item = cache->items[hash_index];
+
+ while(item && lv_lru_cmp_keys(item, key, key_size))
+ item = (lv_lru_item_t *) item->next;
+
+ if(item) {
+ *value = item->value;
+ item->access_count = ++cache->access_count;
+ }
+ else {
+ *value = NULL;
+ }
+
+ return LV_LRU_OK;
+}
+
+lv_lru_res_t lv_lru_remove(lv_lru_t * cache, const void * key, size_t key_size)
+{
+ test_for_missing_cache();
+ test_for_missing_key();
+
+ // loop until we find the item, or hit the end of a chain
+ lv_lru_item_t * item = NULL, *prev = NULL;
+ uint32_t hash_index = lv_lru_hash(cache, key, key_size);
+ item = cache->items[hash_index];
+
+ while(item && lv_lru_cmp_keys(item, key, key_size)) {
+ prev = item;
+ item = (lv_lru_item_t *) item->next;
+ }
+
+ if(item) {
+ lv_lru_remove_item(cache, prev, item, hash_index);
+ }
+
+ return LV_LRU_OK;
+}
+
+void lv_lru_remove_lru_item(lv_lru_t * cache)
+{
+ lv_lru_item_t * min_item = NULL, *min_prev = NULL;
+ lv_lru_item_t * item = NULL, *prev = NULL;
+ uint32_t i = 0, min_index = -1;
+ uint64_t min_access_count = -1;
+
+ for(; i < cache->hash_table_size; i++) {
+ item = cache->items[i];
+ prev = NULL;
+
+ while(item) {
+ if(item->access_count < min_access_count || (int64_t) min_access_count == -1) {
+ min_access_count = item->access_count;
+ min_item = item;
+ min_prev = prev;
+ min_index = i;
+ }
+ prev = item;
+ item = item->next;
+ }
+ }
+
+ if(min_item) {
+ lv_lru_remove_item(cache, min_prev, min_item, min_index);
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_length)
+{
+ uint32_t m = 0x5bd1e995;
+ uint32_t r = 24;
+ uint32_t h = cache->seed ^ key_length;
+ char * data = (char *) key;
+
+ while(key_length >= 4) {
+ uint32_t k = *(uint32_t *) data;
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+ data += 4;
+ key_length -= 4;
+ }
+
+ if(key_length >= 3) {
+ h ^= data[2] << 16;
+ }
+ if(key_length >= 2) {
+ h ^= data[1] << 8;
+ }
+ if(key_length >= 1) {
+ h ^= data[0];
+ h *= m;
+ }
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h % cache->hash_table_size;
+}
+
+static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_length)
+{
+ if(key_length != item->key_length) {
+ return 1;
+ }
+ else {
+ return memcmp(key, item->key, key_length);
+ }
+}
+
+static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index)
+{
+ if(prev) {
+ prev->next = item->next;
+ }
+ else {
+ cache->items[hash_index] = (lv_lru_item_t *) item->next;
+ }
+
+ // free memory and update the free memory counter
+ cache->free_memory += item->value_length;
+ cache->value_free(item->value);
+ cache->key_free(item->key);
+
+ // push the item to the free items queue
+ lv_memset_00(item, sizeof(lv_lru_item_t));
+ item->next = cache->free_items;
+ cache->free_items = item;
+}
+
+static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache)
+{
+ lv_lru_item_t * item = NULL;
+
+ if(cache->free_items) {
+ item = cache->free_items;
+ cache->free_items = item->next;
+ lv_memset_00(item, sizeof(lv_lru_item_t));
+ }
+ else {
+ item = (lv_lru_item_t *) lv_mem_alloc(sizeof(lv_lru_item_t));
+ lv_memset_00(item, sizeof(lv_lru_item_t));
+ }
+
+ return item;
+}