diff --git a/src/misc/cache/lv_cache_entry.c b/src/misc/cache/lv_cache_entry.c index 91b40c75ea..66a9f84a5f 100644 --- a/src/misc/cache/lv_cache_entry.c +++ b/src/misc/cache/lv_cache_entry.c @@ -10,11 +10,14 @@ #include "../../stdlib/lv_sprintf.h" #include "../lv_assert.h" #include "lv_cache_entry_private.h" +#include "../../misc/lv_math.h" /********************* * DEFINES *********************/ +#define LV_CACHE_ENTRY_ALIGN_NODE_SIZE(size) (LV_ALIGN_UP(size, sizeof(void *))) + /********************** * TYPEDEFS **********************/ @@ -87,7 +90,8 @@ bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry) void * lv_cache_entry_get_data(lv_cache_entry_t * entry) { LV_ASSERT_NULL(entry); - return (uint8_t *)entry - entry->node_size; + + return (uint8_t *)entry - LV_CACHE_ENTRY_ALIGN_NODE_SIZE(entry->node_size); } void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry) @@ -114,7 +118,7 @@ void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data) 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); + return (lv_cache_entry_t *)((uint8_t *)data + LV_CACHE_ENTRY_ALIGN_NODE_SIZE(node_size)); } void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache) @@ -131,23 +135,24 @@ const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry) uint32_t lv_cache_entry_get_size(const uint32_t node_size) { - return node_size + sizeof(lv_cache_entry_t); + return LV_CACHE_ENTRY_ALIGN_NODE_SIZE(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) +lv_cache_entry_t * lv_cache_entry_alloc(uint32_t node_size, const lv_cache_t * cache) { + /* Align node_size so that accessing the entry data afterwards is done on an aligned byte address */ void * res = lv_malloc_zeroed(lv_cache_entry_get_size(node_size)); LV_ASSERT_MALLOC(res) - if(res == NULL) { + if(!res) { LV_LOG_ERROR("malloc failed"); return NULL; } - lv_cache_entry_t * entry = (lv_cache_entry_t *)((uint8_t *)res + node_size); + lv_cache_entry_t * entry = (lv_cache_entry_t *)((uint8_t *)res + LV_CACHE_ENTRY_ALIGN_NODE_SIZE(node_size)); lv_cache_entry_init(entry, cache, node_size); return entry; } -void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size) +void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, uint32_t node_size) { LV_ASSERT_NULL(entry); LV_ASSERT_NULL(cache); diff --git a/tests/src/test_cases/cache/test_cache.c b/tests/src/test_cases/cache/test_cache.c index 43c7246b35..2037d935e5 100644 --- a/tests/src/test_cases/cache/test_cache.c +++ b/tests/src/test_cases/cache/test_cache.c @@ -320,5 +320,45 @@ void test_cache_entry_alloc(void) lv_cache_entry_delete(entry); lv_cache_destroy(cache, NULL); } +void test_cache_entry_alloc_unaligned_node_size(void) +{ + const uint32_t unaligned_sizes[] = { 3, 13 }; + for(size_t s = 0; s < sizeof(unaligned_sizes) / sizeof(unaligned_sizes[0]); ++s) { + uint32_t node_size = unaligned_sizes[s]; + + lv_cache_t * cache = create_cache(&lv_cache_class_lru_rb_size, CACHE_SIZE_BYTES); + TEST_ASSERT_NOT_NULL(cache); + + lv_cache_entry_t * entry = lv_cache_entry_alloc(node_size, cache); + TEST_ASSERT_NOT_NULL(entry); + + TEST_ASSERT_EQUAL_MESSAGE( + 0, ((uintptr_t)entry) % sizeof(void *), + "lv_cache_entry_t * must be pointer-aligned regardless of node_size"); + + void * data = lv_cache_entry_get_data(entry); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_EQUAL_MESSAGE( + 0, ((uintptr_t)data) % sizeof(void *), + "lv_cache_entry_get_data() must be pointer-aligned regardless of node_size"); + + lv_cache_entry_t * roundtripped = lv_cache_entry_get_entry(data, node_size); + TEST_ASSERT_EQUAL_PTR_MESSAGE( + entry, roundtripped, + "lv_cache_entry_get_entry(lv_cache_entry_get_data(e), node_size) " + "must recover the original entry pointer"); + + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + node_size, lv_cache_entry_get_node_size(entry), + "get_node_size() must return the original unaligned node_size"); + + TEST_ASSERT_TRUE_MESSAGE( + (uintptr_t)data < (uintptr_t)entry, + "data pointer must lie before the entry header in the allocation block"); + + lv_cache_entry_delete(entry); + lv_cache_destroy(cache, NULL); + } +} #endif