/*
 ******************************************************************************
 * @file   app_mem.c
 * @author Sifli software development team
 ******************************************************************************
 */
/*
 * @attention
 * Copyright (c) 2019 - 2024,  Sifli Technology
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Sifli integrated circuit
 *    in a product or a software update for such product, 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.
 *
 * 3. Neither the name of Sifli nor the names of its contributors may be used to endorse
 *    or promote products derived from this software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Sifli integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY SIFLI TECHNOLOGY "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SIFLI TECHNOLOGY OR CONTRIBUTORS 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.
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include <stddef.h>
#include <string.h>
#include "app_mem.h"
#if defined(APP_USING_TLSF_MEM)
    #include "app_tlsf.h"
#endif
#ifndef DBG_TAG
    #define DBG_TAG  "APP.MEM"
#endif
#ifndef DBG_LVL
    #define DBG_LVL  DBG_LOG
#endif
#include "log.h"
#include "rtdbg.h"

#if !defined (BSP_USING_PC_SIMULATOR)
    #include "register.h"
    #include "bsp_board.h"
    #include "mem_map.h"
#endif

/**
 * Since snapshot generation for transition animations is performed in advance when compressed buffer mode is active,
 * the buffer reuse feature for animations cannot be enabled in this mode.
 * Doing so would result in data overwrite within the buffer during the reuse process.
 */
#if defined (BSP_USING_PSRAM) && !defined(LCD_FB_USING_TWO_COMPRESSED) && !defined(LCD_FB_USING_ONE_COMPRESSED)
    #define ANIM_BUF_AS_MEMHEAP
#endif

typedef struct
{
    rt_list_t       list;
    void            *ptr;
    void (*free)(void *);
}
mem_async_node_t;

static rt_list_t app_mem_async_list;
static struct rt_mutex  mem_asyn_mutex;

/**
 * @brief  Note: the following MACRO was defined by menuconfig..
 */

/**
 * @brief If no prsam exists. disable all MARCO relative to PSRAM.
 */
#if !defined(BSP_USING_PSRAM) && !defined(BSP_USING_PC_SIMULATOR)
    #undef  FREETYPE_PSRAM_CACHE
    #undef  PSRAM_CACHE_SIZE
    #define PSRAM_CACHE_SIZE                0
#endif

/**
 * @brief If freetype is used but FT_CACHE_SIZE is not defined, set FT_CACHE_SIZE to default to 120KB.
 */
#if FT_CACHE_SIZE == 0 && defined (LV_USING_FREETYPE_ENGINE)
    #undef  FT_CACHE_SIZE
    #define FT_CACHE_SIZE                   (120 *1024)
#endif

/**
 * @brief If freetype cache defined, enable FT_CAHCE_STANDALONE.
 */
#if FT_CACHE_SIZE > 0 && (defined (FREETYPE_SRAM_CACHE) || defined (FREETYPE_PSRAM_CACHE))
    #define  FT_CAHCE_STANDALONE
#endif

/**
 * @brief  System heap initialize. Only used for Board.
           if SYS_HEAP_IN_PSRAM was defined, Configure the system heap on PSRAM
           Due to some DMA scenarios requiring the use of SRAM,
           It is necessary to allocate a corresponding SRAM heap to these scenarios.
           SYS_HEAP_IN_PSRAM / SRAM_CACHE_SIZE /SYS_HEAP_SIZE_IN_PSRAM was configed by menuconfig.
 */

#if !defined (BSP_USING_PC_SIMULATOR)
#if defined (SYS_HEAP_IN_PSRAM)
    PSRAM_RET_SECT_BEG
    ALIGN_32 static rt_uint8_t           app_psram_sysheap_buf[SYS_HEAP_SIZE_IN_PSRAM] L2_CACHE_RET_BSS_SECT(psram_ret_cache);
    PSRAM_RET_SECT_END
#endif

static int app_sysheap_init(void)
{
#if defined(SYS_HEAP_IN_PSRAM)
    rt_system_heap_init((void *)app_psram_sysheap_buf, (void *)((uint8_t *)app_psram_sysheap_buf + sizeof(app_psram_sysheap_buf)));
#else
    rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
#endif
    return 0;
}
#endif

/**
 * @brief  All app memheap initialize.
 *         Including  sysheap/SRAM/PSRAM/FREETYPE heap initialize.
 */
static struct rt_memheap               *p_psram_memheap;
static struct rt_memheap               *p_sram_memheap;
static struct rt_memheap               *p_ft_memheap;
#if MSG_CACHE_SIZE > 0
    static struct rt_memheap           *p_msg_memheap;
    PSRAM_RET_SECT_BEG
    ALIGN_4 static uint8_t              app_msg_buf[MSG_CACHE_SIZE];
    PSRAM_RET_SECT_END
    static struct rt_memheap            app_msg_memheap;
#endif

static struct rt_memheap               *p_qjs_memheap;

#if defined (QUICKJS_USING_PSRAM) && defined (QUICKJS_PSRAM_SIZE)
    static struct rt_memheap            app_qjs_memheap;
    PSRAM_RET_SECT_BEG
    ALIGN_4 static uint8_t              app_qjs_buf[QUICKJS_PSRAM_SIZE] L2_CACHE_RET_BSS_SECT(psram_ret_cache);
    PSRAM_RET_SECT_END
#endif

#if SRAM_CACHE_SIZE > 0
    SRAM_SECT_BEG
    L1_NON_RET_BSS_SECT(sram_non_ret_cache, ALIGN_4 static uint8_t app_sram_buf[SRAM_CACHE_SIZE]);
    SRAM_SECT_END
    static struct rt_memheap            app_sram_memheap;
#endif

#if PSRAM_CACHE_SIZE > 0

    PSRAM_RET_SECT_BEG
    #ifndef BSP_USING_PC_SIMULATOR         /* Automatically calculate the length through l2_cache_non_ret_bss_psramheap_base */
        /*  Note:
        *  l2_cache_non_ret_bss_psramheap_base is defined in solution\framework\__template__\config\hcpu\linker_script\link_flash_xxx_sct .
        *  section l2_cache_non_ret_bss_psramheap_base shall always be defined at the end of the PSRAM.
        *  Otherwise, the address obtained here will be incorrect.
        */
        /* Define the variable prsam_heap_cache_beg and place it at the position of the last variable in the PSRAM. */
        ALIGN(32) static uint32_t            psramheap_base  SECTION(STRINGIFY(.psramheap_base));
        /* Must align 32 bytes because of DMA */
        #define PSRAMHEAP_BASE              ((uint32_t) &psramheap_base)

        #if defined(PSRAMHEAP_IN_PSRAM2)
            #define PSRAM_HEAP_SIZE             (PSRAM2_BASE_ADDR + PSRAM2_SIZE - PSRAMHEAP_BASE)
        #else
            /* Automatically calculate the size of the PSRAM CACHE HEAP.*/
            #ifdef USING_SEC_ENV /* for 52x */
                #define PSRAM_HEAP_SIZE             (PSRAM_BASE + PSRAM_SIZE - PSRAMHEAP_BASE - NMI_SEC_CODE_SIZE - NMI_SEC_SHARE_SIZE)
            #else
                #define PSRAM_HEAP_SIZE             (PSRAM_BASE + PSRAM_SIZE - PSRAMHEAP_BASE)
            #endif
        #endif


    #else                                   /* Define the length through menuconfig. */
        ALIGN_4 static uint8_t              app_psram_buf[PSRAM_CACHE_SIZE] L2_CACHE_RET_BSS_SECT(psram_ret_cache);
        #define PSRAMHEAP_BASE              app_psram_buf
        #define PSRAM_HEAP_SIZE             PSRAM_CACHE_SIZE
    #endif
    PSRAM_RET_SECT_END

    static struct rt_memheap            app_psram_memheap;

