feat(lv_lru_rb): add rb tree based lru cache manager (#4670)
Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com> Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com> Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
2
lvgl.h
2
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"
|
||||
|
||||
|
||||
311
src/misc/lv_lru_rb.c
Normal file
311
src/misc/lv_lru_rb.c
Normal file
@@ -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 *));
|
||||
}
|
||||
64
src/misc/lv_lru_rb.h
Normal file
64
src/misc/lv_lru_rb.h
Normal file
@@ -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*/
|
||||
Reference in New Issue
Block a user