From 7e45fe460c5936d5bf56afcd83c4fd94eb84b2fd Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 9 Oct 2024 05:27:10 +0200 Subject: [PATCH] simple implementation for grow-only malloc (minimalistic) --- CMakeLists.txt | 1 + dev/lwmem_opts.h | 9 +- dev/main.cpp | 11 +- .../example_realloc_enlarge_full.c | 29 +- .../example_realloc_enlarge_full_log.c | 2 +- lwmem/src/include/lwmem/lwmem.h | 14 +- lwmem/src/lwmem/lwmem.c | 292 ++++++++++++------ tests/lwmem_test.c | 117 ++++--- tests/lwmem_test_simple.c | 68 ++++ 9 files changed, 380 insertions(+), 163 deletions(-) create mode 100644 tests/lwmem_test_simple.c diff --git a/CMakeLists.txt b/CMakeLists.txt index dbb61cc..838327c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ else() target_sources(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/dev/main.cpp ${CMAKE_CURRENT_LIST_DIR}/tests/lwmem_test.c + ${CMAKE_CURRENT_LIST_DIR}/tests/lwmem_test_simple.c # win32 port ${CMAKE_CURRENT_LIST_DIR}/lwmem/src/system/lwmem_sys_win32.c diff --git a/dev/lwmem_opts.h b/dev/lwmem_opts.h index 40e978f..5eb8912 100644 --- a/dev/lwmem_opts.h +++ b/dev/lwmem_opts.h @@ -41,9 +41,10 @@ * Open "include/lwmem/lwmem_opt.h" and * copy & replace here settings you want to change values */ -#define LWMEM_CFG_OS 1 -#define LWMEM_CFG_OS_MUTEX_HANDLE HANDLE -#define LWMEM_CFG_ENABLE_STATS 0 -#define LWMEM_CFG_CLEAN_MEMORY 1 +#define LWMEM_CFG_OS 1 +#define LWMEM_CFG_OS_MUTEX_HANDLE HANDLE +#define LWMEM_CFG_ENABLE_STATS 0 +#define LWMEM_CFG_CLEAN_MEMORY 1 +#define LWMEM_CFG_SUPPORT_REALLOC_AND_FREE 1 #endif /* LWMEM_HDR_OPTS_H */ diff --git a/dev/main.cpp b/dev/main.cpp index bddb83c..e04b8ec 100644 --- a/dev/main.cpp +++ b/dev/main.cpp @@ -5,20 +5,29 @@ #include "lwmem/lwmem.hpp" extern "C" void lwmem_test_run(void); +extern "C" void lwmem_test_simple_run(void); extern "C" void lwmem_test_memory_structure(void); /* Setup manager */ -Lwmem::LwmemLight<1024> manager; +static Lwmem::LwmemLight<1024> manager; int main(void) { +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE lwmem_test_memory_structure(); //lwmem_test_run(); +#else + lwmem_test_simple_run(); +#endif +#if 1 /* Test C++ code */ void* ret = manager.malloc(123); std::cout << ret << std::endl; +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE manager.free(ret); +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ +#endif return 0; } diff --git a/docs/examples_src/example_realloc_enlarge_full.c b/docs/examples_src/example_realloc_enlarge_full.c index 2cc8f74..9d6c349 100644 --- a/docs/examples_src/example_realloc_enlarge_full.c +++ b/docs/examples_src/example_realloc_enlarge_full.c @@ -1,19 +1,20 @@ -#define ASSERT(x) do { \ - if (!(x)) { \ - printf("Assert failed with condition (" # x ")\r\n"); \ - } else {\ - printf("Assert passed with condition (" # x ")\r\n"); \ - }\ -} while (0) +#define ASSERT(x) \ + do { \ + if (!(x)) { \ + printf("Assert failed with condition (" #x ")\r\n"); \ + } else { \ + printf("Assert passed with condition (" #x ")\r\n"); \ + } \ + } while (0) /* For debug purposes */ lwmem_region_t* regions_used; -size_t regions_count = 1; /* Use only 1 region for debug purposes of non-free areas */ +size_t regions_count = 1; /* Use only 1 region for debug purposes of non-free areas */ int main(void) { - uint8_t* ptr1, *ptr2, *ptr3, *ptr4; - uint8_t* rptr1, *rptr2, *rptr3, *rptr4; + uint8_t *ptr1, *ptr2, *ptr3, *ptr4; + uint8_t *rptr1, *rptr2, *rptr3, *rptr4; /* Create regions for debug purpose */ if (!lwmem_debug_create_regions(®ions_used, regions_count, 128)) { @@ -31,11 +32,11 @@ main(void) { ptr2 = lwmem_malloc(4); ptr3 = lwmem_malloc(4); ptr4 = lwmem_malloc(16); - lwmem_free(ptr1); /* Free but keep value for future comparison */ - lwmem_free(ptr3); /* Free but keep value for future comparison */ + lwmem_free(ptr1); /* Free but keep value for future comparison */ + lwmem_free(ptr3); /* Free but keep value for future comparison */ lwmem_debug_print(1, 1); printf("Debug above is effectively state 3\r\n"); - lwmem_debug_save_state(); /* Every restore operations rewinds here */ + lwmem_debug_save_state(); /* Every restore operations rewinds here */ /* We always try to reallocate pointer ptr2 */ @@ -53,7 +54,7 @@ main(void) { printf("State 3b\r\n"); rptr2 = lwmem_realloc(ptr2, 20); lwmem_debug_print(1, 1); - ASSERT(rptr2 == ptr2); + ASSERT(rptr2 == ptr1); /* Create 3c case */ printf("\r\n------------------------------------------------------------------------\r\n"); diff --git a/docs/examples_src/example_realloc_enlarge_full_log.c b/docs/examples_src/example_realloc_enlarge_full_log.c index 3755d2a..4a11289 100644 --- a/docs/examples_src/example_realloc_enlarge_full_log.c +++ b/docs/examples_src/example_realloc_enlarge_full_log.c @@ -52,7 +52,7 @@ State 3b | 4 | 0034A548 | 1 | 56 | 48 |Free block | | 5 | 0034A580 | 0 | 0 | 0 |End of region | |-------|----------|--------|------|------------------|----------------| -Assert failed with condition (rptr2 == ptr2) +Assert passed with condition (rptr2 == ptr1) ------------------------------------------------------------------------ -- > State restored to last saved! diff --git a/lwmem/src/include/lwmem/lwmem.h b/lwmem/src/include/lwmem/lwmem.h index 5698584..54f9041 100644 --- a/lwmem/src/include/lwmem/lwmem.h +++ b/lwmem/src/include/lwmem/lwmem.h @@ -83,10 +83,16 @@ typedef struct { * \brief LwMEM main structure */ typedef struct lwmem { - lwmem_block_t start_block; /*!< Holds beginning of memory allocation regions */ - lwmem_block_t* end_block; /*!< Pointer to the last memory location in regions linked list */ size_t mem_available_bytes; /*!< Memory size available for allocation */ - size_t mem_regions_count; /*!< Number of regions used for allocation */ +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE + lwmem_block_t start_block; /*!< Holds beginning of memory allocation regions */ + lwmem_block_t* end_block; /*!< Pointer to the last memory location in regions linked list */ + size_t mem_regions_count; /*!< Number of regions used for allocation */ +#else + uint8_t* mem_next_available_ptr; /*!< Pointer for next allocation */ + uint8_t is_initialized; /*!< Set to `1` when initialized */ +#endif + #if LWMEM_CFG_OS || __DOXYGEN__ LWMEM_CFG_OS_MUTEX_HANDLE mutex; /*!< System mutex for OS */ #endif /* LWMEM_CFG_OS || __DOXYGEN__ */ @@ -112,7 +118,7 @@ void* lwmem_malloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t void* lwmem_calloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t nitems, const size_t size); #if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE || __DOXYGEN__ void* lwmem_realloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, void* const ptr, const size_t size); -uint8_t lwmem_realloc_s_ex(lwmem_t* lwobj, const lwmem_region_t* region, void** const ptr, const size_t size); +int lwmem_realloc_s_ex(lwmem_t* lwobj, const lwmem_region_t* region, void** const ptr, const size_t size); void lwmem_free_ex(lwmem_t* lwobj, void* const ptr); void lwmem_free_s_ex(lwmem_t* lwobj, void** const ptr); size_t lwmem_get_size_ex(lwmem_t* lwobj, void* ptr); diff --git a/lwmem/src/lwmem/lwmem.c b/lwmem/src/lwmem/lwmem.c index be867bb..da181cf 100644 --- a/lwmem/src/lwmem/lwmem.c +++ b/lwmem/src/lwmem/lwmem.c @@ -169,6 +169,8 @@ */ static lwmem_t lwmem_default; +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE + /** * \brief Get region aligned start address and aligned size * \param[in] region: Region to check for size and address @@ -187,12 +189,6 @@ prv_get_region_addr_size(const lwmem_region_t* region, uint8_t** msa, size_t* ms *msa = NULL; *msz = 0; - /* Check region size and align it to config bits */ - mem_size = region->size & ~LWMEM_ALIGN_BITS; /* Size does not include lower bits */ - if (mem_size < (2 * LWMEM_BLOCK_MIN_SIZE)) { - return 0; - } - /* * Start address must be aligned to configuration * Increase start address and decrease effective region size @@ -442,8 +438,6 @@ prv_alloc(lwmem_t* const lwobj, const lwmem_region_t* region, const size_t size) return retval; } -#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE - /** * \brief Free input pointer * \param[in] lwobj: LwMEM instance. Set to `NULL` to use default instance @@ -582,8 +576,7 @@ prv_realloc(lwmem_t* const lwobj, const lwmem_region_t* region, void* const ptr, /* Input block points to address somewhere between "prev" and "prev->next" pointers */ /* Check if "block" and next free "prev->next" create contiguous memory with size of at least new requested size */ - if ((LWMEM_TO_BYTE_PTR(block) + block_size) - == LWMEM_TO_BYTE_PTR(prev->next) /* Blocks create contiguous block */ + if ((LWMEM_TO_BYTE_PTR(block) + block_size) == LWMEM_TO_BYTE_PTR(prev->next) && (block_size + prev->next->size) >= final_size) { /* Size is greater or equal to requested */ /* @@ -593,8 +586,8 @@ prv_realloc(lwmem_t* const lwobj, const lwmem_region_t* region, void* const ptr, lwobj->mem_available_bytes -= prev->next->size; /* For now decrease effective available bytes */ LWMEM_UPDATE_MIN_FREE(lwobj); block->size = block_size + prev->next->size; /* Increase effective size of new block */ - prev->next = - prev->next->next; /* Set next to next's next, effectively remove expanded block from free list */ + prev->next = prev->next->next; /* Set next to next's next, + effectively remove expanded block from free list */ prv_split_too_big_block(lwobj, block, final_size); /* Split block if it is too big */ LWMEM_BLOCK_SET_ALLOC(block); /* Set block as allocated */ @@ -624,10 +617,10 @@ prv_realloc(lwmem_t* const lwobj, const lwmem_region_t* region, void* const ptr, lwobj->mem_available_bytes -= prev->size; /* For now decrease effective available bytes */ LWMEM_UPDATE_MIN_FREE(lwobj); - prev->size += block_size; /* Increase size of input block size */ - prevprev->next = - prev->next; /* Remove prev from free list as it is now being used for allocation together with existing block */ - block = prev; /* Move block pointer to previous one */ + prev->size += block_size; /* Increase size of input block size */ + prevprev->next = prev->next; /* Remove prev from free list as it is now being used + for allocation together with existing block */ + block = prev; /* Move block pointer to previous one */ prv_split_too_big_block(lwobj, block, final_size); /* Split block if it is too big */ LWMEM_BLOCK_SET_ALLOC(block); /* Set block as allocated */ @@ -698,79 +691,21 @@ prv_realloc(lwmem_t* const lwobj, const lwmem_region_t* region, void* const ptr, return retval; } -#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ - /** - * \brief Initializes and assigns user regions for memory used by allocator algorithm - * \param[in] lwobj: LwMEM instance. Set to `NULL` to use default instance - * \param[in] regions: Pointer to array of regions with address and respective size. - * Regions must be in increasing order (start address) and must not overlap in-between. - * Last region entry must have address `NULL` and size set to `0` - * \code{.c} -//Example definition -lwmem_region_t regions[] = { - { (void *)0x10000000, 0x1000 }, //Region starts at address 0x10000000 and is 0x1000 bytes long - { (void *)0x20000000, 0x2000 }, //Region starts at address 0x20000000 and is 0x2000 bytes long - { (void *)0x30000000, 0x3000 }, //Region starts at address 0x30000000 and is 0x3000 bytes long - { NULL, 0 } //Array termination indicator -} -\endcode - * \return `0` on failure, number of final regions used for memory manager on success - * \note This function is not thread safe when used with operating system. - * It must be called only once to setup memory regions + * \brief Assign the memory structure for advanced memory allocation system + * + * \param lwobj + * \param regions + * \return size_t */ -size_t -lwmem_assignmem_ex(lwmem_t* lwobj, const lwmem_region_t* regions) { - uint8_t* mem_start_addr; - size_t mem_size, len = 0; - lwmem_block_t *first_block, *prev_end_block; +static size_t +prv_assignmem(lwmem_t* lwobj, const lwmem_region_t* regions) { + uint8_t* mem_start_addr = NULL; + size_t mem_size = 0; + lwmem_block_t *first_block = NULL, *prev_end_block = NULL; - lwobj = LWMEM_GET_LWOBJ(lwobj); - /* Check first things first */ - if (regions == NULL || lwobj->end_block != NULL /* Init function may only be called once per lwmem instance */ - || (((size_t)LWMEM_CFG_ALIGN_NUM) & (((size_t)LWMEM_CFG_ALIGN_NUM) - 1)) > 0) { /* Must be power of 2 */ - return 0; - } - - /* Check values entered by application */ - mem_start_addr = (void*)0; - mem_size = 0; - for (size_t i = 0;; ++i) { - /* - * Check for valid entry or end of array descriptor - * - * Invalid entry is considered as "end-of-region" indicator - */ - if (regions[i].size == 0 && regions[i].start_addr == NULL) { - len = i; - if (len == 0) { - return 0; - } - break; - } - - /* New region(s) must be higher (in address space) than previous one */ - if ((mem_start_addr + mem_size) > LWMEM_TO_BYTE_PTR(regions[i].start_addr)) { - return 0; - } - - /* Save new values for next round */ - mem_start_addr = regions[i].start_addr; - mem_size = regions[i].size; - } - - /* Process further checks of valid inputs */ - if (regions == NULL || len == 0 -#if LWMEM_CFG_OS - || lwmem_sys_mutex_isvalid(&(lwobj->mutex)) /* Check if mutex valid already = must not be */ - || !lwmem_sys_mutex_create(&(lwobj->mutex)) /* Final step = try to create mutex for new instance */ -#endif /* LWMEM_CFG_OS */ - ) { - return 0; - } - - for (size_t i = 0; i < len; ++i, ++regions) { - /* Get region start address and size */ + for (size_t idx = 0; regions->size > 0 && regions->start_addr != NULL; ++idx, ++regions) { + /* Get region start address and size, stop on failure */ if (!prv_get_region_addr_size(regions, &mem_start_addr, &mem_size)) { continue; } @@ -831,6 +766,161 @@ lwmem_assignmem_ex(lwmem_t* lwobj, const lwmem_region_t* regions) { return lwobj->mem_regions_count; /* Return number of regions used by manager */ } +#else /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ + +/** + * \brief Assign the regions for simple algorithm + * + * At this point, regions check has been performed, so we assume + * everything is ready to proceed + * + * \param lwobj: LwMEM object + * \param regions: List of regions to assign + * \return Number of regions used + */ +static size_t +prv_assignmem_simple(lwmem_t* const lwobj, const lwmem_region_t* regions) { + uint8_t* mem_start_addr = regions[0].start_addr; + size_t mem_size = regions[0].size; + + /* Adjust alignment data */ + if (((size_t)mem_start_addr) & LWMEM_ALIGN_BITS) { + mem_start_addr += ((size_t)LWMEM_CFG_ALIGN_NUM) - ((size_t)mem_start_addr & LWMEM_ALIGN_BITS); + mem_size -= (size_t)(mem_start_addr - LWMEM_TO_BYTE_PTR(regions[0].start_addr)); + } + + /* Align mem to alignment*/ + mem_size = mem_size & ~LWMEM_ALIGN_BITS; + + /* Store the available information */ + lwobj->mem_available_bytes = mem_size; + lwobj->mem_next_available_ptr = mem_start_addr; + lwobj->is_initialized = 1; + return 1; /* One region is being used only for now */ +} + +/** + * \brief Simple allocation algorithm, that can only allocate memory, + * but it does not support free. + * + * It uses simple first-in-first-serve concept, + * where memory grows upward gradually, up until it reaches the end + * of memory area + * + * \param lwobj: LwMEM object + * \param region: Selected region. Not used in the current revision, + * but footprint remains the same if one day library will support it + * \param size: Requested allocation size + * \return `NULL` on failure, or pointer to allocated memory + */ +static void* +prv_alloc_simple(lwmem_t* const lwobj, const lwmem_region_t* region, const size_t size) { + void* retval = NULL; + const size_t alloc_size = LWMEM_ALIGN(size); + + if (alloc_size <= lwobj->mem_available_bytes) { + retval = lwobj->mem_next_available_ptr; + + /* Get ready for next iteration */ + lwobj->mem_next_available_ptr += alloc_size; + lwobj->mem_available_bytes -= alloc_size; + } + (void)region; + return retval; +} + +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ + +/** + * \brief Initializes and assigns user regions for memory used by allocator algorithm + * \param[in] lwobj: LwMEM instance. Set to `NULL` to use default instance + * \param[in] regions: Pointer to array of regions with address and respective size. + * Regions must be in increasing order (start address) and must not overlap in-between. + * Last region entry must have address `NULL` and size set to `0` + * \code{.c} +//Example definition +lwmem_region_t regions[] = { + { (void *)0x10000000, 0x1000 }, //Region starts at address 0x10000000 and is 0x1000 bytes long + { (void *)0x20000000, 0x2000 }, //Region starts at address 0x20000000 and is 0x2000 bytes long + { (void *)0x30000000, 0x3000 }, //Region starts at address 0x30000000 and is 0x3000 bytes long + { NULL, 0 } //Array termination indicator +} +\endcode + * \return `0` on failure, number of final regions used for memory manager on success + * \note This function is not thread safe when used with operating system. + * It must be called only once to setup memory regions + */ +size_t +lwmem_assignmem_ex(lwmem_t* lwobj, const lwmem_region_t* regions) { + uint8_t* mem_start_addr = NULL; + size_t mem_size = 0, len = 0; + + lwobj = LWMEM_GET_LWOBJ(lwobj); + + /* Check first things first */ + if (regions == NULL || (((size_t)LWMEM_CFG_ALIGN_NUM) & (((size_t)LWMEM_CFG_ALIGN_NUM) - 1)) > 0 +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE + || lwobj->end_block != NULL /* Init function may only be called once per lwmem instance */ +#else + || lwobj->is_initialized /* Already initialized? */ +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ + ) { + return 0; + } + + /* Check values entered by application */ + mem_start_addr = (void*)0; + mem_size = 0; + for (size_t idx = 0;; ++idx) { + /* + * Check for valid entry or end of array descriptor + * Invalid entry is considered as "end-of-region" indicator + */ + if (regions[idx].size == 0 && regions[idx].start_addr == NULL) { + len = idx; + if (len == 0) { + return 0; + } + break; + } + +#if !LWMEM_CFG_SUPPORT_REALLOC_AND_FREE + /* + * In case of simple allocation algorithm, we (for now!) only allow one region. + * Return zero value if user passed more than one region in a sequence. + */ + else if (idx > 0) { + return 0; + } +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ + + /* New region(s) must be higher (in address space) than previous one */ + if ((mem_start_addr + mem_size) > LWMEM_TO_BYTE_PTR(regions[idx].start_addr)) { + return 0; + } + + /* Save new values for next round */ + mem_start_addr = regions[idx].start_addr; + mem_size = regions[idx].size; + } + + /* Final init and check before initializing the regions */ + if (len == 0 +#if LWMEM_CFG_OS + || lwmem_sys_mutex_isvalid(&(lwobj->mutex)) /* Check if mutex valid already = must not be */ + || !lwmem_sys_mutex_create(&(lwobj->mutex)) /* Final step = try to create mutex for new instance */ +#endif /* LWMEM_CFG_OS */ + ) { + return 0; + } + +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE + return prv_assignmem(lwobj, regions); +#else /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ + return prv_assignmem_simple(lwobj, regions); +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ +} + /** * \brief Allocate memory of requested size in specific lwmem instance and optional region. * \note This is an extended malloc version function declaration to support advanced features @@ -843,10 +933,16 @@ lwmem_assignmem_ex(lwmem_t* lwobj, const lwmem_region_t* regions) { */ void* lwmem_malloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t size) { - void* ptr; + void* ptr = NULL; + lwobj = LWMEM_GET_LWOBJ(lwobj); + LWMEM_PROTECT(lwobj); +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE ptr = prv_alloc(lwobj, region, size); +#else /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ + ptr = prv_alloc_simple(lwobj, region, size); +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ LWMEM_UNPROTECT(lwobj); return ptr; } @@ -856,7 +952,7 @@ lwmem_malloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t size) * in specific lwmem instance and region. * * It resets allocated block of memory to zero if allocation is successful - + * * \note This is an extended calloc version function declaration to support advanced features * \param[in] lwobj: LwMEM instance. Set to `NULL` to use default instance * \param[in] region: Optional region instance within LwMEM instance to force allocation from. @@ -868,16 +964,22 @@ lwmem_malloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t size) */ void* lwmem_calloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t nitems, const size_t size) { - void* ptr; - const size_t s = size * nitems; + void* ptr = NULL; + const size_t alloc_size = size * nitems; lwobj = LWMEM_GET_LWOBJ(lwobj); + LWMEM_PROTECT(lwobj); - ptr = prv_alloc(lwobj, region, s); - if (ptr != NULL) { - LWMEM_MEMSET(ptr, 0x00, s); - } +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE + ptr = prv_alloc(lwobj, region, alloc_size); +#else /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ + ptr = prv_alloc_simple(lwobj, region, alloc_size); +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ LWMEM_UNPROTECT(lwobj); + + if (ptr != NULL) { + LWMEM_MEMSET(ptr, 0x00, alloc_size); + } return ptr; } @@ -943,7 +1045,7 @@ lwmem_realloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, void* const ptr, * \return `1` if successfully reallocated, `0` otherwise * \note This function is thread safe when \ref LWMEM_CFG_OS is enabled */ -uint8_t +int lwmem_realloc_s_ex(lwmem_t* lwobj, const lwmem_region_t* region, void** const ptr, const size_t size) { void* new_ptr; diff --git a/tests/lwmem_test.c b/tests/lwmem_test.c index 3737c2b..559e231 100644 --- a/tests/lwmem_test.c +++ b/tests/lwmem_test.c @@ -1,70 +1,75 @@ -#include "lwmem/lwmem.h" #include +#include "lwmem/lwmem.h" + +#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE /* Assert check */ -#define ASSERT(x) do { \ - if (!(x)) { \ - printf("Assert on line %d failed with condition (" # x ")\r\n", (int)__LINE__); \ - } else {\ - printf("Assert on line %d passed with condition (" # x ")\r\n", (int)__LINE__); \ - }\ -} while (0) +#define ASSERT(x) \ + do { \ + if (!(x)) { \ + printf("Assert on line %d failed with condition (" #x ")\r\n", (int)__LINE__); \ + } else { \ + printf("Assert on line %d passed with condition (" #x ")\r\n", (int)__LINE__); \ + } \ + } while (0) /********************************************/ /* Test case helpers */ -#define UINT_PTR_CAST(x) ((uintptr_t)(x)) -#define IS_ALLOC_IN_REGION(ptr, region) ASSERT( \ - UINT_PTR_CAST(ptr) >= UINT_PTR_CAST((region)->start_addr) \ - && UINT_PTR_CAST(ptr) < (UINT_PTR_CAST((region)->start_addr) + (region)->size) \ -) +#define UINT_PTR_CAST(x) ((uintptr_t)(x)) +#define IS_ALLOC_IN_REGION(ptr, region) \ + ASSERT(UINT_PTR_CAST(ptr) >= UINT_PTR_CAST((region)->start_addr) \ + && UINT_PTR_CAST(ptr) < (UINT_PTR_CAST((region)->start_addr) + (region)->size)) /********************************************/ /* Configuration for default lwmem instance */ /* Region memory declaration */ -uint8_t lw_mem1[1024], lw_mem2[256], lw_mem3[128]; +static struct { + uint8_t m1[128]; + uint8_t m2[256]; + uint8_t m3[1024]; +} lw_mem; /* Regions descriptor */ -lwmem_region_t -lw_regions[] = { - { lw_mem3, sizeof(lw_mem3) }, - { lw_mem2, sizeof(lw_mem2) }, - { lw_mem1, sizeof(lw_mem1) }, - { NULL, 0 } +static lwmem_region_t lw_regions[] = { + {lw_mem.m1, sizeof(lw_mem.m1)}, + {lw_mem.m2, sizeof(lw_mem.m2)}, + {lw_mem.m3, sizeof(lw_mem.m3)}, + {NULL, 0}, }; /********************************************/ /********************************************/ /* Configuration for custom lwmem instance */ /* LwMEM instance */ -lwmem_t lw_c; +static lwmem_t lw_c; -/* Region memory declaration */ -uint8_t lw_c_mem1[1024], lw_c_mem2[256], lw_c_mem3[128]; +static struct { + uint8_t m1[128]; + uint8_t m2[256]; + uint8_t m3[1024]; +} lw_mem_c; /* Regions descriptor */ -lwmem_region_t -lw_c_regions[] = { - { lw_c_mem3, sizeof(lw_c_mem3) }, - { lw_c_mem2, sizeof(lw_c_mem2) }, - { lw_c_mem1, sizeof(lw_c_mem1) }, - { NULL, 0 } +static lwmem_region_t lw_c_regions[] = { + {lw_mem_c.m1, sizeof(lw_mem_c.m1)}, + {lw_mem_c.m2, sizeof(lw_mem_c.m2)}, + {lw_mem_c.m3, sizeof(lw_mem_c.m3)}, + {NULL, 0}, }; + /********************************************/ void lwmem_test_run(void) { - void* ptr_1, * ptr_2, * ptr_3; - void* ptr_c_1, * ptr_c_2, * ptr_c_3; + void *ptr_1 = NULL, *ptr_2 = NULL, *ptr_3 = NULL; + void *ptr_c_1 = NULL, *ptr_c_2 = NULL, *ptr_c_3 = NULL; /* Initialize default lwmem instance */ /* Use one of 2 possible function calls: */ lwmem_assignmem(lw_regions); //lwmem_assignmem_ex(NULL, lw_regions); - /* Initialize another, custom instance */ - lwmem_assignmem_ex(&lw_c, lw_c_regions); - /* Regions initialized... */ /********************************************/ @@ -73,15 +78,18 @@ lwmem_test_run(void) { /* Allocation of 64 bytes must in in first region */ ptr_1 = lwmem_malloc(64); + ASSERT(ptr_1 != NULL); IS_ALLOC_IN_REGION(ptr_1, &lw_regions[0]); /* Allocation of 256 bytes can only be in 3rd region */ ptr_2 = lwmem_malloc(256); + ASSERT(ptr_2 != NULL); IS_ALLOC_IN_REGION(ptr_2, &lw_regions[2]); /* Allocation of 128 bytes can be in second or third region (depends on memory availability), but in case of these tests it can be (and should be) in second region */ ptr_3 = lwmem_malloc(128); + ASSERT(ptr_3 != NULL); IS_ALLOC_IN_REGION(ptr_3, &lw_regions[1]); /* Free all pointers to default state */ @@ -92,10 +100,12 @@ lwmem_test_run(void) { /* Force allocation region to be used */ /* Allocation of 16-bytes forced to 2nd region */ ptr_1 = lwmem_malloc_ex(NULL, &lw_regions[1], 16); + ASSERT(ptr_1 != NULL); IS_ALLOC_IN_REGION(ptr_1, &lw_regions[1]); /* Allocate ptr 2 in any region of default lwmem, the first available must be 1st region */ ptr_2 = lwmem_malloc_ex(NULL, NULL, 16); + ASSERT(ptr_2 != NULL); IS_ALLOC_IN_REGION(ptr_2, &lw_regions[0]); /* Free pointers */ @@ -106,33 +116,41 @@ lwmem_test_run(void) { /* Run tests on custom region */ /********************************************/ + /* Initialize another, custom instance */ + lwmem_assignmem_ex(&lw_c, lw_c_regions); + /* Allocation of 64 bytes must in in first region */ ptr_c_1 = lwmem_malloc_ex(&lw_c, NULL, 64); + ASSERT(ptr_c_1 != NULL); IS_ALLOC_IN_REGION(ptr_c_1, &lw_c_regions[0]); /* Allocation of 256 bytes can only be in 3rd region */ ptr_c_2 = lwmem_malloc_ex(&lw_c, NULL, 256); + ASSERT(ptr_c_2 != NULL); IS_ALLOC_IN_REGION(ptr_c_2, &lw_c_regions[2]); /* Allocation of 128 bytes can be in second or third region (depends on memory availability), but in case of these tests it can be (and should be) in second region */ ptr_c_3 = lwmem_malloc_ex(&lw_c, NULL, 128); + ASSERT(ptr_c_3 != NULL); IS_ALLOC_IN_REGION(ptr_c_3, &lw_c_regions[1]); /* Free all pointers to default state */ lwmem_free(ptr_c_1); lwmem_free(ptr_c_2); lwmem_free(ptr_c_3); + + printf("Done\r\n"); } /* For debug purposes */ -lwmem_region_t* regions_used; -size_t regions_count = 4; /* Use only 1 region for debug purposes of non-free areas */ +static lwmem_region_t* regions_used; +static size_t regions_count = 4; /* Use only 1 region for debug purposes of non-free areas */ void lwmem_test_memory_structure(void) { - uint8_t* ptr1, *ptr2, *ptr3, *ptr4; - uint8_t* rptr1, *rptr2, *rptr3, *rptr4; + uint8_t *ptr1, *ptr2, *ptr3, *ptr4; + uint8_t *rptr1, *rptr2, *rptr3, *rptr4; size_t used_regions; /* @@ -156,16 +174,20 @@ lwmem_test_memory_structure(void) { /* Test case 1, allocate 3 blocks, each of different size */ /* We know that sizeof internal metadata block is 8 bytes on win32 */ - printf("\r\n\r\nAllocating 4 pointers and freeing first and third..\r\n"); + printf("\r\n------------------------------------------------------------------------\r\n"); + printf("Allocating 4 pointers\r\n\r\n"); ptr1 = lwmem_malloc(8); ptr2 = lwmem_malloc(4); ptr3 = lwmem_malloc(4); ptr4 = lwmem_malloc(16); - lwmem_free(ptr1); /* Free but keep value for future comparison */ - lwmem_free(ptr3); /* Free but keep value for future comparison */ + lwmem_debug_print(1, 1); + printf("\r\n------------------------------------------------------------------------\r\n"); + printf("Freeing first and third pointers\r\n\r\n"); + lwmem_free(ptr1); /* Free but keep value for future comparison */ + lwmem_free(ptr3); /* Free but keep value for future comparison */ lwmem_debug_print(1, 1); printf("Debug above is effectively state 3\r\n"); - lwmem_debug_save_state(); /* Every restore operations rewinds here */ + lwmem_debug_save_state(); /* Every restore operations rewinds here */ /* We always try to reallocate pointer ptr2 */ @@ -183,7 +205,7 @@ lwmem_test_memory_structure(void) { printf("State 3b\r\n"); rptr2 = lwmem_realloc(ptr2, 20); lwmem_debug_print(1, 1); - ASSERT(rptr2 == ptr2); + ASSERT(rptr2 == ptr1); /* Create 3c case */ printf("\r\n------------------------------------------------------------------------\r\n"); @@ -200,4 +222,11 @@ lwmem_test_memory_structure(void) { rptr4 = lwmem_realloc(ptr2, 36); lwmem_debug_print(1, 1); ASSERT(rptr4 != ptr1 && rptr4 != ptr2 && rptr4 != ptr3 && rptr4 != ptr4); -} \ No newline at end of file + + printf("ptr1: %08X\r\nptr2: %08X\r\nptr3: %08X\r\nptr4: %08X\r\n", (unsigned)ptr1, (unsigned)ptr2, (unsigned)ptr3, + (unsigned)ptr4); + printf("r_ptr1: %08X\r\nr_ptr2: %08X\r\nr_ptr3: %08X\r\nr_ptr4: %08X\r\n", (unsigned)rptr1, (unsigned)rptr2, + (unsigned)rptr3, (unsigned)rptr4); +} + +#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ \ No newline at end of file diff --git a/tests/lwmem_test_simple.c b/tests/lwmem_test_simple.c new file mode 100644 index 0000000..19fcc06 --- /dev/null +++ b/tests/lwmem_test_simple.c @@ -0,0 +1,68 @@ +#include +#include "lwmem/lwmem.h" + +#if !LWMEM_CFG_SUPPORT_REALLOC_AND_FREE + +/* Assert check */ +#define ASSERT(x) \ + do { \ + if (!(x)) { \ + printf("Assert on line %d failed with condition (" #x ")\r\n", (int)__LINE__); \ + } else { \ + printf("Assert on line %d passed with condition (" #x ")\r\n", (int)__LINE__); \ + } \ + } while (0) + +/********************************************/ +/* Configuration for default lwmem instance */ + +/* Region memory declaration */ +static uint8_t lw_mem1[1024], lw_mem2[256], lw_mem3[128]; + +/* Regions descriptor */ +static lwmem_region_t lw_regions_too_many[] = { + {lw_mem3, sizeof(lw_mem3)}, + {lw_mem2, sizeof(lw_mem2)}, + {lw_mem1, sizeof(lw_mem1)}, + {NULL, 0}, +}; + +/********************************************/ +/********************************************/ +/* Region memory declaration */ +/* Use uint32 for alignment reasons */ +static uint32_t lw_c_mem1[64 / 4]; + +/* Regions descriptor */ +static lwmem_region_t lw_c_regions[] = { + {lw_c_mem1, sizeof(lw_c_mem1)}, + {NULL, 0}, +}; + +/********************************************/ + +void +lwmem_test_simple_run(void) { + size_t retval; + void* ptr; + + /* Should fail -> too many regions */ + retval = lwmem_assignmem(lw_regions_too_many); + ASSERT(retval == 0); + + /* Should fly now */ + retval = lwmem_assignmem(lw_c_regions); + ASSERT(retval != 0); + + /* We have 64 bytes from now on */ + + /* Try to allocate memory */ + ptr = lwmem_malloc(32); + ASSERT(ptr != NULL); + ptr = lwmem_malloc(32); + ASSERT(ptr != NULL); + ptr = lwmem_malloc(4); + ASSERT(ptr == NULL); +} + +#endif /* !LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */ \ No newline at end of file