#endif

#if defined (FT_CAHCE_STANDALONE)
    PSRAM_RET_SECT_X_BEG
    ALIGN_4 static uint8_t              app_ft_buf[FT_CACHE_SIZE] L2_CACHE_RET_BSS_SECT(psram_ret_cache_x);
    PSRAM_RET_SECT_X_END
    static struct rt_memheap            app_ft_memheap;
#endif

uint32_t app_memheap_get_size(void)
{
#if PSRAM_CACHE_SIZE > 0
    return PSRAM_HEAP_SIZE;
#else
    return 0;
#endif
}

/**
 * @brief  Init memory heap.
           It is called in board.c(simulator) or drv_common.c(board).
 */
int app_memheap_init(void)
{
#if !defined (BSP_USING_PC_SIMULATOR)
    app_sysheap_init();
#endif

    /* Initialize PSRAM memheap. PSRAM_CACHE_SIZE was configed by menuconfig */
#if defined (BSP_USING_PSRAM) && PSRAM_CACHE_SIZE > 0
    //"PSRAM_HEAP_SIZE < 0!!! (Please check it in app_mem.c. (PSRAM_BASE + PSRAM_SIZE - (uint32_t) &prsam_heap_cache_base))!!!"
    RT_ASSERT(PSRAM_HEAP_SIZE > 500 * 1024);
    p_psram_memheap = &app_psram_memheap;
    rt_memset((void *)PSRAMHEAP_BASE, 0x0, PSRAM_HEAP_SIZE);
    rt_memheap_init(p_psram_memheap, "psram_memheap", (void *) PSRAMHEAP_BASE, PSRAM_HEAP_SIZE);
#if defined(APP_USING_TLSF_MEM) || defined(RT_USING_MEMHEAP_AS_HEAP)
    rt_err_t err = rt_memheap_add_to_sys(p_psram_memheap);
    RT_ASSERT(RT_EOK == err);
#endif
#endif

    /* Initialize SRAM memheap. SRAM_CACHE_SIZE was configed by menuconfig */
#if SRAM_CACHE_SIZE > 0
    p_sram_memheap = &app_sram_memheap;
    rt_memheap_init(p_sram_memheap, "sram_memheap", (void *)app_sram_buf, sizeof(app_sram_buf));
#endif

    /* Initialize Freetype memheap. . FT_CAHCE_STANDALONE / FT_CACHE_SIZE was configed by menuconfig*/
#ifdef FT_CAHCE_STANDALONE
    p_ft_memheap = &app_ft_memheap;
    rt_memheap_init(p_ft_memheap, "ft_memheap", (void *)app_ft_buf, sizeof(app_ft_buf));
#endif

#if MSG_CACHE_SIZE > 0
    p_msg_memheap = &app_msg_memheap;
    rt_memheap_init(p_msg_memheap, "msg_memheap", (void *)app_msg_buf, sizeof(app_msg_buf));
#endif

#if defined (QUICKJS_USING_PSRAM) && (QUICKJS_PSRAM_SIZE > 0)
    p_qjs_memheap = &app_qjs_memheap;
    rt_memheap_init(p_qjs_memheap, "qjs_memheap", (void *)app_qjs_buf, sizeof(app_qjs_buf));
#endif


#if defined (USING_BLOCK_MEM)
    bmem_init();
#endif

    return 0;
}

/**
 * @brief  Allocate mem from sram_memheap.
           These memory are used for DMA or performance related scenarios
 * @param  size Size of the memory to allocate in bytes
 * @retval pointer to the allocated memory
 */
void *app_sram_alloc(rt_size_t size)
{
#if defined (SYS_HEAP_IN_PSRAM)
    void *p = app_cache_alloc(size, CACHE_SRAM);
#else
    void *p = rt_malloc(size);
#endif
    RET_ADDR_TRACE(p);
    return p;
}

/**
 * @brief  Allocate mem from sram_memheap.
           The allocated memory is filled with bytes of value zero
 * @param  count Number of objects to allocate.
 * @param  size Size of the objects to allocate.
 * @retval pointer Pointer of reallocated memory.
 */
void *app_sram_calloc(rt_size_t count, rt_size_t size)
{
#if defined (SYS_HEAP_IN_PSRAM)
    void *p = app_cache_calloc(count, size, CACHE_SRAM);
#else
    void *p = rt_calloc(count, size);
#endif
    RET_ADDR_TRACE(p);
    return p;
}

/**
 * @brief  Reallocate mem from sram_memheap.
           Reallocate a memory with a new size. The old content will be kept
 * @param  ptr pointer to an allocated memory.
 *         Its content will be copied to the new memory block and freed
 * @param  newsize the desired new size in byte
 * @retval pointer Pointer of reallocated memory.
 */
void *app_sram_realloc(void *ptr, rt_size_t newsize)
{
#if defined (SYS_HEAP_IN_PSRAM)
    void *p = app_cache_realloc(ptr, newsize);
#else
    void *p = rt_realloc(ptr, newsize);
#endif
    RET_ADDR_TRACE(ptr);
    return p;
}

/**
 * @brief  Aree mem from sram_memheap.
 * @param  ptr the address of memory which will be released.
 */
void app_sram_free(void *ptr)
{
#if defined (SYS_HEAP_IN_PSRAM)
    app_cache_free(ptr);
#else
    rt_free(ptr);
#endif
}

static uint8_t mem_log;
#if defined (RT_USING_FINSH)
void app_mem_log(void)
{
#if !defined (BSP_USING_PC_SIMULATOR)
    LOG_I("%s: PSRAM_BASE %x PSRAM_SIZE %d PSRAMHEAP_BASE %x PSRAM_HEAP_SIZE %d\n", __func__, PSRAM_BASE, PSRAM_SIZE, PSRAMHEAP_BASE, PSRAM_HEAP_SIZE);
#endif
    mem_log = (mem_log + 1) % 0x03;
    LOG_I("%s: mem_log %d", __func__, mem_log);
}
MSH_CMD_EXPORT_ALIAS(app_mem_log, mem_log, mem_log);
#endif

