Fri, 11 Apr 2025 15:12:20 +0200
add cxMempoolTransferObject() - resolves #640
--- a/CHANGELOG Fri Apr 11 14:49:23 2025 +0200 +++ b/CHANGELOG Fri Apr 11 15:12:20 2025 +0200 @@ -1,7 +1,7 @@ Version 3.2 - tbd ------------------------ - * adds cxMempoolTransfer() + * adds cxMempoolTransfer() and cxMempoolTransferObject() * changes grow strategy for the mempory pool to reduce reallocations Version 3.1 - 2025-02-11
--- a/docs/Writerside/topics/about.md Fri Apr 11 14:49:23 2025 +0200 +++ b/docs/Writerside/topics/about.md Fri Apr 11 15:12:20 2025 +0200 @@ -28,7 +28,7 @@ ### Version 3.2 - preview {collapsible="true"} -* adds cxMempoolTransfer() +* adds cxMempoolTransfer() and cxMempoolTransferObject() * changes grow strategy for the mempory pool to reduce reallocations ### Version 3.1 - 2025-02-11 {collapsible="true"}
--- a/docs/Writerside/topics/mempool.h.md Fri Apr 11 14:49:23 2025 +0200 +++ b/docs/Writerside/topics/mempool.h.md Fri Apr 11 15:12:20 2025 +0200 @@ -62,6 +62,9 @@ #include <cx/mempool.h> int cxMempoolTransfer(CxMempool *source, CxMempool *dest); + +int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, + const void *obj); ``` Memory managed by a pool can be transferred to another pool. @@ -71,7 +74,11 @@ That means, that all references to the allocator of the `source` pool remain valid and continue to work with the `dest` pool. The transferred allocator will be destroyed when the `dest` pool gets destroyed. -The function returns zero when the transfer was successful and non-zero if a necessary memory allocation was not possible, +The function `cxMempoolTransferObject()` transfers a _single_ object managed by the `source` pool to the `dest` pool. +Memory that was registered with `cxMempoolRegister()` cannot be transferred this way. +Also, if `obj` has a reference to `source->allocator`, it must be updated to `dest->allocator` manually. + +The functions returns zero when the transfer was successful and non-zero if a necessary memory allocation was not possible, or the `source` and `dest` pointers point to the same pool. In case of an error, no memory is transferred and both pools are in a valid state.
--- a/src/cx/mempool.h Fri Apr 11 14:49:23 2025 +0200 +++ b/src/cx/mempool.h Fri Apr 11 15:12:20 2025 +0200 @@ -176,6 +176,29 @@ CxMempool *dest ); +/** + * Transfers an object from one pool to another. + * + * You can only transfer objects that have been allocated by this pool. + * Objects that have been registered with cxMempoolRegister() cannot be transferred this way. + * + * @attention If the object maintains a reference to the pool's allocator, + * you must make sure to update that reference to the allocator of the destination pool. + * + * @param source the pool to move the memory from + * @param dest the pool where to transfer the memory to + * @param obj pointer to the object that shall be transferred + * @retval zero success + * @retval non-zero failure or object was not found in the source pool + */ +cx_attr_nonnull +cx_attr_export +int cxMempoolTransferObject( + CxMempool *source, + CxMempool *dest, + const void *obj +); + #ifdef __cplusplus } // extern "C" #endif
--- a/src/mempool.c Fri Apr 11 14:49:23 2025 +0200 +++ b/src/mempool.c Fri Apr 11 15:12:20 2025 +0200 @@ -255,7 +255,7 @@ // ensure enough capacity in the destination pool if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) { - return 1; + return 1; // LCOV_EXCL_LINE } // allocate a replacement allocator for the source pool @@ -283,3 +283,35 @@ return 0; } + +int cxMempoolTransferObject( + CxMempool *source, + CxMempool *dest, + const void *obj +) { + // safety check + if (source == dest) return 1; + + // first, make sure that the dest pool can take the object + if (cx_mempool_ensure_capacity(dest, dest->size + 1)) { + return 1; // LCOV_EXCL_LINE + } + // search for the object + for (size_t i = 0; i < source->size; i++) { + struct cx_mempool_memory_s *mem = source->data[i]; + if (mem->c == obj) { + // remove from the source pool + size_t last_index = source->size - 1; + if (i != last_index) { + source->data[i] = source->data[last_index]; + source->data[last_index] = NULL; + } + source->size--; + // add to the target pool + dest->data[dest->size++] = mem; + return 0; + } + } + // not found + return 1; +}
--- a/tests/test_mempool.c Fri Apr 11 14:49:23 2025 +0200 +++ b/tests/test_mempool.c Fri Apr 11 15:12:20 2025 +0200 @@ -172,11 +172,11 @@ src->auto_destr = test_mempool_destructor; // allocate first object - int *a = cxMalloc(src->allocator, sizeof(int)); + int *c = cxMalloc(src->allocator, sizeof(int)); // allocate second object - int *b = cxMalloc(src->allocator, sizeof(int)); + c = cxMalloc(src->allocator, sizeof(int)); // register foreign object - int *c = malloc(sizeof(int)); + c = malloc(sizeof(int)); cxMempoolRegister(src, c, test_mempool_destructor); // check source pool @@ -206,6 +206,44 @@ // free the foreign object free(c); + + // cover illegal arguments + result = cxMempoolTransfer(dest, dest); + CX_TEST_ASSERT(result != 0); + } +} + +CX_TEST(test_mempool_transfer_object) { + CxMempool *src = cxMempoolCreateSimple(4); + CxMempool *dest = cxMempoolCreateSimple(4); + CX_TEST_DO { + int *b = cxMalloc(src->allocator, sizeof(int)); + b = cxMalloc(src->allocator, sizeof(int)); + int *c = malloc(sizeof(int)); + cxMempoolRegister(src, c, free); + + CX_TEST_ASSERT(src->size == 3); + int result = cxMempoolTransferObject(src, dest, b); + CX_TEST_ASSERT(result == 0); + CX_TEST_ASSERT(src->size == 2); + CX_TEST_ASSERT(dest->size == 1); + result = cxMempoolTransferObject(src, dest, b); + CX_TEST_ASSERT(result != 0); + CX_TEST_ASSERT(src->size == 2); + CX_TEST_ASSERT(dest->size == 1); + // cannot transfer foreign memory this way + result = cxMempoolTransferObject(src, dest, c); + CX_TEST_ASSERT(result != 0); + CX_TEST_ASSERT(src->size == 2); + CX_TEST_ASSERT(dest->size == 1); + result = cxMempoolTransferObject(dest, dest, b); + CX_TEST_ASSERT(result != 0); + CX_TEST_ASSERT(src->size == 2); + CX_TEST_ASSERT(dest->size == 1); + + cxMempoolFree(src); + cxMempoolFree(dest); + // let valgrind check that everything worked } } @@ -220,6 +258,7 @@ cx_test_register(suite, test_mempool_destroy); cx_test_register(suite, test_mempool_register); cx_test_register(suite, test_mempool_transfer); + cx_test_register(suite, test_mempool_transfer_object); return suite; }