LVGL 库提交
This commit is contained in:
+17
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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*/
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
Vendored
+358
@@ -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;
|
||||
}
|
||||
Vendored
+237
@@ -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
@@ -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
@@ -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*/
|
||||
@@ -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
@@ -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*/
|
||||
Reference in New Issue
Block a user