/**
 * @brief  Allocate mem sequentially from block_mem/sys_heap/sram_memheap/psram_memheap.
 * @param  size Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *app_malloc(uint32_t size)
{
    SIMULATOR_MEM_LEAKAGE_MALLOC(LEAK_APP, size);

    void *ret = NULL;

    if (0 == size)
        return NULL;

#if defined (USING_BLOCK_MEM)
    ret = bmem_alloc(size);
#endif

    if (!ret)
        ret = rt_malloc(size);

    //if (!ret && SRAM_CACHE_SIZE > 0)
    //    ret = rt_memheap_alloc(p_sram_memheap, size);
#if !defined(APP_USING_TLSF_MEM)
    if (!ret && PSRAM_CACHE_SIZE > 0)
        ret = rt_memheap_alloc(p_psram_memheap, size);
#endif
    if (!ret)
        LOG_I("%s: fail!!! %d", __func__, size);

    if (2 == mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, ret, size, RET_ADDR);
    RET_ADDR_TRACE(ret);
    return ret;
}

/**
 * @brief  Allocate mem sequentially from block_mem/sys_heap/sram_memheap/psram_memheap.
           The allocated memory is filled with bytes of value zero
 * @param  count Number of objects to allocate.
 * @param  size Size of the objects to allocate.
 * @retval pointer Pointer of allocated memory.
 */
void *app_calloc(uint32_t count, uint32_t size)
{
    SIMULATOR_MEM_LEAKAGE_CALLOC(LEAK_APP, count, size);

    /* allocate 'count' objects of size 'size' */
    void *p = app_malloc(count * size);
    /* zero the memory */
    if (p) rt_memset(p, 0, count * size);
    RET_ADDR_TRACE(p);
    if (2 == mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, p, size, RET_ADDR);
    return p;
}

/**
 * @brief  Reallocate mem sequentially from block_mem/sys_heap/sram_memheap/psram_memheap.
           Reallocate a memory with a new size. The old content will be kept
 * @param  ptr pointer to an allocated memory.
 *         Its content will be copied to the new memory block and freed
 * @param  newsize the desired new size in byte
 * @retval pointer Pointer of allocated memory.
 */
void *app_realloc(void *p, uint32_t new_size)
{
    SIMULATOR_MEM_LEAKAGE_REALLOC(LEAK_APP, p, new_size);

    void *ret = NULL;
    if (0 < new_size)
    {
        if (!p)
        {
            ret = app_malloc(new_size);
            goto end;
        }
        if (new_size < app_mem_get_size(p))
            return p;
        ret = app_malloc(new_size);
        if (ret)
            app_memcpy(ret, p, app_mem_get_size(p));
    }

    if (p && (ret || 0 == new_size)) app_free(p);
end:
    if (2 == mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, p, new_size, RET_ADDR);
    RET_ADDR_TRACE(ret);
    return ret;
}

/**
 * @brief  This function will release the previously allocated memory block by
 *         app_malloc/app_calloc/app_realloc.
 *         The released memory block is taken back to memheap.
 * @param  p the address of memory which will be released.
 */
void app_free(void *p)
{
    SIMULATOR_MEM_LEAKAGE_FREE(LEAK_APP, p);

    if (2 == mem_log) LOG_I("%s: ptr %p ret_addr %p", __func__, p, RET_ADDR);
    if (p)
    {
#if defined (USING_BLOCK_MEM)
        if (0 == bmem_free(p))
            return;
#endif
        if (app_mem_is_sysheap(p))
            rt_free(p);
        else
            rt_memheap_free(p);
    }
}

/**
 * brief  Duplicate a string using application-specific memory allocation.
 *        This function creates a dynamically allocated copy of the input string.
 *        It safely handles NULL inputs and ensures the copied string is null-terminated.
 *        Memory allocated by this function should be freed with app_free() to avoid leaks.
 * @param s Input string to duplicate. If NULL, the function returns NULL.
 * @return Pointer to the newly allocated string, or NULL if allocation fails.
 * @note Uses app_malloc() for memory allocation, which may have specific behaviors
 *       (e.g., tracking, alignment) depending on the application's memory management system.
 * @see app_free() - Corresponding function to free the allocated memory.
 */
char *app_strdup(const char *s)
{
    char *ptr = NULL;
    if (s)
    {
        // Allocate memory for the string plus null terminator
        ptr = app_malloc(strlen(s) + 1);
        if (ptr)
            strcpy(ptr, s);  // Copy string contents
    }
    RET_ADDR_TRACE(ptr);  // Track memory allocation (application-specific)
    return ptr;
}

/**
 * @brief  Allocate mem sequentially from sram_memheap/psram_memheap.
 * @param  size Size of the memory to allocate in bytes
 * @param  cache_type Allocted memory type, CACHE_SRAM or CACHE_PSRAM
 * @retval pointer Pointer of allocated memory.
 */
void *app_cache_alloc(size_t size, uint16_t cache_type)
{
    SIMULATOR_MEM_LEAKAGE_MALLOC(LEAK_CACHE, size);

    if (0 == size) return NULL;

    void *ret = NULL;
#if MSG_CACHE_SIZE > 0
    if (CACHE_MSG == cache_type && p_msg_memheap)
        ret = rt_memheap_alloc(p_msg_memheap, size);
#endif
#ifdef BSP_USING_PSRAM
    if (CACHE_SRAM == cache_type && p_sram_memheap)
        ret = rt_memheap_alloc(p_sram_memheap, size);
    if (!ret && p_psram_memheap)
    {
        ret = rt_memheap_alloc(p_psram_memheap, size);
#if defined(PKG_USING_LITTLEVGL2RTT) && \
    (defined(SOLUTION_RES_USING_NAND) || defined(SOLUTION_RES_USING_FS))
        /* clean cache. try again. */
        if ((!ret || p_psram_memheap->available_size < 150 * 1024))
        {
            lv_img_cache_clean(MAX_IMG_CACHE_SIZE);
            if (!ret) ret = rt_memheap_alloc(p_psram_memheap, size);
        }
#endif
    }
#else
    ret = rt_malloc(size);
#endif
    RET_ADDR_TRACE(ret);
    if (mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, ret, size, RET_ADDR);
    return ret;
}

/**
 * @brief  Allocate mem sequentially from sram_memheap/psram_memheap.
           The allocated memory is filled with bytes of value zero
 * @param  size Size of the memory to allocate in bytes
 * @param  cache_type Allocted memory type, CACHE_SRAM or CACHE_PSRAM
 * @retval pointer Pointer of allocated memory.
 */
void *app_cache_calloc(rt_size_t count, rt_size_t size, uint16_t cache_type)
{
    SIMULATOR_MEM_LEAKAGE_CALLOC(LEAK_CACHE, count, size);

    void *ret = app_cache_alloc(count * size, cache_type);
    if (ret)
        app_memset(ret, 0x00, count * size);
    RET_ADDR_TRACE(ret);
    if (mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, ret, size, RET_ADDR);
    return ret;
}

/**
 * @brief  Reallocate mem sequentially from sram_memheap/psram_memheap.
           Reallocate a memory with a new size. The old content will be kept
 * @param  ptr Pointer to an allocated memory.
 *         Its content will be copied to the new memory block and freed
 * @param  nbytes The desired new size in byte
 * @retval pointer Pointer of allocated memory.
 */
