diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f5116a5..cd2734f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ project(collectc VERSION 0.0.1) -file(GLOB source_files "*.c" "sized/*.c") -file(GLOB header_files "include/*.h" "include/sized/*.h") +file(GLOB source_files "*.c" "sized/*.c" "memory/*.c") +file(GLOB header_files "include/*.h" "include/sized/*.h" "include/memory/*.h") include_directories("./include") diff --git a/src/include/memory/cc_dynamic_pool.h b/src/include/memory/cc_dynamic_pool.h new file mode 100644 index 0000000..e868159 --- /dev/null +++ b/src/include/memory/cc_dynamic_pool.h @@ -0,0 +1,81 @@ +/* + * Collections-C + * Copyright (C) 2013-2024 Srđan Panić + * + * This file is part of Collections-C. + * + * Collections-C is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Collections-C is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Collections-C. If not, see . + */ + +#ifndef _CC_DYNAMIC_POOL_H_ +#define _CC_DYNAMIC_POOL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cc_common.h" + +/** +A memory pool allocated on the heap. By default its size +is fixed, but it can be optinally set to expand once it's +full. +*/ +typedef struct cc_dynamic_pool_s CC_DynamicPool; + +typedef struct cc_dynamic_pool_conf_s { + /** + * The rate at which the pool expands (initial_size * exp_factor). + * Must be a positive. Only has an effect if the the pool expansion + * isn't fixed. + * + * Example: + * - if (exp_factor == 1) then linear growth + * - if (exp_factor > 1) then accelarated growth + * - if (exp_factor < 1) then decaying growth + **/ + float exp_factor; + + /** + * If true, the pool will not try to expand when full. */ + bool is_fixed; + + /** + * Memory allocators used to allocate the Array structure and the + * underlying data buffers. */ + void *(*mem_alloc) (size_t size); + void *(*mem_calloc) (size_t blocks, size_t size); + void (*mem_free) (void *block); +} CC_DynamicPoolConf; + + +enum cc_stat cc_dynamic_pool_new (size_t initial_size, CC_DynamicPool** out); +enum cc_stat cc_dynamic_pool_new_conf (size_t initial_size, CC_DynamicPoolConf const * const conf, CC_DynamicPool **out); +void cc_dynamic_pool_conf_init (CC_DynamicPoolConf *conf); + +void cc_dynamic_pool_destroy (CC_DynamicPool* pool); +void cc_dynamic_pool_reset (CC_DynamicPool* pool); +size_t cc_dynamic_pool_used_bytes(CC_DynamicPool* pool); +size_t cc_dynamic_pool_free_bytes(CC_DynamicPool* pool); + +void* cc_dynamic_pool_malloc (size_t size, CC_DynamicPool *pool); +void* cc_dynamic_pool_calloc (size_t count, size_t size, CC_DynamicPool *pool); +//void* cc_dynamic_pool_realloc (void* ptr, size_t size, CC_DynamicPool* pool); +void cc_dynamic_pool_free (void* ptr, CC_DynamicPool* pool); + +#ifdef __cplusplus +} +#endif /*_cplusplus_*/ + +#endif /*_CC_DYNAMIC_POOL_H_*/ \ No newline at end of file diff --git a/src/include/memory/cc_static_pool.h b/src/include/memory/cc_static_pool.h new file mode 100644 index 0000000..f3d842a --- /dev/null +++ b/src/include/memory/cc_static_pool.h @@ -0,0 +1,48 @@ +/* + * Collections-C + * Copyright (C) 2013-2024 Srđan Panić + * + * This file is part of Collections-C. + * + * Collections-C is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Collections-C is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Collections-C. If not, see . + */ + +#ifndef _CC_BLOCK_POOL_H_ +#define _CC_BLOCK_POOL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cc_common.h" + +typedef struct cc_static_pool_s CC_StaticPool; + +enum cc_stat cc_static_pool_new (size_t size, size_t offset, uint8_t* buf, uint8_t* pool_alloc, CC_StaticPool** out); +size_t cc_static_pool_struct_size(); + +void cc_static_pool_reset (CC_StaticPool* pool); +size_t cc_static_pool_used_bytes (CC_StaticPool* pool); +size_t cc_static_pool_free_bytes (CC_StaticPool* pool); + +void* cc_static_pool_malloc (size_t size, CC_StaticPool *pool); +void* cc_static_pool_calloc (size_t count, size_t size, CC_StaticPool *pool); +void cc_static_pool_free (void* ptr, CC_StaticPool* pool); + + +#ifdef __cplusplus +} +#endif /*_cplusplus_*/ + +#endif /*_CC_BLOCK_POOL_H_*/ \ No newline at end of file diff --git a/src/memory/cc_dynamic_pool.c b/src/memory/cc_dynamic_pool.c new file mode 100644 index 0000000..9886f06 --- /dev/null +++ b/src/memory/cc_dynamic_pool.c @@ -0,0 +1,263 @@ +/* + * Collections-C + * Copyright (C) 2013-2024 Srđan Panić + * + * This file is part of Collections-C. + * + * Collections-C is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Collections-C is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Collections-C. If not, see . + */ + +#include "memory/cc_dynamic_pool.h" + +struct cc_page_info_s { + void *previous; + size_t size; +}; + +typedef struct cc_page_info_s PageInfo; + + +struct cc_dynamic_pool_s { + bool is_fixed; + float exp_factor; + size_t top_page_size; + + uint8_t* page; + uint8_t* high_ptr; /*Start of the highest block*/ + uint8_t* low_ptr; /*Start of the page*/ + uint8_t* free_ptr; /*Next free location*/ + + void* (*mem_alloc) (size_t size); + void* (*mem_calloc) (size_t blocks, size_t size); + void (*mem_free) (void *block); +}; + +/** + * Initializes the config to default values. + */ +void cc_dynamic_pool_conf_init(CC_DynamicPoolConf *conf) +{ + conf->exp_factor = 1; + conf->is_fixed = true; + conf->mem_alloc = malloc; + conf->mem_calloc = calloc; + conf->mem_free = free; +} + +/** + * Creates a new pool based on the specified config. + * + * @param[in] size - size of the pool in bytes + * @param[in] conf - configuration + * @param[out] out - pointer to where the newly created pool is to be + * stored + * + * @return CC_OK if the creation was successful or CC_ERR_ALLOC + * if the allocation failed. + */ +enum cc_stat cc_dynamic_pool_new_conf( + size_t size, + CC_DynamicPoolConf const * const conf, + CC_DynamicPool **out) +{ + CC_DynamicPool* pool = conf->mem_calloc(1, sizeof(CC_DynamicPool)); + + if (!pool) { + return CC_ERR_ALLOC; + } + uint8_t* page = conf->mem_alloc(size + sizeof(PageInfo)); + + if (!page) { + conf->mem_free(pool); + return CC_ERR_ALLOC; + } + PageInfo* page_info = (PageInfo*)page; + page_info->previous = NULL; + page_info->size = size; + + pool->exp_factor = conf->exp_factor; + pool->is_fixed = conf->is_fixed; + pool->top_page_size = size; + pool->page = page; + pool->high_ptr = page + sizeof(PageInfo); + pool->low_ptr = pool->high_ptr; + pool->free_ptr = pool->high_ptr; + + pool->mem_alloc = conf->mem_alloc; + pool->mem_calloc = conf->mem_calloc; + pool->mem_free = conf->mem_free; + + *out = pool; + + return CC_OK; +} + +/** + * Creates a new non-expandable pool of size in bytes. + * + * @param[in] size - size of the pool in bytes + * @param[out] out pointer to where the newly created CC_DynamicPool is to be stored + */ +enum cc_stat cc_dynamic_pool_new(size_t size, CC_DynamicPool **out) +{ + CC_DynamicPoolConf c; + cc_dynamic_pool_conf_init(&c); + return cc_dynamic_pool_new_conf(size, &c, out); +} + +/** + * Destroys the pool and frees all the buffers from memory. + * + * @param[in] pool + */ +void cc_dynamic_pool_destroy(CC_DynamicPool* pool) +{ + PageInfo* p = (PageInfo*)pool->page; + do { + PageInfo* del = p; + p = p->previous; + pool->mem_free(del); + } while (p); + + pool->mem_free(pool); +} + +/** + * Resets the pool to its initial state. All previously allocated + * blocks should be considered invalidated. + * + * @param[in] pool - pool to reset + */ +void cc_dynamic_pool_reset(CC_DynamicPool* pool) +{ + PageInfo* top_page = (PageInfo*)pool->page; + PageInfo* del_page = top_page->previous; + + while (del_page) { + top_page = del_page; + del_page = top_page->previous; + pool->mem_free(del_page); + } + pool->page = (uint8_t*) top_page; + pool->top_page_size = ((PageInfo*)top_page)->size; + pool->low_ptr = (uint8_t*) top_page + (sizeof(PageInfo)); + pool->high_ptr = pool->low_ptr; + pool->free_ptr = pool->low_ptr; +} + +/** + * Allocates a block memory of size in bytes. + * + * @param[in] size - number of bytes to be allocated + * @param[in] pool - pool inside which the bytes are allocated + * @return pointer to the newly allocated block. + */ +void* cc_dynamic_pool_malloc(size_t size, CC_DynamicPool* pool) +{ + if (size >= pool->top_page_size) { + return NULL; + } + uint8_t* page_max = pool->low_ptr + pool->top_page_size; + uint8_t* new_high = pool->high_ptr + size; + + if (new_high >= page_max) { + size_t next_max = (size_t) (pool->top_page_size * pool->exp_factor); + if (pool->is_fixed || size > next_max) { + return NULL; + } + uint8_t* new_page = pool->mem_alloc(next_max + sizeof(PageInfo)); + + if (!new_page) { + return NULL; + } + PageInfo* pinfo = (PageInfo*)new_page; + pinfo->previous = pool->page; + pinfo->size = next_max; + + pool->page = new_page; + pool->high_ptr = new_page + sizeof(PageInfo); + pool->low_ptr = pool->high_ptr; + pool->free_ptr = pool->high_ptr; + pool->top_page_size = next_max; + } + uint8_t* ptr = pool->free_ptr; + pool->high_ptr = ptr; + pool->free_ptr = ptr + size; + return ptr; +} + +/** + * Allocates a block of memory for count elements of + * size in bytes and sets all the allocated bytes to 0. + * + * @param[in] count - + * @param[in] size - + * @param[in] pool - + * + * @return Pointer to the newly allocated block. + */ +void* cc_dynamic_pool_calloc(size_t count, size_t size, CC_DynamicPool* pool) +{ + uint8_t* ptr; + if (ptr = cc_dynamic_pool_malloc(count * size, pool)) { + memset(ptr, 0, (count * size)); + } + return ptr; +} + +/** + * Frees the block only if it is the most recent block allocated. + * Otherwise it has no effect. + * + * @param[in] ptr - pointer to the block + * @param[in] pool - pool from which this block is to be freed + */ +void cc_dynamic_pool_free(void *ptr, CC_DynamicPool *pool) +{ + // Roll back the top entry + if (ptr == pool->high_ptr) { + pool->free_ptr = pool->high_ptr; + } +} + +/** + * Returns the total number of used bytes inside the poool. + * + * @param[in] pool + * + * @return used bytes + */ +size_t cc_dynamic_pool_used_bytes(CC_DynamicPool *pool) +{ + size_t total = pool->free_ptr - pool->low_ptr; + PageInfo* pi = (PageInfo*)pool->page; + + while (pi->previous) { + total += pi->size; + pi = pi->previous; + } + return total; +} + +/** + * Returns the number of free bytes inside the pool. + * + * @param[in] pool + * + * @return free bytes + */ +size_t cc_dynamic_pool_free_bytes(CC_DynamicPool* pool) +{ + return pool->top_page_size - (pool->free_ptr - pool->low_ptr); +} \ No newline at end of file diff --git a/src/memory/cc_static_pool.c b/src/memory/cc_static_pool.c new file mode 100644 index 0000000..f8c730d --- /dev/null +++ b/src/memory/cc_static_pool.c @@ -0,0 +1,163 @@ +/* + * Collections-C + * Copyright (C) 2013-2024 Srđan Panić + * + * This file is part of Collections-C. + * + * Collections-C is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Collections-C is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Collections-C. If not, see . + */ + +#include "memory/cc_static_pool.h" + +struct cc_static_pool_s { + size_t size; + uint8_t* block; + uint8_t* high_ptr; + uint8_t* low_ptr; + uint8_t* free_ptr; +}; + + +/** + * Creates a new CC_StaticPool structure insdie the pool_alloc buffer. The size of the + * pool_alloc buffer should be at least cc_static_pool_struct_size(CC_StaticPool) + * bytes long. + * + * @param[in] size - size of the provided data buffer in bytes + * @param[in] offset - the offset from the begining of the data buffer in bytes + * @param[in] data_buf - + * @param[in] pool_alloc - buffer into which the pool structure itself is going to be stored. + * @param[out] out - pointer to the newly created pool structure + * + * @return CC_OK if the creation was successful. + */ +enum cc_stat cc_static_pool_new( + size_t size, + size_t offset, + uint8_t* data_buf, + uint8_t* pool_alloc, + CC_StaticPool** out) +{ + CC_StaticPool *pool = (CC_StaticPool*)pool_alloc; + pool->block = data_buf + offset; + pool->free_ptr = pool->block; + pool->high_ptr = pool->block; + pool->low_ptr = pool->block; + pool->size = size; + + *out = pool; + + return CC_OK; +} + + +/** + * Returns the size of the CC_StaticPool structure in bytes. + */ +size_t cc_static_pool_struct_size() +{ + return sizeof(CC_StaticPool); +} + +/** + * Resets the pool to its initial state. All previously allocated blocks + * should be considered invalidated + * + * @param[in] pool - pool to reset + */ +void cc_static_pool_reset(CC_StaticPool* pool) +{ + pool->free_ptr = pool->low_ptr; + pool->high_ptr = pool->low_ptr; +} + +/** + * Allocates a block memory of size in bytes. + * + * @param[in] size - number of bytes to be allocated + * @param[in] pool - pool inside which the bytes are allocated + * @return pointer to the newly allocated block. + */ +void* cc_static_pool_malloc(size_t size, CC_StaticPool* pool) +{ + uint8_t* page_max = pool->low_ptr + pool->size; + uint8_t* new_high = pool->high_ptr + size; + + if (new_high >= page_max) { + return NULL; + } + uint8_t* ptr = pool->free_ptr; + pool->high_ptr = ptr; + pool->free_ptr = ptr + size; + + return ptr; +} + +/** + * Allocates a block of memory for count elements of + * size in bytes and sets all the allocated bytes to 0. + * + * @param[in] count - + * @param[in] size - + * @param[in] pool - + * + * @return Pointer to the newly allocated block. + */ +void* cc_static_pool_calloc(size_t count, size_t size, CC_StaticPool* pool) +{ + uint8_t* ptr = NULL; + if (ptr = cc_static_pool_malloc(count * size, pool)) { + memset(ptr, 0, (count * size)); + } + return ptr; +} + +/** + * Frees the block only if it is the most recent block allocated. + * Otherwise it has no effect. + * + * @param[in] ptr - pointer to the block + * @param[in] pool - pool from which this block is to be freed + */ +void cc_static_pool_free(void* ptr, CC_StaticPool* pool) +{ + // Roll back the top entry + if (ptr == pool->high_ptr) { + pool->free_ptr = pool->high_ptr; + } +} + +/** + * Returns the total number of used bytes inside the poool. + * + * @param[in] pool + * + * @return used bytes + */ +size_t cc_static_pool_used_bytes(CC_StaticPool* pool) +{ + return pool->free_ptr - pool->low_ptr; +} + +/** + * Returns the number of free bytes inside the pool. + * + * @param[in] pool + * + * @return free bytes + */ +size_t cc_static_pool_free_bytes(CC_StaticPool* pool) +{ + return pool->size - (pool->free_ptr - pool->low_ptr); +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 60cf0f0..27cad7f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,8 @@ set(rbuf_test_sources munit.c "ring_buffer_test.c") set(tsttable_test_sources munit.c "tst_table_test.c") set(array_sized_test_sources munit.c array_sized_test.c) +set(dynamic_pool_test_sources munit.c "dynamic_pool_test.c") +set(static_pool_test_sources munit.c static_pool_test.c) include_directories(${PROJECT_SOURCE_DIR}/include ${collectc_INCLUDE_DIRS}) message(${collectc_INCLUDE_DIRS}) @@ -44,6 +46,8 @@ add_executable(rbuf_test ${rbuf_test_sources}) add_executable(tsttable_test ${tsttable_test_sources}) add_executable(array_sized_test ${array_sized_test_sources}) +add_executable(dynamic_pool_test ${dynamic_pool_test_sources}) +add_executable(static_pool_test ${static_pool_test_sources}) target_link_libraries(array_test collectc) target_link_libraries(deque_test collectc) @@ -60,6 +64,8 @@ target_link_libraries(rbuf_test collectc) target_link_libraries(tsttable_test collectc) target_link_libraries(array_sized_test collectc) +target_link_libraries(dynamic_pool_test collectc) +target_link_libraries(static_pool_test collectc) add_test(ArrayTest array_test) add_test(DequeTest deque_test) @@ -76,3 +82,5 @@ add_test(RbufTest rbuf_test) add_test(TSTTableTest tsttable_test) add_test(ArraySizedTest array_sized_test) +add_test(DynamicPoolTest heap_pool_test) +add_test(StaticPoolTest static_pool_test) \ No newline at end of file diff --git a/test/dynamic_pool_test.c b/test/dynamic_pool_test.c new file mode 100644 index 0000000..fa61aeb --- /dev/null +++ b/test/dynamic_pool_test.c @@ -0,0 +1,235 @@ +#include "munit.h" +#include "memory/cc_dynamic_pool.h" +#include "cc_list.h" +#include + + +static CC_DynamicPool* pool = NULL; + + +void pool_free(void* ptr) +{ + cc_dynamic_pool_free(ptr, pool); +} + +void *pool_malloc(size_t size) +{ + return cc_dynamic_pool_malloc(size, pool); +} + +void *pool_calloc(size_t count, size_t size) +{ + return cc_dynamic_pool_calloc(count, size, pool); +} + +#define CONTROL_VAL 0xFFFFFFFFFFFFFFFF + +static MunitResult test_alignment(const MunitParameter params[], void* fixture) +{ + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x1110011100110011; + + munit_assert_int(CC_OK, ==, cc_dynamic_pool_new(sizeof(uint64_t) * 3, &pool)); + + uint64_t* a = pool_malloc(sizeof(uint64_t)); + uint64_t* b = pool_malloc(sizeof(uint64_t)); + uint64_t* c = pool_malloc(sizeof(uint64_t)); + + munit_assert_ptr_not_equal(a, b); + munit_assert_ptr_not_equal(b, c); + munit_assert_ptr_not_equal(a, c); + + *a = control_val1; + *b = control_val2; + *c = control_val3; + + munit_assert_uint64(*a, == , control_val1); + munit_assert_uint64(*b, == , control_val2); + munit_assert_uint64(*c, == , control_val3); + + munit_assert_ptr_equal(b, (a+1)); + munit_assert_ptr_equal(c, (a+2)); + + cc_dynamic_pool_destroy(pool); + + return MUNIT_OK; +} + + +static MunitResult test_limit(const MunitParameter params[], void* fixture) +{ + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x1110011100110011; + + // Only allocate space for 2 x uint64_t + munit_assert_int(CC_OK, ==, cc_dynamic_pool_new(sizeof(uint64_t) * 2, &pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size(0, == , cc_dynamic_pool_used_bytes(pool)); + + // Start filling + uint64_t* a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_used_bytes(pool)); + + uint64_t* b = pool_malloc(sizeof(uint64_t)); + munit_assert_size(0, == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_dynamic_pool_used_bytes(pool)); + + // No more space from now on + uint64_t* c = pool_malloc(sizeof(uint64_t)); + + munit_assert_ptr_not_null(a); + munit_assert_ptr_not_null(b); + munit_assert_ptr_null(c); + + cc_dynamic_pool_destroy(pool); + + return MUNIT_OK; +} + + +static MunitResult test_expansion(const MunitParameter params[], void* fixture) +{ + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x3310011100110011; + + // Make an expandable pool + CC_DynamicPoolConf conf; + cc_dynamic_pool_conf_init(&conf); + conf.exp_factor = 1; + conf.is_fixed = false; + + munit_assert_int(CC_OK, ==, cc_dynamic_pool_new_conf(sizeof(uint64_t) * 2, &conf, &pool)); + + munit_assert_size((sizeof(uint64_t) * 2), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size(0, == , cc_dynamic_pool_used_bytes(pool)); + + // Start filling + uint64_t* a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(a); + + uint64_t* b = pool_malloc(sizeof(uint64_t)); + munit_assert_size(0, == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(b); + + // This should trigger a resize + uint64_t* c = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 3), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(c); + + cc_dynamic_pool_destroy(pool); + + return MUNIT_OK; +} + +static MunitResult test_reset(const MunitParameter params[], void* fixture) +{ + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x1110011100110011; + size_t pool_size = sizeof(uint64_t) * 2; + + // Make an expandable pool + CC_DynamicPoolConf conf; + cc_dynamic_pool_conf_init(&conf); + conf.exp_factor = 1; + conf.is_fixed = false; + + munit_assert_int(CC_OK, ==, cc_dynamic_pool_new_conf(pool_size, &conf, &pool)); + + munit_assert_size((sizeof(uint64_t) * 2), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size(0, == , cc_dynamic_pool_used_bytes(pool)); + + // Start filling + uint64_t* a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(a); + + uint64_t* b = pool_malloc(sizeof(uint64_t)); + munit_assert_size(0, == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(b); + + // This should trigger a resize + uint64_t* c = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 3), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(c); + + // Reset + cc_dynamic_pool_reset(pool); + + // Should be initial size + munit_assert_size(cc_dynamic_pool_free_bytes(pool), == , pool_size); + + // Try putting stuff back in + a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(a); + + b = pool_malloc(sizeof(uint64_t)); + munit_assert_size(0, == , cc_dynamic_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_dynamic_pool_used_bytes(pool)); + munit_assert_ptr_not_null(b); + + cc_dynamic_pool_destroy(pool); + + return MUNIT_OK; +} + + +static MunitResult test_with_list(const MunitParameter params[], void* fixture) +{ + munit_assert_int(CC_OK, ==, cc_dynamic_pool_new(sizeof(size_t) * 100000, &pool)); + + CC_ListConf conf; + cc_list_conf_init(&conf); + conf.mem_alloc = pool_malloc; + conf.mem_calloc = pool_calloc; + conf.mem_free = pool_free; + + CC_List* list; + cc_list_new_conf(&conf, &list); + + uint64_t *data = NULL; + for (int i = 0; i < 50; i++) { + data = pool_malloc(sizeof(uint64_t)); + *data = CONTROL_VAL; + munit_assert_not_null(data); + munit_assert_int(CC_OK, == , cc_list_add(list, data)); + } + + CC_LIST_FOREACH(ret, list, { + munit_assert_uint64(*((uint64_t*)ret), == , CONTROL_VAL); + }); + + cc_dynamic_pool_destroy(pool); + return MUNIT_OK; +} + +static MunitTest test_suite_tests[] = { + { (char*)"/heap_pool/test_alignment", test_alignment, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/heap_pool/test_limit", test_limit, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/heap_pool/test_expansion", test_expansion, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/heap_pool/test_with_list", test_with_list, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/heap_pool/test_reset", test_reset, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite test_suite = { + (char*)"", test_suite_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)]) +{ + return munit_suite_main(&test_suite, (void*)"test", argc, argv); +} \ No newline at end of file diff --git a/test/static_pool_test.c b/test/static_pool_test.c new file mode 100644 index 0000000..eec5aed --- /dev/null +++ b/test/static_pool_test.c @@ -0,0 +1,244 @@ +#include "munit.h" +#include "memory/cc_static_pool.h" +#include "cc_list.h" +#include + +static CC_StaticPool* pool = NULL; + + +void pool_free(void* ptr) +{ + cc_static_pool_free(ptr, pool); +} + +void *pool_malloc(size_t size) +{ + return cc_static_pool_malloc(size, pool); +} + +void *pool_calloc(size_t count, size_t size) +{ + return cc_static_pool_calloc(count, size, pool); +} + +static MunitResult test_alignment(const MunitParameter params[], void* fixture) +{ + uint8_t pool_alloc[100] = { 0 }; + uint64_t vals[3] = { 0 }; + + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x1110011100110011; + + cc_static_pool_new(sizeof(vals), 0, (uint8_t*) vals, pool_alloc, &pool); + + uint64_t* a = pool_malloc(sizeof(uint64_t)); + uint64_t* b = pool_malloc(sizeof(uint64_t)); + uint64_t* c = pool_malloc(sizeof(uint64_t)); + + munit_assert_ptr_not_equal(a, b); + munit_assert_ptr_not_equal(b, c); + munit_assert_ptr_not_equal(a, c); + + *a = control_val1; + *b = control_val2; + *c = control_val3; + + munit_assert_uint64(*a, == , control_val1); + munit_assert_uint64(*b, == , control_val2); + munit_assert_uint64(*c, == , control_val3); + + munit_assert_ptr_equal(b, (a+1)); + munit_assert_ptr_equal(c, (a+2)); + + return MUNIT_OK; +} + +static MunitResult test_limit(const MunitParameter params[], void* fixture) +{ + uint8_t pool_alloc[100] = { 0 }; + uint64_t vals[3] = { 0 }; + + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x1110011100110011; + + // Only allocate space for 2 x uint64_t + cc_static_pool_new(sizeof(uint64_t) * 2, 0, (uint8_t*) vals, pool_alloc, &pool); + + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_free_bytes(pool)); + munit_assert_size(0, == , cc_static_pool_used_bytes(pool)); + + // Start filling + uint64_t* a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_used_bytes(pool)); + + uint64_t* b = pool_malloc(sizeof(uint64_t)); + munit_assert_size(0, == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_used_bytes(pool)); + + // No more space from now on + uint64_t* c = pool_malloc(sizeof(uint64_t)); + + munit_assert_ptr_not_null(a); + munit_assert_ptr_not_null(b); + munit_assert_ptr_null(c); + + return MUNIT_OK; +} + +static MunitResult test_buf_offset(const MunitParameter params[], void* fixture) +{ + uint8_t pool_alloc[100] = { 0 }; + uint64_t vals[3] = { 0 }; + + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x1110011100110011; + + // Space for 2 uint64_t with 3 byte offset + cc_static_pool_new(sizeof(uint64_t) * 2, 3, (uint8_t*) vals, pool_alloc, &pool); + + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_free_bytes(pool)); + munit_assert_size(0, == , cc_static_pool_used_bytes(pool)); + + // Start filling + uint64_t* a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_used_bytes(pool)); + + uint64_t* b = pool_malloc(sizeof(uint64_t)); + munit_assert_size(0, == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_used_bytes(pool)); + + // No more space from now on + uint64_t* c = pool_malloc(sizeof(uint64_t)); + + munit_assert_ptr_not_null(a); + munit_assert_ptr_not_null(b); + munit_assert_ptr_null(c); + + return MUNIT_OK; +} + +static MunitResult test_reset(const MunitParameter params[], void* fixture) +{ + uint8_t pool_alloc[100] = { 0 }; + uint64_t vals[3] = { 0 }; + + uint64_t control_val1 = 0xFFAABBCCDDEE0011; + uint64_t control_val2 = 0xABCDEFF123456789; + uint64_t control_val3 = 0x1110011100110011; + + cc_static_pool_new(sizeof(vals), 0, (uint8_t*)vals, pool_alloc, &pool); + + munit_assert_size((sizeof(uint64_t) * 3), == , cc_static_pool_free_bytes(pool)); + munit_assert_size(0, == , cc_static_pool_used_bytes(pool)); + + // Start filling + uint64_t* a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_used_bytes(pool)); + munit_assert_ptr_not_null(a); + + uint64_t* b = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_used_bytes(pool)); + munit_assert_ptr_not_null(b); + + uint64_t* c = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 0), == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 3), == , cc_static_pool_used_bytes(pool)); + munit_assert_ptr_not_null(c); + + // Reset + cc_static_pool_reset(pool); + + // Should be initial size + munit_assert_size(cc_static_pool_free_bytes(pool), == , (sizeof(uint64_t) * 3)); + + // Try putting stuff back in + a = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_used_bytes(pool)); + munit_assert_ptr_not_null(a); + + b = pool_malloc(sizeof(uint64_t)); + munit_assert_size((sizeof(uint64_t) * 1), == , cc_static_pool_free_bytes(pool)); + munit_assert_size((sizeof(uint64_t) * 2), == , cc_static_pool_used_bytes(pool)); + munit_assert_ptr_not_null(b); + + return MUNIT_OK; +} + +static MunitResult test_stack_list(const MunitParameter params[], void* fixture) +{ + uint64_t control_val = 0xFFAABBCCDDEE0011; + uint8_t pool_alloc[100] = { 0 }; + uint8_t dbuf[10000] = { 0 }; + + cc_static_pool_new(sizeof(dbuf), 0, (uint8_t*)dbuf, pool_alloc, &pool); + + CC_ListConf conf; + cc_list_conf_init(&conf); + conf.mem_alloc = pool_malloc; + conf.mem_calloc = pool_calloc; + conf.mem_free = pool_free; + + CC_List* list; + cc_list_new_conf(&conf, &list); + + uint64_t *data = NULL; + for (int i = 0; i < 50; i++) { + data = pool_malloc(sizeof(uint64_t)); + *data = control_val; + munit_assert_not_null(data); + munit_assert_int(CC_OK, == , cc_list_add(list, data)); + } + + CC_LIST_FOREACH(ret, list, { + munit_assert_uint64(*((uint64_t*)ret), == , control_val); + }); + + return MUNIT_OK; +} + +static MunitResult test_free_rollback(const MunitParameter params[], void* fixture) +{ + uint64_t control_val = 0xFFAABBCCDDEE0011; + uint8_t pool_alloc[100] = { 0 }; + uint8_t dbuf[200] = { 0 }; + + cc_static_pool_new(sizeof(dbuf), 0, (uint8_t*)dbuf, pool_alloc, &pool); + + uint64_t *data = NULL; + for (int i = 0; i < 1000; i++) { + data = pool_malloc(sizeof(uint64_t)); + *data = control_val; + munit_assert_not_null(data); + munit_assert_uint64(*data, == , control_val); + pool_free(data); + } + + return MUNIT_OK; +} + +static MunitTest test_suite_tests[] = { + { (char*)"/static_pool/test_alignment", test_alignment, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/static_pool/test_limit", test_limit, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/static_pool/test_reset", test_reset, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/static_pool/test_stack_list", test_stack_list, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/static_pool/test_free", test_free_rollback, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { (char*)"/static_pool/test_buf_offset", test_buf_offset, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite test_suite = { + (char*)"", test_suite_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)]) +{ + return munit_suite_main(&test_suite, (void*)"test", argc, argv); +} \ No newline at end of file