LVGL 库提交

This commit is contained in:
2026-08-30 08:57:07 +08:00
parent 26c9098630
commit e2f199c6bf
1056 changed files with 556743 additions and 45 deletions
+17
View File
@@ -0,0 +1,17 @@
/**
* @file lv_cache_clazz.h
*
*/
#ifndef LV_CACHE_CLAZZ_H
#define LV_CACHE_CLAZZ_H
/*********************
* INCLUDES
*********************/
#include "lv_cache_lru_rb.h"
#include "lv_cache_lru_ll.h"
#endif //LV_CACHE_CLAZZ_H
+443
View File
@@ -0,0 +1,443 @@
/**
* @file lv_cache_lru_ll.c
*
*/
/*********************************\
* *
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┓ *
* ┌ ─ ─ ─ ┐ *
* ┃ Cache insert ┃ *
* │Hitting│ head *
* ┃ ─ ─ ─ ─ ┃ *
* ┌─────┐ *
* ┃ │ B │ ┃ *
* └──▲──┘ *
* ┃ │ ┃ *
* ┌──┴──┐ *
* ┃ │ E │ ┃ *
* └──▲──┘ *
* ┃ │ ┃ *
* ┌──┴──┐ *
* ┃ │ A │ ┌ ─ ─ ─ ─ ─ ┐ ┃ *
* └──▲──┘ LRU *
* ┃ │ │ Cache │ ┃ *
* ┌──┴──┐ ─ ─ ─ ─ ─ ─ *
* ┃ │ D │ ┃ *
* └──▲──┘ *
* ┃ │ ┃ *
* ┌──┴──┐ *
* ┃ │ C │ ┃ *
* └──▲──┘ *
* ┃ ┌ ─ ─│─ ─ ┐ ┃ *
* ┌──┴──┐ *
* ┃ │ │ F │ │ ┃ *
* └─────┘ *
* ┃ └ ─ ─ ─ ─ ┘ ┃ *
* remove *
* ┃ tail ┃ *
* ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ *
* *
\*********************************/
/*********************
* INCLUDES
*********************/
#include "lv_cache_lru_ll.h"
#include "../lv_cache_entry.h"
#include "../../../stdlib/lv_sprintf.h"
#include "../../../stdlib/lv_string.h"
#include "../../lv_ll.h"
#include "../../lv_iter.h"
#include "../../lv_assert.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef uint32_t (get_data_size_cb_t)(const void * data);
struct _lv_cache_lru_ll_t {
lv_cache_t cache;
lv_ll_t ll;
get_data_size_cb_t * get_data_size_cb;
};
typedef struct _lv_cache_lru_ll_t lv_cache_lru_ll_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void * alloc_cb(void);
static bool init_cnt_cb(lv_cache_t * cache);
static bool init_size_cb(lv_cache_t * cache);
static void destroy_cb(lv_cache_t * cache, void * user_data);
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data);
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data);
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data);
static void drop_all_cb(lv_cache_t * cache, void * user_data);
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data);
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
void * user_data);
static void * alloc_new_node(lv_cache_lru_ll_t * lru, void * key, void * user_data);
static uint32_t cnt_get_data_size_cb(const void * data);
static uint32_t size_get_data_size_cb(const void * data);
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache);
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem);
/**********************
* GLOBAL VARIABLES
**********************/
const lv_cache_class_t lv_cache_class_lru_ll_count = {
.alloc_cb = alloc_cb,
.init_cb = init_cnt_cb,
.destroy_cb = destroy_cb,
.get_cb = get_cb,
.add_cb = add_cb,
.remove_cb = remove_cb,
.drop_cb = drop_cb,
.drop_all_cb = drop_all_cb,
.get_victim_cb = get_victim_cb,
.reserve_cond_cb = reserve_cond_cb,
.iter_create_cb = cache_iter_create_cb,
};
const lv_cache_class_t lv_cache_class_lru_ll_size = {
.alloc_cb = alloc_cb,
.init_cb = init_size_cb,
.destroy_cb = destroy_cb,
.get_cb = get_cb,
.add_cb = add_cb,
.remove_cb = remove_cb,
.drop_cb = drop_cb,
.drop_all_cb = drop_all_cb,
.get_victim_cb = get_victim_cb,
.reserve_cond_cb = reserve_cond_cb,
.iter_create_cb = cache_iter_create_cb,
};
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static void * alloc_new_node(lv_cache_lru_ll_t * lru, void * key, void * user_data)
{
LV_UNUSED(user_data);
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
void * new_node = lv_ll_ins_head(&lru->ll);
if(new_node == NULL) {
return NULL;
}
lv_memcpy(new_node, key, lru->cache.node_size);
lv_cache_entry_t * entry = lv_cache_entry_get_entry(new_node, lru->cache.node_size);
lv_cache_entry_init(entry, &lru->cache, lru->cache.node_size);
return entry;
}
static void * alloc_cb(void)
{
void * res = lv_malloc(sizeof(lv_cache_lru_ll_t));
LV_ASSERT_MALLOC(res);
if(res == NULL) {
LV_LOG_ERROR("malloc failed");
return NULL;
}
lv_memzero(res, sizeof(lv_cache_lru_ll_t));
return res;
}
static bool init_cnt_cb(lv_cache_t * cache)
{
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
LV_ASSERT_NULL(lru->cache.ops.free_cb);
LV_ASSERT(lru->cache.node_size > 0);
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
return false;
}
lv_ll_init(&lru->ll, lv_cache_entry_get_size(lru->cache.node_size));
lru->get_data_size_cb = cnt_get_data_size_cb;
return true;
}
static bool init_size_cb(lv_cache_t * cache)
{
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
LV_ASSERT_NULL(lru->cache.ops.free_cb);
LV_ASSERT(lru->cache.node_size > 0);
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
return false;
}
lv_ll_init(&lru->ll, lv_cache_entry_get_size(lru->cache.node_size));
lru->get_data_size_cb = size_get_data_size_cb;
return true;
}
static void destroy_cb(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
cache->clz->drop_all_cb(cache, user_data);
cache->size = 0;
}
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data)
{
LV_UNUSED(user_data);
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
/* Linear search */
void * node = NULL;
LV_LL_READ(&lru->ll, node) {
if(lru->cache.ops.compare_cb(node, key) == 0) {
break;
}
}
/*cache hit*/
if(node) {
void * head = lv_ll_get_head(&lru->ll);
lv_ll_move_before(&lru->ll, node, head);
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node, cache->node_size);
return entry;
}
return NULL;
}
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data)
{
LV_UNUSED(user_data);
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
lv_cache_entry_t * entry = alloc_new_node(lru, (void *)key, user_data);
if(entry == NULL) {
return NULL;
}
cache->size += lru->get_data_size_cb(key);
return entry;
}
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
{
LV_UNUSED(user_data);
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(entry);
if(lru == NULL || entry == NULL) {
return;
}
void * node = lv_cache_entry_get_data(entry);
lv_ll_remove(&lru->ll, node);
cache->size -= lru->get_data_size_cb(node);
}
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data)
{
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return;
}
void * node = cache->clz->get_cb(cache, key, user_data);
if(node == NULL) {
return;
}
lv_ll_remove(&lru->ll, node);
lru->cache.ops.free_cb(node, user_data);
cache->size -= lru->get_data_size_cb(node);
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node, cache->node_size);
lv_cache_entry_delete(entry);
}
static void drop_all_cb(lv_cache_t * cache, void * user_data)
{
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
uint32_t used_cnt = 0;
void * node;
LV_LL_READ(&lru->ll, node) {
/*free user handled data and do other clean up*/
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node, cache->node_size);
if(lv_cache_entry_get_ref(entry) == 0) {
lru->cache.ops.free_cb(node, user_data);
}
else {
LV_LOG_WARN("entry (%p) is still referenced (%" LV_PRId32 ")", (void *)entry, lv_cache_entry_get_ref(entry));
used_cnt++;
}
}
if(used_cnt > 0) {
LV_LOG_WARN("%" LV_PRId32 " entries are still referenced", used_cnt);
}
lv_ll_clear(&lru->ll);
cache->size = 0;
}
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
void * tail;
LV_LL_READ_BACK(&lru->ll, tail) {
lv_cache_entry_t * entry = lv_cache_entry_get_entry(tail, cache->node_size);
if(lv_cache_entry_get_ref(entry) == 0) {
return entry;
}
}
return NULL;
}
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
void * user_data)
{
LV_UNUSED(user_data);
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return LV_CACHE_RESERVE_COND_ERROR;
}
uint32_t data_size = key ? lru->get_data_size_cb(key) : 0;
if(data_size > lru->cache.max_size) {
LV_LOG_ERROR("data size (%" LV_PRIu32 ") is larger than max size (%" LV_PRIu32 ")", data_size, lru->cache.max_size);
return LV_CACHE_RESERVE_COND_TOO_LARGE;
}
return cache->size + reserved_size + data_size > lru->cache.max_size
? LV_CACHE_RESERVE_COND_NEED_VICTIM
: LV_CACHE_RESERVE_COND_OK;
}
static uint32_t cnt_get_data_size_cb(const void * data)
{
LV_UNUSED(data);
return 1;
}
static uint32_t size_get_data_size_cb(const void * data)
{
lv_cache_slot_size_t * slot = (lv_cache_slot_size_t *)data;
return slot->size;
}
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache)
{
return lv_iter_create(cache, lv_cache_entry_get_size(cache->node_size), 0, cache_iter_next_cb);
}
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem)
{
lv_cache_lru_ll_t * lru = (lv_cache_lru_ll_t *)instance;
void ** ll_node = context;
LV_ASSERT_NULL(ll_node);
if(*ll_node == NULL) *ll_node = lv_ll_get_head(&lru->ll);
else *ll_node = lv_ll_get_next(&lru->ll, *ll_node);
void * node = *ll_node;
if(node == NULL) return LV_RESULT_INVALID;
lv_memcpy(elem, node, lv_cache_entry_get_size(lru->cache.node_size));
return LV_RESULT_OK;
}
+44
View File
@@ -0,0 +1,44 @@
/**
* @file lv_cache_lru_ll.h
*
*/
#ifndef LV_CACHE_LRU_LL_H
#define LV_CACHE_LRU_LL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_cache_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/*************************
* GLOBAL VARIABLES
*************************/
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_ll_count;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_ll_size;
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_LRU_LL_H*/
+497
View File
@@ -0,0 +1,497 @@
/**
* @file lv_cache_lru_rb.c
*
*/
/***************************************************************************\
* *
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┓ *
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┌ ─ ─ ─ ┐ *
* ┌ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ 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_cache_lru_rb.h"
#include "../lv_cache_entry.h"
#include "../../../stdlib/lv_sprintf.h"
#include "../../../stdlib/lv_string.h"
#include "../../lv_ll.h"
#include "../../lv_rb_private.h"
#include "../../lv_rb.h"
#include "../../lv_iter.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef uint32_t (get_data_size_cb_t)(const void * data);
struct _lv_lru_rb_t {
lv_cache_t cache;
lv_rb_t rb;
lv_ll_t ll;
get_data_size_cb_t * get_data_size_cb;
};
typedef struct _lv_lru_rb_t lv_lru_rb_t_;
/**********************
* STATIC PROTOTYPES
**********************/
static void * alloc_cb(void);
static bool init_cnt_cb(lv_cache_t * cache);
static bool init_size_cb(lv_cache_t * cache);
static void destroy_cb(lv_cache_t * cache, void * user_data);
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data);
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data);
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data);
static void drop_all_cb(lv_cache_t * cache, void * user_data);
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data);
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
void * user_data);
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);
static uint32_t cnt_get_data_size_cb(const void * data);
static uint32_t size_get_data_size_cb(const void * data);
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache);
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem);
/**********************
* GLOBAL VARIABLES
**********************/
const lv_cache_class_t lv_cache_class_lru_rb_count = {
.alloc_cb = alloc_cb,
.init_cb = init_cnt_cb,
.destroy_cb = destroy_cb,
.get_cb = get_cb,
.add_cb = add_cb,
.remove_cb = remove_cb,
.drop_cb = drop_cb,
.drop_all_cb = drop_all_cb,
.get_victim_cb = get_victim_cb,
.reserve_cond_cb = reserve_cond_cb,
.iter_create_cb = cache_iter_create_cb,
};
const lv_cache_class_t lv_cache_class_lru_rb_size = {
.alloc_cb = alloc_cb,
.init_cb = init_size_cb,
.destroy_cb = destroy_cb,
.get_cb = get_cb,
.add_cb = add_cb,
.remove_cb = remove_cb,
.drop_cb = drop_cb,
.drop_all_cb = drop_all_cb,
.get_victim_cb = get_victim_cb,
.reserve_cond_cb = reserve_cond_cb,
.iter_create_cb = cache_iter_create_cb,
};
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static void * alloc_new_node(lv_lru_rb_t_ * lru, void * key, void * user_data)
{
LV_UNUSED(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_HANDLER2;
void * data = node->data;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, lru->cache.node_size);
lv_memcpy(data, key, lru->cache.node_size);
void * lru_node = lv_ll_ins_head(&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 *));
lv_cache_entry_init(entry, &lru->cache, lru->cache.node_size);
goto FAILED_HANDLER2;
FAILED_HANDLER1:
lv_rb_drop_node(&lru->rb, node);
node = NULL;
FAILED_HANDLER2:
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 *));
}
static void * alloc_cb(void)
{
void * res = lv_malloc(sizeof(lv_lru_rb_t_));
LV_ASSERT_MALLOC(res);
if(res == NULL) {
LV_LOG_ERROR("malloc failed");
return NULL;
}
lv_memzero(res, sizeof(lv_lru_rb_t_));
return res;
}
static bool init_cnt_cb(lv_cache_t * cache)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
LV_ASSERT_NULL(lru->cache.ops.free_cb);
LV_ASSERT(lru->cache.node_size > 0);
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
return false;
}
/*add void* to store the ll node pointer*/
if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
return false;
}
lv_ll_init(&lru->ll, sizeof(void *));
lru->get_data_size_cb = cnt_get_data_size_cb;
return true;
}
static bool init_size_cb(lv_cache_t * cache)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
LV_ASSERT_NULL(lru->cache.ops.free_cb);
LV_ASSERT(lru->cache.node_size > 0);
if(lru->cache.node_size <= 0 || lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
return false;
}
/*add void* to store the ll node pointer*/
if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
return false;
}
lv_ll_init(&lru->ll, sizeof(void *));
lru->get_data_size_cb = size_get_data_size_cb;
return true;
}
static void destroy_cb(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
cache->clz->drop_all_cb(cache, user_data);
}
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
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->ll);
if(head) {
lv_rb_node_t * node = *(lv_rb_node_t **)head;
void * data = node->data;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
if(lru->cache.ops.compare_cb(data, key) == 0) {
return entry;
}
}
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->ll);
lv_ll_move_before(&lru->ll, lru_node, head);
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node->data, cache->node_size);
return entry;
}
return NULL;
}
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
lv_rb_node_t * new_node = alloc_new_node(lru, (void *)key, user_data);
if(new_node == NULL) {
return NULL;
}
lv_cache_entry_t * entry = lv_cache_entry_get_entry(new_node->data, cache->node_size);
cache->size += lru->get_data_size_cb(key);
return entry;
}
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(entry);
if(lru == NULL || entry == NULL) {
return;
}
void * data = lv_cache_entry_get_data(entry);
lv_rb_node_t * node = lv_rb_find(&lru->rb, data);
if(node == NULL) {
return;
}
void * lru_node = *get_lru_node(lru, node);
lv_rb_remove_node(&lru->rb, node);
lv_ll_remove(&lru->ll, lru_node);
lv_free(lru_node);
cache->size -= lru->get_data_size_cb(data);
}
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
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;
}
void * data = node->data;
lru->cache.ops.free_cb(data, user_data);
cache->size -= lru->get_data_size_cb(data);
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
void * lru_node = *get_lru_node(lru, node);
lv_rb_remove_node(&lru->rb, node);
lv_cache_entry_delete(entry);
lv_ll_remove(&lru->ll, lru_node);
lv_free(lru_node);
}
static void drop_all_cb(lv_cache_t * cache, void * user_data)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
uint32_t used_cnt = 0;
lv_rb_node_t ** node;
LV_LL_READ(&lru->ll, node) {
/*free user handled data and do other clean up*/
void * search_key = (*node)->data;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(search_key, cache->node_size);
if(lv_cache_entry_get_ref(entry) == 0) {
lru->cache.ops.free_cb(search_key, user_data);
}
else {
LV_LOG_WARN("entry (%p) is still referenced (%" LV_PRId32 ")", (void *)entry, lv_cache_entry_get_ref(entry));
used_cnt++;
}
}
if(used_cnt > 0) {
LV_LOG_WARN("%" LV_PRId32 " entries are still referenced", used_cnt);
}
lv_rb_destroy(&lru->rb);
lv_ll_clear(&lru->ll);
cache->size = 0;
}
static lv_cache_entry_t * get_victim_cb(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
lv_rb_node_t ** tail;
LV_LL_READ_BACK(&lru->ll, tail) {
lv_rb_node_t * tail_node = *tail;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(tail_node->data, cache->node_size);
if(lv_cache_entry_get_ref(entry) == 0) {
return entry;
}
}
return NULL;
}
static lv_cache_reserve_cond_res_t reserve_cond_cb(lv_cache_t * cache, const void * key, size_t reserved_size,
void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return LV_CACHE_RESERVE_COND_ERROR;
}
uint32_t data_size = key ? lru->get_data_size_cb(key) : 0;
if(data_size > lru->cache.max_size) {
LV_LOG_ERROR("data size (%" LV_PRIu32 ") is larger than max size (%" LV_PRIu32 ")", data_size, lru->cache.max_size);
return LV_CACHE_RESERVE_COND_TOO_LARGE;
}
return cache->size + reserved_size + data_size > lru->cache.max_size
? LV_CACHE_RESERVE_COND_NEED_VICTIM
: LV_CACHE_RESERVE_COND_OK;
}
static uint32_t cnt_get_data_size_cb(const void * data)
{
LV_UNUSED(data);
return 1;
}
static uint32_t size_get_data_size_cb(const void * data)
{
lv_cache_slot_size_t * slot = (lv_cache_slot_size_t *)data;
return slot->size;
}
static lv_iter_t * cache_iter_create_cb(lv_cache_t * cache)
{
return lv_iter_create(cache, lv_cache_entry_get_size(cache->node_size), sizeof(void *), cache_iter_next_cb);
}
static lv_result_t cache_iter_next_cb(void * instance, void * context, void * elem)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)instance;
lv_rb_node_t *** ll_node = context;
LV_ASSERT_NULL(ll_node);
if(*ll_node == NULL) *ll_node = lv_ll_get_head(&lru->ll);
else *ll_node = lv_ll_get_next(&lru->ll, *ll_node);
lv_rb_node_t ** node = *ll_node;
if(node == NULL) return LV_RESULT_INVALID;
uint32_t node_size = lru->cache.node_size;
void * search_key = (*node)->data;
lv_memcpy(elem, search_key, lv_cache_entry_get_size(node_size));
return LV_RESULT_OK;
}
+44
View File
@@ -0,0 +1,44 @@
/**
* @file lv_cache_lru_rb.h
*
*/
#ifndef LV_CACHE_LRU_RB_H
#define LV_CACHE_LRU_RB_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_cache_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/*************************
* GLOBAL VARIABLES
*************************/
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_count;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_size;
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_LRU_RB_H*/
+16
View File
@@ -0,0 +1,16 @@
/**
* @file lv_cache_instance.h
*
*/
#ifndef LV_CACHE_INSTANCE_H
#define LV_CACHE_INSTANCE_H
/*********************
* INCLUDES
*********************/
#include "lv_image_header_cache.h"
#include "lv_image_cache.h"
#endif //LV_CACHE_INSTANCE_H
+190
View File
@@ -0,0 +1,190 @@
/**
* @file lv_image_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../draw/lv_image_decoder_private.h"
#include "../../lv_assert.h"
#include "../../../core/lv_global.h"
#include "../../../misc/lv_iter.h"
#include "lv_image_cache.h"
/*********************
* DEFINES
*********************/
#define CACHE_NAME "IMAGE"
#define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache)
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_cache_compare_res_t image_cache_compare_cb(const lv_image_cache_data_t * lhs,
const lv_image_cache_data_t * rhs);
static void image_cache_free_cb(lv_image_cache_data_t * entry, void * user_data);
static void iter_inspect_cb(void * elem);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_image_cache_init(uint32_t size)
{
if(img_cache_p != NULL) {
return LV_RESULT_OK;
}
img_cache_p = lv_cache_create(&lv_cache_class_lru_rb_size,
sizeof(lv_image_cache_data_t), size, (lv_cache_ops_t) {
.compare_cb = (lv_cache_compare_cb_t) image_cache_compare_cb,
.create_cb = NULL,
.free_cb = (lv_cache_free_cb_t) image_cache_free_cb,
});
lv_cache_set_name(img_cache_p, CACHE_NAME);
return img_cache_p != NULL ? LV_RESULT_OK : LV_RESULT_INVALID;
}
void lv_image_cache_resize(uint32_t new_size, bool evict_now)
{
lv_cache_set_max_size(img_cache_p, new_size, NULL);
if(evict_now) {
lv_cache_reserve(img_cache_p, new_size, NULL);
}
}
void lv_image_cache_drop(const void * src)
{
/*If user invalidate image, the header cache should be invalidated too.*/
lv_image_header_cache_drop(src);
if(src == NULL) {
lv_cache_drop_all(img_cache_p, NULL);
return;
}
lv_image_cache_data_t search_key = {
.src = src,
.src_type = lv_image_src_get_type(src),
};
lv_cache_drop(img_cache_p, &search_key, NULL);
}
bool lv_image_cache_is_enabled(void)
{
return lv_cache_is_enabled(img_cache_p);
}
lv_iter_t * lv_image_cache_iter_create(void)
{
return lv_cache_iter_create(img_cache_p);
}
void lv_image_cache_dump(void)
{
lv_iter_t * iter = lv_image_cache_iter_create();
if(iter == NULL) return;
LV_LOG_USER("Image cache dump:");
LV_LOG_USER("\tsize\tdata_size\tcf\trc\ttype\tdecoded\t\t\tsrc");
lv_iter_inspect(iter, iter_inspect_cb);
}
/**********************
* STATIC FUNCTIONS
**********************/
inline static lv_cache_compare_res_t image_cache_common_compare(const void * lhs_src, lv_image_src_t lhs_src_type,
const void * rhs_src, lv_image_src_t rhs_src_type)
{
if(lhs_src_type == rhs_src_type) {
if(lhs_src_type == LV_IMAGE_SRC_FILE) {
int32_t cmp_res = lv_strcmp(lhs_src, rhs_src);
if(cmp_res != 0) {
return cmp_res > 0 ? 1 : -1;
}
}
else if(lhs_src_type == LV_IMAGE_SRC_VARIABLE) {
if(lhs_src != rhs_src) {
return lhs_src > rhs_src ? 1 : -1;
}
}
return 0;
}
return lhs_src_type > rhs_src_type ? 1 : -1;
}
static lv_cache_compare_res_t image_cache_compare_cb(
const lv_image_cache_data_t * lhs,
const lv_image_cache_data_t * rhs)
{
return image_cache_common_compare(lhs->src, lhs->src_type, rhs->src, rhs->src_type);
}
static void image_cache_free_cb(lv_image_cache_data_t * entry, void * user_data)
{
LV_UNUSED(user_data);
/* Destroy the decoded draw buffer if necessary. */
lv_draw_buf_t * decoded = (lv_draw_buf_t *)entry->decoded;
if(lv_draw_buf_has_flag(decoded, LV_IMAGE_FLAGS_ALLOCATED)) {
lv_draw_buf_destroy(decoded);
}
/*Free the duplicated file name*/
if(entry->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)entry->src);
}
static void iter_inspect_cb(void * elem)
{
lv_image_cache_data_t * data = (lv_image_cache_data_t *)elem;
lv_draw_buf_t * decoded = (lv_draw_buf_t *)data->decoded;
lv_image_header_t * header = &decoded->header;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, img_cache_p->node_size);
LV_UNUSED(decoded);
LV_UNUSED(header);
LV_UNUSED(entry);
/* size data_size cf rc type decoded src*/
#define IMAGE_CACHE_DUMP_FORMAT " %4dx%-4d %9"LV_PRIu32" %d %"LV_PRId32" "
switch(data->src_type) {
case LV_IMAGE_SRC_FILE:
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "file\t%-12p\t%s", header->w, header->h, decoded->data_size, header->cf,
lv_cache_entry_get_ref(entry), (void *)data->decoded, (char *)data->src);
break;
case LV_IMAGE_SRC_VARIABLE:
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "var \t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
break;
default:
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "unkn\t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
break;
}
}
+81
View File
@@ -0,0 +1,81 @@
/**
* @file lv_image_cache.h
*
*/
#ifndef LV_IMAGE_CACHE_H
#define LV_IMAGE_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize image cache.
* @param size size of the cache in bytes.
* @return LV_RESULT_OK: initialization succeeded, LV_RESULT_INVALID: failed.
*/
lv_result_t lv_image_cache_init(uint32_t size);
/**
* Resize image cache.
* If set to 0, the cache will be disabled.
* @param new_size new size of the cache in bytes.
* @param evict_now true: evict the images should be removed by the eviction policy, false: wait for the next cache cleanup.
*/
void lv_image_cache_resize(uint32_t new_size, bool evict_now);
/**
* Invalidate image cache. Use NULL to invalidate all images.
* @param src pointer to an image source.
*/
void lv_image_cache_drop(const void * src);
/**
* Return true if the image cache is enabled.
* @return true: enabled, false: disabled.
*/
bool lv_image_cache_is_enabled(void);
/**
* Create an iterator to iterate over the image cache.
* @return an iterator to iterate over the image cache.
*/
lv_iter_t * lv_image_cache_iter_create(void);
/**
* Dump the content of the image cache in a human-readable format with cache order.
*/
void lv_image_cache_dump(void);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMAGE_CACHE_H*/
+179
View File
@@ -0,0 +1,179 @@
/**
* @file lv_image_header_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../draw/lv_image_decoder_private.h"
#include "../../lv_assert.h"
#include "../../../core/lv_global.h"
#include "lv_image_header_cache.h"
#include "../../lv_iter.h"
/*********************
* DEFINES
*********************/
#define CACHE_NAME "IMAGE_HEADER"
#define img_header_cache_p (LV_GLOBAL_DEFAULT()->img_header_cache)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_cache_compare_res_t image_header_cache_compare_cb(const lv_image_header_cache_data_t * lhs,
const lv_image_header_cache_data_t * rhs);
static void image_header_cache_free_cb(lv_image_header_cache_data_t * entry, void * user_data);
static void iter_inspect_cb(void * elem);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_image_header_cache_init(uint32_t count)
{
if(img_header_cache_p != NULL) {
return LV_RESULT_OK;
}
img_header_cache_p = lv_cache_create(&lv_cache_class_lru_rb_count,
sizeof(lv_image_header_cache_data_t), count, (lv_cache_ops_t) {
.compare_cb = (lv_cache_compare_cb_t) image_header_cache_compare_cb,
.create_cb = NULL,
.free_cb = (lv_cache_free_cb_t) image_header_cache_free_cb
});
lv_cache_set_name(img_header_cache_p, CACHE_NAME);
return img_header_cache_p != NULL ? LV_RESULT_OK : LV_RESULT_INVALID;
}
void lv_image_header_cache_resize(uint32_t count, bool evict_now)
{
lv_cache_set_max_size(img_header_cache_p, count, NULL);
if(evict_now) {
lv_cache_reserve(img_header_cache_p, count, NULL);
}
}
void lv_image_header_cache_drop(const void * src)
{
if(src == NULL) {
lv_cache_drop_all(img_header_cache_p, NULL);
return;
}
lv_image_header_cache_data_t search_key = {
.src = src,
.src_type = lv_image_src_get_type(src),
};
lv_cache_drop(img_header_cache_p, &search_key, NULL);
}
bool lv_image_header_cache_is_enabled(void)
{
return lv_cache_is_enabled(img_header_cache_p);
}
lv_iter_t * lv_image_header_cache_iter_create(void)
{
return lv_cache_iter_create(img_header_cache_p);
}
void lv_image_header_cache_dump(void)
{
lv_iter_t * iter = lv_image_cache_iter_create();
if(iter == NULL) return;
LV_LOG_USER("Image cache dump:");
LV_LOG_USER("\tsize\tdata_size\tcf\trc\ttype\tdecoded\t\t\tsrc");
lv_iter_inspect(iter, iter_inspect_cb);
}
/**********************
* STATIC FUNCTIONS
**********************/
inline static lv_cache_compare_res_t image_cache_common_compare(const void * lhs_src, lv_image_src_t lhs_src_type,
const void * rhs_src, lv_image_src_t rhs_src_type)
{
if(lhs_src_type == rhs_src_type) {
if(lhs_src_type == LV_IMAGE_SRC_FILE) {
int32_t cmp_res = lv_strcmp(lhs_src, rhs_src);
if(cmp_res != 0) {
return cmp_res > 0 ? 1 : -1;
}
}
else if(lhs_src_type == LV_IMAGE_SRC_VARIABLE) {
if(lhs_src != rhs_src) {
return lhs_src > rhs_src ? 1 : -1;
}
}
return 0;
}
return lhs_src_type > rhs_src_type ? 1 : -1;
}
static lv_cache_compare_res_t image_header_cache_compare_cb(
const lv_image_header_cache_data_t * lhs,
const lv_image_header_cache_data_t * rhs)
{
return image_cache_common_compare(lhs->src, lhs->src_type, rhs->src, rhs->src_type);
}
static void image_header_cache_free_cb(lv_image_header_cache_data_t * entry, void * user_data)
{
LV_UNUSED(user_data); /*Unused*/
if(entry->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)entry->src);
}
static void iter_inspect_cb(void * elem)
{
lv_image_cache_data_t * data = (lv_image_cache_data_t *)elem;
lv_draw_buf_t * decoded = (lv_draw_buf_t *)data->decoded;
lv_image_header_t * header = &decoded->header;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, img_header_cache_p->node_size);
LV_UNUSED(decoded);
LV_UNUSED(header);
LV_UNUSED(entry);
/* size data_size cf rc type decoded src*/
#define IMAGE_CACHE_DUMP_FORMAT " %4dx%-4d %9"LV_PRIu32" %d %" LV_PRId32 " "
switch(data->src_type) {
case LV_IMAGE_SRC_FILE:
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "file\t%-12p\t%s", header->w, header->h, decoded->data_size, header->cf,
lv_cache_entry_get_ref(entry), (void *)data->decoded, (char *)data->src);
break;
case LV_IMAGE_SRC_VARIABLE:
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "var \t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
break;
default:
LV_LOG_USER(IMAGE_CACHE_DUMP_FORMAT "unkn\t%-12p\t%p", header->w, header->h, decoded->data_size, header->cf,
lv_cache_entry_get_ref(entry), (void *)data->decoded, data->src);
break;
}
}
@@ -0,0 +1,82 @@
/**
* @file lv_image_header_cache.h
*
*/
#ifndef LV_IMAGE_HEADER_CACHE_H
#define LV_IMAGE_HEADER_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize image header cache.
* @param count initial size of the cache in count of image headers.
* @return LV_RESULT_OK: initialization succeeded, LV_RESULT_INVALID: failed.
*/
lv_result_t lv_image_header_cache_init(uint32_t count);
/**
* Resize image header cache.
* If set to 0, the cache is disabled.
* @param count new max count of cached image headers.
* @param evict_now true: evict the image headers should be removed by the eviction policy, false: wait for the next cache cleanup.
*/
void lv_image_header_cache_resize(uint32_t count, bool evict_now);
/**
* Invalidate image header cache. Use NULL to invalidate all image headers.
* It's also automatically called when an image is invalidated.
* @param src pointer to an image source.
*/
void lv_image_header_cache_drop(const void * src);
/**
* Return true if the image header cache is enabled.
* @return true: enabled, false: disabled.
*/
bool lv_image_header_cache_is_enabled(void);
/**
* Create an iterator to iterate over the image header cache.
* @return an iterator to iterate over the image header cache.
*/
lv_iter_t * lv_image_header_cache_iter_create(void);
/**
* Dump the content of the image header cache in a human-readable format with cache order.
*/
void lv_image_header_cache_dump(void);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMAGE_HEADER_CACHE_H*/
+358
View File
@@ -0,0 +1,358 @@
/**
* @file lv_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cache.h"
#include "../../stdlib/lv_sprintf.h"
#include "../lv_assert.h"
#include "lv_cache_entry_private.h"
#include "lv_cache_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data);
static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
size_t node_size, size_t max_size,
lv_cache_ops_t ops)
{
lv_cache_t * cache = cache_class->alloc_cb();
LV_ASSERT_MALLOC(cache);
cache->clz = cache_class;
cache->node_size = node_size;
cache->max_size = max_size;
cache->size = 0;
cache->ops = ops;
if(cache->clz->init_cb(cache) == false) {
LV_LOG_ERROR("Cache init failed");
lv_free(cache);
return NULL;
}
lv_mutex_init(&cache->lock);
return cache;
}
void lv_cache_destroy(lv_cache_t * cache, void * user_data)
{
LV_ASSERT_NULL(cache);
lv_mutex_lock(&cache->lock);
cache->clz->destroy_cb(cache, user_data);
lv_mutex_unlock(&cache->lock);
lv_mutex_delete(&cache->lock);
lv_free(cache);
}
lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
LV_PROFILER_CACHE_BEGIN;
lv_mutex_lock(&cache->lock);
if(cache->size == 0) {
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return NULL;
}
lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
if(entry != NULL) {
lv_cache_entry_acquire_data(entry);
}
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return entry;
}
void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
{
LV_ASSERT_NULL(entry);
LV_PROFILER_CACHE_BEGIN;
lv_mutex_lock(&cache->lock);
lv_cache_entry_release_data(entry, user_data);
if(lv_cache_entry_get_ref(entry) == 0 && lv_cache_entry_is_invalid(entry)) {
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
lv_cache_entry_delete(entry);
}
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
}
lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
LV_PROFILER_CACHE_BEGIN;
lv_mutex_lock(&cache->lock);
if(cache->max_size == 0) {
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return NULL;
}
lv_cache_entry_t * entry = cache_add_internal_no_lock(cache, key, user_data);
if(entry != NULL) {
lv_cache_entry_acquire_data(entry);
}
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return entry;
}
lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
LV_PROFILER_CACHE_BEGIN;
lv_mutex_lock(&cache->lock);
lv_cache_entry_t * entry = NULL;
if(cache->size != 0) {
entry = cache->clz->get_cb(cache, key, user_data);
if(entry != NULL) {
lv_cache_entry_acquire_data(entry);
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return entry;
}
}
if(cache->max_size == 0) {
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return NULL;
}
entry = cache_add_internal_no_lock(cache, key, user_data);
if(entry == NULL) {
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return NULL;
}
bool create_res = cache->ops.create_cb(lv_cache_entry_get_data(entry), user_data);
if(create_res == false) {
cache->clz->remove_cb(cache, entry, user_data);
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
lv_cache_entry_delete(entry);
entry = NULL;
}
else {
lv_cache_entry_acquire_data(entry);
}
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return entry;
}
void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_PROFILER_CACHE_BEGIN;
for(lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data);
reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data))
cache_evict_one_internal_no_lock(cache, user_data);
LV_PROFILER_CACHE_END;
}
void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
LV_PROFILER_CACHE_BEGIN;
lv_mutex_lock(&cache->lock);
cache_drop_internal_no_lock(cache, key, user_data);
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
}
bool lv_cache_evict_one(lv_cache_t * cache, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_PROFILER_CACHE_BEGIN;
lv_mutex_lock(&cache->lock);
bool res = cache_evict_one_internal_no_lock(cache, user_data);
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
return res;
}
void lv_cache_drop_all(lv_cache_t * cache, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_PROFILER_CACHE_BEGIN;
lv_mutex_lock(&cache->lock);
cache->clz->drop_all_cb(cache, user_data);
lv_mutex_unlock(&cache->lock);
LV_PROFILER_CACHE_END;
}
void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data)
{
LV_UNUSED(user_data);
cache->max_size = max_size;
}
size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
return cache->max_size;
}
size_t lv_cache_get_size(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
return cache->size;
}
size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
return cache->max_size - cache->size;
}
bool lv_cache_is_enabled(lv_cache_t * cache)
{
return cache->max_size > 0;
}
void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data)
{
LV_UNUSED(user_data);
cache->ops.compare_cb = compare_cb;
}
void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data)
{
LV_UNUSED(user_data);
cache->ops.create_cb = alloc_cb;
}
void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data)
{
LV_UNUSED(user_data);
cache->ops.free_cb = free_cb;
}
void lv_cache_set_name(lv_cache_t * cache, const char * name)
{
if(cache == NULL) return;
cache->name = name;
}
const char * lv_cache_get_name(lv_cache_t * cache)
{
return cache->name;
}
lv_iter_t * lv_cache_iter_create(lv_cache_t * cache)
{
LV_ASSERT_NULL(cache);
if(cache == NULL || cache->clz->iter_create_cb == NULL) return NULL;
return cache->clz->iter_create_cb(cache);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
{
lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
if(entry == NULL) {
return;
}
if(lv_cache_entry_get_ref(entry) == 0) {
cache->clz->remove_cb(cache, entry, user_data);
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
lv_cache_entry_delete(entry);
}
else {
lv_cache_entry_set_invalid(entry, true);
cache->clz->remove_cb(cache, entry, user_data);
}
}
static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data)
{
lv_cache_entry_t * victim = cache->clz->get_victim_cb(cache, user_data);
if(victim == NULL) {
LV_LOG_ERROR("No victim found");
return false;
}
cache->clz->remove_cb(cache, victim, user_data);
cache->ops.free_cb(lv_cache_entry_get_data(victim), user_data);
lv_cache_entry_delete(victim);
return true;
}
static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
{
lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data);
if(reserve_cond_res == LV_CACHE_RESERVE_COND_TOO_LARGE) {
LV_LOG_ERROR("data %p is too large that exceeds max size (%" LV_PRIu32 ")", key, cache->max_size);
return NULL;
}
for(; reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data))
if(cache_evict_one_internal_no_lock(cache, user_data) == false)
return NULL;
lv_cache_entry_t * entry = cache->clz->add_cb(cache, key, user_data);
return entry;
}
+237
View File
@@ -0,0 +1,237 @@
/**
* @file lv_cache.h
*
*/
#ifndef LV_CACHE_H
#define LV_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_types.h"
#include "lv_cache_entry.h"
#include "class/lv_cache_class.h"
#include "instance/lv_cache_instance.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a cache object with the given parameters.
* @param cache_class The class of the cache. Currently only support one two builtin classes:
* - lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
* - lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy.
* @param node_size The node size is the size of the data stored in the cache..
* @param max_size The max size is the maximum amount of memory or count that the cache can hold.
* - lv_cache_class_lru_rb_count: max_size is the maximum count of nodes in the cache.
* - lv_cache_class_lru_rb_size: max_size is the maximum size of the cache in bytes.
* @param ops A set of operations that can be performed on the cache. See lv_cache_ops_t for details.
* @return Returns a pointer to the created cache object on success, `NULL` on error.
*/
lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
size_t node_size, size_t max_size,
lv_cache_ops_t ops);
/**
* Destroy a cache object.
* @param cache The cache object pointer to destroy.
* @param user_data A user data pointer that will be passed to the free callback.
*/
void lv_cache_destroy(lv_cache_t * cache, void * user_data);
/**
* Acquire a cache entry with the given key. If entry not in cache, it will return `NULL` (not found).
* If the entry is found, it's priority will be changed by the cache's policy. And the `lv_cache_entry_t::ref_cnt` will be incremented.
* @param cache The cache object pointer to acquire the entry.
* @param key The key of the entry to acquire.
* @param user_data A user data pointer that will be passed to the create callback.
* @return Returns a pointer to the acquired cache entry on success with `lv_cache_entry_t::ref_cnt` incremented, `NULL` on error.
*/
lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data);
/**
* Acquire a cache entry with the given key. If the entry is not in the cache, it will create a new entry with the given key.
* If the entry is found, it's priority will be changed by the cache's policy. And the `lv_cache_entry_t::ref_cnt` will be incremented.
* If you want to use this API to simplify the code, you should provide a `lv_cache_ops_t::create_cb` that creates a new entry with the given key.
* This API is a combination of lv_cache_acquire() and lv_cache_add(). The effect is the same as calling lv_cache_acquire() and lv_cache_add() separately.
* And the internal impact on cache is also consistent with these two APIs.
* @param cache The cache object pointer to acquire the entry.
* @param key The key of the entry to acquire or create.
* @param user_data A user data pointer that will be passed to the create callback.
* @return Returns a pointer to the acquired or created cache entry on success with `lv_cache_entry_t::ref_cnt` incremented, `NULL` on error.
*/
lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data);
/**
* Add a new cache entry with the given key and data. If the cache is full, the cache's policy will be used to evict an entry.
* @param cache The cache object pointer to add the entry.
* @param key The key of the entry to add.
* @param user_data A user data pointer that will be passed to the create callback.
* @return Returns a pointer to the added cache entry on success with `lv_cache_entry_t::ref_cnt` incremented, `NULL` on error.
*/
lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data);
/**
* Release a cache entry. The `lv_cache_entry_t::ref_cnt` will be decremented. If the `lv_cache_entry_t::ref_cnt` is zero, it will issue an error.
* If the entry passed to this function is the last reference to the data and the entry is marked as invalid, the cache's policy will be used to evict the entry.
* @param cache The cache object pointer to release the entry.
* @param entry The cache entry pointer to release.
* @param user_data A user data pointer that will be passed to the free callback.
*/
void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
/**
* Reserve a certain amount of memory/count in the cache. This function is useful when you want to reserve a certain amount of memory/count in advance,
* for example, when you know that you will need it later.
* When the current cache size is max than the reserved size, the function will evict entries until the reserved size is reached.
* @param cache The cache object pointer to reserve.
* @param reserved_size The amount of memory/count to reserve.
* @param user_data A user data pointer that will be passed to the free callback.
*/
void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data);
/**
* Drop a cache entry with the given key. If the entry is not in the cache, nothing will happen to it.
* If the entry is found, it will be removed from the cache and its data will be freed when the last reference to it is released.
* @note The data will not be freed immediately but when the last reference to it is released. But this entry will not be found by lv_cache_acquire().
* If you want cache a same key again, you should use lv_cache_add() or lv_cache_acquire_or_create().
* @param cache The cache object pointer to drop the entry.
* @param key The key of the entry to drop.
* @param user_data A user data pointer that will be passed to the free callback.
*/
void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data);
/**
* Drop all cache entries. All entries will be removed from the cache and their data will be freed when the last reference to them is released.
* @note If some entries are still referenced by other objects, it will issue an error. And this case shouldn't happen in normal cases..
* @param cache The cache object pointer to drop all entries.
* @param user_data A user data pointer that will be passed to the free callback.
*/
void lv_cache_drop_all(lv_cache_t * cache, void * user_data);
/**
* Evict one entry from the cache. The eviction policy will be used to select the entry to evict.
* @param cache The cache object pointer to evict an entry.
* @param user_data A user data pointer that will be passed to the free callback.
* @return Returns true if an entry is evicted, false if no entry is evicted.
*/
bool lv_cache_evict_one(lv_cache_t * cache, void * user_data);
/**
* Set the maximum size of the cache.
* If the current cache size is greater than the new maximum size, the cache's policy will be used to evict entries until the new maximum size is reached.
* If set to 0, the cache will be disabled.
* @note But this behavior will happen only new entries are added to the cache.
* @param cache The cache object pointer to set the maximum size.
* @param max_size The new maximum size of the cache.
* @param user_data A user data pointer that will be passed to the free callback.
*/
void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data);
/**
* Get the maximum size of the cache.
* @param cache The cache object pointer to get the maximum size.
* @param user_data A user data pointer that will be passed to the free callback.
* @return Returns the maximum size of the cache.
*/
size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data);
/**
* Get the current size of the cache.
* @param cache The cache object pointer to get the current size.
* @param user_data A user data pointer that will be passed to the free callback.
* @return Returns the current size of the cache.
*/
size_t lv_cache_get_size(lv_cache_t * cache, void * user_data);
/**
* Get the free size of the cache.
* @param cache The cache object pointer to get the free size.
* @param user_data A user data pointer that will be passed to the free callback.
* @return Returns the free size of the cache.
*/
size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data);
/**
* Return true if the cache is enabled.
* Disabled cache means that when the max_size of the cache is 0. In this case, all cache operations will be no-op.
* @param cache The cache object pointer to check if it's disabled.
* @return Returns true if the cache is enabled, false otherwise.
*/
bool lv_cache_is_enabled(lv_cache_t * cache);
/**
* Set the compare callback of the cache.
* @param cache The cache object pointer to set the compare callback.
* @param compare_cb The compare callback to set.
* @param user_data A user data pointer.
*/
void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data);
/**
* Set the create callback of the cache.
* @param cache The cache object pointer to set the create callback.
* @param alloc_cb The create callback to set.
* @param user_data A user data pointer.
*/
void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data);
/**
* Set the free callback of the cache.
* @param cache The cache object pointer to set the free callback.
* @param free_cb The free callback to set.
* @param user_data A user data pointer.
*/
void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data);
/**
* Give a name for a cache object. Only the pointer of the string is saved.
* @param cache The cache object pointer to set the name.
* @param name The name of the cache.
*/
void lv_cache_set_name(lv_cache_t * cache, const char * name);
/**
* Get the name of a cache object.
* @param cache The cache object pointer to get the name.
* @return Returns the name of the cache.
*/
const char * lv_cache_get_name(lv_cache_t * cache);
/**
* Create an iterator for the cache object. The iterator is used to iterate over all cache entries.
* @param cache The cache object pointer to create the iterator.
* @return Returns a pointer to the created iterator on success, `NULL` on error.
*/
lv_iter_t * lv_cache_iter_create(lv_cache_t * cache);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_CACHE_H */
+185
View File
@@ -0,0 +1,185 @@
/**
* @file lv_cache_entry.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cache_entry.h"
#include "../../stdlib/lv_sprintf.h"
#include "../lv_assert.h"
#include "lv_cache.h"
#include "lv_cache_entry_private.h"
#include "lv_cache_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_cache_entry_t {
const lv_cache_t * cache;
int32_t ref_cnt;
uint32_t node_size;
bool is_invalid;
};
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_cache_entry_reset_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
entry->ref_cnt = 0;
}
void lv_cache_entry_inc_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
entry->ref_cnt++;
}
void lv_cache_entry_dec_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
entry->ref_cnt--;
if(entry->ref_cnt < 0) {
LV_LOG_WARN("ref_cnt(%" LV_PRIu32 ") < 0", entry->ref_cnt);
entry->ref_cnt = 0;
}
}
int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return entry->ref_cnt;
}
uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry)
{
return entry->node_size;
}
void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size)
{
LV_ASSERT_NULL(entry);
entry->node_size = node_size;
}
void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid)
{
LV_ASSERT_NULL(entry);
entry->is_invalid = is_invalid;
}
bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return entry->is_invalid;
}
void * lv_cache_entry_get_data(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return (uint8_t *)entry - entry->node_size;
}
void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
lv_cache_entry_inc_ref(entry);
return lv_cache_entry_get_data(entry);
}
void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data)
{
LV_UNUSED(user_data);
LV_ASSERT_NULL(entry);
if(lv_cache_entry_get_ref(entry) == 0) {
LV_LOG_ERROR("ref_cnt(%" LV_PRIu32 ") == 0", entry->ref_cnt);
return;
}
lv_cache_entry_dec_ref(entry);
}
lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size)
{
LV_ASSERT_NULL(data);
return (lv_cache_entry_t *)((uint8_t *)data + node_size);
}
void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache)
{
LV_ASSERT_NULL(entry);
entry->cache = cache;
}
const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return entry->cache;
}
uint32_t lv_cache_entry_get_size(const uint32_t node_size)
{
return node_size + sizeof(lv_cache_entry_t);
}
lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache)
{
void * res = lv_malloc_zeroed(lv_cache_entry_get_size(node_size));
LV_ASSERT_MALLOC(res)
if(res == NULL) {
LV_LOG_ERROR("malloc failed");
return NULL;
}
lv_cache_entry_t * entry = (lv_cache_entry_t *)res;
lv_cache_entry_init(entry, cache, node_size);
return (lv_cache_entry_t *)((uint8_t *)entry + node_size);
}
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size)
{
LV_ASSERT_NULL(entry);
LV_ASSERT_NULL(cache);
entry->cache = cache;
entry->node_size = node_size;
entry->ref_cnt = 0;
entry->is_invalid = false;
}
void lv_cache_entry_delete(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
void * data = lv_cache_entry_get_data(entry);
lv_free(data);
}
/**********************
* STATIC FUNCTIONS
**********************/
+113
View File
@@ -0,0 +1,113 @@
/**
* @file lv_cache_entry.h
*
*/
#ifndef LV_CACHE_ENTRY_H
#define LV_CACHE_ENTRY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the size of a cache entry.
* @param node_size The size of the node in the cache.
* @return The size of the cache entry.
*/
uint32_t lv_cache_entry_get_size(const uint32_t node_size);
/**
* Get the reference count of a cache entry.
* @param entry The cache entry to get the reference count of.
* @return The reference count of the cache entry.
*/
int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry);
/**
* Get the node size of a cache entry. Which is the same size with lv_cache_entry_get_size()'s node_size parameter.
* @param entry The cache entry to get the node size of.
* @return The node size of the cache entry.
*/
uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry);
/**
* Check if a cache entry is invalid.
* @param entry The cache entry to check.
* @return True: the cache entry is invalid. False: the cache entry is valid.
*/
bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry);
/**
* Get the data of a cache entry.
* @param entry The cache entry to get the data of.
* @return The pointer to the data of the cache entry.
*/
void * lv_cache_entry_get_data(lv_cache_entry_t * entry);
/**
* Get the cache instance of a cache entry.
* @param entry The cache entry to get the cache instance of.
* @return The pointer to the cache instance of the cache entry.
*/
const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry);
/**
* Get the cache entry of a data. The data should be allocated by the cache instance.
* @param data The data to get the cache entry of.
* @param node_size The size of the node in the cache.
* @return The pointer to the cache entry of the data.
*/
lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size);
/**
* Allocate a cache entry.
* @param node_size The size of the node in the cache.
* @param cache The cache instance to allocate the cache entry from.
* @return The pointer to the allocated cache entry.
*/
lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache);
/**
* Initialize a cache entry.
* @param entry The cache entry to initialize.
* @param cache The cache instance to allocate the cache entry from.
* @param node_size The size of the node in the cache.
*/
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size);
/**
* Deallocate a cache entry. And the data of the cache entry will be freed.
* @param entry The cache entry to deallocate.
*/
void lv_cache_entry_delete(lv_cache_entry_t * entry);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_ENTRY_H*/
+52
View File
@@ -0,0 +1,52 @@
/**
* @file lv_cache_entry_private.h
*
*/
#ifndef LV_CACHE_ENTRY_PRIVATE_H
#define LV_CACHE_ENTRY_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_types.h"
#include "../../osal/lv_os.h"
#include "../lv_profiler.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_cache_entry_reset_ref(lv_cache_entry_t * entry);
void lv_cache_entry_inc_ref(lv_cache_entry_t * entry);
void lv_cache_entry_dec_ref(lv_cache_entry_t * entry);
void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size);
void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid);
void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache);
void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry);
void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_CACHE_ENTRY_PRIVATE_H */
+199
View File
@@ -0,0 +1,199 @@
/**
* @file lv_cache_private.h
*
*/
#ifndef LV_CACHE_PRIVATE_H
#define LV_CACHE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_types.h"
#include "../../osal/lv_os.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* The result of the cache reserve condition callback
*/
typedef enum {
LV_CACHE_RESERVE_COND_OK, /**< The condition is met and no entries need to be evicted */
LV_CACHE_RESERVE_COND_TOO_LARGE, /**< The condition is not met and the reserve size is too large */
LV_CACHE_RESERVE_COND_NEED_VICTIM, /**< The condition is not met and a victim is needed to be evicted */
LV_CACHE_RESERVE_COND_ERROR /**< An error occurred while checking the condition */
} lv_cache_reserve_cond_res_t;
struct _lv_cache_ops_t;
struct _lv_cache_t;
struct _lv_cache_class_t;
struct _lv_cache_entry_t;
typedef struct _lv_cache_ops_t lv_cache_ops_t;
typedef struct _lv_cache_class_t lv_cache_class_t;
typedef int8_t lv_cache_compare_res_t;
typedef bool (*lv_cache_create_cb_t)(void * node, void * user_data);
typedef void (*lv_cache_free_cb_t)(void * node, void * user_data);
typedef lv_cache_compare_res_t (*lv_cache_compare_cb_t)(const void * a, const void * b);
/**
* The cache instance allocation function, used by the cache class to allocate memory for cache instances.
* @return It should return a pointer to the allocated instance.
*/
typedef void * (*lv_cache_alloc_cb_t)(void);
/**
* The cache instance initialization function, used by the cache class to initialize the cache instance.
* @return It should return true if the initialization is successful, false otherwise.
*/
typedef bool (*lv_cache_init_cb_t)(lv_cache_t * cache);
/**
* The cache instance destruction function, used by the cache class to destroy the cache instance.
*/
typedef void (*lv_cache_destroy_cb_t)(lv_cache_t * cache, void * user_data);
/**
* The cache get function, used by the cache class to get a cache entry by its key.
* @return `NULL` if the key is not found.
*/
typedef lv_cache_entry_t * (*lv_cache_get_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
/**
* The cache add function, used by the cache class to add a cache entry with a given key.
* This function only cares about how to add the entry, it doesn't check if the entry already exists and doesn't care about is it a victim or not.
* @return the added cache entry, or NULL if the entry is not added.
*/
typedef lv_cache_entry_t * (*lv_cache_add_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
/**
* The cache remove function, used by the cache class to remove a cache entry from the cache but doesn't free the memory..
* This function only cares about how to remove the entry, it doesn't care about is it a victim or not.
*/
typedef void (*lv_cache_remove_cb_t)(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
/**
* The cache drop function, used by the cache class to remove a cache entry from the cache and free the memory.
*/
typedef void (*lv_cache_drop_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
/**
* The cache drop all function, used by the cache class to remove all cache entries from the cache and free the memory.
*/
typedef void (*lv_cache_drop_all_cb_t)(lv_cache_t * cache, void * user_data);
/**
* The cache get victim function, used by the cache class to get a victim entry to be evicted.
*/
typedef lv_cache_entry_t * (*lv_cache_get_victim_cb)(lv_cache_t * cache, void * user_data);
/**
* The cache reserve condition function, used by the cache class to check if a new entry can be added to the cache without exceeding its maximum size.
* See lv_cache_reserve_cond_res_t for the possible results.
*/
typedef lv_cache_reserve_cond_res_t (*lv_cache_reserve_cond_cb)(lv_cache_t * cache, const void * key, size_t size,
void * user_data);
/**
* The cache iterator creation function, used by the cache class to create an iterator for the cache.
* @return A pointer to the created iterator, or NULL if the iterator cannot be created.
*/
typedef lv_iter_t * (*lv_cache_iter_create_cb)(lv_cache_t * cache);
/**
* The cache operations struct
*/
struct _lv_cache_ops_t {
lv_cache_compare_cb_t compare_cb; /**< Compare function for keys */
lv_cache_create_cb_t create_cb; /**< Create function for nodes */
lv_cache_free_cb_t free_cb; /**< Free function for nodes */
};
/**
* The cache entry struct
*/
struct _lv_cache_t {
const lv_cache_class_t * clz; /**< Cache class. There are two built-in classes:
* - lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
* - lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy. */
uint32_t node_size; /**< Size of a node */
uint32_t max_size; /**< Maximum size of the cache */
uint32_t size; /**< Current size of the cache */
lv_cache_ops_t ops; /**< Cache operations struct _lv_cache_ops_t */
lv_mutex_t lock; /**< Cache lock used to protect the cache in multithreading environments */
const char * name; /**< Name of the cache */
};
/**
* Cache class struct for building custom cache classes
*
* Examples:
* - lv_cache_class_lru_rb_count for LRU-based cache with count-based eviction policy.
* - lv_cache_class_lru_rb_size for LRU-based cache with size-based eviction policy.
*/
struct _lv_cache_class_t {
lv_cache_alloc_cb_t alloc_cb; /**< The allocation function for cache entries */
lv_cache_init_cb_t init_cb; /**< The initialization function for cache entries */
lv_cache_destroy_cb_t destroy_cb; /**< The destruction function for cache entries */
lv_cache_get_cb_t get_cb; /**< The get function for cache entries */
lv_cache_add_cb_t add_cb; /**< The add function for cache entries */
lv_cache_remove_cb_t remove_cb; /**< The remove function for cache entries */
lv_cache_drop_cb_t drop_cb; /**< The drop function for cache entries */
lv_cache_drop_all_cb_t drop_all_cb; /**< The drop all function for cache entries */
lv_cache_get_victim_cb get_victim_cb; /**< The get victim function for cache entries */
lv_cache_reserve_cond_cb reserve_cond_cb; /**< The reserve condition function for cache entries */
lv_cache_iter_create_cb iter_create_cb; /**< The iterator creation function for cache entries */
};
/*-----------------
* Cache entry slot
*----------------*/
struct _lv_cache_slot_size_t;
typedef struct _lv_cache_slot_size_t lv_cache_slot_size_t;
/**
* Cache entry slot struct
*
* To add new fields to the cache entry, add them to a new struct and add it to the first
* field of the cache data struct. And this one is a size slot for the cache entry.
*/
struct _lv_cache_slot_size_t {
size_t size;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_PRIVATE_H*/