void *app_cache_realloc(void *ptr, size_t nbytes)
{
    SIMULATOR_MEM_LEAKAGE_REALLOC(LEAK_CACHE, ptr, nbytes);

    void *new_ptr = NULL;
    uint16_t cache_type = CACHE_PSRAM;
#ifdef BSP_USING_PSRAM
    if (0 < nbytes)
    {
        if (ptr && app_mem_get_size(ptr) >= nbytes)
            return ptr;
        if (p_sram_memheap && app_mem_is_in_memheap(p_sram_memheap, ptr))
            cache_type = CACHE_SRAM;
    }
    new_ptr = app_cache_alloc(nbytes, cache_type);
    if (new_ptr && ptr)
        app_memcpy(new_ptr, ptr, app_mem_get_size(ptr));
#endif
    if (ptr && (new_ptr || 0 == nbytes)) app_cache_free(ptr);
    RET_ADDR_TRACE(new_ptr);
    if (mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, new_ptr, nbytes, RET_ADDR);
    return new_ptr;
}

/**
 * @brief  This function will release the previously allocated memory block by
 *         app_cache_alloc/app_cache_calloc/app_cache_realloc.
 *         The released memory block is taken back to sram_memheap/psram_memheap.
 * @param  p the address of memory which will be released
 */
void app_cache_free(void *ptr)
{
    SIMULATOR_MEM_LEAKAGE_FREE(LEAK_CACHE, ptr);

    if (!ptr) return;
    if (mem_log) LOG_I("%s: ptr %p ret_addr %p", __func__, ptr, RET_ADDR);
#ifdef BSP_USING_PSRAM
    rt_memheap_free(ptr);
#else
    rt_free(ptr);
#endif
}

/**
 * @brief  Allocated cache copy of image in sram_memheap/psram_memheap.
           especially for rotating image, to accelerate rotating.
 * @param  src_img Pointer to img_obj.
 * @param  cache_type Allocted memory type, CACHE_SRAM or CACHE_PSRAM
 * @retval pointer Pointer of allocated memory, which type is lv_img_dsc_t.
 */
lv_img_dsc_t *app_cache_copy_alloc(lv_obj_t *src_img, uint16_t cache_type)
{
    if (NULL == src_img) return NULL;
    lv_img_t *ext = (lv_img_t *) src_img;
    if (NULL == ext) return NULL;
    lv_img_dsc_t *dsc = NULL;

    if (ext->src_type == LV_IMG_SRC_VARIABLE)
    {
        dsc = rt_malloc(sizeof(lv_img_dsc_t));
        RT_ASSERT(dsc);
        dsc->header.always_zero = 0;
        dsc->header.cf = ext->cf;
        dsc->header.w = ext->w;
        dsc->header.h = ext->h;
        dsc->data_size = lv_img_buf_get_img_size(dsc->header.w, dsc->header.h, dsc->header.cf);
        dsc->data = ((lv_img_dsc_t *)ext->src)->data;
    }
    else
    {
        return NULL;
    }

    void *cache_buf = app_cache_alloc(dsc->data_size, cache_type);
    if (!cache_buf)
    {
        if (dsc) rt_free(dsc);
        return NULL;
    }

    app_memcpy((uint8_t *)cache_buf, (uint8_t *)dsc->data, dsc->data_size);
    dsc->data = cache_buf;

    lv_img_set_src(src_img, dsc);
    if (mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, cache_buf, dsc->data_size, RET_ADDR);
    return dsc;
}

/**
 * @brief  Free cache copy of image, which allocated using app_cache_copy_alloc.
 * @param  rel_mem Pointer of allocated memory, which type is lv_img_dsc_t.
*/
void app_cache_copy_free(lv_img_dsc_t *rel_dsc)
{
    if (NULL == rel_dsc) return;
    if (mem_log) LOG_I("%s: ptr %p ret_addr %p", __func__, rel_dsc->data, RET_ADDR);
    app_cache_free((void *)rel_dsc->data);
    rt_free(rel_dsc);
}

/**
 * @brief  Allocate mem for qjs.
 * @param  nbytes Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *qjs_alloc(size_t nbytes)
{
    SIMULATOR_MEM_LEAKAGE_MALLOC(LEAK_QJS, nbytes);

    if (p_qjs_memheap)
        return rt_memheap_alloc(p_qjs_memheap, nbytes);
    else
        return app_cache_alloc(nbytes, CACHE_PSRAM);
}

/**
 * @brief  This function will release the previously allocated memory block by qjs_alloc/qjs_realloc.
 * @param  ptr the address of memory which will be released.
 */
void qjs_free(void *ptr)
{
    SIMULATOR_MEM_LEAKAGE_FREE(LEAK_QJS, ptr);

    if (p_qjs_memheap)
        rt_memheap_free(ptr);
    else
        app_cache_free(ptr);
}

/**
 * @brief  Reallocate mem for qjs.
 * @param  ptr pointer to an allocated memory.
 * @param  nbytes the desired new size in byte
 * @retval pointer Pointer of allocated memory.
 */
void *qjs_realloc(void *ptr, size_t nbytes)
{
    SIMULATOR_MEM_LEAKAGE_REALLOC(LEAK_QJS, ptr, nbytes);

    if (p_qjs_memheap)
        return rt_memheap_realloc(p_qjs_memheap, ptr, nbytes);
    else
        return app_cache_realloc(ptr, nbytes);
}

/*--------------------------------------transform animations begin--------------------------------------*/

/**
    For app trans animation.
    The interface of app_anim_xxx is designed for transform animations.
    If transform animations is not supported, these interfaces will be disabled
 */

#define ANIM_BUF_EMPTY     4
#if !defined (TLV_TRANS_ANIM_NONE) || !defined (APP_TRANS_ANIMATION_NONE)
    #ifdef APP_TRANS_ANIM_WITH_MASK
        #define ANIM_BUF_PIX_SIZE       (LV_COLOR_DEPTH / 8 + 1)
    #else
        #define ANIM_BUF_PIX_SIZE       (LV_COLOR_DEPTH / 8)
    #endif
    #define ANIM_WIDTH          (LV_HOR_RES_MAX * APP_TRANS_ANIM_SNAPSHOT_SCALE / 100)
    #define ANIM_HEIGHT         (LV_VER_RES_MAX * APP_TRANS_ANIM_SNAPSHOT_SCALE / 100)
    #define ANIM_BUF_SIZE       (ANIM_HEIGHT * ANIM_WIDTH * ANIM_BUF_PIX_SIZE + 1024)

    PSRAM_NON_RET_SECT_BEG
    #if defined(TRANS_ANIMATION_3_BUF)
        ALIGN_4 static uint8_t     app_anim_buf[3 * ANIM_BUF_SIZE] L2_CACHE_NON_RET_BSS_SECT(psram_non_ret_cache);
    #elif defined (TRANS_ANIMATION_2_BUF) || defined (APP_TRANS_ANIMATION_SCALE_BOTH)
        ALIGN_4 static uint8_t     app_anim_buf[2 * ANIM_BUF_SIZE] L2_CACHE_NON_RET_BSS_SECT(psram_non_ret_cache);
    #else
        ALIGN_4 static uint8_t     app_anim_buf[ANIM_BUF_SIZE] L2_CACHE_NON_RET_BSS_SECT(psram_non_ret_cache);
    #endif
    PSRAM_NON_RET_SECT_END
