simple implementation for grow-only malloc (minimalistic)

This commit is contained in:
Tilen Majerle 2024-10-09 05:27:10 +02:00
parent 254ea2855f
commit 7e45fe460c
9 changed files with 380 additions and 163 deletions

View File

@ -10,6 +10,7 @@ else()
target_sources(${PROJECT_NAME} PUBLIC target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/dev/main.cpp ${CMAKE_CURRENT_LIST_DIR}/dev/main.cpp
${CMAKE_CURRENT_LIST_DIR}/tests/lwmem_test.c ${CMAKE_CURRENT_LIST_DIR}/tests/lwmem_test.c
${CMAKE_CURRENT_LIST_DIR}/tests/lwmem_test_simple.c
# win32 port # win32 port
${CMAKE_CURRENT_LIST_DIR}/lwmem/src/system/lwmem_sys_win32.c ${CMAKE_CURRENT_LIST_DIR}/lwmem/src/system/lwmem_sys_win32.c

View File

@ -41,9 +41,10 @@
* Open "include/lwmem/lwmem_opt.h" and * Open "include/lwmem/lwmem_opt.h" and
* copy & replace here settings you want to change values * copy & replace here settings you want to change values
*/ */
#define LWMEM_CFG_OS 1 #define LWMEM_CFG_OS 1
#define LWMEM_CFG_OS_MUTEX_HANDLE HANDLE #define LWMEM_CFG_OS_MUTEX_HANDLE HANDLE
#define LWMEM_CFG_ENABLE_STATS 0 #define LWMEM_CFG_ENABLE_STATS 0
#define LWMEM_CFG_CLEAN_MEMORY 1 #define LWMEM_CFG_CLEAN_MEMORY 1
#define LWMEM_CFG_SUPPORT_REALLOC_AND_FREE 1
#endif /* LWMEM_HDR_OPTS_H */ #endif /* LWMEM_HDR_OPTS_H */

View File

@ -5,20 +5,29 @@
#include "lwmem/lwmem.hpp" #include "lwmem/lwmem.hpp"
extern "C" void lwmem_test_run(void); extern "C" void lwmem_test_run(void);
extern "C" void lwmem_test_simple_run(void);
extern "C" void lwmem_test_memory_structure(void); extern "C" void lwmem_test_memory_structure(void);
/* Setup manager */ /* Setup manager */
Lwmem::LwmemLight<1024> manager; static Lwmem::LwmemLight<1024> manager;
int int
main(void) { main(void) {
#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE
lwmem_test_memory_structure(); lwmem_test_memory_structure();
//lwmem_test_run(); //lwmem_test_run();
#else
lwmem_test_simple_run();
#endif
#if 1
/* Test C++ code */ /* Test C++ code */
void* ret = manager.malloc(123); void* ret = manager.malloc(123);
std::cout << ret << std::endl; std::cout << ret << std::endl;
#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE
manager.free(ret); manager.free(ret);
#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */
#endif
return 0; return 0;
} }

View File

@ -1,19 +1,20 @@
#define ASSERT(x) do { \ #define ASSERT(x) \
if (!(x)) { \ do { \
printf("Assert failed with condition (" # x ")\r\n"); \ if (!(x)) { \
} else {\ printf("Assert failed with condition (" #x ")\r\n"); \
printf("Assert passed with condition (" # x ")\r\n"); \ } else { \
}\ printf("Assert passed with condition (" #x ")\r\n"); \
} while (0) } \
} while (0)
/* For debug purposes */ /* For debug purposes */
lwmem_region_t* regions_used; 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 int
main(void) { main(void) {
uint8_t* ptr1, *ptr2, *ptr3, *ptr4; uint8_t *ptr1, *ptr2, *ptr3, *ptr4;
uint8_t* rptr1, *rptr2, *rptr3, *rptr4; uint8_t *rptr1, *rptr2, *rptr3, *rptr4;
/* Create regions for debug purpose */ /* Create regions for debug purpose */
if (!lwmem_debug_create_regions(&regions_used, regions_count, 128)) { if (!lwmem_debug_create_regions(&regions_used, regions_count, 128)) {
@ -31,11 +32,11 @@ main(void) {
ptr2 = lwmem_malloc(4); ptr2 = lwmem_malloc(4);
ptr3 = lwmem_malloc(4); ptr3 = lwmem_malloc(4);
ptr4 = lwmem_malloc(16); ptr4 = lwmem_malloc(16);
lwmem_free(ptr1); /* 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_free(ptr3); /* Free but keep value for future comparison */
lwmem_debug_print(1, 1); lwmem_debug_print(1, 1);
printf("Debug above is effectively state 3\r\n"); 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 */ /* We always try to reallocate pointer ptr2 */
@ -53,7 +54,7 @@ main(void) {
printf("State 3b\r\n"); printf("State 3b\r\n");
rptr2 = lwmem_realloc(ptr2, 20); rptr2 = lwmem_realloc(ptr2, 20);
lwmem_debug_print(1, 1); lwmem_debug_print(1, 1);
ASSERT(rptr2 == ptr2); ASSERT(rptr2 == ptr1);
/* Create 3c case */ /* Create 3c case */
printf("\r\n------------------------------------------------------------------------\r\n"); printf("\r\n------------------------------------------------------------------------\r\n");

View File

@ -52,7 +52,7 @@ State 3b
| 4 | 0034A548 | 1 | 56 | 48 |Free block | | 4 | 0034A548 | 1 | 56 | 48 |Free block |
| 5 | 0034A580 | 0 | 0 | 0 |End of region | | 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! -- > State restored to last saved!

View File

@ -83,10 +83,16 @@ typedef struct {
* \brief LwMEM main structure * \brief LwMEM main structure
*/ */
typedef struct lwmem { 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_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__ #if LWMEM_CFG_OS || __DOXYGEN__
LWMEM_CFG_OS_MUTEX_HANDLE mutex; /*!< System mutex for OS */ LWMEM_CFG_OS_MUTEX_HANDLE mutex; /*!< System mutex for OS */
#endif /* LWMEM_CFG_OS || __DOXYGEN__ */ #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); 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__ #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); 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_ex(lwmem_t* lwobj, void* const ptr);
void lwmem_free_s_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); size_t lwmem_get_size_ex(lwmem_t* lwobj, void* ptr);

View File

@ -169,6 +169,8 @@
*/ */
static lwmem_t lwmem_default; static lwmem_t lwmem_default;
#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE
/** /**
* \brief Get region aligned start address and aligned size * \brief Get region aligned start address and aligned size
* \param[in] region: Region to check for size and address * \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; *msa = NULL;
*msz = 0; *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 * Start address must be aligned to configuration
* Increase start address and decrease effective region size * 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; return retval;
} }
#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE
/** /**
* \brief Free input pointer * \brief Free input pointer
* \param[in] lwobj: LwMEM instance. Set to `NULL` to use default instance * \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 */ /* 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 */ /* 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) if ((LWMEM_TO_BYTE_PTR(block) + block_size) == LWMEM_TO_BYTE_PTR(prev->next)
== LWMEM_TO_BYTE_PTR(prev->next) /* Blocks create contiguous block */
&& (block_size + prev->next->size) >= final_size) { /* Size is greater or equal to requested */ && (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 */ lwobj->mem_available_bytes -= prev->next->size; /* For now decrease effective available bytes */
LWMEM_UPDATE_MIN_FREE(lwobj); LWMEM_UPDATE_MIN_FREE(lwobj);
block->size = block_size + prev->next->size; /* Increase effective size of new block */ block->size = block_size + prev->next->size; /* Increase effective size of new block */
prev->next = prev->next = prev->next->next; /* Set next to next's next,
prev->next->next; /* Set next to next's next, effectively remove expanded block from free list */ effectively remove expanded block from free list */
prv_split_too_big_block(lwobj, block, final_size); /* Split block if it is too big */ 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 */ 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 */ lwobj->mem_available_bytes -= prev->size; /* For now decrease effective available bytes */
LWMEM_UPDATE_MIN_FREE(lwobj); LWMEM_UPDATE_MIN_FREE(lwobj);
prev->size += block_size; /* Increase size of input block size */ prev->size += block_size; /* Increase size of input block size */
prevprev->next = prevprev->next = prev->next; /* Remove prev from free list as it is now being used
prev->next; /* Remove prev from free list as it is now being used for allocation together with existing block */ for allocation together with existing block */
block = prev; /* Move block pointer to previous one */ block = prev; /* Move block pointer to previous one */
prv_split_too_big_block(lwobj, block, final_size); /* Split block if it is too big */ 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 */ 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; return retval;
} }
#endif /* LWMEM_CFG_SUPPORT_REALLOC_AND_FREE */
/** /**
* \brief Initializes and assigns user regions for memory used by allocator algorithm * \brief Assign the memory structure for advanced memory allocation system
* \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. * \param lwobj
* Regions must be in increasing order (start address) and must not overlap in-between. * \param regions
* Last region entry must have address `NULL` and size set to `0` * \return size_t
* \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 static size_t
lwmem_assignmem_ex(lwmem_t* lwobj, const lwmem_region_t* regions) { prv_assignmem(lwmem_t* lwobj, const lwmem_region_t* regions) {
uint8_t* mem_start_addr; uint8_t* mem_start_addr = NULL;
size_t mem_size, len = 0; size_t mem_size = 0;
lwmem_block_t *first_block, *prev_end_block; lwmem_block_t *first_block = NULL, *prev_end_block = NULL;
lwobj = LWMEM_GET_LWOBJ(lwobj); for (size_t idx = 0; regions->size > 0 && regions->start_addr != NULL; ++idx, ++regions) {
/* Check first things first */ /* Get region start address and size, stop on failure */
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 */
if (!prv_get_region_addr_size(regions, &mem_start_addr, &mem_size)) { if (!prv_get_region_addr_size(regions, &mem_start_addr, &mem_size)) {
continue; 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 */ 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. * \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 * \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* void*
lwmem_malloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t size) { 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); lwobj = LWMEM_GET_LWOBJ(lwobj);
LWMEM_PROTECT(lwobj); LWMEM_PROTECT(lwobj);
#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE
ptr = prv_alloc(lwobj, region, size); 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); LWMEM_UNPROTECT(lwobj);
return ptr; 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. * in specific lwmem instance and region.
* *
* It resets allocated block of memory to zero if allocation is successful * 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 * \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] lwobj: LwMEM instance. Set to `NULL` to use default instance
* \param[in] region: Optional region instance within LwMEM instance to force allocation from. * \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* void*
lwmem_calloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t nitems, const size_t size) { lwmem_calloc_ex(lwmem_t* lwobj, const lwmem_region_t* region, const size_t nitems, const size_t size) {
void* ptr; void* ptr = NULL;
const size_t s = size * nitems; const size_t alloc_size = size * nitems;
lwobj = LWMEM_GET_LWOBJ(lwobj); lwobj = LWMEM_GET_LWOBJ(lwobj);
LWMEM_PROTECT(lwobj); LWMEM_PROTECT(lwobj);
ptr = prv_alloc(lwobj, region, s); #if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE
if (ptr != NULL) { ptr = prv_alloc(lwobj, region, alloc_size);
LWMEM_MEMSET(ptr, 0x00, s); #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); LWMEM_UNPROTECT(lwobj);
if (ptr != NULL) {
LWMEM_MEMSET(ptr, 0x00, alloc_size);
}
return ptr; 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 * \return `1` if successfully reallocated, `0` otherwise
* \note This function is thread safe when \ref LWMEM_CFG_OS is enabled * \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) { lwmem_realloc_s_ex(lwmem_t* lwobj, const lwmem_region_t* region, void** const ptr, const size_t size) {
void* new_ptr; void* new_ptr;

View File

@ -1,70 +1,75 @@
#include "lwmem/lwmem.h"
#include <stdio.h> #include <stdio.h>
#include "lwmem/lwmem.h"
#if LWMEM_CFG_SUPPORT_REALLOC_AND_FREE
/* Assert check */ /* Assert check */
#define ASSERT(x) do { \ #define ASSERT(x) \
if (!(x)) { \ do { \
printf("Assert on line %d failed with condition (" # x ")\r\n", (int)__LINE__); \ if (!(x)) { \
} else {\ printf("Assert on line %d failed with condition (" #x ")\r\n", (int)__LINE__); \
printf("Assert on line %d passed with condition (" # x ")\r\n", (int)__LINE__); \ } else { \
}\ printf("Assert on line %d passed with condition (" #x ")\r\n", (int)__LINE__); \
} while (0) } \
} while (0)
/********************************************/ /********************************************/
/* Test case helpers */ /* Test case helpers */
#define UINT_PTR_CAST(x) ((uintptr_t)(x)) #define UINT_PTR_CAST(x) ((uintptr_t)(x))
#define IS_ALLOC_IN_REGION(ptr, region) ASSERT( \ #define IS_ALLOC_IN_REGION(ptr, region) \
UINT_PTR_CAST(ptr) >= UINT_PTR_CAST((region)->start_addr) \ ASSERT(UINT_PTR_CAST(ptr) >= UINT_PTR_CAST((region)->start_addr) \
&& UINT_PTR_CAST(ptr) < (UINT_PTR_CAST((region)->start_addr) + (region)->size) \ && UINT_PTR_CAST(ptr) < (UINT_PTR_CAST((region)->start_addr) + (region)->size))
)
/********************************************/ /********************************************/
/* Configuration for default lwmem instance */ /* Configuration for default lwmem instance */
/* Region memory declaration */ /* 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 */ /* Regions descriptor */
lwmem_region_t static lwmem_region_t lw_regions[] = {
lw_regions[] = { {lw_mem.m1, sizeof(lw_mem.m1)},
{ lw_mem3, sizeof(lw_mem3) }, {lw_mem.m2, sizeof(lw_mem.m2)},
{ lw_mem2, sizeof(lw_mem2) }, {lw_mem.m3, sizeof(lw_mem.m3)},
{ lw_mem1, sizeof(lw_mem1) }, {NULL, 0},
{ NULL, 0 }
}; };
/********************************************/ /********************************************/
/********************************************/ /********************************************/
/* Configuration for custom lwmem instance */ /* Configuration for custom lwmem instance */
/* LwMEM instance */ /* LwMEM instance */
lwmem_t lw_c; static lwmem_t lw_c;
/* Region memory declaration */ static struct {
uint8_t lw_c_mem1[1024], lw_c_mem2[256], lw_c_mem3[128]; uint8_t m1[128];
uint8_t m2[256];
uint8_t m3[1024];
} lw_mem_c;
/* Regions descriptor */ /* Regions descriptor */
lwmem_region_t static lwmem_region_t lw_c_regions[] = {
lw_c_regions[] = { {lw_mem_c.m1, sizeof(lw_mem_c.m1)},
{ lw_c_mem3, sizeof(lw_c_mem3) }, {lw_mem_c.m2, sizeof(lw_mem_c.m2)},
{ lw_c_mem2, sizeof(lw_c_mem2) }, {lw_mem_c.m3, sizeof(lw_mem_c.m3)},
{ lw_c_mem1, sizeof(lw_c_mem1) }, {NULL, 0},
{ NULL, 0 }
}; };
/********************************************/ /********************************************/
void void
lwmem_test_run(void) { lwmem_test_run(void) {
void* ptr_1, * ptr_2, * ptr_3; void *ptr_1 = NULL, *ptr_2 = NULL, *ptr_3 = NULL;
void* ptr_c_1, * ptr_c_2, * ptr_c_3; void *ptr_c_1 = NULL, *ptr_c_2 = NULL, *ptr_c_3 = NULL;
/* Initialize default lwmem instance */ /* Initialize default lwmem instance */
/* Use one of 2 possible function calls: */ /* Use one of 2 possible function calls: */
lwmem_assignmem(lw_regions); lwmem_assignmem(lw_regions);
//lwmem_assignmem_ex(NULL, lw_regions); //lwmem_assignmem_ex(NULL, lw_regions);
/* Initialize another, custom instance */
lwmem_assignmem_ex(&lw_c, lw_c_regions);
/* Regions initialized... */ /* Regions initialized... */
/********************************************/ /********************************************/
@ -73,15 +78,18 @@ lwmem_test_run(void) {
/* Allocation of 64 bytes must in in first region */ /* Allocation of 64 bytes must in in first region */
ptr_1 = lwmem_malloc(64); ptr_1 = lwmem_malloc(64);
ASSERT(ptr_1 != NULL);
IS_ALLOC_IN_REGION(ptr_1, &lw_regions[0]); IS_ALLOC_IN_REGION(ptr_1, &lw_regions[0]);
/* Allocation of 256 bytes can only be in 3rd region */ /* Allocation of 256 bytes can only be in 3rd region */
ptr_2 = lwmem_malloc(256); ptr_2 = lwmem_malloc(256);
ASSERT(ptr_2 != NULL);
IS_ALLOC_IN_REGION(ptr_2, &lw_regions[2]); IS_ALLOC_IN_REGION(ptr_2, &lw_regions[2]);
/* Allocation of 128 bytes can be in second or third region (depends on memory availability), /* 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 */ but in case of these tests it can be (and should be) in second region */
ptr_3 = lwmem_malloc(128); ptr_3 = lwmem_malloc(128);
ASSERT(ptr_3 != NULL);
IS_ALLOC_IN_REGION(ptr_3, &lw_regions[1]); IS_ALLOC_IN_REGION(ptr_3, &lw_regions[1]);
/* Free all pointers to default state */ /* Free all pointers to default state */
@ -92,10 +100,12 @@ lwmem_test_run(void) {
/* Force allocation region to be used */ /* Force allocation region to be used */
/* Allocation of 16-bytes forced to 2nd region */ /* Allocation of 16-bytes forced to 2nd region */
ptr_1 = lwmem_malloc_ex(NULL, &lw_regions[1], 16); ptr_1 = lwmem_malloc_ex(NULL, &lw_regions[1], 16);
ASSERT(ptr_1 != NULL);
IS_ALLOC_IN_REGION(ptr_1, &lw_regions[1]); 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 */ /* Allocate ptr 2 in any region of default lwmem, the first available must be 1st region */
ptr_2 = lwmem_malloc_ex(NULL, NULL, 16); ptr_2 = lwmem_malloc_ex(NULL, NULL, 16);
ASSERT(ptr_2 != NULL);
IS_ALLOC_IN_REGION(ptr_2, &lw_regions[0]); IS_ALLOC_IN_REGION(ptr_2, &lw_regions[0]);
/* Free pointers */ /* Free pointers */
@ -106,33 +116,41 @@ lwmem_test_run(void) {
/* Run tests on custom region */ /* 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 */ /* Allocation of 64 bytes must in in first region */
ptr_c_1 = lwmem_malloc_ex(&lw_c, NULL, 64); 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]); IS_ALLOC_IN_REGION(ptr_c_1, &lw_c_regions[0]);
/* Allocation of 256 bytes can only be in 3rd region */ /* Allocation of 256 bytes can only be in 3rd region */
ptr_c_2 = lwmem_malloc_ex(&lw_c, NULL, 256); 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]); 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), /* 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 */ 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); 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]); IS_ALLOC_IN_REGION(ptr_c_3, &lw_c_regions[1]);
/* Free all pointers to default state */ /* Free all pointers to default state */
lwmem_free(ptr_c_1); lwmem_free(ptr_c_1);
lwmem_free(ptr_c_2); lwmem_free(ptr_c_2);
lwmem_free(ptr_c_3); lwmem_free(ptr_c_3);
printf("Done\r\n");
} }
/* For debug purposes */ /* For debug purposes */
lwmem_region_t* regions_used; static lwmem_region_t* regions_used;
size_t regions_count = 4; /* Use only 1 region for debug purposes of non-free areas */ static size_t regions_count = 4; /* Use only 1 region for debug purposes of non-free areas */
void void
lwmem_test_memory_structure(void) { lwmem_test_memory_structure(void) {
uint8_t* ptr1, *ptr2, *ptr3, *ptr4; uint8_t *ptr1, *ptr2, *ptr3, *ptr4;
uint8_t* rptr1, *rptr2, *rptr3, *rptr4; uint8_t *rptr1, *rptr2, *rptr3, *rptr4;
size_t used_regions; size_t used_regions;
/* /*
@ -156,16 +174,20 @@ lwmem_test_memory_structure(void) {
/* Test case 1, allocate 3 blocks, each of different size */ /* Test case 1, allocate 3 blocks, each of different size */
/* We know that sizeof internal metadata block is 8 bytes on win32 */ /* 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); ptr1 = lwmem_malloc(8);
ptr2 = lwmem_malloc(4); ptr2 = lwmem_malloc(4);
ptr3 = lwmem_malloc(4); ptr3 = lwmem_malloc(4);
ptr4 = lwmem_malloc(16); ptr4 = lwmem_malloc(16);
lwmem_free(ptr1); /* Free but keep value for future comparison */ lwmem_debug_print(1, 1);
lwmem_free(ptr3); /* Free but keep value for future comparison */ 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); lwmem_debug_print(1, 1);
printf("Debug above is effectively state 3\r\n"); 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 */ /* We always try to reallocate pointer ptr2 */
@ -183,7 +205,7 @@ lwmem_test_memory_structure(void) {
printf("State 3b\r\n"); printf("State 3b\r\n");
rptr2 = lwmem_realloc(ptr2, 20); rptr2 = lwmem_realloc(ptr2, 20);
lwmem_debug_print(1, 1); lwmem_debug_print(1, 1);
ASSERT(rptr2 == ptr2); ASSERT(rptr2 == ptr1);
/* Create 3c case */ /* Create 3c case */
printf("\r\n------------------------------------------------------------------------\r\n"); printf("\r\n------------------------------------------------------------------------\r\n");
@ -200,4 +222,11 @@ lwmem_test_memory_structure(void) {
rptr4 = lwmem_realloc(ptr2, 36); rptr4 = lwmem_realloc(ptr2, 36);
lwmem_debug_print(1, 1); lwmem_debug_print(1, 1);
ASSERT(rptr4 != ptr1 && rptr4 != ptr2 && rptr4 != ptr3 && rptr4 != ptr4); ASSERT(rptr4 != ptr1 && rptr4 != ptr2 && rptr4 != ptr3 && rptr4 != ptr4);
}
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 */

68
tests/lwmem_test_simple.c Normal file
View File

@ -0,0 +1,68 @@
#include <stdio.h>
#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 */