tft lvgl test ok

This commit is contained in:
TuxMonkey
2026-07-20 21:59:16 +08:00
parent 8928b7c7db
commit b2169513e1
766 changed files with 443311 additions and 22 deletions

View File

@@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2014 Marco Paland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Original repository: https://github.com/mpaland/printf

View File

@@ -0,0 +1 @@
Released under the BSD license.

View File

@@ -0,0 +1,273 @@
/**
* @file lv_mem_core_builtin.c
*/
/*********************
* INCLUDES
*********************/
#include "../lv_mem.h"
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
#include "lv_tlsf.h"
#include "../lv_string.h"
#include "../../misc/lv_assert.h"
#include "../../misc/lv_log.h"
#include "../../misc/lv_ll.h"
#include "../../misc/lv_math.h"
#include "../../osal/lv_os_private.h"
#include "../../core/lv_global.h"
#ifdef LV_MEM_POOL_INCLUDE
#include LV_MEM_POOL_INCLUDE
#endif
/*********************
* DEFINES
*********************/
/*memset the allocated memories to 0xaa and freed memories to 0xbb (just for testing purposes)*/
#ifndef LV_MEM_ADD_JUNK
#define LV_MEM_ADD_JUNK 0
#endif
#ifdef LV_ARCH_64
#define MEM_UNIT uint64_t
#define ALIGN_MASK 0x7
#else
#define MEM_UNIT uint32_t
#define ALIGN_MASK 0x3
#endif
#define state LV_GLOBAL_DEFAULT()->tlsf_state
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_mem_walker(void * ptr, size_t size, int used, void * user);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_MEM
#define LV_TRACE_MEM(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define LV_TRACE_MEM(...)
#endif
#define _COPY(d, s) *d = *s; d++; s++;
#define _SET(d, v) *d = v; d++;
#define _REPEAT8(expr) expr expr expr expr expr expr expr expr
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_mem_init(void)
{
#if LV_USE_OS
lv_mutex_init(&state.mutex);
#endif
#if LV_MEM_ADR == 0
#ifdef LV_MEM_POOL_ALLOC
state.tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_POOL_ALLOC(LV_MEM_SIZE), LV_MEM_SIZE);
#else
/*Allocate a large array to store the dynamically allocated data*/
static MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)] LV_ATTRIBUTE_LARGE_RAM_ARRAY;
state.tlsf = lv_tlsf_create_with_pool((void *)work_mem_int, LV_MEM_SIZE);
#endif
#else
state.tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_ADR, LV_MEM_SIZE);
#endif
lv_ll_init(&state.pool_ll, sizeof(lv_pool_t));
/*Record the first pool*/
lv_pool_t * pool_p = lv_ll_ins_tail(&state.pool_ll);
LV_ASSERT_MALLOC(pool_p);
*pool_p = lv_tlsf_get_pool(state.tlsf);
#if LV_MEM_ADD_JUNK
LV_LOG_WARN("LV_MEM_ADD_JUNK is enabled which makes LVGL much slower");
#endif
}
void lv_mem_deinit(void)
{
lv_ll_clear(&state.pool_ll);
lv_tlsf_destroy(state.tlsf);
#if LV_USE_OS
lv_mutex_delete(&state.mutex);
#endif
}
lv_mem_pool_t lv_mem_add_pool(void * mem, size_t bytes)
{
lv_mem_pool_t new_pool = lv_tlsf_add_pool(state.tlsf, mem, bytes);
if(!new_pool) {
LV_LOG_WARN("failed to add memory pool, address: %p, size: %zu", mem, bytes);
return NULL;
}
lv_pool_t * pool_p = lv_ll_ins_tail(&state.pool_ll);
LV_ASSERT_MALLOC(pool_p);
*pool_p = new_pool;
return new_pool;
}
void lv_mem_remove_pool(lv_mem_pool_t pool)
{
lv_pool_t * pool_p;
LV_LL_READ(&state.pool_ll, pool_p) {
if(*pool_p == pool) {
lv_ll_remove(&state.pool_ll, pool_p);
lv_free(pool_p);
lv_tlsf_remove_pool(state.tlsf, pool);
return;
}
}
LV_LOG_WARN("invalid pool: %p", pool);
}
void * lv_malloc_core(size_t size)
{
#if LV_USE_OS
lv_mutex_lock(&state.mutex);
#endif
void * p = lv_tlsf_malloc(state.tlsf, size);
if(p) {
state.cur_used += lv_tlsf_block_size(p);
state.max_used = LV_MAX(state.cur_used, state.max_used);
}
#if LV_USE_OS
lv_mutex_unlock(&state.mutex);
#endif
return p;
}
void * lv_realloc_core(void * p, size_t new_size)
{
#if LV_USE_OS
lv_mutex_lock(&state.mutex);
#endif
size_t old_size = lv_tlsf_block_size(p);
void * p_new = lv_tlsf_realloc(state.tlsf, p, new_size);
if(p_new) {
state.cur_used -= old_size;
state.cur_used += lv_tlsf_block_size(p_new);
state.max_used = LV_MAX(state.cur_used, state.max_used);
}
#if LV_USE_OS
lv_mutex_unlock(&state.mutex);
#endif
return p_new;
}
void lv_free_core(void * p)
{
#if LV_USE_OS
lv_mutex_lock(&state.mutex);
#endif
#if LV_MEM_ADD_JUNK
lv_memset(p, 0xbb, lv_tlsf_block_size(p));
#endif
size_t size = lv_tlsf_block_size(p);
lv_tlsf_free(state.tlsf, p);
if(state.cur_used > size) state.cur_used -= size;
else state.cur_used = 0;
#if LV_USE_OS
lv_mutex_unlock(&state.mutex);
#endif
}
void lv_mem_monitor_core(lv_mem_monitor_t * mon_p)
{
/*Init the data*/
lv_memzero(mon_p, sizeof(lv_mem_monitor_t));
LV_TRACE_MEM("begin");
lv_pool_t * pool_p;
LV_LL_READ(&state.pool_ll, pool_p) {
lv_tlsf_walk_pool(*pool_p, lv_mem_walker, mon_p);
}
mon_p->used_pct = 100 - (uint64_t)100U * mon_p->free_size / mon_p->total_size;
if(mon_p->free_size > 0) {
mon_p->frag_pct = (uint64_t)mon_p->free_biggest_size * 100U / mon_p->free_size;
mon_p->frag_pct = 100 - mon_p->frag_pct;
}
else {
mon_p->frag_pct = 0; /*no fragmentation if all the RAM is used*/
}
mon_p->max_used = state.max_used;
LV_TRACE_MEM("finished");
}
lv_result_t lv_mem_test_core(void)
{
#if LV_USE_OS
lv_mutex_lock(&state.mutex);
#endif
if(lv_tlsf_check(state.tlsf)) {
LV_LOG_WARN("failed");
#if LV_USE_OS
lv_mutex_unlock(&state.mutex);
#endif
return LV_RESULT_INVALID;
}
lv_pool_t * pool_p;
LV_LL_READ(&state.pool_ll, pool_p) {
if(lv_tlsf_check_pool(*pool_p)) {
LV_LOG_WARN("pool failed");
#if LV_USE_OS
lv_mutex_unlock(&state.mutex);
#endif
return LV_RESULT_INVALID;
}
}
LV_TRACE_MEM("passed");
#if LV_USE_OS
lv_mutex_unlock(&state.mutex);
#endif
return LV_RESULT_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_mem_walker(void * ptr, size_t size, int used, void * user)
{
LV_UNUSED(ptr);
lv_mem_monitor_t * mon_p = user;
mon_p->total_size += size;
if(used) {
mon_p->used_cnt++;
}
else {
mon_p->free_cnt++;
mon_p->free_size += size;
if(size > mon_p->free_biggest_size)
mon_p->free_biggest_size = size;
}
}
#endif /*LV_STDLIB_BUILTIN*/

View File

@@ -0,0 +1,878 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
// embedded systems with a very limited resources. These routines are thread
// safe and reentrant!
// Use this instead of the bloated standard/newlib printf cause these use
// malloc for printf (and may not be thread safe).
//
///////////////////////////////////////////////////////////////////////////////
/*Original repository: https://github.com/mpaland/printf*/
#include "../../lv_conf_internal.h"
#if LV_USE_STDLIB_SPRINTF == LV_STDLIB_BUILTIN
#include "../lv_sprintf.h"
#include "../lv_string.h"
#include "../../misc/lv_types.h"
#define PRINTF_DISABLE_SUPPORT_FLOAT (!LV_USE_FLOAT)
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
// numeric number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_NTOA_BUFFER_SIZE
#define PRINTF_NTOA_BUFFER_SIZE 32U
#endif
// 'ftoa' conversion buffer size, this must be big enough to hold one converted
// float number including padded zeros (dynamically created on stack)
// default: 32 byte
#ifndef PRINTF_FTOA_BUFFER_SIZE
#define PRINTF_FTOA_BUFFER_SIZE 32U
#endif
// support for the floating point type (%f)
// default: activated
#if !PRINTF_DISABLE_SUPPORT_FLOAT
#define PRINTF_SUPPORT_FLOAT
#endif
// support for exponential floating point notation (%e/%g)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
#define PRINTF_SUPPORT_EXPONENTIAL
#endif
// define the default floating point precision
// default: 6 digits
#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
#endif
// define the largest float suitable to print with %f
// default: 1e9
#ifndef PRINTF_MAX_FLOAT
#define PRINTF_MAX_FLOAT 1e9
#endif
// support for the long long types (%llu or %p)
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
#define PRINTF_SUPPORT_LONG_LONG
#endif
// support for the ptrdiff_t type (%t)
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
// default: activated
#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
#define PRINTF_SUPPORT_PTRDIFF_T
#endif
///////////////////////////////////////////////////////////////////////////////
// internal flag definitions
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_UPPERCASE (1U << 5U)
#define FLAGS_CHAR (1U << 6U)
#define FLAGS_SHORT (1U << 7U)
#define FLAGS_LONG (1U << 8U)
#define FLAGS_LONG_LONG (1U << 9U)
#define FLAGS_PRECISION (1U << 10U)
#define FLAGS_ADAPT_EXP (1U << 11U)
typedef struct {
const char * fmt;
va_list * va;
} lv_vaformat_t;
// import float.h for DBL_MAX
#if defined(PRINTF_SUPPORT_FLOAT)
#include <float.h>
#endif
// output function type
typedef void (*out_fct_type)(char character, void * buffer, size_t idx, size_t maxlen);
// wrapper (used as buffer) for output function type
typedef struct {
void (*fct)(char character, void * arg);
void * arg;
} out_fct_wrap_type;
// internal buffer output
static inline void _out_buffer(char character, void * buffer, size_t idx, size_t maxlen)
{
if(idx < maxlen) {
((char *)buffer)[idx] = character;
}
}
// internal null output
static inline void _out_null(char character, void * buffer, size_t idx, size_t maxlen)
{
LV_UNUSED(character);
LV_UNUSED(buffer);
LV_UNUSED(idx);
LV_UNUSED(maxlen);
}
// internal test if char is a digit (0-9)
// \return true if char is a digit
static inline bool _is_digit(char ch)
{
return (ch >= '0') && (ch <= '9');
}
// internal ASCII string to unsigned int conversion
static unsigned int _atoi(const char ** str)
{
unsigned int i = 0U;
while(_is_digit(**str)) {
i = i * 10U + (unsigned int)(*((*str)++) - '0');
}
return i;
}
// output the specified string in reverse, taking care of any zero-padding
static size_t _out_rev(out_fct_type out, char * buffer, size_t idx, size_t maxlen, const char * buf, size_t len,
unsigned int width, unsigned int flags)
{
const size_t start_idx = idx;
// pad spaces up to given width
if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
size_t i;
for(i = len; i < width; i++) {
out(' ', buffer, idx++, maxlen);
}
}
// reverse string
while(len) {
out(buf[--len], buffer, idx++, maxlen);
}
// append pad spaces up to given width
if(flags & FLAGS_LEFT) {
while(idx - start_idx < width) {
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
// internal itoa format
static size_t _ntoa_format(out_fct_type out, char * buffer, size_t idx, size_t maxlen, char * buf, size_t len,
bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
{
// pad leading zeros
if(!(flags & FLAGS_LEFT)) {
if(width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
// handle hash
if(flags & FLAGS_HASH) {
if(!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
len--;
if(len && (base == 16U)) {
len--;
}
}
if((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'x';
}
else if((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'X';
}
else if((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'b';
}
if(len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
if(len < PRINTF_NTOA_BUFFER_SIZE) {
if(negative) {
buf[len++] = '-';
}
else if(flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
}
else if(flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
// internal itoa for 'long' type
static size_t _ntoa_long(out_fct_type out, char * buffer, size_t idx, size_t maxlen, unsigned long value, bool negative,
unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if(!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if(!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while(value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
// internal itoa for 'long long' type
#if defined(PRINTF_SUPPORT_LONG_LONG)
static size_t _ntoa_long_long(out_fct_type out, char * buffer, size_t idx, size_t maxlen, unsigned long long value,
bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
{
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// no hash for 0 values
if(!value) {
flags &= ~FLAGS_HASH;
}
// write if precision != 0 and value is != 0
if(!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while(value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
#endif // PRINTF_SUPPORT_LONG_LONG
#if defined(PRINTF_SUPPORT_FLOAT)
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
static size_t _etoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
unsigned int width, unsigned int flags);
#endif
// internal ftoa for fixed decimal floating point
static size_t _ftoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
unsigned int width, unsigned int flags)
{
char buf[PRINTF_FTOA_BUFFER_SIZE];
size_t len = 0U;
double diff = 0.0;
// powers of 10
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
// test for special values
if(value != value)
return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
if(value < -DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
if(value > DBL_MAX)
return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width,
flags);
// test for very large values
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
if((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
#else
return 0U;
#endif
}
// test for negative
bool negative = false;
if(value < 0) {
negative = true;
value = 0 - value;
}
// set default precision, if not set explicitly
if(!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
buf[len++] = '0';
prec--;
}
int whole = (int)value;
double tmp = (value - whole) * pow10[prec];
unsigned long frac = (unsigned long)tmp;
diff = tmp - frac;
if(diff > 0.5) {
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if(frac >= pow10[prec]) {
frac = 0;
++whole;
}
}
else if(diff < 0.5) {
}
else if((frac == 0U) || (frac & 1U)) {
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if(prec == 0U) {
diff = value - (double)whole;
if((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
}
else {
unsigned int count = prec;
// now do fractional part, as an unsigned number
while(len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = (char)(48U + (frac % 10U));
if(!(frac /= 10U)) {
break;
}
}
// add extra 0s
while((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
buf[len++] = '0';
}
if(len < PRINTF_FTOA_BUFFER_SIZE) {
// add decimal
buf[len++] = '.';
}
}
// do whole part, number is reversed
while(len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if(!(whole /= 10)) {
break;
}
}
// pad leading zeros
if(!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
if(width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
if(len < PRINTF_FTOA_BUFFER_SIZE) {
if(negative) {
buf[len++] = '-';
}
else if(flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
}
else if(flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
}
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
static size_t _etoa(out_fct_type out, char * buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
unsigned int width, unsigned int flags)
{
// check for NaN and special values
if((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
}
// determine the sign
const bool negative = value < 0;
if(negative) {
value = -value;
}
// default precision
if(!(flags & FLAGS_PRECISION)) {
prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
union {
uint64_t U;
double F;
} conv;
conv.F = value;
int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
exp2 = (int)(expval * 3.321928094887362 + 0.5);
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
const double z2 = z * z;
conv.U = (uint64_t)(exp2 + 1023) << 52U;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if(value < conv.F) {
expval--;
conv.F /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if(flags & FLAGS_ADAPT_EXP) {
// do we want to fall-back to "%f" mode?
if((value >= 1e-4) && (value < 1e6)) {
if((int)prec > expval) {
prec = (unsigned)((int)prec - expval - 1);
}
else {
prec = 0;
}
flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
// no characters in exponent
minwidth = 0U;
expval = 0;
}
else {
// we use one sigfig for the whole part
if((prec > 0) && (flags & FLAGS_PRECISION)) {
--prec;
}
}
}
// will everything fit?
unsigned int fwidth = width;
if(width > minwidth) {
// we didn't fall-back so subtract the characters required for the exponent
fwidth -= minwidth;
}
else {
// not enough characters, so go back to default sizing
fwidth = 0U;
}
if((flags & FLAGS_LEFT) && minwidth) {
// if we're padding on the right, DON'T pad the floating part
fwidth = 0U;
}
// rescale the float value
if(expval) {
value /= conv.F;
}
// output the floating part
const size_t start_idx = idx;
idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
// output the exponent part
if(minwidth) {
// output the exponential symbol
out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
// output the exponent value
idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1,
FLAGS_ZEROPAD | FLAGS_PLUS);
// might need to right-pad spaces
if(flags & FLAGS_LEFT) {
while(idx - start_idx < width) out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
// internal vsnprintf
static int lv_vsnprintf_inner(out_fct_type out, char * buffer, const size_t maxlen, const char * format, va_list va)
{
unsigned int flags, width, precision, n;
size_t idx = 0U;
if(!buffer) {
// use null output function
out = _out_null;
}
while(*format) {
// format specifier? %[flags][width][.precision][length]
if(*format != '%') {
// no
out(*format, buffer, idx++, maxlen);
format++;
continue;
}
else {
// yes, evaluate it
format++;
}
// evaluate flags
flags = 0U;
do {
switch(*format) {
case '0':
flags |= FLAGS_ZEROPAD;
format++;
n = 1U;
break;
case '-':
flags |= FLAGS_LEFT;
format++;
n = 1U;
break;
case '+':
flags |= FLAGS_PLUS;
format++;
n = 1U;
break;
case ' ':
flags |= FLAGS_SPACE;
format++;
n = 1U;
break;
case '#':
flags |= FLAGS_HASH;
format++;
n = 1U;
break;
default :
n = 0U;
break;
}
} while(n);
// evaluate width field
width = 0U;
if(_is_digit(*format)) {
width = _atoi(&format);
}
else if(*format == '*') {
const int w = va_arg(va, int);
if(w < 0) {
flags |= FLAGS_LEFT; // reverse padding
width = (unsigned int) - w;
}
else {
width = (unsigned int)w;
}
format++;
}
// evaluate precision field
precision = 0U;
if(*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if(_is_digit(*format)) {
precision = _atoi(&format);
}
else if(*format == '*') {
const int prec = (int)va_arg(va, int);
precision = prec > 0 ? (unsigned int)prec : 0U;
format++;
}
}
// evaluate length field
switch(*format) {
case 'l' :
flags |= FLAGS_LONG;
format++;
if(*format == 'l') {
flags |= FLAGS_LONG_LONG;
format++;
}
break;
case 'h' :
flags |= FLAGS_SHORT;
format++;
if(*format == 'h') {
flags |= FLAGS_CHAR;
format++;
}
break;
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
case 't' :
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
#endif
case 'j' :
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
case 'z' :
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
default :
break;
}
// evaluate specifier
switch(*format) {
case 'd' :
case 'i' :
case 'u' :
case 'x' :
case 'X' :
case 'p' :
case 'P' :
case 'o' :
case 'b' : {
// set the base
unsigned int base;
if(*format == 'x' || *format == 'X') {
base = 16U;
}
else if(*format == 'p' || *format == 'P') {
base = 16U;
flags |= FLAGS_HASH; // always hash for pointer format
#if defined(PRINTF_SUPPORT_LONG_LONG)
if(sizeof(uintptr_t) == sizeof(long long))
flags |= FLAGS_LONG_LONG;
else
#endif
flags |= FLAGS_LONG;
if(*(format + 1) == 'V')
format++;
}
else if(*format == 'o') {
base = 8U;
}
else if(*format == 'b') {
base = 2U;
}
else {
base = 10U;
flags &= ~FLAGS_HASH; // no hash for dec format
}
// uppercase
if(*format == 'X' || *format == 'P') {
flags |= FLAGS_UPPERCASE;
}
// no plus or space flag for u, x, X, o, b
if((*format != 'i') && (*format != 'd')) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
// ignore '0' flag when precision is given
if(flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
// convert the integer
if((*format == 'i') || (*format == 'd')) {
// signed
if(flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
const long long value = va_arg(va, long long);
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base,
precision, width, flags);
#endif
}
else if(flags & FLAGS_LONG) {
const long value = va_arg(va, long);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision,
width, flags);
}
else {
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va,
int) : va_arg(va, int);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision,
width, flags);
}
}
else if(*format == 'V') {
lv_vaformat_t * vaf = va_arg(va, lv_vaformat_t *);
va_list copy;
va_copy(copy, *vaf->va);
idx += lv_vsnprintf_inner(out, buffer + idx, maxlen - idx, vaf->fmt, copy);
va_end(copy);
}
else {
// unsigned
if(flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
#endif
}
else if(flags & FLAGS_LONG) {
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
}
else {
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va,
unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
}
}
format++;
break;
}
#if defined(PRINTF_SUPPORT_FLOAT)
case 'f' :
case 'F' :
if(*format == 'F') flags |= FLAGS_UPPERCASE;
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#if defined(PRINTF_SUPPORT_EXPONENTIAL)
case 'e':
case 'E':
case 'g':
case 'G':
if((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP;
if((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE;
idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
format++;
break;
#endif // PRINTF_SUPPORT_EXPONENTIAL
#endif // PRINTF_SUPPORT_FLOAT
case 'c' : {
unsigned int l = 1U;
// pre padding
if(!(flags & FLAGS_LEFT)) {
while(l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// char output
out((char)va_arg(va, int), buffer, idx++, maxlen);
// post padding
if(flags & FLAGS_LEFT) {
while(l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 's' : {
const char * p = va_arg(va, char *);
unsigned int l = lv_strnlen(p, precision ? precision : (size_t) -1);
// pre padding
if(flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if(!(flags & FLAGS_LEFT)) {
while(l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// string output
while((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if(flags & FLAGS_LEFT) {
while(l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case '%' :
out('%', buffer, idx++, maxlen);
format++;
break;
default :
out(*format, buffer, idx++, maxlen);
format++;
break;
}
}
// termination
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return (int)idx;
}
///////////////////////////////////////////////////////////////////////////////
/// GLOBAL FUNCTIONS FOR LVGL
///////////////////////////////////////////////////////////////////////////////
int lv_snprintf(char * buffer, size_t count, const char * format, ...)
{
va_list va;
va_start(va, format);
const int ret = lv_vsnprintf_inner(_out_buffer, buffer, count, format, va);
va_end(va);
return ret;
}
int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va)
{
return lv_vsnprintf_inner(_out_buffer, buffer, count, format, va);
}
#endif /*LV_STDLIB_BUILTIN*/

View File

@@ -0,0 +1,319 @@
/**
* @file lv_string_builtin.c
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_STDLIB_STRING == LV_STDLIB_BUILTIN
#include "../../misc/lv_assert.h"
#include "../../misc/lv_log.h"
#include "../../misc/lv_math.h"
#include "../../stdlib/lv_string.h"
#include "../../stdlib/lv_mem.h"
/*********************
* DEFINES
*********************/
#ifdef LV_ARCH_64
#define MEM_UNIT uint64_t
#define ALIGN_MASK 0x7
#else
#define MEM_UNIT uint32_t
#define ALIGN_MASK 0x3
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_MEM
#define LV_TRACE_MEM(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define LV_TRACE_MEM(...)
#endif
#define _COPY(d, s) *d = *s; d++; s++;
#define _SET(d, v) *d = v; d++;
#define _REPEAT8(expr) expr expr expr expr expr expr expr expr
/**********************
* GLOBAL FUNCTIONS
**********************/
void * LV_ATTRIBUTE_FAST_MEM lv_memcpy(void * dst, const void * src, size_t len)
{
volatile uint8_t * d8 = dst;
const uint8_t * s8 = src;
/*Simplify for small memories*/
if(len < 16) {
while(len) {
*d8 = *s8;
d8++;
s8++;
len--;
}
return dst;
}
lv_uintptr_t d_align = (lv_uintptr_t)d8 & ALIGN_MASK;
lv_uintptr_t s_align = (lv_uintptr_t)s8 & ALIGN_MASK;
/*Byte copy for unaligned memories*/
if(s_align != d_align) {
while(len > 32) {
_REPEAT8(_COPY(d8, s8));
_REPEAT8(_COPY(d8, s8));
_REPEAT8(_COPY(d8, s8));
_REPEAT8(_COPY(d8, s8));
len -= 32;
}
while(len) {
_COPY(d8, s8)
len--;
}
return dst;
}
/*Make the memories aligned*/
if(d_align) {
d_align = ALIGN_MASK + 1 - d_align;
while(d_align && len) {
_COPY(d8, s8);
d_align--;
len--;
}
}
uint32_t * d32 = (uint32_t *)d8;
const uint32_t * s32 = (uint32_t *)s8;
while(len > 32) {
_REPEAT8(_COPY(d32, s32))
len -= 32;
}
d8 = (uint8_t *)d32;
s8 = (const uint8_t *)s32;
while(len) {
_COPY(d8, s8)
len--;
}
return dst;
}
void LV_ATTRIBUTE_FAST_MEM lv_memset(void * dst, uint8_t v, size_t len)
{
uint8_t * d8 = (uint8_t *)dst;
uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
/*Make the address aligned*/
if(d_align) {
d_align = ALIGN_MASK + 1 - d_align;
while(d_align && len) {
_SET(d8, v);
len--;
d_align--;
}
}
uint32_t v32 = (uint32_t)v + ((uint32_t)v << 8) + ((uint32_t)v << 16) + ((uint32_t)v << 24);
uint32_t * d32 = (uint32_t *)d8;
while(len > 32) {
_REPEAT8(_SET(d32, v32));
len -= 32;
}
d8 = (uint8_t *)d32;
while(len) {
_SET(d8, v);
len--;
}
}
void * LV_ATTRIBUTE_FAST_MEM lv_memmove(void * dst, const void * src, size_t len)
{
if(dst < src || (char *)dst > ((char *)src + len)) {
return lv_memcpy(dst, src, len);
}
if(dst > src) {
char * tmp = (char *)dst + len - 1;
char * s = (char *)src + len - 1;
while(len--) {
*tmp-- = *s--;
}
}
else {
char * tmp = (char *)dst;
char * s = (char *)src;
while(len--) {
*tmp++ = *s++;
}
}
return dst;
}
int lv_memcmp(const void * p1, const void * p2, size_t len)
{
const char * s1 = (const char *) p1;
const char * s2 = (const char *) p2;
while(--len > 0 && (*s1 == *s2)) {
s1++;
s2++;
}
return *s1 - *s2;
}
/* See https://en.cppreference.com/w/c/string/byte/strlen for reference */
size_t lv_strlen(const char * str)
{
size_t i = 0;
while(str[i]) i++;
return i;
}
size_t lv_strnlen(const char * str, size_t max_len)
{
size_t i = 0;
while(i < max_len && str[i]) i++;
return i;
}
size_t lv_strlcpy(char * dst, const char * src, size_t dst_size)
{
size_t i = 0;
if(dst_size > 0) {
for(; i < dst_size - 1 && src[i]; i++) {
dst[i] = src[i];
}
dst[i] = '\0';
}
while(src[i]) i++;
return i;
}
char * lv_strncpy(char * dst, const char * src, size_t dst_size)
{
size_t i;
for(i = 0; i < dst_size && src[i]; i++) {
dst[i] = src[i];
}
for(; i < dst_size; i++) {
dst[i] = '\0';
}
return dst;
}
char * lv_strcpy(char * dst, const char * src)
{
char * tmp = dst;
while((*dst++ = *src++) != '\0');
return tmp;
}
int lv_strcmp(const char * s1, const char * s2)
{
while(*s1 && (*s1 == *s2)) {
s1++;
s2++;
}
return *(const unsigned char *)s1 - *(const unsigned char *)s2;
}
int lv_strncmp(const char * s1, const char * s2, size_t len)
{
if(len == 0) {
return 0;
}
while(len > 0 && *s1 && (*s1 == *s2)) {
if(--len == 0) {
return 0;
}
s1++;
s2++;
}
return *(const unsigned char *)s1 - *(const unsigned char *)s2;
}
char * lv_strdup(const char * src)
{
size_t len = lv_strlen(src) + 1;
char * dst = lv_malloc(len);
if(dst == NULL) return NULL;
lv_memcpy(dst, src, len); /*memcpy is faster than strncpy when length is known*/
return dst;
}
char * lv_strndup(const char * src, size_t max_len)
{
size_t len = lv_strnlen(src, max_len);
char * dst = lv_malloc(len + 1);
if(dst == NULL) return NULL;
lv_memcpy(dst, src, len);
dst[len] = '\0';
return dst;
}
char * lv_strcat(char * dst, const char * src)
{
lv_strcpy(dst + lv_strlen(dst), src);
return dst;
}
char * lv_strncat(char * dst, const char * src, size_t src_len)
{
char * tmp = dst;
while(*dst != '\0') {
dst++;
}
while(src_len != 0 && *src != '\0') {
src_len--;
*dst++ = *src++;
}
*dst = '\0';
return tmp;
}
char * lv_strchr(const char * s, int c)
{
for(; ; s++) {
if(*s == c) {
return (char *)s;
}
if(*s == '\0') {
break;
}
}
return NULL;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_STDLIB_BUILTIN*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
#include "../../lv_conf_internal.h"
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
#ifndef LV_TLSF_H
#define LV_TLSF_H
/*
** Two Level Segregated Fit memory allocator, version 3.1.
** Written by Matthew Conte
** http://tlsf.baisoku.org
**
** Based on the original documentation by Miguel Masmano:
** http://www.gii.upv.es/tlsf/main/docs
**
** This implementation was written to the specification
** of the document, therefore no GPL restrictions apply.
**
** Copyright (c) 2006-2016, Matthew Conte
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the copyright holder nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "../../misc/lv_ll.h"
#include "../../misc/lv_types.h"
#if defined(__cplusplus)
extern "C" {
#endif
/* lv_tlsf_t: a TLSF structure. Can contain 1 to N pools. */
/* lv_pool_t: a block of memory that TLSF can manage. */
typedef void * lv_tlsf_t;
typedef void * lv_pool_t;
/* Create/destroy a memory pool. */
lv_tlsf_t lv_tlsf_create(void * mem);
lv_tlsf_t lv_tlsf_create_with_pool(void * mem, size_t bytes);
void lv_tlsf_destroy(lv_tlsf_t tlsf);
lv_pool_t lv_tlsf_get_pool(lv_tlsf_t tlsf);
/* Add/remove memory pools. */
lv_pool_t lv_tlsf_add_pool(lv_tlsf_t tlsf, void * mem, size_t bytes);
void lv_tlsf_remove_pool(lv_tlsf_t tlsf, lv_pool_t pool);
/* malloc/memalign/realloc/free replacements. */
void * lv_tlsf_malloc(lv_tlsf_t tlsf, size_t bytes);
void * lv_tlsf_memalign(lv_tlsf_t tlsf, size_t align, size_t bytes);
void * lv_tlsf_realloc(lv_tlsf_t tlsf, void * ptr, size_t size);
size_t lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr);
/* Returns internal block size, not original request size */
size_t lv_tlsf_block_size(void * ptr);
/* Overheads/limits of internal structures. */
size_t lv_tlsf_size(void);
size_t lv_tlsf_align_size(void);
size_t lv_tlsf_block_size_min(void);
size_t lv_tlsf_block_size_max(void);
size_t lv_tlsf_pool_overhead(void);
size_t lv_tlsf_alloc_overhead(void);
/* Debugging. */
typedef void (*lv_tlsf_walker)(void * ptr, size_t size, int used, void * user);
void lv_tlsf_walk_pool(lv_pool_t pool, lv_tlsf_walker walker, void * user);
/* Returns nonzero if any internal consistency check fails. */
int lv_tlsf_check(lv_tlsf_t tlsf);
int lv_tlsf_check_pool(lv_pool_t pool);
#if defined(__cplusplus)
};
#endif
#endif /*LV_TLSF_H*/
#endif /*LV_STDLIB_BUILTIN*/

View File

@@ -0,0 +1,54 @@
/**
* @file lv_tlsf_private.h
*
*/
#ifndef LV_TLSF_PRIVATE_H
#define LV_TLSF_PRIVATE_H
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_tlsf.h"
#include "../../osal/lv_os_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
#if LV_USE_OS
lv_mutex_t mutex;
#endif
lv_tlsf_t tlsf;
size_t cur_used;
size_t max_used;
lv_ll_t pool_ll;
} lv_tlsf_state_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN*/
#endif /*LV_TLSF_PRIVATE_H*/

View File

@@ -0,0 +1,94 @@
/**
* @file lv_mem_core_clib.c
*/
/*********************
* INCLUDES
*********************/
#include "../lv_mem.h"
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_CLIB
#include "../../stdlib/lv_mem.h"
#include <stdlib.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_mem_init(void)
{
return; /*Nothing to init*/
}
void lv_mem_deinit(void)
{
return; /*Nothing to deinit*/
}
lv_mem_pool_t lv_mem_add_pool(void * mem, size_t bytes)
{
/*Not supported*/
LV_UNUSED(mem);
LV_UNUSED(bytes);
return NULL;
}
void lv_mem_remove_pool(lv_mem_pool_t pool)
{
/*Not supported*/
LV_UNUSED(pool);
return;
}
void * lv_malloc_core(size_t size)
{
return malloc(size);
}
void * lv_realloc_core(void * p, size_t new_size)
{
return realloc(p, new_size);
}
void lv_free_core(void * p)
{
free(p);
}
void lv_mem_monitor_core(lv_mem_monitor_t * mon_p)
{
/*Not supported*/
LV_UNUSED(mon_p);
return;
}
lv_result_t lv_mem_test_core(void)
{
/*Not supported*/
return LV_RESULT_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_STDLIB_CLIB*/

View File

@@ -0,0 +1,58 @@
/**
* @file lv_sprintf_clib.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_STDLIB_SPRINTF == LV_STDLIB_CLIB
#include <stdio.h>
#include <stdarg.h>
#include "../lv_sprintf.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
int lv_snprintf(char * buffer, size_t count, const char * format, ...)
{
va_list va;
va_start(va, format);
const int ret = vsnprintf(buffer, count, format, va);
va_end(va);
return ret;
}
int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va)
{
return vsnprintf(buffer, count, format, va);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif

View File

@@ -0,0 +1,140 @@
/**
* @file lv_string_clib.c
*/
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_STDLIB_STRING == LV_STDLIB_CLIB
#include "../lv_string.h"
#include "../lv_mem.h" /*Need lv_malloc*/
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void * LV_ATTRIBUTE_FAST_MEM lv_memcpy(void * dst, const void * src, size_t len)
{
return memcpy(dst, src, len);
}
void LV_ATTRIBUTE_FAST_MEM lv_memset(void * dst, uint8_t v, size_t len)
{
memset(dst, v, len);
}
void * LV_ATTRIBUTE_FAST_MEM lv_memmove(void * dst, const void * src, size_t len)
{
return memmove(dst, src, len);
}
int lv_memcmp(const void * p1, const void * p2, size_t len)
{
return memcmp(p1, p2, len);
}
size_t lv_strlen(const char * str)
{
return strlen(str);
}
size_t lv_strnlen(const char * str, size_t max_len)
{
return strnlen(str, max_len);
}
size_t lv_strlcpy(char * dst, const char * src, size_t dst_size)
{
size_t src_len = strlen(src);
if(dst_size > 0) {
size_t copy_size = src_len < dst_size ? src_len : dst_size - 1;
memcpy(dst, src, copy_size);
dst[copy_size] = '\0';
}
return src_len;
}
char * lv_strncpy(char * dst, const char * src, size_t dest_size)
{
return strncpy(dst, src, dest_size);
}
char * lv_strcpy(char * dst, const char * src)
{
return strcpy(dst, src);
}
int lv_strcmp(const char * s1, const char * s2)
{
return strcmp(s1, s2);
}
int lv_strncmp(const char * s1, const char * s2, size_t len)
{
return strncmp(s1, s2, len);
}
char * lv_strdup(const char * src)
{
/*strdup uses malloc, so use the lv_malloc when LV_USE_STDLIB_MALLOC is not LV_STDLIB_CLIB */
size_t len = lv_strlen(src) + 1;
char * dst = lv_malloc(len);
if(dst == NULL) return NULL;
lv_memcpy(dst, src, len); /*do memcpy is faster than strncpy when length is known*/
return dst;
}
char * lv_strndup(const char * src, size_t max_len)
{
size_t len = lv_strnlen(src, max_len);
char * dst = lv_malloc(len + 1);
if(dst == NULL) return NULL;
lv_memcpy(dst, src, len);
dst[len] = '\0';
return dst;
}
char * lv_strcat(char * dst, const char * src)
{
return strcat(dst, src);
}
char * lv_strncat(char * dst, const char * src, size_t src_len)
{
return strncat(dst, src, src_len);
}
char * lv_strchr(const char * str, int c)
{
return strchr(str, c);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_STDLIB_CLIB*/

View File

@@ -0,0 +1,188 @@
/**
* @file lv_mem.c
*/
/*********************
* INCLUDES
*********************/
#include "lv_mem_private.h"
#include "lv_string.h"
#include "../misc/lv_assert.h"
#include "../misc/lv_log.h"
#include "../core/lv_global.h"
#if LV_USE_OS == LV_OS_PTHREAD
#include <pthread.h>
#endif
/*********************
* DEFINES
*********************/
/*memset the allocated memories to 0xaa and freed memories to 0xbb (just for testing purposes)*/
#ifndef LV_MEM_ADD_JUNK
#define LV_MEM_ADD_JUNK 0
#endif
#define zero_mem LV_GLOBAL_DEFAULT()->memory_zero
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void * lv_malloc_core(size_t size);
void * lv_realloc_core(void * p, size_t new_size);
void lv_free_core(void * p);
void lv_mem_monitor_core(lv_mem_monitor_t * mon_p);
lv_result_t lv_mem_test_core(void);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_MEM
#define LV_TRACE_MEM(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define LV_TRACE_MEM(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
void * lv_malloc(size_t size)
{
LV_TRACE_MEM("allocating %lu bytes", (unsigned long)size);
if(size == 0) {
LV_TRACE_MEM("using zero_mem");
return &zero_mem;
}
void * alloc = lv_malloc_core(size);
if(alloc == NULL) {
LV_LOG_INFO("couldn't allocate memory (%lu bytes)", (unsigned long)size);
#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
LV_LOG_INFO("used: %zu (%3d %%), frag: %3d %%, biggest free: %zu",
mon.total_size - mon.free_size, mon.used_pct, mon.frag_pct,
mon.free_biggest_size);
#endif
return NULL;
}
#if LV_MEM_ADD_JUNK
lv_memset(alloc, 0xaa, size);
#endif
LV_TRACE_MEM("allocated at %p", alloc);
return alloc;
}
void * lv_malloc_zeroed(size_t size)
{
LV_TRACE_MEM("allocating %lu bytes", (unsigned long)size);
if(size == 0) {
LV_TRACE_MEM("using zero_mem");
return &zero_mem;
}
void * alloc = lv_malloc_core(size);
if(alloc == NULL) {
LV_LOG_INFO("couldn't allocate memory (%lu bytes)", (unsigned long)size);
#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
LV_LOG_INFO("used: %zu (%3d %%), frag: %3d %%, biggest free: %zu",
mon.total_size - mon.free_size, mon.used_pct, mon.frag_pct,
mon.free_biggest_size);
#endif
return NULL;
}
lv_memzero(alloc, size);
LV_TRACE_MEM("allocated at %p", alloc);
return alloc;
}
void * lv_calloc(size_t num, size_t size)
{
LV_TRACE_MEM("allocating number of %zu each %zu bytes", num, size);
return lv_malloc_zeroed(num * size);
}
void * lv_zalloc(size_t size)
{
return lv_malloc_zeroed(size);
}
void lv_free(void * data)
{
LV_TRACE_MEM("freeing %p", data);
if(data == &zero_mem) return;
if(data == NULL) return;
lv_free_core(data);
}
void * lv_reallocf(void * data_p, size_t new_size)
{
void * new = lv_realloc(data_p, new_size);
if(!new) {
lv_free(data_p);
}
return new;
}
void * lv_realloc(void * data_p, size_t new_size)
{
LV_TRACE_MEM("reallocating %p with %lu size", data_p, (unsigned long)new_size);
if(new_size == 0) {
LV_TRACE_MEM("using zero_mem");
lv_free(data_p);
return &zero_mem;
}
if(data_p == &zero_mem) return lv_malloc(new_size);
void * new_p = lv_realloc_core(data_p, new_size);
if(new_p == NULL) {
LV_LOG_ERROR("couldn't reallocate memory");
return NULL;
}
LV_TRACE_MEM("reallocated at %p", new_p);
return new_p;
}
lv_result_t lv_mem_test(void)
{
if(zero_mem != ZERO_MEM_SENTINEL) {
LV_LOG_WARN("zero_mem is written");
return LV_RESULT_INVALID;
}
return lv_mem_test_core();
}
void lv_mem_monitor(lv_mem_monitor_t * mon_p)
{
lv_memzero(mon_p, sizeof(lv_mem_monitor_t));
lv_mem_monitor_core(mon_p);
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,166 @@
/**
* @file lv_mem.h
*
*/
#ifndef LV_MEM_H
#define LV_MEM_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "lv_string.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef void * lv_mem_pool_t;
/**
* Heap information structure.
*/
typedef struct {
size_t total_size; /**< Total heap size */
size_t free_cnt;
size_t free_size; /**< Size of available memory */
size_t free_biggest_size;
size_t used_cnt;
size_t max_used; /**< Max size of Heap memory used */
uint8_t used_pct; /**< Percentage used */
uint8_t frag_pct; /**< Amount of fragmentation */
} lv_mem_monitor_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize to use malloc/free/realloc etc
*/
void lv_mem_init(void);
/**
* Drop all dynamically allocated memory and reset the memory pools' state
*/
void lv_mem_deinit(void);
lv_mem_pool_t lv_mem_add_pool(void * mem, size_t bytes);
void lv_mem_remove_pool(lv_mem_pool_t pool);
/**
* Allocate memory dynamically
* @param size requested size in bytes
* @return pointer to allocated uninitialized memory, or NULL on failure
*/
void * lv_malloc(size_t size);
/**
* Allocate a block of zeroed memory dynamically
* @param num requested number of element to be allocated.
* @param size requested size of each element in bytes.
* @return pointer to allocated zeroed memory, or NULL on failure
*/
void * lv_calloc(size_t num, size_t size);
/**
* Allocate zeroed memory dynamically
* @param size requested size in bytes
* @return pointer to allocated zeroed memory, or NULL on failure
*/
void * lv_zalloc(size_t size);
/**
* Allocate zeroed memory dynamically
* @param size requested size in bytes
* @return pointer to allocated zeroed memory, or NULL on failure
*/
void * lv_malloc_zeroed(size_t size);
/**
* Free an allocated data
* @param data pointer to an allocated memory
*/
void lv_free(void * data);
/**
* Reallocate a memory with a new size. The old content will be kept.
* @param data_p 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, NULL on failure
*/
void * lv_realloc(void * data_p, size_t new_size);
/**
* Reallocate a memory with a new size. The old content will be kept.
* In case of failure, the old pointer is free'd.
* @param data_p 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, NULL on failure
*/
void * lv_reallocf(void * data_p, size_t new_size);
/**
* Used internally to execute a plain `malloc` operation
* @param size size in bytes to `malloc`
*/
void * lv_malloc_core(size_t size);
/**
* Used internally to execute a plain `free` operation
* @param p memory address to free
*/
void lv_free_core(void * p);
/**
* Used internally to execute a plain realloc operation
* @param p memory address to realloc
* @param new_size size in bytes to realloc
*/
void * lv_realloc_core(void * p, size_t new_size);
/**
* Used internally by lv_mem_monitor() to gather LVGL heap state information.
* @param mon_p pointer to lv_mem_monitor_t object to be populated.
*/
void lv_mem_monitor_core(lv_mem_monitor_t * mon_p);
lv_result_t lv_mem_test_core(void);
/**
* @brief Tests the memory allocation system by allocating and freeing a block of memory.
* @return LV_RESULT_OK if the memory allocation system is working properly, or LV_RESULT_INVALID if there is an error.
*/
lv_result_t lv_mem_test(void);
/**
* Give information about the work memory of dynamic allocation
* @param mon_p pointer to a lv_mem_monitor_t variable,
* the result of the analysis will be stored here
*/
void lv_mem_monitor(lv_mem_monitor_t * mon_p);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MEM_H*/

View File

@@ -0,0 +1,39 @@
/**
* @file lv_mem_private.h
*
*/
#ifndef LV_MEM_PRIVATE_H
#define LV_MEM_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_mem.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MEM_PRIVATE_H*/

View File

@@ -0,0 +1,60 @@
/**
* @file lv_sprintf.h
*
*/
#ifndef LV_SPRINTF_H
#define LV_SPRINTF_H
#if defined(__has_include)
#if __has_include(LV_INTTYPES_INCLUDE)
#include LV_INTTYPES_INCLUDE
/* platform-specific printf format for int32_t, usually "d" or "ld" */
#define LV_PRId32 PRId32
#define LV_PRIu32 PRIu32
#define LV_PRIx32 PRIx32
#define LV_PRIX32 PRIX32
#define LV_PRId64 PRId64
#define LV_PRIu64 PRIu64
#define LV_PRIx64 PRIx64
#define LV_PRIX64 PRIX64
#else
#define LV_PRId32 "d"
#define LV_PRIu32 "u"
#define LV_PRIx32 "x"
#define LV_PRIX32 "X"
#define LV_PRId64 "lld"
#define LV_PRIu64 "llu"
#define LV_PRIx64 "llx"
#define LV_PRIX64 "llX"
#endif
#else
/* hope this is correct for ports without __has_include or without inttypes.h */
#define LV_PRId32 "d"
#define LV_PRIu32 "u"
#define LV_PRIx32 "x"
#define LV_PRIX32 "X"
#define LV_PRId64 "lld"
#define LV_PRIu64 "llu"
#define LV_PRIx64 "llx"
#define LV_PRIX64 "llX"
#endif
#include "../misc/lv_types.h"
#ifdef __cplusplus
extern "C" {
#endif
int lv_snprintf(char * buffer, size_t count, const char * format, ...);
int lv_vsnprintf(char * buffer, size_t count, const char * format, va_list va);
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_SPRINTF_H */

View File

@@ -0,0 +1,204 @@
/**
* @file lv_string.h
*
*/
#ifndef LV_STRING_H
#define LV_STRING_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Copies a block of memory from a source address to a destination address.
* @param dst Pointer to the destination array where the content is to be copied.
* @param src Pointer to the source of data to be copied.
* @param len Number of bytes to copy.
* @return Pointer to the destination array.
* @note The function does not check for any overlapping of the source and destination memory blocks.
*/
void * lv_memcpy(void * dst, const void * src, size_t len);
/**
* @brief Fills a block of memory with a specified value.
* @param dst Pointer to the destination array to fill with the specified value.
* @param v Value to be set. The value is passed as an int, but the function fills
* the block of memory using the unsigned char conversion of this value.
* @param len Number of bytes to be set to the value.
*/
void lv_memset(void * dst, uint8_t v, size_t len);
/**
* @brief Move a block of memory from source to destination
* @param dst Pointer to the destination array where the content is to be copied.
* @param src Pointer to the source of data to be copied.
* @param len Number of bytes to copy
* @return Pointer to the destination array.
*/
void * lv_memmove(void * dst, const void * src, size_t len);
/**
* @brief This function will compare two memory blocks
* @param p1 Pointer to the first memory block
* @param p2 Pointer to the second memory block
* @param len Number of bytes to compare
* @return The difference between the value of the first unmatching byte.
*/
int lv_memcmp(const void * p1, const void * p2, size_t len);
/**
* Same as `memset(dst, 0x00, len)`.
* @param dst pointer to the destination buffer
* @param len number of byte to set
*/
static inline void lv_memzero(void * dst, size_t len)
{
lv_memset(dst, 0x00, len);
}
/**
* @brief Computes the length of the string str up to (but not including) the terminating null character.
* @param str Pointer to the null-terminated byte string to be examined.
* @return The length of the string in bytes.
*/
size_t lv_strlen(const char * str);
/**
* @brief Computes the length of the string str up to (but not including) the terminating null character,
* or the given maximum length.
* @param str Pointer to byte string that is null-terminated or at least max_len bytes long.
* @param max_len Maximum number of characters to examine.
* @return The length of the string in bytes.
*/
size_t lv_strnlen(const char * str, size_t max_len);
/**
* @brief Copies up to dst_size-1 (non-null) characters from src to dst. A null terminator is always added.
* @param dst Pointer to the destination array where the content is to be copied.
* @param src Pointer to the source of data to be copied.
* @param dst_size Maximum number of characters to be copied to dst, including the null character.
* @return The length of src. The return value is equivalent to the value returned by lv_strlen(src)
*/
size_t lv_strlcpy(char * dst, const char * src, size_t dst_size);
/**
* @brief Copies up to dest_size characters from the string pointed to by src to the character array pointed to by dst
* and fills the remaining length with null bytes.
* @param dst Pointer to the destination array where the content is to be copied.
* @param src Pointer to the source of data to be copied.
* @param dest_size Maximum number of characters to be copied to dst.
* @return A pointer to the destination array, which is dst.
* @note dst will not be null terminated if dest_size bytes were copied from src before the end of src was reached.
*/
char * lv_strncpy(char * dst, const char * src, size_t dest_size);
/**
* @brief Copies the string pointed to by src, including the terminating null character,
* to the character array pointed to by dst.
* @param dst Pointer to the destination array where the content is to be copied.
* @param src Pointer to the source of data to be copied.
* @return A pointer to the destination array, which is dst.
*/
char * lv_strcpy(char * dst, const char * src);
/**
* @brief This function will compare two strings without specified length.
* @param s1 pointer to the first string
* @param s2 pointer to the second string
* @return the difference between the value of the first unmatching character.
*/
int lv_strcmp(const char * s1, const char * s2);
/**
* @brief This function will compare two strings up to the given length.
* @param s1 pointer to the first string
* @param s2 pointer to the second string
* @param len the maximum amount of characters to compare
* @return the difference between the value of the first unmatching character.
*/
int lv_strncmp(const char * s1, const char * s2, size_t len);
/** Returns true if the two strings are equal.
* Just a wrapper around strcmp for convenience.
* @param s1 pointer to the first string
* @param s2 pointer to the second string
* @return true: the strings are equal; false: otherwise
*/
static inline bool lv_streq(const char * s1, const char * s2)
{
return lv_strcmp(s1, s2) == 0;
}
/**
* @brief Duplicate a string by allocating a new one and copying the content.
* @param src Pointer to the source of data to be copied.
* @return A pointer to the new allocated string. NULL if failed.
*/
char * lv_strdup(const char * src);
/**
* @brief Duplicate a string by allocating a new one and copying the content
* up to the end or the specified maximum length, whichever comes first.
* @param src Pointer to the source of data to be copied.
* @param max_len Maximum number of characters to be copied.
* @return Pointer to a newly allocated null-terminated string. NULL if failed.
*/
char * lv_strndup(const char * src, size_t max_len);
/**
* @brief Copies the string pointed to by src, including the terminating null character,
* to the end of the string pointed to by dst.
* @param dst Pointer to the destination string where the content is to be appended.
* @param src Pointer to the source of data to be copied.
* @return A pointer to the destination string, which is dst.
*/
char * lv_strcat(char * dst, const char * src);
/**
* @brief Copies up to src_len characters from the string pointed to by src
* to the end of the string pointed to by dst.
* A terminating null character is appended to dst even if no null character
* was encountered in src after src_len characters were copied.
* @param dst Pointer to the destination string where the content is to be appended.
* @param src Pointer to the source of data to be copied.
* @param src_len Maximum number of characters from src to be copied to the end of dst.
* @return A pointer to the destination string, which is dst.
*/
char * lv_strncat(char * dst, const char * src, size_t src_len);
/**
* @brief Searches for the first occurrence of character c in the string str.
* @param str Pointer to the null-terminated byte string to be searched.
* @param c The character to be searched for.
* @return A pointer to the first occurrence of character c in the string str, or a null pointer if c is not found.
*/
char * lv_strchr(const char * str, int c);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_STRING_H*/