#else
    /* Only for compile when APP_TRANS_ANIMATION_NONE */
    ALIGN_4 static uint8_t     app_anim_buf[ANIM_BUF_EMPTY];
#endif

/**
 * @brief  Allocate fixed_length memory for switch_animation's snapshot.
           This function only used for equal length allocation. Each index map to fixed position of the animation buffer.
 * @param  nbytes allocate size from snapshot memory.
 * @param  index  Snapshot index
*/
void *app_anim_buf_alloc(size_t nbytes, uint8_t index)
{
    void *ptr = NULL;

    //Note: the first 4 bytes of app_anim_buf is length which had allocated.
    size_t num = sizeof(app_anim_buf) / nbytes;

    if (index + 1 <= num)
    {
        ptr = &app_anim_buf[index * nbytes];
    }
    else
    {
        LOG_I("%s failed: index %d size %d abuf %d", __func__, index, nbytes, sizeof(app_anim_buf));
        return NULL;
    }

    /* To detect overflow. */
    app_anim_buf[sizeof(app_anim_buf) - 1] = 0x00;

    LOG_I("%s: index %d size %d abuf %d", __func__, index, nbytes, sizeof(app_anim_buf));

    return ptr;
}

static uint32_t anim_size;

/**
 * @brief  Allocate variable_length memory for switch_animation's snapshot.
           This function only used for variable_length length allocation. Each index map to fixed position of the animation buffer.
 * @param  nbytes allocate size from snapshot memory.
 * @param  first 1: allocate from beginning of app_anim_buf; 0: following the previous allocation.
*/
void *app_anim_buf_alloc_ext(size_t nbytes, uint8_t first)
{
    void *ptr = NULL;

    /* Note: the first 4 bytes of app_anim_buf is length which had allocated. */

    if (first)
    {
        anim_size  = 0;
    }

    if (anim_size + nbytes <= sizeof(app_anim_buf))
    {
        ptr = &app_anim_buf[anim_size];
        anim_size += nbytes;
    }
    else
    {
        LOG_I("%s: size %d apos %d abuf %d", __func__, nbytes, anim_size, sizeof(app_anim_buf));
        return NULL;
    }

    //to detect overflow.
    app_anim_buf[sizeof(app_anim_buf) - 1] = 0x00;

    LOG_I("%s: size %d apos %d abuf %d", __func__, nbytes, anim_size, sizeof(app_anim_buf));

    return ptr;
}

/**
 * @brief  Free snapshot memory. NOTE: Note that this code is actually not useful!!! only for check overflow.
 * @param  ptr Snapshot pointer to be free, which successsful apply by app_anim_buf_alloc_ext
*/
void *app_anim_buf_free(void *ptr)
{
    //check overflow.
    RT_ASSERT(app_anim_buf[sizeof(app_anim_buf) - 1] == 0x00);
    return ptr;
}

static struct rt_memheap                app_anim_memheap;
static struct rt_memheap               *p_anim_memheap;

/**
 * @brief  Temporarily set anim_buf to memheap. Once app_cache_xxx fails to allocate memory,
 *         use anim_buf for memory allocation.
 * @param  as_memheap Enable or Disable. If FORCE_REUSE_ANIM_BUF is setted,
 *         it indicates forced reuse of the animbuf. In this case, the epic synchronous screen delivery mechanism is required.
*/
int app_anim_buf_set_as_memheap(int as_memheap)
{
    /* Compatible with previous usage of reusable animation buf,
       where 1 indicates reuse and 0 indicates release for reuse */
    as_memheap &= ~FORCE_REUSE_ANIM_BUF;

#ifdef ANIM_BUF_AS_MEMHEAP
    if (as_memheap && sizeof(app_anim_buf) > ANIM_BUF_EMPTY)
    {
        /* A previous user may still hold animation buffers while leaving a page.
           Reuse the registered heap until those references are released. */
        if (p_anim_memheap)
        {
            app_mem_free_asyn_node();
            return 0;
        }

        p_anim_memheap = &app_anim_memheap;
        rt_memheap_init(p_anim_memheap, "anim_buf", (void *) &app_anim_buf, sizeof(app_anim_buf));
    }
    else
#endif
    {
        if (p_anim_memheap)
        {
            uint32_t delay_cnt = 0;

            app_mem_free_asyn_node();
#define DELAY_PERIOD_MAX 8
            while (p_anim_memheap->actual_used_size && delay_cnt < DELAY_PERIOD_MAX)
            {
                rt_thread_mdelay(LV_DISP_DEF_REFR_PERIOD);
                app_mem_free_asyn_node();
                delay_cnt++;
            }
            if (delay_cnt)
                LOG_I("%s: animation buffer deinit delay %dms", __func__,
                      delay_cnt * LV_DISP_DEF_REFR_PERIOD);
            if (p_anim_memheap->actual_used_size)
            {
                LOG_E("%s: animation buffer still in use actual=%u available=%u; keep memheap active",
                      __func__, p_anim_memheap->actual_used_size,
                      p_anim_memheap->available_size);
                return -RT_EBUSY;
            }

            LOG_I("%s: animation buffer deinit actual=%u available=%u", __func__,
                  p_anim_memheap->actual_used_size,
                  p_anim_memheap->available_size);
            rt_memheap_detach(p_anim_memheap);
            p_anim_memheap = NULL;
        }
        else
        {
            LOG_I("Animation buf not initialized.");
            return -1;
        }
    }
    return 0;
}

/**
 * @brief  Allocate mem sequentially from app_anim_memheap.
 * @param  size Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *app_anim_alloc(size_t size)
{
    SIMULATOR_MEM_LEAKAGE_MALLOC(LEAK_ANIM, size);

    if (0 == size) return NULL;

    void *ret = NULL;
    if (p_anim_memheap)
        ret = rt_memheap_alloc(p_anim_memheap, size);
    if (!ret)
        ret = app_cache_alloc(size, CACHE_PSRAM);
    if (mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, ret, size, RET_ADDR);
    RET_ADDR_TRACE(ret);
    return ret;
}

/**
 * @brief  Allocate mem sequentially from app_anim_memheap.
           The allocated memory is filled with bytes of value zero
 * @param  size Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *app_anim_calloc(rt_size_t count, rt_size_t size)
{
    SIMULATOR_MEM_LEAKAGE_CALLOC(LEAK_ANIM, count, size);

    void *ret = app_anim_alloc(count * size);
    if (ret)
        app_memset(ret, 0x00, count * size);
    RET_ADDR_TRACE(ret);
    if (mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, ret, size, RET_ADDR);
    return ret;
}

/**
 * @brief  Reallocate mem sequentially from app_anim_memheap.
           Reallocate a memory with a new size. The old content will be kept
 * @param  ptr Pointer to an allocated memory.
 *         Its content will be copied to the new memory block and freed
 * @param  nbytes The desired new size in byte
 * @retval pointer Pointer of allocated memory.
 */
