hekate/bdk/libs/lvgl/lv_misc/lv_mem.c
CTCaer 185526d134 Introducing Bootloader Development Kit (BDK)
BDK will allow developers to use the full collection of drivers,
with limited editing, if any, for making payloads for Nintendo Switch.

Using a single source for everything will also help decoupling
Switch specific code and easily port it to other Tegra X1/X1+ platforms.
And maybe even to lower targets.

Everything is now centrilized into bdk folder.
Every module or project can utilize it by simply including it.

This is just the start and it will continue to improve.
2020-06-14 15:25:21 +03:00

471 lines
12 KiB
C

/*
* Copyright (c) 2019-2020 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_mem.c
* General and portable implementation of malloc and free.
* The dynamic memory monitoring is also supported.
*/
/*********************
* INCLUDES
*********************/
#include "lv_mem.h"
#include "lv_math.h"
#include <string.h>
#include <assert.h>
#if LV_MEM_CUSTOM != 0
#include LV_MEM_CUSTOM_INCLUDE
#endif
/*********************
* DEFINES
*********************/
#define LV_MEM_ADD_JUNK 0 /*Add memory junk on alloc (0xaa) and free(0xbb) (just for testing purposes)*/
#ifdef LV_MEM_ENV64
# define MEM_UNIT uint64_t
#else
# define MEM_UNIT uint32_t
#endif
/**********************
* TYPEDEFS
**********************/
#if LV_ENABLE_GC == 0 /*gc custom allocations must not include header*/
/*The size of this union must be 32 bytes (uint32_t * 8)*/
typedef union {
struct {
MEM_UNIT used: 1; //1: if the entry is used
MEM_UNIT d_size: 31; //Size of the data
};
MEM_UNIT header; //The header (used + d_size)
MEM_UNIT align[8]; //Align header size to MEM_UNIT * 8 bytes
} lv_mem_header_t;
static_assert(sizeof(lv_mem_header_t) == 32, "Node header must be 32 bytes!");
typedef struct {
lv_mem_header_t header;
uint8_t first_data; /*First data byte in the allocated data (Just for easily create a pointer)*/
} lv_mem_ent_t;
#endif /* LV_ENABLE_GC */
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_MEM_CUSTOM == 0
static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e);
static void * ent_alloc(lv_mem_ent_t * e, uint32_t size);
static void ent_trunc(lv_mem_ent_t * e, uint32_t size);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if LV_MEM_CUSTOM == 0
static uint8_t * work_mem;
#endif
static uint32_t zero_mem; /*Give the address of this variable if 0 byte should be allocated*/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initiaiize the dyn_mem module (work memory and other variables)
*/
void lv_mem_init(void)
{
#if LV_MEM_CUSTOM == 0
#if LV_MEM_ADR == 0
/*Allocate a large array to store the dynamically allocated data*/
static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
work_mem = (uint8_t *) work_mem_int;
#else
work_mem = (uint8_t *) LV_MEM_ADR;
#endif
lv_mem_ent_t * full = (lv_mem_ent_t *)work_mem;
full->header.used = 0;
/*The total mem size id reduced by the first header and the close patterns */
full->header.d_size = LV_MEM_SIZE - sizeof(lv_mem_header_t);
#endif
}
/**
* Allocate a memory dynamically
* @param size size of the memory to allocate in bytes
* @return pointer to the allocated memory
*/
void * lv_mem_alloc(uint32_t size)
{
if(size == 0) {
return &zero_mem;
}
/*Round the size to lv_mem_header_t*/
if(size & (sizeof(lv_mem_header_t) - 1)) {
size = size & (~(sizeof(lv_mem_header_t) - 1));
size += sizeof(lv_mem_header_t);
}
void * alloc = NULL;
#if LV_MEM_CUSTOM == 0 /*Use the allocation from dyn_mem*/
lv_mem_ent_t * e = NULL;
//Search for a appropriate entry
do {
//Get the next entry
e = ent_get_next(e);
/*If there is next entry then try to allocate there*/
if(e != NULL) {
alloc = ent_alloc(e, size);
}
//End if there is not next entry OR the alloc. is successful
} while(e != NULL && alloc == NULL);
#else /*Use custom, user defined malloc function*/
#if LV_ENABLE_GC == 1 /*gc must not include header*/
alloc = LV_MEM_CUSTOM_ALLOC(size);
#else /* LV_ENABLE_GC */
/*Allocate a header too to store the size*/
alloc = LV_MEM_CUSTOM_ALLOC(size + sizeof(lv_mem_header_t));
if(alloc != NULL) {
((lv_mem_ent_t *) alloc)->header.d_size = size;
((lv_mem_ent_t *) alloc)->header.used = 1;
alloc = &((lv_mem_ent_t *) alloc)->first_data;
}
#endif /* LV_ENABLE_GC */
#endif /* LV_MEM_CUSTOM */
#if LV_MEM_ADD_JUNK
if(alloc != NULL) memset(alloc, 0xaa, size);
#endif
if(alloc == NULL) LV_LOG_WARN("Couldn't allocate memory");
return alloc;
}
/**
* Free an allocated data
* @param data pointer to an allocated memory
*/
void lv_mem_free(const void * data)
{
if(data == &zero_mem) return;
if(data == NULL) return;
#if LV_MEM_ADD_JUNK
memset((void *)data, 0xbb, lv_mem_get_size(data));
#endif
#if LV_ENABLE_GC==0
/*e points to the header*/
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t));
e->header.used = 0;
#endif
#if LV_MEM_CUSTOM == 0
#if LV_MEM_AUTO_DEFRAG
/* Make a simple defrag.
* Join the following free entries after this*/
lv_mem_ent_t * e_next;
e_next = ent_get_next(e);
while(e_next != NULL) {
if(e_next->header.used == 0) {
e->header.d_size += e_next->header.d_size + sizeof(e->header);
} else {
break;
}
e_next = ent_get_next(e_next);
}
#endif
#else /*Use custom, user defined free function*/
#if LV_ENABLE_GC==0
LV_MEM_CUSTOM_FREE(e);
#else
LV_MEM_CUSTOM_FREE((void*)data);
#endif /*LV_ENABLE_GC*/
#endif
}
/**
* Reallocate a memory with a new size. The old content will be kept.
* @param data pointer to an allocated memory.
* Its content will be copied to the new memory block and freed
* @param new_size the desired new size in byte
* @return pointer to the new memory
*/
#if LV_ENABLE_GC==0
void * lv_mem_realloc(void * data_p, uint32_t new_size)
{
/*Round the size to lv_mem_header_t*/
if(new_size & (sizeof(lv_mem_header_t) - 1)) {
new_size = new_size & (~(sizeof(lv_mem_header_t) - 1));
new_size += sizeof(lv_mem_header_t);
}
/*data_p could be previously freed pointer (in this case it is invalid)*/
if(data_p != NULL) {
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t));
if(e->header.used == 0) {
data_p = NULL;
}
}
uint32_t old_size = lv_mem_get_size(data_p);
if(old_size == new_size) return data_p; /*Also avoid reallocating the same memory*/
#if LV_MEM_CUSTOM == 0
/* Only truncate the memory is possible
* If the 'old_size' was extended by a header size in 'ent_trunc' it avoids reallocating this same memory */
if(new_size < old_size) {
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t));
ent_trunc(e, new_size);
return &e->first_data;
}
#endif
void * new_p;
new_p = lv_mem_alloc(new_size);
if(new_p != NULL && data_p != NULL) {
/*Copy the old data to the new. Use the smaller size*/
if(old_size != 0) {
memcpy(new_p, data_p, LV_MATH_MIN(new_size, old_size));
lv_mem_free(data_p);
}
}
if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory");
return new_p;
}
#else /* LV_ENABLE_GC */
void * lv_mem_realloc(void * data_p, uint32_t new_size)
{
void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size);
if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory");
return new_p;
}
#endif /* lv_enable_gc */
/**
* Join the adjacent free memory blocks
*/
void lv_mem_defrag(void)
{
#if LV_MEM_CUSTOM == 0
lv_mem_ent_t * e_free;
lv_mem_ent_t * e_next;
e_free = ent_get_next(NULL);
while(1) {
/*Search the next free entry*/
while(e_free != NULL) {
if(e_free->header.used != 0) {
e_free = ent_get_next(e_free);
} else {
break;
}
}
if(e_free == NULL) return;
/*Joint the following free entries to the free*/
e_next = ent_get_next(e_free);
while(e_next != NULL) {
if(e_next->header.used == 0) {
e_free->header.d_size += e_next->header.d_size + sizeof(e_next->header);
} else {
break;
}
e_next = ent_get_next(e_next);
}
if(e_next == NULL) return;
/*Continue from the lastly checked entry*/
e_free = e_next;
}
#endif
}
/**
* Give information about the work memory of dynamic allocation
* @param mon_p pointer to a dm_mon_p variable,
* the result of the analysis will be stored here
*/
void lv_mem_monitor(lv_mem_monitor_t * mon_p)
{
/*Init the data*/
memset(mon_p, 0, sizeof(lv_mem_monitor_t));
#if LV_MEM_CUSTOM == 0
lv_mem_ent_t * e;
e = NULL;
e = ent_get_next(e);
while(e != NULL) {
if(e->header.used == 0) {
mon_p->free_cnt++;
mon_p->free_size += e->header.d_size;
if(e->header.d_size > mon_p->free_biggest_size) {
mon_p->free_biggest_size = e->header.d_size;
}
} else {
mon_p->used_cnt++;
}
e = ent_get_next(e);
}
mon_p->total_size = LV_MEM_SIZE;
mon_p->used_pct = 100 - ((uint64_t)100U * mon_p->free_size) / mon_p->total_size;
mon_p->frag_pct = (uint32_t)mon_p->free_biggest_size * 100U / mon_p->free_size;
mon_p->frag_pct = 100 - mon_p->frag_pct;
#endif
}
/**
* Give the size of an allocated memory
* @param data pointer to an allocated memory
* @return the size of data memory in bytes
*/
#if LV_ENABLE_GC==0
uint32_t lv_mem_get_size(const void * data)
{
if(data == NULL) return 0;
if(data == &zero_mem) return 0;
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t));
return e->header.d_size;
}
#else /* LV_ENABLE_GC */
uint32_t lv_mem_get_size(const void * data)
{
return LV_MEM_CUSTOM_GET_SIZE(data);
}
#endif /*LV_ENABLE_GC*/
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_MEM_CUSTOM == 0
/**
* Give the next entry after 'act_e'
* @param act_e pointer to an entry
* @return pointer to an entry after 'act_e'
*/
static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e)
{
lv_mem_ent_t * next_e = NULL;
if(act_e == NULL) { /*NULL means: get the first entry*/
next_e = (lv_mem_ent_t *) work_mem;
} else { /*Get the next entry */
uint8_t * data = &act_e->first_data;
next_e = (lv_mem_ent_t *)&data[act_e->header.d_size];
if(&next_e->first_data >= &work_mem[LV_MEM_SIZE]) next_e = NULL;
}
return next_e;
}
/**
* Try to do the real allocation with a given size
* @param e try to allocate to this entry
* @param size size of the new memory in bytes
* @return pointer to the allocated memory or NULL if not enough memory in the entry
*/
static void * ent_alloc(lv_mem_ent_t * e, uint32_t size)
{
void * alloc = NULL;
/*If the memory is free and big enough then use it */
if(e->header.used == 0 && e->header.d_size >= size) {
/*Truncate the entry to the desired size */
ent_trunc(e, size),
e->header.used = 1;
/*Save the allocated data*/
alloc = &e->first_data;
}
return alloc;
}
/**
* Truncate the data of entry to the given size
* @param e Pointer to an entry
* @param size new size in bytes
*/
static void ent_trunc(lv_mem_ent_t * e, uint32_t size)
{
/*Don't let empty space only for a header without data*/
if(e->header.d_size == size + sizeof(lv_mem_header_t)) {
size = e->header.d_size;
}
/* Create the new entry after the current if there is space for it */
if(e->header.d_size != size) {
uint8_t * e_data = &e->first_data;
lv_mem_ent_t * after_new_e = (lv_mem_ent_t *)&e_data[size];
after_new_e->header.used = 0;
after_new_e->header.d_size = e->header.d_size - size - sizeof(lv_mem_header_t);
}
/* Set the new size for the original entry */
e->header.d_size = size;
}
#endif