From 5d38a26a8af7b5db187a307f1646d8a6ed470f61 Mon Sep 17 00:00:00 2001 From: Benign X <1341398182@qq.com> Date: Thu, 30 Nov 2023 16:44:45 +0800 Subject: [PATCH] feat(lv_lru_rb): add rb tree based lru cache manager (#4670) Signed-off-by: pengyiqiang Co-authored-by: pengyiqiang Co-authored-by: Gabor Kiss-Vamosi --- lvgl.h | 2 + src/misc/lv_lru_rb.c | 311 +++++++++++++++++++++++++++++++++++++++++++ src/misc/lv_lru_rb.h | 64 +++++++++ 3 files changed, 377 insertions(+) create mode 100644 src/misc/lv_lru_rb.c create mode 100644 src/misc/lv_lru_rb.h diff --git a/lvgl.h b/lvgl.h index bff014939..a8afdc3a7 100644 --- a/lvgl.h +++ b/lvgl.h @@ -35,6 +35,8 @@ extern "C" { #include "src/misc/lv_anim_timeline.h" #include "src/misc/lv_profiler_builtin.h" #include "src/misc/lv_rb.h" +#include "src/misc/lv_lru_rb.h" + #include "src/tick/lv_tick.h" diff --git a/src/misc/lv_lru_rb.c b/src/misc/lv_lru_rb.c new file mode 100644 index 000000000..8465fdf16 --- /dev/null +++ b/src/misc/lv_lru_rb.c @@ -0,0 +1,311 @@ +/** + * @file lv_lru_rb.c + * + */ + +/** + * @brief cache model + * */ + +/*************************************************************************\ +* * +* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┓ * +* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┌ ─ ─ ─ ┐ * +* ┌ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ Cache insert ┃ * +* ┃ RB Tree │ │Hitting│ head * +* └ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ ─ ─ ─ ─ ┃ * +* ┃ ┌─┬─┬─┬─┐ ┌─────┐ * +* ┌──│◄│B│►│ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┃─ ─ ╋ ─ ─▶│ B │ ┃ * +* ┃ │ └─┴─┴─┴─┘ └──▲──┘ * +* │ │ ┃ ┃ │ ┃ * +* ┃ │ │ ┌──┴──┐ * +* │ └──────┐ ┌ ─┃─ ─ ╋ ─ ─▶│ E │ ┃ * +* ┃ ▼ ┌ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └──▲──┘ * +* ┌─┬─┬─┬─┐ ▼ │ ┃ ┃ │ ┃ * +* ┃│◄│A│►│ │─ ─ ┘ ┌─┬─┬─┬─┐ │ ┌──┴──┐ * +* └─┴─┴─┴─┘ ┌───│◄│D│►│ │─ ─ ─ ─ ─ ─│─ ╋ ┐ ┃ ─ ▶│ A │ ┌ ─ ─ ─ ─ ┐ ┃ * +* ┃ │ └─┴─┴─┴─┘ └──▲──┘ LRU * +* │ │ │ ┃ │ ┃ │ │ Cache │ ┃ * +* ┃ ▼ └──────┐ ┌──┴──┐ ─ ─ ─ ─ ─ * +* ┌─┬─┬─┬─┐ ▼ │ ┃ └ ─┃─ ─ ▶│ D │ ┃ * +* ┃ │◄│C│►│ │─ ─ ┌─┬─┬─┬─┐ └──▲──┘ * +* └─┴─┴─┴─┘ │ │◄│E│►│ │─ ┘ ┃ ┃ │ ┃ * +* ┃ └─┴─┴─┴─┘ ┌──┴──┐ * +* │ │ ─ ╋ ─ ─┃─ ─ ▶│ C │ ┃ * +* ┃ ─ ─ ─ ─ ┼ ─ ─ ┘ └──▲──┘ * +* ▼ ┃ ┃ ┌ ─ ─│─ ─ ┐ ┃ * +* ┃ ┌─┬─┬─┬─┐ ┌──┴──┐ * +* │◄│F│►│ │─ ─┃─ ─ ╋ ─ ┼▶│ F │ │ ┃ * +* ┃ └─┴─┴─┴─┘ └─────┘ * +* ┃ ┃ └ ─ ─ ─ ─ ┘ ┃ * +* ┃ remove * +* ┃ ┃ tail ┃ * +* ┗ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ * +* * +\*************************************************************************/ + +/********************* + * INCLUDES + *********************/ +#include "lv_lru_rb.h" +#include "lv_ll.h" +#include "lv_rb.h" +#include "../stdlib/lv_string.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +struct _lv_lru_rb_t { + lv_rb_t rb; + lv_ll_t lru_ll; + + size_t max_size; + size_t size; + lv_lru_rb_alloc_cb_t alloc_cb; + lv_lru_rb_free_cb_t free_cb; +}; +/********************** + * STATIC PROTOTYPES + **********************/ +static void * alloc_new_node(lv_lru_rb_t * lru, void * key, void * user_data); +inline static void ** get_lru_node(lv_lru_rb_t * lru, lv_rb_node_t * node); +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_lru_rb_t * lv_lru_rb_create(size_t node_size, size_t max_size, lv_lru_rb_compare_cb_t compare_cb, + lv_lru_rb_alloc_cb_t alloc_cb, lv_lru_rb_free_cb_t free_cb) +{ + LV_ASSERT_NULL(compare_cb); + LV_ASSERT_NULL(alloc_cb); + LV_ASSERT_NULL(free_cb); + LV_ASSERT(node_size > 0); + LV_ASSERT(max_size > 0); + + if(node_size == 0 || max_size == 0 || compare_cb == NULL || alloc_cb == NULL || free_cb == NULL) { + return NULL; + } + + lv_lru_rb_t * lru; + lru = lv_malloc(sizeof(lv_lru_rb_t)); + LV_ASSERT_MALLOC(lru); + if(lru == NULL) { + return NULL; + } + + lv_memzero(lru, sizeof(lv_lru_rb_t)); + + /*add void* to store the ll node pointer*/ + if(!lv_rb_init(&lru->rb, (lv_rb_compare_t)compare_cb, node_size + sizeof(void *))) { + return NULL; + } + _lv_ll_init(&lru->lru_ll, sizeof(void *)); + + lv_lru_rb_set_max_size(lru, max_size, NULL); + lv_lru_rb_set_compare_cb(lru, compare_cb, NULL); + lv_lru_rb_set_alloc_cb(lru, alloc_cb, NULL); + lv_lru_rb_set_free_cb(lru, free_cb, NULL); + + return lru; +} + +void lv_lru_rb_destroy(lv_lru_rb_t * lru, void * user_data) +{ + LV_ASSERT_NULL(lru); + + if(lru == NULL) { + return; + } + + lv_lru_rb_clear(lru, user_data); + + lv_free(lru); +} + +void * lv_lru_rb_get(lv_lru_rb_t * lru, const void * key, void * user_data) +{ + LV_ASSERT_NULL(lru); + LV_ASSERT_NULL(key); + + if(lru == NULL || key == NULL) { + return NULL; + } + + /*try the first ll node first*/ + void * head = _lv_ll_get_head(&lru->lru_ll); + if(head) { + lv_rb_node_t * node = *(lv_rb_node_t **)head; + if(lru->rb.compare(node->data, key) == 0) { + return node->data; + } + } + + lv_rb_node_t * node = lv_rb_find(&lru->rb, key); + /*cache hit*/ + if(node) { + void * lru_node = *get_lru_node(lru, node); + head = _lv_ll_get_head(&lru->lru_ll); + _lv_ll_move_before(&lru->lru_ll, lru_node, head); + return node->data; + } + + while(lru->size >= lru->max_size) { + lv_rb_node_t * tail = *(lv_rb_node_t **)_lv_ll_get_tail(&lru->lru_ll); + lv_lru_rb_reset(lru, tail->data, user_data); + } + + /*cache miss*/ + lv_rb_node_t * new_node = alloc_new_node(lru, (void *)key, user_data); + if(new_node == NULL) { + return NULL; + } + + lru->size++; + return new_node->data; +} + +void lv_lru_rb_reset(lv_lru_rb_t * lru, const void * key, void * user_data) +{ + LV_ASSERT_NULL(lru); + LV_ASSERT_NULL(key); + + if(lru == NULL || key == NULL) { + return; + } + + lv_rb_node_t * node = lv_rb_find(&lru->rb, key); + if(node == NULL) { + return; + } + + lru->free_cb(node->data, user_data); + + void * lru_node = *get_lru_node(lru, node); + lv_rb_remove(&lru->rb, key); + _lv_ll_remove(&lru->lru_ll, lru_node); + + lv_free(lru_node); + + lru->size--; +} + +void lv_lru_rb_clear(lv_lru_rb_t * lru, void * user_data) +{ + LV_ASSERT_NULL(lru); + + if(lru == NULL) { + return; + } + + lv_rb_node_t ** node; + _LV_LL_READ(&lru->lru_ll, node) { + /*free user handled data and do other clean up*/ + lru->free_cb((*node)->data, user_data); + } + + lv_rb_destroy(&lru->rb); + _lv_ll_clear(&lru->lru_ll); + + lru->size = 0; +} + +void lv_lru_rb_set_max_size(lv_lru_rb_t * lru, size_t max_size, void * user_data) +{ + LV_UNUSED(user_data); + lru->max_size = max_size; +} + +size_t lv_lru_rb_get_max_size(lv_lru_rb_t * lru, void * user_data) +{ + LV_UNUSED(user_data); + return lru->max_size; +} + +size_t lv_lru_rb_get_size(lv_lru_rb_t * lru, void * user_data) +{ + LV_UNUSED(user_data); + return lru->size; +} + +size_t lv_lru_rb_get_free_size(lv_lru_rb_t * lru, void * user_data) +{ + return lv_lru_rb_get_max_size(lru, user_data) - lv_lru_rb_get_size(lru, user_data); +} + +void lv_lru_rb_set_compare_cb(lv_lru_rb_t * lru, lv_lru_rb_compare_cb_t compare_cb, void * user_data) +{ + LV_UNUSED(user_data); + lru->rb.compare = (lv_rb_compare_t)compare_cb; +} + +void lv_lru_rb_set_alloc_cb(lv_lru_rb_t * lru, lv_lru_rb_alloc_cb_t alloc_cb, void * user_data) +{ + LV_UNUSED(user_data); + lru->alloc_cb = alloc_cb; +} + +void lv_lru_rb_set_free_cb(lv_lru_rb_t * lru, lv_lru_rb_free_cb_t free_cb, void * user_data) +{ + LV_UNUSED(user_data); + lru->free_cb = free_cb; +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void * alloc_new_node(lv_lru_rb_t * lru, void * key, void * user_data) +{ + LV_ASSERT_NULL(lru); + LV_ASSERT_NULL(key); + + if(lru == NULL || key == NULL) { + return NULL; + } + + lv_rb_node_t * node = lv_rb_insert(&lru->rb, key); + if(node == NULL) + goto FAILED_HANDLER3; + + lv_memcpy(node->data, key, lru->rb.size - sizeof(void *)); + + bool alloc_res = lru->alloc_cb(node->data, user_data); + if(alloc_res == false) + goto FAILED_HANDLER2; + + void * lru_node = _lv_ll_ins_head(&lru->lru_ll); + if(lru_node == NULL) + goto FAILED_HANDLER1; + + lv_memcpy(lru_node, &node, sizeof(void *)); + lv_memcpy(get_lru_node(lru, node), &lru_node, sizeof(void *)); + goto FAILED_HANDLER3; + +FAILED_HANDLER1: + lru->free_cb(node->data, user_data); +FAILED_HANDLER2: + node = NULL; + lv_rb_remove(&lru->rb, key); +FAILED_HANDLER3: + return node; +} + +inline static void ** get_lru_node(lv_lru_rb_t * lru, lv_rb_node_t * node) +{ + return (void **)((char *)node->data + lru->rb.size - sizeof(void *)); +} diff --git a/src/misc/lv_lru_rb.h b/src/misc/lv_lru_rb.h new file mode 100644 index 000000000..6a71cc31c --- /dev/null +++ b/src/misc/lv_lru_rb.h @@ -0,0 +1,64 @@ +/** + * @file lv_lru_rb.h + * + */ + +#ifndef LV_LRU_RB_H +#define LV_LRU_RB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "lv_types.h" + +#include "stdbool.h" +#include "lv_assert.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +struct _lv_lru_rb_t; +typedef struct _lv_lru_rb_t lv_lru_rb_t; + +typedef int8_t lv_lru_rb_compare_res_t; +typedef bool (*lv_lru_rb_alloc_cb_t)(void * node, void * user_data); +typedef void (*lv_lru_rb_free_cb_t)(void * node, void * user_data); +typedef lv_lru_rb_compare_res_t (*lv_lru_rb_compare_cb_t)(const void * a, const void * b); + +/********************** + * GLOBAL PROTOTYPES + **********************/ +lv_lru_rb_t * lv_lru_rb_create(size_t node_size, size_t max_size, lv_lru_rb_compare_cb_t compare_cb, + lv_lru_rb_alloc_cb_t alloc_cb, lv_lru_rb_free_cb_t free_cb); +void lv_lru_rb_destroy(lv_lru_rb_t * lru, void * user_data); +void * lv_lru_rb_get(lv_lru_rb_t * lru, const void * key, void * user_data); +void lv_lru_rb_reset(lv_lru_rb_t * lru, const void * key, void * user_data); +void lv_lru_rb_clear(lv_lru_rb_t * lru, void * user_data); +void lv_lru_rb_set_max_size(lv_lru_rb_t * lru, size_t max_size, void * user_data); +size_t lv_lru_rb_get_max_size(lv_lru_rb_t * lru, void * user_data); +size_t lv_lru_rb_get_size(lv_lru_rb_t * lru, void * user_data); +size_t lv_lru_rb_get_free_size(lv_lru_rb_t * lru, void * user_data); +void lv_lru_rb_set_compare_cb(lv_lru_rb_t * lru, lv_lru_rb_compare_cb_t compare_cb, void * user_data); +void lv_lru_rb_set_alloc_cb(lv_lru_rb_t * lru, lv_lru_rb_alloc_cb_t alloc_cb, void * user_data); +void lv_lru_rb_set_free_cb(lv_lru_rb_t * lru, lv_lru_rb_free_cb_t free_cb, void * user_data); +/************************* + * GLOBAL VARIABLES + *************************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_LRU_RB_H*/