void *app_anim_realloc(void *ptr, size_t nbytes)
{
    SIMULATOR_MEM_LEAKAGE_REALLOC(LEAK_ANIM, ptr, nbytes);

    void *new_ptr = NULL;
    if (0 < nbytes)
    {
        if (ptr && app_mem_get_size(ptr) >= nbytes)
            return ptr;
        new_ptr = app_anim_alloc(nbytes);
        if (new_ptr && ptr)
            app_memcpy(new_ptr, ptr, app_mem_get_size(ptr));
    }
    if (ptr && (new_ptr || 0 == nbytes)) app_anim_free(ptr);
    if (mem_log) LOG_I("%s: ptr %p size %d ret_addr %p", __func__, new_ptr, nbytes, RET_ADDR);
    RET_ADDR_TRACE(new_ptr);
    return new_ptr;
}

/**
 * @brief  This function will release the previously allocated memory block by
 *         app_cache_alloc/app_cache_calloc/app_cache_realloc.
 *         The released memory block is taken back to sram_memheap/psram_memheap.
 * @param  p the address of memory which will be released
 */
void app_anim_free(void *ptr)
{
    SIMULATOR_MEM_LEAKAGE_FREE(LEAK_ANIM, ptr);

    if (mem_log) LOG_I("%s: ptr %p ret_addr %p", __func__, ptr, RET_ADDR);
    if (ptr) app_cache_free(ptr);
}

/**
 * @brief  Duplicates a string by allocating new memory and copying the content.
  *         The memory for the new string is allocated using `app_anim_alloc`.
  *         Therefore, when the duplicated string is no longer needed, it must be
  *         freed using the corresponding function (e.g., `app_anim_free`) to prevent memory leaks.
  * @param  s Pointer to the null-terminated source string to be duplicated.
  *           If `s` is NULL, the function returns NULL immediately.
  * @return On success, returns a pointer to the newly allocated duplicated string.
  *         On failure (e.g., if memory allocation fails), returns NULL.
  */
char *app_anim_strdup(const char *s)
{
    char *ptr = NULL;
    if (s)
    {
        ptr = app_anim_alloc(strlen(s) + 1);
        if (ptr)
            strcpy(ptr, s);
    }
    RET_ADDR_TRACE(ptr);
    return ptr;
}

/*--------------------------------------trans animations end--------------------------------------*/


/*--------------------------------------fixed mem (for svg) begin--------------------------------------*/

/**
 * The following interfaces are designed exclusively for single-threaded use and must not be utilized across multiple threads.
 * Furthermore, each invocation of app_svg_memheap_config, followed by app_svg_alloc, and another app_svg_alloc,
 * forms an inseparable and complete cycle. These function calls must not be interleaved with any other calls to this interface suite.
 */

typedef enum
{
    MEM_DYN,
    MEM_FIXED_STATIC,
    MEM_FIXED_ALLOCED
} svg_mem_type_t;

typedef struct
{
    void               *heap;
    svg_mem_mode_t      mode;
    uint32_t            fixed_size;
    uint32_t            idx;
    uint32_t            max_cnt;
    uint32_t            max_idx;
} svg_mem_t;

static svg_mem_t app_svg_mem;

/**
 * @brief  Configuration fix mem before app_svg_alloc.
 * @param  mode Whether to reuse anim_buf
 * @param  max_num MAX number of the memory to allocate, if resuse anim buf and max num is 0, it means use max num of anim buf
 * @param  fixed_size Fixed block length
 */
void app_svg_memheap_config(svg_mem_mode_t mode, size_t max_num, size_t fixed_size)
{
    uint32_t anim_buf_max_num = sizeof(app_anim_buf) / ((fixed_size + 4));
    if (mode == FIX_MODE_REUSE_ANIM_BUF && max_num == 0)
    {
        max_num = anim_buf_max_num;
    }
    uint32_t heap_size = (fixed_size + 4) * max_num;

    RT_ASSERT(0 == app_svg_mem.idx);
    app_svg_mem.fixed_size = fixed_size;
    app_svg_mem.mode = mode;
    app_svg_mem.max_cnt = max_num;
    app_svg_mem.max_idx = 0;
    if (!p_anim_memheap && FIX_MODE_REUSE_ANIM_BUF == mode && heap_size < sizeof(app_anim_buf))
    {
        app_svg_mem.heap = &app_anim_buf[0];
    }
    else
    {
        app_svg_mem.mode = FIX_MODE_NONE;
        app_svg_mem.heap = app_cache_alloc(heap_size, CACHE_SRAM);
        RT_ASSERT(app_svg_mem.heap);
    }
}

/**
 * @brief  Allocate fixed mem sequentially.
 * @param  nbytes Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *app_svg_alloc(size_t nbytes)
{
    uint32_t *p = NULL;
    svg_mem_type_t type = MEM_DYN;

    if (nbytes == app_svg_mem.fixed_size)
    {
        if (app_svg_mem.idx + 1 < app_svg_mem.max_cnt)
        {
            p = (uint32_t *)((uint8_t *) app_svg_mem.heap + app_svg_mem.idx * (app_svg_mem.fixed_size + 4));
            type = MEM_FIXED_STATIC;
        }
        else
        {
            p = (uint32_t *)app_cache_alloc(nbytes + 4, CACHE_SRAM);
            RT_ASSERT(p);
            type = MEM_FIXED_ALLOCED;
        }

        p[0] = (type << 30) + app_svg_mem.idx;
        app_svg_mem.idx++;
        app_svg_mem.max_idx++;
    }
    else
    {
        p = (uint32_t *)app_cache_alloc(nbytes + 4, CACHE_PSRAM);
        RT_ASSERT(p);
        p[0] = type << 30;
    }
    return (void *)((uint32_t) p + 4);
}

/**
 * @brief  This function will release the previously allocated memory block by app_svg_alloc.
 * @param  ptr the address of memory which will be released.
 */
void app_svg_free(void *ptr)
{
    uint32_t *p = (uint32_t *)((uint32_t) ptr - 4);
    svg_mem_type_t type = p[0] >> 30;

    RT_ASSERT(app_svg_mem.max_idx >= (p[0] & 0x3fffffff));
    if (MEM_FIXED_ALLOCED == type || MEM_DYN == type)
    {
        app_cache_free(p);
    }

    if (MEM_DYN != type)
    {
        app_svg_mem.idx--;
        if (0 == app_svg_mem.idx && FIX_MODE_REUSE_ANIM_BUF != app_svg_mem.mode)
        {
            app_cache_free(app_svg_mem.heap);
            app_svg_mem.heap = NULL;
        }
    }
}

/*--------------------------------------fixed mem (for svg) end--------------------------------------*/

/**
 * @brief  D-Cache Clean by address
 * @param  data Address
 * @param  size Size of memory block (in number of bytes)
*/
void app_mem_flush_cache(void *data, uint32_t size)
{
#if !defined (BSP_USING_PC_SIMULATOR)
    if (!app_mem_is_sysheap(data))
        SCB_CleanDCache_by_Addr(data, (size + 3) >> 2 << 2);
#endif
}

