add cxMempoolTransferObject() - resolves #640

Fri, 11 Apr 2025 15:12:20 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 11 Apr 2025 15:12:20 +0200
changeset 1283
89935fea4b7c
parent 1282
0c8077f67e54
child 1284
b2103354baed

add cxMempoolTransferObject() - resolves #640

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/mempool.h.md file | annotate | diff | comparison | revisions
src/cx/mempool.h file | annotate | diff | comparison | revisions
src/mempool.c file | annotate | diff | comparison | revisions
tests/test_mempool.c file | annotate | diff | comparison | revisions
--- 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;
 }

mercurial