/**
 * @brief  I-Cache Invalid by address
 * @param  data Address
 * @param  size Size of memory block (in number of bytes)
*/
void app_mem_invalid_icache(void *data, uint32_t size)
{
#if !defined (BSP_USING_PC_SIMULATOR)
    if (!app_mem_is_sysheap(data))
        SCB_InvalidateICache_by_Addr(data, (size + 3) >> 2 << 2);
#endif
}

#if 1//def LV_USING_FREETYPE_ENGINE

#include "lv_freetype.h"

#if !defined (FT_CAHCE_STANDALONE)
    static uint32_t ft_alloc_size;
#endif

/**
 * @brief  Allocate mem sequentially from ft_memheap. used for freetype.
 * @param  nbytes Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *ft_smalloc(rt_size_t nbytes)
{
    SIMULATOR_MEM_LEAKAGE_MALLOC(LEAK_FT, nbytes);

    void *p = NULL;
    if (0 == nbytes) return NULL;
#if defined (FT_CAHCE_STANDALONE)
    p = rt_memheap_alloc(p_ft_memheap, nbytes);
    if (!p)
    {
        lv_freetype_clean_cache(FT_CACHE_QUAD_CLEAN);
        //try to allocate image_psram memheap.
        p = rt_memheap_alloc(p_ft_memheap, nbytes);
        if (!p)
        {
            lv_freetype_clean_cache(FT_CACHE_HALF_CLEAN);
            //try to allocate image_psram memheap.
            p = rt_memheap_alloc(p_ft_memheap, nbytes);
        }
    }
#else
    p = app_malloc(nbytes);
#endif

    RET_ADDR_TRACE(p);
#if !defined (FT_CAHCE_STANDALONE)
    if (p) ft_alloc_size += app_mem_get_size(p);
#endif
    //if (!p) LOG_I("warning!!! ft_smalloc %d fail!!!", nbytes);

    return p;
}

/**
 * @brief  Allocate mem sequentially from ft_memheap.
 *         The allocated memory is filled with bytes of value zero
 * @param  count Number of objects to allocate.
 * @param  size Size of the objects to allocate.
 * @retval pointer Pointer of allocated memory.
 */
void *ft_scalloc(rt_size_t count, rt_size_t size)
{
    SIMULATOR_MEM_LEAKAGE_CALLOC(LEAK_FT, count, size);

    void *p = NULL;

    p = ft_smalloc(count * size);
    if (p)
        app_memset(p, 0x00, count * size);
    //if (!p) LOG_I("warning!!! ft_scalloc %d fail!!!", count * size);
    RET_ADDR_TRACE(p);
    return p;
}

/**
 * @brief  Reallocate mem sequentially from ft_memheap.
 *         Reallocate a memory with a new size. The old content will be kept
 * @param  ptr pointer to an allocated memory.
 *         Its content will be copied to the new memory block and freed
 * @param  nbytes the desired new size in byte
 * @retval pointer Pointer of allocated memory.
 */
void *ft_srealloc(void *ptr, rt_size_t nbytes)
{
    SIMULATOR_MEM_LEAKAGE_REALLOC(LEAK_FT, ptr, nbytes);

    void *p = NULL;

    if (ptr && app_mem_get_size(ptr) >= nbytes)
        return ptr;
    p = ft_smalloc(nbytes);
    if (p && ptr)
        app_memcpy(p, ptr, app_mem_get_size(ptr));
    if (ptr && (p || 0 == nbytes)) ft_sfree(ptr);
    //if (!p) LOG_I("warning!!! ft_srealloc %d fail!!!", nbytes);
    RET_ADDR_TRACE(p);

    return p;
}

/**
 * @brief  Free mem which allocated from ft_memheap.
 */
void ft_sfree(void *ptr)
{
    SIMULATOR_MEM_LEAKAGE_FREE(LEAK_FT, ptr);

    if (!ptr) return;
#if !defined (FT_CAHCE_STANDALONE)
    ft_alloc_size -= app_mem_get_size(ptr);
#endif
    app_free(ptr);
}

/**
 * @brief  Get remain size of freetype cache.
 */
uint32_t ft_cache_alloc_size(void)
{
#ifdef FT_CAHCE_STANDALONE
    return app_ft_memheap.pool_size - app_ft_memheap.available_size;
#else
    return ft_alloc_size;
#endif
}

/**
 * @brief  Allocate mem for hindi_shaper.
 * @param  size Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *hindi_shaper_malloc(size_t size)
{
    void *ptr = NULL;
#if PSRAM_CACHE_SIZE > 0
    ptr = app_cache_alloc(size, CACHE_PSRAM);
#else
    ptr = rt_malloc(size);
#endif
    RT_ASSERT(ptr);
    return ptr;
}

/**
 * @brief  Free mem which allocated from hindi_shaper_malloc.
 */
void hindi_shaper_free(void *ptr)
{
#if PSRAM_CACHE_SIZE > 0
    app_cache_free(ptr);
#else
    rt_free(ptr);
#endif
}
#endif

#if PKG_USING_FFMPEG

static uint8_t ffmpeg_anim_buf_en = 0;

void ffmpeg_enable_anim_buf(uint8_t en)
{
    ffmpeg_anim_buf_en = en;
}
/**
 * @brief  Allocate mem for ffmpeg.
 * @param  size Size of the memory to allocate in bytes
 * @retval pointer Pointer of allocated memory.
 */
void *ffmpeg_alloc(size_t nbytes)
{
    SIMULATOR_MEM_LEAKAGE_MALLOC(LEAK_FFMPEG, nbytes);

    if (ffmpeg_anim_buf_en)
        return app_anim_alloc(nbytes);
    else
        return app_cache_calloc(1, nbytes, CACHE_PSRAM);
}

/**
 * @brief  Reallocate mem sequentially from psram_memheap.
           Reallocate a memory with a new size. The old content will be kept
 * @param  ptr Pointer to an allocated memory.
 *         Its content will be copied to the new memory block and freed
 * @param  nbytes The desired new size in byte
 * @retval pointer Pointer of allocated memory.
 */
void *ffmpeg_realloc(void *p, size_t new_size)
{
    SIMULATOR_MEM_LEAKAGE_REALLOC(LEAK_FFMPEG, p, new_size);

    if (ffmpeg_anim_buf_en)
    {
        if (!p)
            return app_anim_calloc(1, new_size);

        if (!new_size)
        {
            app_anim_free(p);
            return NULL;
        }

        return app_anim_realloc(p, new_size);
    }

    if (!p)
        return app_cache_calloc(1, new_size, CACHE_PSRAM);

    if (!new_size)
    {
        app_cache_free(p);
        return NULL;
    }

    return app_cache_realloc(p, new_size);
}

/**
 * @brief  Free mem which allocated from ffmpeg_alloc.
 */
void ffmpeg_free(void *p)
{
    SIMULATOR_MEM_LEAKAGE_FREE(LEAK_FFMPEG, p);

    if (ffmpeg_anim_buf_en)
        app_anim_free(p);
    else
        app_cache_free(p);
}
#endif

void app_mem_check(void)
{
    //TBD...
}


/*----------------------------------------------- Internal use. beging -------------------------------------------*/

/**
 * @brief  This following code is for internal use within the solution only and is not intended for customer use.
 */
void app_mem_insert_asyn_node(void *ptr, void (*free_fun)(void *))
{
#ifdef MEM_ASYN_FREE
    mem_async_node_t *node = lv_mem_alloc(sizeof(mem_async_node_t));
    RT_ASSERT(node);
    rt_mutex_take(&mem_asyn_mutex, RT_WAITING_FOREVER);
    node->ptr = ptr;
    node->free = free_fun;
    rt_list_insert_before(&app_mem_async_list, &node->list);
    rt_mutex_release(&mem_asyn_mutex);
#endif
}

void app_mem_free_asyn_node(void)
{
#ifdef MEM_ASYN_FREE
    rt_mutex_take(&mem_asyn_mutex, RT_WAITING_FOREVER);
    rt_list_t *pos, *n;
    rt_list_for_each_safe(pos, n, (&app_mem_async_list))
    {
        mem_async_node_t *node = rt_list_entry(pos, mem_async_node_t, list);
        //LOG_I("header %p size %d ref_count %d", header, header->size, header->ref_count);
        if (0 == MEM_GET_HEADER_IE(node->ptr, ref_count))
        {
            node->free(node->ptr);
            rt_list_remove(pos);
            lv_mem_free(node);
        }
    }
    rt_mutex_release(&mem_asyn_mutex);
#endif
}

#if defined(LV_USING_FREETYPE_ENGINE) && !defined(PKG_SCHRIFT) && !defined(USING_VGLITE)
    extern int ft_get_bitmap_offset(void);
#endif

/* type, 0 : img cache; 1 : font */
void app_mem_set_ref_count(void *ptr, int ref_count, int type)
{
#ifdef MEM_ASYN_FREE
    if (!ptr) return;
#if 0//!defined (BSP_USING_PC_SIMULATOR)
    uint32_t psram2_addr = 0, psram2_size = 0;
#ifdef PSRAM2_BASE_ADDR /* For solution, PSRAM2_BASE_ADDR defined in flash_map.h */
    psram2_addr = PSRAM2_BASE_ADDR;
    psram2_size = PSRAM2_SIZE;
#endif
    /* Determine whether src is on RAM. If it is obtained directly from RAM, otherwise call the cache_image mechanism to cache it */
    if (((HPSYS_RAM0_BASE <= (uint32_t) ptr && (uint32_t) ptr < HPSYS_RAM_END) ||
            (PSRAM_BASE <= (uint32_t) ptr && (uint32_t) ptr < PSRAM_BASE + PSRAM_SIZE) ||
            (psram2_addr <= (uint32_t) ptr && (uint32_t) ptr < psram2_addr + psram2_size)))
#endif
    {
        uint32_t offset = 0;
#if defined(LV_USING_FREETYPE_ENGINE) && !defined(PKG_SCHRIFT) && !defined(USING_VGLITE)
        offset = MEM_ASYN_FONT == type ? ft_get_bitmap_offset() : 0;
#endif
        rt_mutex_take(&mem_asyn_mutex, RT_WAITING_FOREVER);
        uint8_t *p = (uint8_t *) ptr - offset;
        if ((MEM_GET_HEADER_IE(p, magic) & 0xfffe) == MEM_MAGIC &&
                MEM_GET_HEADER_IE(p, ref_count_magic) == REF_COUNT_MAGIC)
        {
            int16_t temp = MEM_GET_HEADER_IE(p, ref_count) + ref_count;
            MEM_SET_HEADER_IE(p, ref_count, temp);
            RT_ASSERT(temp >= 0);
        }
        rt_mutex_release(&mem_asyn_mutex);
    }
#endif
}

static int  mem_asyn_list_init(void)
{
    rt_list_init(&app_mem_async_list);
    rt_mutex_init(&mem_asyn_mutex, "mem_asyn_mutex", RT_IPC_FLAG_FIFO);
    return 0;
}
INIT_PRE_APP_EXPORT(mem_asyn_list_init);

/*----------------------------------------------- Internal use. end -------------------------------------------*/


/**
 * @brief  ulog_ram_mem_malloc is for ulog, mem alloc.
 */
void *ulog_ram_mem_malloc(uint32_t size)
{
#if defined(ULOG_BACKEND_USING_RAM) && defined (BSP_USING_PSRAM)
    return app_cache_alloc(size, CACHE_PSRAM);
#else
    return NULL;
#endif
}

/**
 * @brief  ulog_ram_mem_free is for ulog, mem free.
 */
void ulog_ram_mem_free(void *ptr)
{
    if (ptr) app_cache_free(ptr);
}

/**
 * @brief  ulog_ram_mem_realloc is for ulog, mem realloc.
 */
void *ulog_ram_mem_realloc(void *ptr, rt_size_t size)
{
#if defined(ULOG_BACKEND_USING_RAM) && defined (BSP_USING_PSRAM)
    return app_cache_realloc(ptr, size);
#else
    return NULL;
#endif
}

/**
 * @brief  Audio module memory allocation function.
 * @param  size: Memory size to allocate (unit: bytes)
 * @retval Pointer to allocated memory on success; triggers assertion on failure (no return)
 */
void *audio_mem_malloc(uint32_t size)
{
    void *ptr = app_malloc(size);
    RT_ASSERT(ptr);
    return ptr;
}

/**
 * @brief  Audio module memory free function (paired with audio_mem_malloc)
 * @param  ptr: Pointer to the memory to free
 * @retval None
 */
void audio_mem_free(void *ptr)
{
    app_free(ptr);
}

/**
 * @brief  Audio module memory allocation and zero-initialization function
 *         Implemented based on audio_mem_malloc; automatically zeros the allocated memory region
 *         Triggers RT_ASSERT on allocation failure
 * @param  count: Number of elements to allocate
 * @param  size: Size of each element (unit: bytes)
 * @retval Pointer to allocated and zero-initialized memory on success; triggers assertion on failure
 */

void *audio_mem_calloc(uint32_t count, uint32_t size)
{
    void *ptr = NULL;
    ptr = audio_mem_malloc(count * size);
    if (ptr)
        memset(ptr, 0, count * size);
    return ptr;
}

/**
 * @brief  Audio module memory reallocation function
 * @param  mem_address: Pointer to original memory
 * @param  newsize: New memory size after reallocation (unit: bytes)
 * @retval Pointer to new memory on success; NULL on failure (no assertion, requires upper-layer handling)
 */
void *audio_mem_realloc(void *mem_address, unsigned int newsize)
{
    void *ptr = app_realloc(mem_address, newsize);
    return ptr;
}

/**
 * @brief Define several types of block memory, including the size and number of each type of block memory.
 * BMEM_REGISTER(x, y): x is size (bytes), y is number.
 */

#ifndef APP_BMEM_SELF_DEFINED
    #if defined (USING_BLOCK_MEM) && defined (BSP_USING_PSRAM)
        PSRAM_RET_SECT_BEG;
        BMEM_REGISTER(16, 4000);
        PSRAM_RET_SECT_END
    #endif
#endif
