]> uap-core.de Git - note.git/commitdiff
use cxMempoolCreateSimple instead of cxMempoolCreate, ucx update
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 25 May 2025 17:05:37 +0000 (19:05 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 25 May 2025 17:05:37 +0000 (19:05 +0200)
27 files changed:
application/application.c
application/store.c
libidav/config.c
libidav/resource.c
ucx/allocator.c
ucx/array_list.c
ucx/buffer.c
ucx/cx/allocator.h
ucx/cx/array_list.h
ucx/cx/buffer.h
ucx/cx/hash_map.h
ucx/cx/linked_list.h
ucx/cx/list.h
ucx/cx/mempool.h
ucx/cx/properties.h
ucx/cx/string.h
ucx/cx/tree.h
ucx/json.c
ucx/linked_list.c
ucx/list.c
ucx/mempool.c
ucx/printf.c
ucx/properties.c
ucx/streams.c
ucx/string.c
ucx/tree.c
ui/common/document.c

index 984a4e195f2818588963a3b467cae53271cbb843..e5fe1e2dc0b8060da49a250fe68561bcc6e76da2 100644 (file)
@@ -64,7 +64,7 @@ void application_startup(UiEvent *event, void *data) {
     // Get available user settings
     // The first entry should be the default, however it must be checked if
     // it is valid for this environment
-    CxMempool *mp = cxMempoolCreate(64, NULL);
+    CxMempool *mp = cxMempoolCreateSimple(64);
     CxList *usersettings = note_store_get_user_settings(mp->allocator, host, user, NULL);
     int settings_valid = 0;
     if(usersettings && cxListSize(usersettings) > 0) {
index dd852e8f419e9cc3a20402b48e582747e5ae4d86..d1a37069262ddfa7b03726d7905fe260399cb3fa 100644 (file)
@@ -327,7 +327,7 @@ int note_store_create_default(const char *host, const char *user) {
  */
 void note_store_reload() {
     // load notebook/collection structure
-    CxMempool *mp = cxMempoolCreate(64, NULL);
+    CxMempool *mp = cxMempoolCreateSimple(64);
     const CxAllocator *a = mp->allocator;
     DBUQuery *q = connection->createQuery(connection, NULL);
     dbuQuerySetSQL(q, SQL_NOTEBOOK_STRUCTURE);
index 9c295bbe9de2e19291d8bbda2ed9ceae40bbf23f..715f98b032c7d058435c1c112b40ca2050b2ba46 100644 (file)
@@ -198,7 +198,7 @@ void dav_cfg_uint_remove(CfgUInt *cint) {
 
 
 DavConfig* dav_config_new(xmlDoc *doc) {
-    CxMempool *cfg_mp = cxMempoolCreate(128, NULL);
+    CxMempool *cfg_mp = cxMempoolCreateSimple(128);
     DavConfig *config = cxMalloc(cfg_mp->allocator, sizeof(DavConfig));
     memset(config, 0, sizeof(DavConfig));
     config->mp = cfg_mp;
index fcc0a4f7e27e62f36ff704d34de670179f7ce701..19ddffa749005884b93eb485cfc7de89a172a1c0 100644 (file)
@@ -786,7 +786,7 @@ int dav_load(DavResource *res) {
 }
 
 int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) {
-    CxMempool *mp = cxMempoolCreate(64, NULL);
+    CxMempool *mp = cxMempoolCreateSimple(64);
     const CxAllocator *a = mp->allocator;
     
     CxList *proplist = cxArrayListCreate(a, NULL, sizeof(DavProperty), numprop);
index f6f20c85b4887606fd71b8675ba9f110727f80d1..b10096c05efc84b4dc272e2d8e244aee2a7fe6d5 100644 (file)
@@ -29,6 +29,7 @@
 #include "cx/allocator.h"
 
 #include <errno.h>
+#include <string.h>
 
 static void *cx_malloc_stdlib(
         cx_attr_unused void *d,
@@ -60,18 +61,19 @@ static void cx_free_stdlib(
     free(mem);
 }
 
-static cx_allocator_class cx_default_allocator_class = {
+static cx_allocator_class cx_stdlib_allocator_class = {
         cx_malloc_stdlib,
         cx_realloc_stdlib,
         cx_calloc_stdlib,
         cx_free_stdlib
 };
 
-struct cx_allocator_s cx_default_allocator = {
-        &cx_default_allocator_class,
+struct cx_allocator_s cx_stdlib_allocator = {
+        &cx_stdlib_allocator_class,
         NULL
 };
-const CxAllocator * const cxDefaultAllocator = &cx_default_allocator;
+const CxAllocator * const cxStdlibAllocator = &cx_stdlib_allocator;
+const CxAllocator * cxDefaultAllocator = cxStdlibAllocator;
 
 int cx_reallocate_(
         void **mem,
@@ -115,6 +117,17 @@ void *cxMalloc(
     return allocator->cl->malloc(allocator->data, n);
 }
 
+void *cxZalloc(
+        const CxAllocator *allocator,
+        size_t n
+) {
+    void *mem = allocator->cl->malloc(allocator->data, n);
+    if (mem != NULL) {
+        memset(mem, 0, n);
+    }
+    return mem;
+}
+
 void *cxRealloc(
         const CxAllocator *allocator,
         void *mem,
index 5387bfb8f7cd7cc82aedc9a030e7d3a825af1006..a66db5110b3ce421f024f424dc9a886ebcb98355 100644 (file)
 
 static void *cx_array_default_realloc(
         void *array,
-        size_t capacity,
+        cx_attr_unused size_t old_capacity,
+        size_t new_capacity,
         size_t elem_size,
         cx_attr_unused CxArrayReallocator *alloc
 ) {
     size_t n;
-    if (cx_szmul(capacity, elem_size, &n)) {
+    if (cx_szmul(new_capacity, elem_size, &n)) {
         errno = EOVERFLOW;
         return NULL;
     }
-    return realloc(array, n);
+    return cxReallocDefault(array, n);
 }
 
 CxArrayReallocator cx_array_default_reallocator_impl = {
@@ -58,13 +59,14 @@ CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator
 
 static void *cx_array_advanced_realloc(
         void *array,
-        size_t capacity,
+        size_t old_capacity,
+        size_t new_capacity,
         size_t elem_size,
         cx_attr_unused CxArrayReallocator *alloc
 ) {
     // check for overflow
     size_t n;
-    if (cx_szmul(capacity, elem_size, &n)) {
+    if (cx_szmul(new_capacity, elem_size, &n)) {
         errno = EOVERFLOW;
         return NULL;
     }
@@ -77,7 +79,7 @@ static void *cx_array_advanced_realloc(
     if (array == alloc->ptr2) {
         newmem = cxMalloc(al, n);
         if (newmem != NULL && array != NULL) {
-            memcpy(newmem, array, n);
+            memcpy(newmem, array, old_capacity*elem_size);
         }
     } else {
         newmem = cxRealloc(al, array, n);
@@ -180,7 +182,7 @@ int cx_array_reserve(
 
         // perform reallocation
         void *newmem = reallocator->realloc(
-                *array, newcap, elem_size, reallocator
+                *array, oldcap, newcap, elem_size, reallocator
         );
         if (newmem == NULL) {
             return 1; // LCOV_EXCL_LINE
@@ -286,7 +288,7 @@ int cx_array_copy(
 
         // perform reallocation
         void *newmem = reallocator->realloc(
-                *target, newcap, elem_size, reallocator
+                *target, oldcap, newcap, elem_size, reallocator
         );
         if (newmem == NULL) {
             return 1;
@@ -366,13 +368,14 @@ int cx_array_insert_sorted(
 
     // store some counts
     size_t old_size = *size;
+    size_t old_capacity = *capacity;
     size_t needed_capacity = old_size + elem_count;
 
     // if we need more than we have, try a reallocation
-    if (needed_capacity > *capacity) {
+    if (needed_capacity > old_capacity) {
         size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX);
         void *new_mem = reallocator->realloc(
-                *target, new_capacity, elem_size, reallocator
+                *target, old_capacity, new_capacity, elem_size, reallocator
         );
         if (new_mem == NULL) {
             // give it up right away, there is no contract
@@ -572,7 +575,7 @@ void cx_array_swap(
 
     // decide if we can use the local buffer
     if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) {
-        tmp = malloc(elem_size);
+        tmp = cxMallocDefault(elem_size);
         // we don't want to enforce error handling
         if (tmp == NULL) abort();
     } else {
@@ -591,7 +594,7 @@ void cx_array_swap(
 
     // free dynamic memory, if it was needed
     if (tmp != sbo_mem) {
-        free(tmp);
+        cxFreeDefault(tmp);
     }
 }
 
@@ -638,50 +641,38 @@ static size_t cx_arl_insert_array(
     // get a correctly typed pointer to the list
     cx_array_list *arl = (cx_array_list *) list;
 
-    // do we need to move some elements?
-    if (index < list->collection.size) {
-        const char *first_to_move = (const char *) arl->data;
-        first_to_move += index * list->collection.elem_size;
-        size_t elems_to_move = list->collection.size - index;
-        size_t start_of_moved = index + n;
-
-        if (cx_array_copy(
-                &arl->data,
-                &list->collection.size,
-                &arl->capacity,
-                0,
-                start_of_moved,
-                first_to_move,
-                list->collection.elem_size,
-                elems_to_move,
-                &arl->reallocator
-        )) {
-            // if moving existing elems is unsuccessful, abort
+    // guarantee enough capacity
+    if (arl->capacity < list->collection.size + n) {
+        size_t new_capacity = list->collection.size + n;
+        new_capacity = new_capacity - (new_capacity % 16) + 16;
+        if (cxReallocateArray(
+                list->collection.allocator,
+                &arl->data, new_capacity,
+                list->collection.elem_size)
+        ) {
             return 0;
         }
+        arl->capacity = new_capacity;
     }
 
-    // note that if we had to move the elements, the following operation
-    // is guaranteed to succeed, because we have the memory already allocated
-    // therefore, it is impossible to leave this function with an invalid array
+    // determine insert position
+    char *arl_data = arl->data;
+    char *insert_pos = arl_data + index * list->collection.elem_size;
 
-    // place the new elements
-    if (cx_array_copy(
-            &arl->data,
-            &list->collection.size,
-            &arl->capacity,
-            0,
-            index,
-            array,
-            list->collection.elem_size,
-            n,
-            &arl->reallocator
-    )) {
-        // array list implementation is "all or nothing"
-        return 0;
-    } else {
-        return n;
+    // do we need to move some elements?
+    if (index < list->collection.size) {
+        size_t elems_to_move = list->collection.size - index;
+        char *target = insert_pos + n * list->collection.elem_size;
+        memmove(target, insert_pos, elems_to_move * list->collection.elem_size);
     }
+
+    // place the new elements, if any
+    if (array != NULL) {
+        memcpy(insert_pos, array, n * list->collection.elem_size);
+    }
+    list->collection.size += n;
+
+    return n;
 }
 
 static size_t cx_arl_insert_sorted(
@@ -709,12 +700,16 @@ static size_t cx_arl_insert_sorted(
     }
 }
 
-static int cx_arl_insert_element(
+static void *cx_arl_insert_element(
         struct cx_list_s *list,
         size_t index,
         const void *element
 ) {
-    return 1 != cx_arl_insert_array(list, index, element, 1);
+    if (cx_arl_insert_array(list, index, element, 1) == 1) {
+        return ((char*)((cx_array_list *) list)->data) + index * list->collection.elem_size;
+    } else {
+        return NULL;
+    }
 }
 
 static int cx_arl_insert_iter(
@@ -724,26 +719,23 @@ static int cx_arl_insert_iter(
 ) {
     struct cx_list_s *list = iter->src_handle.m;
     if (iter->index < list->collection.size) {
-        int result = cx_arl_insert_element(
-                list,
-                iter->index + 1 - prepend,
-                elem
-        );
-        if (result == 0) {
-            iter->elem_count++;
-            if (prepend != 0) {
-                iter->index++;
-                iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size;
-            }
+        if (cx_arl_insert_element(list,
+                iter->index + 1 - prepend, elem) == NULL) {
+            return 1;
+        }
+        iter->elem_count++;
+        if (prepend != 0) {
+            iter->index++;
+            iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size;
         }
-        return result;
+        return 0;
     } else {
-        int result = cx_arl_insert_element(list, list->collection.size, elem);
-        if (result == 0) {
-            iter->elem_count++;
-            iter->index = list->collection.size;
+        if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) {
+            return 1;
         }
-        return result;
+        iter->elem_count++;
+        iter->index = list->collection.size;
+        return 0;
     }
 }
 
index 7e91f7374c12a9562f0a849752b9642d5d61c6e6..658dddf9fd30eef43bc5b23b7ed9d690ebe925c6 100644 (file)
@@ -98,7 +98,7 @@ int cxBufferEnableFlushing(
     CxBuffer *buffer,
     CxBufferFlushConfig config
 ) {
-    buffer->flush = malloc(sizeof(CxBufferFlushConfig));
+    buffer->flush = cxMallocDefault(sizeof(CxBufferFlushConfig));
     if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
     memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
     return 0;
@@ -108,7 +108,7 @@ void cxBufferDestroy(CxBuffer *buffer) {
     if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
         cxFree(buffer->allocator, buffer->bytes);
     }
-    free(buffer->flush);
+    cxFreeDefault(buffer->flush);
     memset(buffer, 0, sizeof(CxBuffer));
 }
 
index 8f16774326df5d0c50483a7fa30cd9eb870d9de6..c169a928d470ae963eadad85df7b27b6c9451080 100644 (file)
@@ -98,10 +98,17 @@ struct cx_allocator_s {
 typedef struct cx_allocator_s CxAllocator;
 
 /**
- * A default allocator using standard library malloc() etc.
+ * A pre-defined allocator using standard library malloc() etc.
  */
 cx_attr_export
-extern const CxAllocator * const cxDefaultAllocator;
+extern const CxAllocator * const cxStdlibAllocator;
+
+/**
+ * The default allocator that is used by UCX.
+ * Initialized with cxStdlibAllocator, but you may change it.
+ */
+cx_attr_export
+extern const CxAllocator * cxDefaultAllocator;
 
 /**
  * Function pointer type for destructor functions.
@@ -135,6 +142,8 @@ typedef void (*cx_destructor_func2)(
  * Reallocate a previously allocated block and changes the pointer in-place,
  * if necessary.
  *
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
  * @par Error handling
  * @c errno will be set by realloc() on failure.
  *
@@ -158,6 +167,8 @@ int cx_reallocate_(
  *
  * The size is calculated by multiplying @p nemb and @p size.
  *
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
  * @par Error handling
  * @c errno will be set by realloc() on failure or when the multiplication of
  * @p nmemb and @p size overflows.
@@ -182,6 +193,8 @@ int cx_reallocatearray_(
  * Reallocate a previously allocated block and changes the pointer in-place,
  * if necessary.
  *
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
  * @par Error handling
  * @c errno will be set by realloc() on failure.
  *
@@ -199,6 +212,8 @@ int cx_reallocatearray_(
  *
  * The size is calculated by multiplying @p nemb and @p size.
  *
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
  * @par Error handling
  * @c errno will be set by realloc() on failure or when the multiplication of
  * @p nmemb and @p size overflows.
@@ -212,6 +227,14 @@ int cx_reallocatearray_(
 #define cx_reallocatearray(mem, nmemb, size) \
     cx_reallocatearray_((void**)(mem), nmemb, size)
 
+/**
+ * Allocates memory and sets every byte to zero.
+ *
+ * @param n (@c size_t) the number of bytes
+ * @return (@c void*) a pointer to the allocated memory
+ */
+#define cx_zalloc(n) calloc(1, n)
+
 /**
  * Free a block allocated by this allocator.
  *
@@ -414,6 +437,57 @@ void *cxCalloc(
         size_t size
 );
 
+/**
+ * Allocate @p n bytes of memory and sets every byte to zero.
+ *
+ * @param allocator the allocator
+ * @param n the number of bytes
+ * @return a pointer to the allocated memory
+ */
+cx_attr_nodiscard
+cx_attr_nonnull
+cx_attr_malloc
+cx_attr_dealloc_ucx
+cx_attr_allocsize(2)
+cx_attr_export
+void *cxZalloc(
+        const CxAllocator *allocator,
+        size_t n
+);
+
+/**
+ * Convenience macro that invokes cxMalloc() with the cxDefaultAllocator.
+ */
+#define cxMallocDefault(...) cxMalloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxZalloc() with the cxDefaultAllocator.
+ */
+#define cxZallocDefault(...) cxZalloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxCalloc() with the cxDefaultAllocator.
+ */
+#define cxCallocDefault(...) cxCalloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxRealloc() with the cxDefaultAllocator.
+ */
+#define cxReallocDefault(...) cxRealloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxReallocate() with the cxDefaultAllocator.
+ */
+#define cxReallocateDefault(...) cxReallocate(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxReallocateArray() with the cxDefaultAllocator.
+ */
+#define cxReallocateArrayDefault(...) cxReallocateArray(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxReallocArray() with the cxDefaultAllocator.
+ */
+#define cxReallocArrayDefault(...) cxReallocArray(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxFree() with the cxDefaultAllocator.
+ */
+#define cxFreeDefault(...) cxFree(cxDefaultAllocator, __VA_ARGS__)
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 847540a5cd830b787cf01683f316d1af51da9b88..b38710d778a508844bb03578b0760003fe8a3faa 100644 (file)
@@ -123,7 +123,8 @@ extern const unsigned cx_array_swap_sbo_size;
  * @endcode
  *
  *
- * The memory for the array is allocated with stdlib malloc().
+ * The memory for the array is allocated with the cxDefaultAllocator.
+ *
  * @param array the name of the array
  * @param capacity the initial capacity
  * @see cx_array_initialize_a()
@@ -133,7 +134,7 @@ extern const unsigned cx_array_swap_sbo_size;
 #define cx_array_initialize(array, capacity) \
         array##_capacity = capacity; \
         array##_size = 0; \
-        array = malloc(sizeof(array[0]) * capacity)
+        array = cxMallocDefault(sizeof(array[0]) * capacity)
 
 /**
  * Initializes an array with the given capacity using the specified allocator.
@@ -149,7 +150,6 @@ extern const unsigned cx_array_swap_sbo_size;
  * cxFree(al, myarray); // don't forget to free with same allocator
  * @endcode
  *
- * The memory for the array is allocated with stdlib malloc().
  * @param allocator (@c CxAllocator*) the allocator
  * @param array the name of the array
  * @param capacity the initial capacity
@@ -178,17 +178,19 @@ struct cx_array_reallocator_s {
      * or to transport other additional data.
      *
      * @param array the array to reallocate
-     * @param capacity the new capacity (number of elements)
+     * @param old_capacity the old number of elements
+     * @param new_capacity the new number of elements
      * @param elem_size the size of each element
      * @param alloc a reference to this allocator
      * @return a pointer to the reallocated memory or @c NULL on failure
      */
     cx_attr_nodiscard
-    cx_attr_nonnull_arg(4)
-    cx_attr_allocsize(2, 3)
+    cx_attr_nonnull_arg(5)
+    cx_attr_allocsize(3, 4)
     void *(*realloc)(
             void *array,
-            size_t capacity,
+            size_t old_capacity,
+            size_t new_capacity,
             size_t elem_size,
             struct cx_array_reallocator_s *alloc
     );
@@ -217,7 +219,7 @@ struct cx_array_reallocator_s {
 typedef struct cx_array_reallocator_s CxArrayReallocator;
 
 /**
- * A default stdlib-based array reallocator.
+ * A default array reallocator that is based on the cxDefaultAllocator.
  */
 cx_attr_export
 extern CxArrayReallocator *cx_array_default_reallocator;
@@ -225,7 +227,7 @@ extern CxArrayReallocator *cx_array_default_reallocator;
 /**
  * Creates a new array reallocator.
  *
- * When @p allocator is @c NULL, the stdlib default allocator will be used.
+ * When @p allocator is @c NULL, the cxDefaultAllocator will be used.
  *
  * When @p stackmem is not @c NULL, the reallocator is supposed to be used
  * @em only for the specific array that is initially located at @p stackmem.
@@ -699,7 +701,7 @@ void cx_array_swap(
  * to cx_cmp_ptr(), if none is given.
  *
  * @param allocator the allocator for allocating the list memory
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
  * @param comparator the comparator for the elements
  * (if @c NULL, and the list is not storing pointers, sort and find
  * functions will not work)
index e546cae7408b32abfd63c5eecb07cd9d2fcdd2d6..867fb3084f207816a62d053700982cd56123b5f5 100644 (file)
@@ -222,7 +222,7 @@ typedef struct cx_buffer_s CxBuffer;
  * @param capacity the capacity of the buffer
  * @param allocator the allocator this buffer shall use for automatic
  * memory management
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
  * @param flags buffer features (see cx_buffer_s.flags)
  * @return zero on success, non-zero if a required allocation failed
  */
@@ -305,7 +305,7 @@ void cxBufferFree(CxBuffer *buffer);
  * @param capacity the capacity of the buffer
  * @param allocator the allocator to use for allocating the structure and the automatic
  * memory management within the buffer
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
  * @param flags buffer features (see cx_buffer_s.flags)
  * @return a pointer to the buffer on success, @c NULL if a required allocation failed
  */
index 07e115564a88c67eb38c52dc988ea97c15dc9e19..6b11e2c1c61cbad1184ff48275ee2287244d9530 100644 (file)
@@ -77,7 +77,7 @@ struct cx_hash_map_s {
  * In other words, when the iterator is finished, @c index==size .
  *
  * @param allocator the allocator to use
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
  * @param itemsize the size of one element
  * @param buckets the initial number of buckets in this hash map
  * @return a pointer to the new hash map
index 94185d60c32cf2bfa38a01d7fff2cf97a04f397c..3da2cae45b759b1b81b62ce3bf91bb2637ff4c50 100644 (file)
@@ -51,7 +51,7 @@ extern "C" {
  * to cx_cmp_ptr(), if none is given.
  *
  * @param allocator the allocator for allocating the list nodes
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
  * @param comparator the comparator for the elements
  * (if @c NULL, and the list is not storing pointers, sort and find
  * functions will not work)
index c45f9d5a34ec24d9a4ede6c2e59647d9e0e3932a..14ddc043c2f86a95a8b4b0cb3c8a1e1b09a3c2d2 100644 (file)
@@ -80,8 +80,10 @@ struct cx_list_class_s {
 
     /**
      * Member function for inserting a single element.
+     * The data pointer may be @c NULL in which case the function shall only allocate memory.
+     * Returns a pointer to the data of the inserted element.
      */
-    int (*insert_element)(
+    void *(*insert_element)(
             struct cx_list_s *list,
             size_t index,
             const void *data
@@ -370,6 +372,7 @@ static inline size_t cxListSize(const CxList *list) {
  * @retval zero success
  * @retval non-zero memory allocation failure
  * @see cxListAddArray()
+ * @see cxListEmplace()
  */
 cx_attr_nonnull
 static inline int cxListAdd(
@@ -377,7 +380,7 @@ static inline int cxListAdd(
         const void *elem
 ) {
     list->collection.sorted = false;
-    return list->cl->insert_element(list, list->collection.size, elem);
+    return list->cl->insert_element(list, list->collection.size, elem) == NULL;
 }
 
 /**
@@ -418,6 +421,7 @@ static inline size_t cxListAddArray(
  * @retval non-zero memory allocation failure or the index is out of bounds
  * @see cxListInsertAfter()
  * @see cxListInsertBefore()
+ * @see cxListEmplaceAt()
  */
 cx_attr_nonnull
 static inline int cxListInsert(
@@ -426,7 +430,41 @@ static inline int cxListInsert(
         const void *elem
 ) {
     list->collection.sorted = false;
-    return list->cl->insert_element(list, index, elem);
+    return list->cl->insert_element(list, index, elem) == NULL;
+}
+
+/**
+ * Allocates memory for an element at the specified index and returns a pointer to that memory.
+ *
+ * @remark When the list is storing pointers, this will return a @c void**.
+ *
+ * @param list the list
+ * @param index the index where to emplace the element
+ * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds
+ * @see cxListEmplace()
+ * @see cxListInsert()
+ */
+cx_attr_nonnull
+static inline void *cxListEmplaceAt(CxList *list, size_t index) {
+    list->collection.sorted = false;
+    return list->cl->insert_element(list, index, NULL);
+}
+
+
+/**
+ * Allocates memory for an element at the end of the list and returns a pointer to that memory.
+ *
+ * @remark When the list is storing pointers, this will return a @c void**.
+ *
+ * @param list the list
+ * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds
+ * @see cxListEmplaceAt()
+ * @see cxListAdd()
+ */
+cx_attr_nonnull
+static inline void *cxListEmplace(CxList *list) {
+    list->collection.sorted = false;
+    return list->cl->insert_element(list, list->collection.size, NULL);
 }
 
 /**
@@ -582,8 +620,9 @@ static inline int cxListRemove(
 /**
  * Removes and returns the element at the specified index.
  *
- * No destructor is called and instead the element is copied to the
+ * No destructor is called, and instead the element is copied to the
  * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
  *
  * @param list the list
  * @param index the index of the element
@@ -601,12 +640,94 @@ static inline int cxListRemoveAndGet(
     return list->cl->remove(list, index, 1, targetbuf) == 0;
 }
 
+/**
+ * Removes and returns the first element of the list.
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list the list
+ * @param targetbuf a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ * @see cxListPopFront()
+ * @see cxListRemoveAndGetLast()
+ */
+cx_attr_nonnull
+cx_attr_access_w(2)
+static inline int cxListRemoveAndGetFirst(
+    CxList *list,
+    void *targetbuf
+) {
+    return list->cl->remove(list, 0, 1, targetbuf) == 0;
+}
+
+/**
+ * Removes and returns the first element of the list.
+ *
+ * Alias for cxListRemoveAndGetFirst().
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list (@c CxList*) the list
+ * @param targetbuf (@c void*) a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ * @see cxListRemoveAndGetFirst()
+ * @see cxListPop()
+ */
+#define cxListPopFront(list, targetbuf) cxListRemoveAndGetFirst((list), (targetbuf))
+
+
+/**
+ * Removes and returns the last element of the list.
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list the list
+ * @param targetbuf a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ */
+cx_attr_nonnull
+cx_attr_access_w(2)
+static inline int cxListRemoveAndGetLast(
+    CxList *list,
+    void *targetbuf
+) {
+    // note: index may wrap - member function will catch that
+    return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0;
+}
+
+/**
+ * Removes and returns the last element of the list.
+ *
+ * Alias for cxListRemoveAndGetLast().
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list (@c CxList*) the list
+ * @param targetbuf (@c void*) a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ * @see cxListRemoveAndGetLast()
+ * @see cxListPopFront()
+ */
+#define cxListPop(list, targetbuf) cxListRemoveAndGetLast((list), (targetbuf))
+
 /**
  * Removes multiple element starting at the specified index.
  *
  * If an element destructor function is specified, it is called for each
  * element. It is guaranteed that the destructor is called before removing
- * the element, however, due to possible optimizations it is neither guaranteed
+ * the element. However, due to possible optimizations, it is neither guaranteed
  * that the destructors are invoked for all elements before starting to remove
  * them, nor that the element is removed immediately after the destructor call
  * before proceeding to the next element.
@@ -626,10 +747,11 @@ static inline size_t cxListRemoveArray(
 }
 
 /**
- * Removes and returns multiple element starting at the specified index.
+ * Removes and returns multiple elements starting at the specified index.
  *
- * No destructor is called and instead the elements are copied to the
+ * No destructor is called, and instead the elements are copied to the
  * @p targetbuf which MUST be large enough to hold all removed elements.
+ * If the list is storing pointers, @p targetbuf is expected to be an array of pointers.
  *
  * @param list the list
  * @param index the index of the element
@@ -665,15 +787,15 @@ static inline void cxListClear(CxList *list) {
 /**
  * Swaps two items in the list.
  *
- * Implementations should only allocate temporary memory for the swap, if
+ * Implementations should only allocate temporary memory for the swap if
  * it is necessary.
  *
  * @param list the list
  * @param i the index of the first element
  * @param j the index of the second element
  * @retval zero success
- * @retval non-zero one of the indices is out of bounds
- * or the swap needed extra memory but allocation failed
+ * @retval non-zero one of the indices is out of bounds,
+ * or the swap needed extra memory, but allocation failed
  */
 cx_attr_nonnull
 static inline int cxListSwap(
@@ -688,6 +810,8 @@ static inline int cxListSwap(
 /**
  * Returns a pointer to the element at the specified index.
  *
+ * If the list is storing pointers, returns the pointer stored at the specified index.
+ *
  * @param list the list
  * @param index the index of the element
  * @return a pointer to the element or @c NULL if the index is out of bounds
@@ -700,6 +824,31 @@ static inline void *cxListAt(
     return list->cl->at(list, index);
 }
 
+/**
+ * Returns a pointer to the first element.
+ *
+ * If the list is storing pointers, returns the first pointer stored in the list.
+ *
+ * @param list the list
+ * @return a pointer to the first element or @c NULL if the list is empty
+ */
+cx_attr_nonnull
+static inline void *cxListFirst(const CxList *list) {
+    return list->cl->at(list, 0);
+}
+
+/**
+ * Returns a pointer to the last element.
+ *
+ * If the list is storing pointers, returns the last pointer stored in the list.
+ *
+ * @param list the list
+ * @return a pointer to the last element or @c NULL if the list is empty
+ */
+cx_attr_nonnull
+static inline void *cxListLast(const CxList *list) {
+    return list->cl->at(list, list->collection.size - 1);
+}
 
 /**
  * Sets the element at the specified index in the list
index 35bf34e53e91ba46e3a5d0bc51b2bbee72e86967..5d8d2849ea7e19af1fe7b6b4144a854e559396dd 100644 (file)
 extern "C" {
 #endif
 
-/** Internal structure for pooled memory. */
-struct cx_mempool_memory_s;
+/** A memory block in a simple memory pool. */
+struct cx_mempool_memory_s {
+    /** The destructor. */
+    cx_destructor_func destructor;
+    /** The actual memory. */
+    char c[];
+};
+
+/** A memory block in an advanced memory pool. */
+struct cx_mempool_memory2_s {
+    /** The destructor. */
+    cx_destructor_func2 destructor;
+    /** Data for the destructor. */
+    void *data;
+    /** The actual memory. */
+    char c[];
+};
+
+/** Represents memory that is not allocated by, but registered with a pool. */
+struct cx_mempool_foreign_memory_s {
+    /** The foreign memory. */
+    void* mem;
+    union {
+        /** Simple destructor. */
+        cx_destructor_func destr;
+        /** Advanced destructor. */
+        cx_destructor_func2 destr2;
+    };
+    /** Data for the advanced destructor. */
+    void *destr2_data;
+};
+
+/** Specifies how individual blocks are allocated. */
+enum cx_mempool_type {
+    /**
+     * Allows registration of cx_destructor_func for each memory block.
+     */
+    CX_MEMPOOL_TYPE_SIMPLE,
+    /**
+     * Allows registration of cx_destructor_func2 for each memory block.
+     */
+    CX_MEMPOOL_TYPE_ADVANCED,
+    /**
+     * No individual destructor registration allowed.
+     *
+     * In this mode, no additional memory per block is allocated.
+     */
+    CX_MEMPOOL_TYPE_PURE,
+};
 
 /**
  * The basic structure of a memory pool.
  * Should be the first member of an actual memory pool implementation.
  */
 struct cx_mempool_s {
+    /** The used allocator, initialized with the cxDefaultAllocator. */
+    const CxAllocator * const base_allocator;
+
     /** The provided allocator. */
     const CxAllocator *allocator;
 
-    /**
-     * A destructor that shall be automatically registered for newly allocated memory.
-     * This destructor MUST NOT free the memory.
-     */
-    cx_destructor_func auto_destr;
-
     /** Array of pooled memory. */
-    struct cx_mempool_memory_s **data;
+    void **data;
 
     /** Number of pooled memory items. */
     size_t size;
 
     /** Memory pool capacity. */
     size_t capacity;
+
+    /** Array of registered memory. */
+    struct cx_mempool_foreign_memory_s *registered;
+
+    /** Number of registered memory items. */
+    size_t registered_size;
+
+    /** Capacity for registered memory. */
+    size_t registered_capacity;
+
+    /**
+     * A destructor that shall be called before deallocating a memory block.
+     * This destructor MUST NOT free the memory itself.
+     *
+     * It is guaranteed that this destructor is called after the individual
+     * destructor of the memory block and before @c destr2.
+     */
+    cx_destructor_func destr;
+
+    /**
+     * A destructor that shall be called before deallocating a memory block.
+     * This destructor MUST NOT free the memory itself.
+     *
+    * It is guaranteed that this destructor is called after the individual
+     * destructor of the memory block and @c destr.
+     */
+    cx_destructor_func2 destr2;
+
+    /**
+     * Additional data for the @c destr2.
+     */
+    void *destr2_data;
 };
 
 /**
@@ -84,31 +160,76 @@ cx_attr_export
 void cxMempoolFree(CxMempool *pool);
 
 /**
- * Creates an array-based memory pool with a shared destructor function.
+ * Creates an array-based memory pool.
  *
- * This destructor MUST NOT free the memory.
+ * The type determines how much additional memory is allocated per block
+ * to register a destructor function.
  *
- * @param capacity the initial capacity of the pool
- * @param destr optional destructor function to use for allocated memory
+ * @param capacity the initial capacity of the pool (an implementation default if zero)
+ * @param type the type of memory pool
  * @return the created memory pool or @c NULL if allocation failed
  */
 cx_attr_nodiscard
 cx_attr_malloc
 cx_attr_dealloc(cxMempoolFree, 1)
 cx_attr_export
-CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr);
+CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
+
+/**
+ * Creates a basic array-based memory pool.
+ *
+ * Convenience macro to create a memory pool of type #CX_MEMPOOL_TYPE_SIMPLE.
+ *
+ * @param capacity (@c size_t) the initial capacity of the pool
+ * @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed
+ */
+#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, CX_MEMPOOL_TYPE_SIMPLE)
+
+/**
+ * Creates a basic array-based memory pool.
+ *
+ * Convenience macro to create a memory pool of type #CX_MEMPOOL_TYPE_ADVANCED.
+ *
+ * @param capacity (@c size_t) the initial capacity of the pool
+ * @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed
+ */
+#define cxMempoolCreateAdvanced(capacity) cxMempoolCreate(capacity, CX_MEMPOOL_TYPE_ADVANCED)
 
 /**
  * Creates a basic array-based memory pool.
  *
+ * Convenience macro to create a memory pool of type #CX_MEMPOOL_TYPE_PURE.
+ *
  * @param capacity (@c size_t) the initial capacity of the pool
  * @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed
  */
-#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, NULL)
+#define cxMempoolCreatePure(capacity) cxMempoolCreate(capacity, CX_MEMPOOL_TYPE_PURE)
+
+/**
+ * Sets the global destructor for all memory blocks within the specified pool.
+ *
+ * @param pool the memory pool
+ * @param fnc the destructor that shall be applied to all memory blocks
+ */
+cx_attr_nonnull_arg(1)
+cx_attr_export
+void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
+
+/**
+ * Sets the global destructor for all memory blocks within the specified pool.
+ *
+ * @param pool the memory pool
+ * @param fnc the destructor that shall be applied to all memory blocks
+ * @param data additional data for the destructor function
+ */
+cx_attr_nonnull_arg(1)
+cx_attr_export
+void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
 
 /**
  * Sets the destructor function for a specific allocated memory object.
  *
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_SIMPLE, the behavior is undefined.
  * If the memory is not managed by a UCX memory pool, the behavior is undefined.
  * The destructor MUST NOT free the memory.
  *
@@ -123,24 +244,56 @@ void cxMempoolSetDestructor(
 );
 
 /**
- * Removes the destructor function for a specific allocated memory object.
+ * Sets the destructor function for a specific allocated memory object.
  *
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_ADVANCED, the behavior is undefined.
  * If the memory is not managed by a UCX memory pool, the behavior is undefined.
  * The destructor MUST NOT free the memory.
  *
  * @param memory the object allocated in the pool
+ * @param fnc the destructor function
+ * @param data additional data for the destructor function
+ */
+cx_attr_nonnull
+cx_attr_export
+void cxMempoolSetDestructor2(
+        void *memory,
+        cx_destructor_func2 fnc,
+        void *data
+);
+
+/**
+ * Removes the destructor function for a specific allocated memory object.
+ *
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_SIMPLE, the behavior is undefined.
+ * If the memory is not managed by a UCX memory pool, the behavior is undefined.
+ *
+ * @param memory the object allocated in the pool
  */
 cx_attr_nonnull
 cx_attr_export
 void cxMempoolRemoveDestructor(void *memory);
 
+/**
+ * Removes the destructor function for a specific allocated memory object.
+ *
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_ADVANCED, the behavior is undefined.
+ * If the memory is not managed by a UCX memory pool, the behavior is undefined.
+ *
+ * @param memory the object allocated in the pool
+ */
+cx_attr_nonnull
+cx_attr_export
+void cxMempoolRemoveDestructor2(void *memory);
+
 /**
  * Registers foreign memory with this pool.
  *
  * The destructor, in contrast to memory allocated by the pool, MUST free the memory.
+ * This function can be used with any pool of any type, since destructors for registered memory
+ * are entirely independent of the pool's memory management.
  *
- * A small portion of memory will be allocated to register the information in the pool.
- * If that allocation fails, this function will return non-zero.
+ * The destructor for the registered memory will be called after all pooled items have been freed.
  *
  * @param pool the pool
  * @param memory the object to register (MUST NOT be already allocated in the pool)
@@ -156,6 +309,35 @@ int cxMempoolRegister(
         cx_destructor_func destr
 );
 
+
+/**
+ * Registers foreign memory with this pool.
+ *
+ * The destructor, in contrast to memory allocated by the pool, MUST free the memory.
+ * This function can be used with any pool of any type, since destructors for registered memory
+ * are entirely independent of the pool's memory management.
+ *
+ * The destructor for the registered memory will be called after all pooled items have been freed.
+ *
+ * @attention The data pointer MUST NOT be @c NULL.
+ * If you wish to register a destructor without additional data, use cxMempoolRegister().
+ *
+ * @param pool the pool
+ * @param memory the object to register (MUST NOT be already allocated in the pool)
+ * @param destr the destructor function
+ * @param data additional data for the destructor function
+ * @retval zero success
+ * @retval non-zero failure
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolRegister2(
+        CxMempool *pool,
+        void *memory,
+        cx_destructor_func2 destr,
+        void *data
+);
+
 /**
  * Transfers all the memory managed by one pool to another.
  *
@@ -164,10 +346,12 @@ int cxMempoolRegister(
  *
  * The source pool will get a completely new allocator and can be reused or destroyed afterward.
  *
+ * This function fails when the destination pool has a different type than the source pool.
+ *
  * @param source the pool to move the memory from
  * @param dest the pool where to transfer the memory to
  * @retval zero success
- * @retval non-zero failure
+ * @retval non-zero allocation failure or incompatible pools
  */
 cx_attr_nonnull
 cx_attr_export
@@ -179,8 +363,7 @@ int cxMempoolTransfer(
 /**
  * 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.
+ * This function fails when the destination pool has a different type than the source pool.
  *
  * @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.
@@ -189,7 +372,7 @@ int cxMempoolTransfer(
  * @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
+ * @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible
  */
 cx_attr_nonnull
 cx_attr_export
index 2942738a59d25a4dcea77f7d97acbbce5557d147..d6df8f5fb2104cd10da4a14b216f58dfec38e673 100644 (file)
@@ -555,7 +555,7 @@ CxPropertiesStatus cxPropertiesNext(
  * zero-terminated C strings (@c char*), which means the @p map should have been
  * created with #CX_STORE_POINTERS.
  *
- * The default stdlib allocator will be used, unless you specify a custom
+ * The cxDefaultAllocator will be used unless you specify a custom
  * allocator in the optional @c data field of the returned sink.
  *
  * @param map the map that shall consume the k/v-pairs.
index 5c8ea88731dba84960815fca94fe2a467e62247f..02c82c85935423ffb85445468f638197e24f8bdf 100644 (file)
@@ -305,7 +305,7 @@ static inline cxstring cx_strcast_c(cxstring str) {
 #endif
 
 /**
- * Passes the pointer in this string to @c free().
+ * Passes the pointer in this string to the cxDefaultAllocator's @c free() function.
  *
  * The pointer in the struct is set to @c NULL and the length is set to zero
  * which means that this function protects you against double-free.
@@ -371,7 +371,6 @@ int cx_strcpy_a(
  *
  * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
  *
- * @param alloc (@c CxAllocator*) the allocator
  * @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to
  * @param src (@c cxstring) the source string
  *
@@ -455,7 +454,7 @@ cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
 /**
  * Concatenates strings and returns a new string.
  *
- * The resulting string will be allocated by standard @c malloc().
+ * The resulting string will be allocated by the cxDefaultAllocator.
  * So developers @em must pass the return value to cx_strfree() eventually.
  *
 * If memory allocation fails, the pointer in the returned string will
@@ -475,7 +474,7 @@ cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
 /**
  * Concatenates strings.
  *
- * The resulting string will be allocated by standard @c malloc().
+ * The resulting string will be allocated by the cxDefaultAllocator.
  * So developers @em must pass the return value to cx_strfree() eventually.
  *
  * If @p str already contains a string, the memory will be reallocated and
@@ -931,8 +930,8 @@ cxmutstr cx_strdup_a_(
 /**
  * Creates a duplicate of the specified string.
  *
- * The new string will contain a copy allocated by standard
- * @c malloc(). So developers @em must pass the return value to cx_strfree().
+ * The new string will contain a copy allocated by the cxDefaultAllocator.
+ * So developers @em must pass the return value to cx_strfree().
  *
  * @note The returned string is guaranteed to be zero-terminated.
  *
@@ -1063,7 +1062,7 @@ cxmutstr cx_strreplacen_a(
  *
  * Replaces at most @p replmax occurrences.
  *
- * The returned string will be allocated by @c malloc() and is guaranteed
+ * The returned string will be allocated by the cxDefaultAllocator and is guaranteed
  * to be zero-terminated.
  *
  * If allocation fails, or the input string is empty,
@@ -1099,7 +1098,7 @@ cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX)
 /**
  * Replaces a string with another string.
  *
- * The returned string will be allocated by @c malloc() and is guaranteed
+ * The returned string will be allocated by the cxDefaultAllocator and is guaranteed
  * to be zero-terminated.
  *
  * If allocation fails, or the input string is empty,
index 96866232b8f778198e9b6a5d426a0a6afaf5d17e..14832dbeff20cb06fea7f008b02cc18ec6f6e33e 100644 (file)
@@ -213,7 +213,7 @@ typedef struct cx_tree_visitor_s {
  */
 cx_attr_nonnull
 static inline void cxTreeIteratorDispose(CxTreeIterator *iter) {
-    free(iter->stack);
+    cxFreeDefault(iter->stack);
     iter->stack = NULL;
 }
 
@@ -226,7 +226,7 @@ static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) {
     struct cx_tree_visitor_queue_s *q = visitor->queue_next;
     while (q != NULL) {
         struct cx_tree_visitor_queue_s *next = q->next;
-        free(q);
+        cxFreeDefault(q);
         q = next;
     }
 }
@@ -443,7 +443,7 @@ int cx_tree_search(
  * Creates a depth-first iterator for a tree with the specified root node.
  *
  * @note A tree iterator needs to maintain a stack of visited nodes, which is
- * allocated using stdlib malloc().
+ * allocated using the cxDefaultAllocator.
  * When the iterator becomes invalid, this memory is automatically released.
  * However, if you wish to cancel the iteration before the iterator becomes
  * invalid by itself, you MUST call cxTreeIteratorDispose() manually to release
@@ -471,8 +471,8 @@ CxTreeIterator cx_tree_iterator(
 /**
  * Creates a breadth-first iterator for a tree with the specified root node.
  *
- * @note A tree visitor needs to maintain a queue of to be visited nodes, which
- * is allocated using stdlib malloc().
+ * @note A tree visitor needs to maintain a queue of to-be visited nodes, which
+ * is allocated using the cxDefaultAllocator.
  * When the visitor becomes invalid, this memory is automatically released.
  * However, if you wish to cancel the iteration before the visitor becomes
  * invalid by itself, you MUST call cxTreeVisitorDispose() manually to release
@@ -958,7 +958,7 @@ void cxTreeFree(CxTree *tree);
  * will free the nodes with the allocator's free() method.
  *
  * @param allocator the allocator that shall be used
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
  * @param create_func a function that creates new nodes
  * @param search_func a function that compares two nodes
  * @param search_data_func a function that compares a node with data
@@ -1022,7 +1022,7 @@ cx_tree_node_base_layout)
  * tree, you need to specify those functions afterwards.
  *
  * @param allocator the allocator that was used for nodes of the wrapped tree
- * (if @c NULL, a default stdlib allocator is assumed)
+ * (if @c NULL, the cxDefaultAllocator is assumed)
  * @param root the root node of the tree that shall be wrapped
  * @param loc_parent offset in the node struct for the parent pointer
  * @param loc_children offset in the node struct for the children linked list
index 4993d97569a32fbc97a2699da796f826e08efac6..484c7d374c7f21de0893c38b63650de0fb59242a 100644 (file)
@@ -500,7 +500,7 @@ static cxmutstr escape_string(cxmutstr str, bool escape_slash) {
 
         if (all_printable && escape) {
             size_t capa = str.length + 32;
-            char *space = malloc(capa);
+            char *space = cxMallocDefault(capa);
             if (space == NULL) return cx_mutstrn(NULL, 0);
             cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
             cxBufferWrite(str.ptr, 1, i, &buf);
@@ -631,10 +631,10 @@ void cxJsonInit(CxJson *json, const CxAllocator *allocator) {
 void cxJsonDestroy(CxJson *json) {
     cxBufferDestroy(&json->buffer);
     if (json->states != json->states_internal) {
-        free(json->states);
+        cxFreeDefault(json->states);
     }
     if (json->vbuf != json->vbuf_internal) {
-        free(json->vbuf);
+        cxFreeDefault(json->vbuf);
     }
     cxJsonValueFree(json->parsed);
     json->parsed = NULL;
@@ -984,67 +984,67 @@ static void json_arr_free_temp(CxJsonValue** values, size_t count) {
         if (values[i] == NULL) break;
         cxJsonValueFree(values[i]);
     }
-    free(values);
+    cxFreeDefault(values);
 }
 // LCOV_EXCL_STOP
 
 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) {
-    CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+    CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
     if (values == NULL) return -1;
     for (size_t i = 0; i < count; i++) {
         values[i] = cxJsonCreateNumber(arr->allocator, num[i]);
         if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
     }
     int ret = cxJsonArrAddValues(arr, values, count);
-    free(values);
+    cxFreeDefault(values);
     return ret;
 }
 
 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) {
-    CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+    CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
     if (values == NULL) return -1;
     for (size_t i = 0; i < count; i++) {
         values[i] = cxJsonCreateInteger(arr->allocator, num[i]);
         if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
     }
     int ret = cxJsonArrAddValues(arr, values, count);
-    free(values);
+    cxFreeDefault(values);
     return ret;
 }
 
 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) {
-    CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+    CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
     if (values == NULL) return -1;
     for (size_t i = 0; i < count; i++) {
         values[i] = cxJsonCreateString(arr->allocator, str[i]);
         if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
     }
     int ret = cxJsonArrAddValues(arr, values, count);
-    free(values);
+    cxFreeDefault(values);
     return ret;
 }
 
 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) {
-    CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+    CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
     if (values == NULL) return -1;
     for (size_t i = 0; i < count; i++) {
         values[i] = cxJsonCreateCxString(arr->allocator, str[i]);
         if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
     }
     int ret = cxJsonArrAddValues(arr, values, count);
-    free(values);
+    cxFreeDefault(values);
     return ret;
 }
 
 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) {
-    CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+    CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
     if (values == NULL) return -1;
     for (size_t i = 0; i < count; i++) {
         values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]);
         if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
     }
     int ret = cxJsonArrAddValues(arr, values, count);
-    free(values);
+    cxFreeDefault(values);
     return ret;
 }
 
index 66cf543c5382c0af3904aad1de1078d6e553003b..7cd2ff2e49a0608d01a2294425746a294ce08534 100644 (file)
@@ -401,7 +401,7 @@ static void cx_linked_list_sort_merge(
 ) {
     void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
     void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
-                    malloc(sizeof(void *) * length) : sbo;
+                    cxMallocDefault(sizeof(void *) * length) : sbo;
     if (sorted == NULL) abort();
     void *rc, *lc;
 
@@ -439,7 +439,7 @@ static void cx_linked_list_sort_merge(
     *begin = sorted[0];
     *end = sorted[length - 1];
     if (sorted != sbo) {
-        free(sorted);
+        cxFreeDefault(sorted);
     }
 }
 
@@ -613,7 +613,9 @@ static int cx_ll_insert_at(
 
     // initialize new new_node
     new_node->prev = new_node->next = NULL;
-    memcpy(new_node->payload, elem, list->collection.elem_size);
+    if (elem != NULL) {
+        memcpy(new_node->payload, elem, list->collection.elem_size);
+    }
 
     // insert
     cx_linked_list *ll = (cx_linked_list *) list;
@@ -659,12 +661,26 @@ static size_t cx_ll_insert_array(
     return n;
 }
 
-static int cx_ll_insert_element(
+static void *cx_ll_insert_element(
         struct cx_list_s *list,
         size_t index,
         const void *element
 ) {
-    return 1 != cx_ll_insert_array(list, index, element, 1);
+    // out-of-bounds check
+    if (index > list->collection.size) return NULL;
+
+    // find position efficiently
+    cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+
+    // perform first insert
+    if (cx_ll_insert_at(list, node, element)) return NULL;
+
+    // return a pointer to the data of the inserted node
+    if (node == NULL) {
+        return ((cx_linked_list *) list)->begin->payload;
+    } else {
+        return node->next->payload;
+    }
 }
 
 static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
@@ -1056,12 +1072,12 @@ static int cx_ll_insert_iter(
         }
         return result;
     } else {
-        int result = cx_ll_insert_element(list, list->collection.size, elem);
-        if (result == 0) {
-            iter->elem_count++;
-            iter->index = list->collection.size;
+        if (cx_ll_insert_element(list, list->collection.size, elem) == NULL) {
+            return 1;
         }
-        return result;
+        iter->elem_count++;
+        iter->index = list->collection.size;
+        return 0;
     }
 }
 
index 497c66e3fb2f4eeb33e695997c8e92c2eb6a89c2..7fbbb8b76de9a6ac806d81afe2b5ab1ad226ef13 100644 (file)
@@ -62,7 +62,7 @@ static void cx_pl_destructor(struct cx_list_s *list) {
     list->climpl->deallocate(list);
 }
 
-static int cx_pl_insert_element(
+static void *cx_pl_insert_element(
         struct cx_list_s *list,
         size_t index,
         const void *element
@@ -282,7 +282,7 @@ size_t cx_list_default_insert_array(
     const char *src = data;
     size_t i = 0;
     for (; i < n; i++) {
-        if (0 != invoke_list_func(
+        if (NULL == invoke_list_func(
             insert_element, list, index + i,
             src + (i * elem_size))) return i;
     }
@@ -329,7 +329,7 @@ size_t cx_list_default_insert_sorted(
 
         // insert the elements at location si
         if (ins == 1) {
-            if (0 != invoke_list_func(
+            if (NULL == invoke_list_func(
                 insert_element, list, di, src)) return inserted;
         } else {
             size_t r = invoke_list_func(insert_array, list, di, src, ins);
@@ -354,7 +354,7 @@ size_t cx_list_default_insert_sorted(
 void cx_list_default_sort(struct cx_list_s *list) {
     size_t elem_size = list->collection.elem_size;
     size_t list_size = list->collection.size;
-    void *tmp = malloc(elem_size * list_size);
+    void *tmp = cxMallocDefault(elem_size * list_size);
     if (tmp == NULL) abort();
 
     // copy elements from source array
@@ -377,7 +377,7 @@ void cx_list_default_sort(struct cx_list_s *list) {
         loc += elem_size;
     }
 
-    free(tmp);
+    cxFreeDefault(tmp);
 }
 
 int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
@@ -387,7 +387,7 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
 
     size_t elem_size = list->collection.elem_size;
 
-    void *tmp = malloc(elem_size);
+    void *tmp = cxMallocDefault(elem_size);
     if (tmp == NULL) return 1;
 
     void *ip = invoke_list_func(at, list, i);
@@ -397,7 +397,7 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
     memcpy(ip, jp, elem_size);
     memcpy(jp, tmp, elem_size);
 
-    free(tmp);
+    cxFreeDefault(tmp);
 
     return 0;
 }
index 1080c552842a2d30bc1c051515fb404434825435..8711439f50d784defc3c4823f69823ee4e7465ee 100644 (file)
 #include <string.h>
 #include <errno.h>
 
-struct cx_mempool_memory_s {
-    /** The destructor. */
-    cx_destructor_func destructor;
-    /** The actual memory. */
-    char c[];
-};
-
 static int cx_mempool_ensure_capacity(
         struct cx_mempool_s *pool,
         size_t needed_capacity
@@ -46,39 +39,61 @@ static int cx_mempool_ensure_capacity(
     size_t newcap = pool->capacity >= 1000 ?
         pool->capacity + 1000 : pool->capacity * 2;
     size_t newmsize;
-    if (pool->capacity > newcap || cx_szmul(newcap,
-            sizeof(struct cx_mempool_memory_s*), &newmsize)) {
+    // LCOV_EXCL_START
+    if (pool->capacity > newcap
+            || cx_szmul(newcap, sizeof(void*), &newmsize)) {
         errno = EOVERFLOW;
         return 1;
-    }
-    struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize);
+    } // LCOV_EXCL_STOP
+    void **newdata = cxRealloc(pool->base_allocator, pool->data, newmsize);
     if (newdata == NULL) return 1;
     pool->data = newdata;
     pool->capacity = newcap;
     return 0;
 }
 
-static void *cx_mempool_malloc(
+static int cx_mempool_ensure_registered_capacity(
+        struct cx_mempool_s *pool,
+        size_t needed_capacity
+) {
+    if (needed_capacity <= pool->registered_capacity) return 0;
+    // we do not expect so many registrations
+    size_t newcap = pool->registered_capacity + 8;
+    size_t newmsize;
+    // LCOV_EXCL_START
+    if (pool->registered_capacity > newcap || cx_szmul(newcap,
+            sizeof(struct cx_mempool_foreign_memory_s), &newmsize)) {
+        errno = EOVERFLOW;
+        return 1;
+    } // LCOV_EXCL_STOP
+    void *newdata = cxRealloc(pool->base_allocator, pool->registered, newmsize);
+    if (newdata == NULL) return 1;
+    pool->registered = newdata;
+    pool->registered_capacity = newcap;
+    return 0;
+}
+
+static void *cx_mempool_malloc_simple(
         void *p,
         size_t n
 ) {
     struct cx_mempool_s *pool = p;
 
     if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
-        return NULL;
+        return NULL; // LCOV_EXCL_LINE
     }
 
-    struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n);
+    struct cx_mempool_memory_s *mem =
+        cxMalloc(pool->base_allocator, sizeof(struct cx_mempool_memory_s) + n);
     if (mem == NULL) return NULL;
-
-    mem->destructor = pool->auto_destr;
+    mem->destructor = NULL;
     pool->data[pool->size] = mem;
     pool->size++;
 
     return mem->c;
 }
 
-static void *cx_mempool_calloc(
+static void *cx_mempool_calloc_simple(
         void *p,
         size_t nelem,
         size_t elsize
@@ -88,53 +103,165 @@ static void *cx_mempool_calloc(
         errno = EOVERFLOW;
         return NULL;
     }
-    void *ptr = cx_mempool_malloc(p, msz);
+    void *ptr = cx_mempool_malloc_simple(p, msz);
     if (ptr == NULL) return NULL;
     memset(ptr, 0, nelem * elsize);
     return ptr;
 }
 
-static void *cx_mempool_realloc(
+static void cx_mempool_free_simple(
+        void *p,
+        void *ptr
+) {
+    if (!ptr) return;
+    struct cx_mempool_s *pool = p;
+
+    struct cx_mempool_memory_s *mem =
+        (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s));
+
+    for (size_t i = 0; i < pool->size; i++) {
+        if (mem == pool->data[i]) {
+            if (mem->destructor) {
+                mem->destructor(mem->c);
+            }
+            if (pool->destr) {
+                pool->destr(mem->c);
+            }
+            if (pool->destr2) {
+                pool->destr2(pool->destr2_data, mem->c);
+            }
+            cxFree(pool->base_allocator, mem);
+            size_t last_index = pool->size - 1;
+            if (i != last_index) {
+                pool->data[i] = pool->data[last_index];
+                pool->data[last_index] = NULL;
+            }
+            pool->size--;
+            return;
+        }
+    }
+    abort(); // LCOV_EXCL_LINE
+}
+
+static void *cx_mempool_realloc_simple(
         void *p,
         void *ptr,
         size_t n
 ) {
+    if (ptr == NULL) {
+        return cx_mempool_malloc_simple(p, n);
+    }
+    if (n == 0) {
+        cx_mempool_free_simple(p, ptr);
+        return NULL;
+    }
     struct cx_mempool_s *pool = p;
 
-    struct cx_mempool_memory_s *mem, *newm;
-    mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func));
-    newm = realloc(mem, n + sizeof(cx_destructor_func));
+    const unsigned overhead = sizeof(struct cx_mempool_memory_s);
+    struct cx_mempool_memory_s *mem =
+        (void *) (((char *) ptr) - overhead);
+    struct cx_mempool_memory_s *newm =
+        cxRealloc(pool->base_allocator, mem, n + overhead);
 
     if (newm == NULL) return NULL;
     if (mem != newm) {
         for (size_t i = 0; i < pool->size; i++) {
             if (pool->data[i] == mem) {
                 pool->data[i] = newm;
-                return ((char*)newm) + sizeof(cx_destructor_func);
+                return ((char*)newm) + overhead;
             }
         }
         abort(); // LCOV_EXCL_LINE
     } else {
-        return ptr;
+        // unfortunately glibc() realloc seems to always move
+        return ptr;  // LCOV_EXCL_LINE
+    }
+}
+
+static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) {
+    const bool has_destr = pool->destr;
+    const bool has_destr2 = pool->destr2;
+    for (size_t i = 0; i < pool->size; i++) {
+        struct cx_mempool_memory_s *mem = pool->data[i];
+        if (mem->destructor) {
+            mem->destructor(mem->c);
+        }
+        if (has_destr) {
+            pool->destr(mem->c);
+        }
+        if (has_destr2) {
+            pool->destr2(pool->destr2_data, mem->c);
+        }
+        cxFree(pool->base_allocator, mem);
+    }
+}
+
+static cx_allocator_class cx_mempool_simple_allocator_class = {
+    cx_mempool_malloc_simple,
+    cx_mempool_realloc_simple,
+    cx_mempool_calloc_simple,
+    cx_mempool_free_simple
+};
+
+static void *cx_mempool_malloc_advanced(
+        void *p,
+        size_t n
+) {
+    struct cx_mempool_s *pool = p;
+
+    if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+        return NULL;  // LCOV_EXCL_LINE
+    }
+
+    struct cx_mempool_memory2_s *mem =
+        cxMalloc(pool->base_allocator, sizeof(struct cx_mempool_memory2_s) + n);
+    if (mem == NULL) return NULL;
+    mem->destructor = NULL;
+    mem->data = NULL;
+    pool->data[pool->size] = mem;
+    pool->size++;
+
+    return mem->c;
+}
+
+static void *cx_mempool_calloc_advanced(
+        void *p,
+        size_t nelem,
+        size_t elsize
+) {
+    size_t msz;
+    if (cx_szmul(nelem, elsize, &msz)) {
+        errno = EOVERFLOW;
+        return NULL;
     }
+    void *ptr = cx_mempool_malloc_advanced(p, msz);
+    if (ptr == NULL) return NULL;
+    memset(ptr, 0, nelem * elsize);
+    return ptr;
 }
 
-static void cx_mempool_free(
+static void cx_mempool_free_advanced(
         void *p,
         void *ptr
 ) {
     if (!ptr) return;
     struct cx_mempool_s *pool = p;
 
-    struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *)
-            ((char *) ptr - sizeof(cx_destructor_func));
+    struct cx_mempool_memory2_s *mem =
+        (void*) ((char *) ptr - sizeof(struct cx_mempool_memory2_s));
 
     for (size_t i = 0; i < pool->size; i++) {
         if (mem == pool->data[i]) {
             if (mem->destructor) {
-                mem->destructor(mem->c);
+                mem->destructor(mem->data, mem->c);
             }
-            free(mem);
+            if (pool->destr) {
+                pool->destr(mem->c);
+            }
+            if (pool->destr2) {
+                pool->destr2(pool->destr2_data, mem->c);
+            }
+            cxFree(pool->base_allocator, mem);
             size_t last_index = pool->size - 1;
             if (i != last_index) {
                 pool->data[i] = pool->data[last_index];
@@ -147,19 +274,207 @@ static void cx_mempool_free(
     abort(); // LCOV_EXCL_LINE
 }
 
-void cxMempoolFree(CxMempool *pool) {
-    if (pool == NULL) return;
-    struct cx_mempool_memory_s *mem;
+static void *cx_mempool_realloc_advanced(
+        void *p,
+        void *ptr,
+        size_t n
+) {
+    if (ptr == NULL) {
+        return cx_mempool_malloc_advanced(p, n);
+    }
+    if (n == 0) {
+        cx_mempool_free_advanced(p, ptr);
+        return NULL;
+    }
+    struct cx_mempool_s *pool = p;
+
+    const unsigned overhead = sizeof(struct cx_mempool_memory2_s);
+    struct cx_mempool_memory2_s *mem =
+        (void *) (((char *) ptr) - overhead);
+    struct cx_mempool_memory2_s *newm =
+        cxRealloc(pool->base_allocator, mem, n + overhead);
+
+    if (newm == NULL) return NULL;
+    if (mem != newm) {
+        for (size_t i = 0; i < pool->size; i++) {
+            if (pool->data[i] == mem) {
+                pool->data[i] = newm;
+                return ((char*)newm) + overhead;
+            }
+        }
+        abort(); // LCOV_EXCL_LINE
+    } else {
+        // unfortunately glibc() realloc seems to always move
+        return ptr;  // LCOV_EXCL_LINE
+    }
+}
+
+static void cx_mempool_free_all_advanced(const struct cx_mempool_s *pool) {
+    const bool has_destr = pool->destr;
+    const bool has_destr2 = pool->destr2;
     for (size_t i = 0; i < pool->size; i++) {
-        mem = pool->data[i];
+        struct cx_mempool_memory2_s *mem = pool->data[i];
         if (mem->destructor) {
-            mem->destructor(mem->c);
+            mem->destructor(mem->data, mem->c);
+        }
+        if (has_destr) {
+            pool->destr(mem->c);
         }
-        free(mem);
+        if (has_destr2) {
+            pool->destr2(pool->destr2_data, mem->c);
+        }
+        cxFree(pool->base_allocator, mem);
     }
-    free(pool->data);
-    free((void*) pool->allocator);
-    free(pool);
+}
+
+static cx_allocator_class cx_mempool_advanced_allocator_class = {
+    cx_mempool_malloc_advanced,
+    cx_mempool_realloc_advanced,
+    cx_mempool_calloc_advanced,
+    cx_mempool_free_advanced
+};
+
+
+static void *cx_mempool_malloc_pure(
+        void *p,
+        size_t n
+) {
+    struct cx_mempool_s *pool = p;
+
+    if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+        return NULL; // LCOV_EXCL_LINE
+    }
+
+    void *mem = cxMalloc(pool->base_allocator, n);
+    if (mem == NULL) return NULL;
+    pool->data[pool->size] = mem;
+    pool->size++;
+
+    return mem;
+}
+
+static void *cx_mempool_calloc_pure(
+        void *p,
+        size_t nelem,
+        size_t elsize
+) {
+    size_t msz;
+    if (cx_szmul(nelem, elsize, &msz)) {
+        errno = EOVERFLOW;
+        return NULL;
+    }
+    void *ptr = cx_mempool_malloc_pure(p, msz);
+    if (ptr == NULL) return NULL;
+    memset(ptr, 0, nelem * elsize);
+    return ptr;
+}
+
+static void cx_mempool_free_pure(
+        void *p,
+        void *ptr
+) {
+    if (!ptr) return;
+    struct cx_mempool_s *pool = p;
+
+    for (size_t i = 0; i < pool->size; i++) {
+        if (ptr == pool->data[i]) {
+            if (pool->destr) {
+                pool->destr(ptr);
+            }
+            if (pool->destr2) {
+                pool->destr2(pool->destr2_data, ptr);
+            }
+            cxFree(pool->base_allocator, ptr);
+            size_t last_index = pool->size - 1;
+            if (i != last_index) {
+                pool->data[i] = pool->data[last_index];
+                pool->data[last_index] = NULL;
+            }
+            pool->size--;
+            return;
+        }
+    }
+    abort(); // LCOV_EXCL_LINE
+}
+
+static void *cx_mempool_realloc_pure(
+        void *p,
+        void *ptr,
+        size_t n
+) {
+    if (ptr == NULL) {
+        return cx_mempool_malloc_pure(p, n);
+    }
+    if (n == 0) {
+        cx_mempool_free_pure(p, ptr);
+        return NULL;
+    }
+    struct cx_mempool_s *pool = p;
+    void *newm = cxRealloc(pool->base_allocator, ptr, n);
+    if (newm == NULL) return NULL;
+    if (ptr != newm) {
+        for (size_t i = 0; i < pool->size; i++) {
+            if (pool->data[i] == ptr) {
+                pool->data[i] = newm;
+                return newm;
+            }
+        }
+        abort(); // LCOV_EXCL_LINE
+    } else {
+        // unfortunately glibc() realloc seems to always move
+        return ptr;  // LCOV_EXCL_LINE
+    }
+}
+
+static void cx_mempool_free_all_pure(const struct cx_mempool_s *pool) {
+    const bool has_destr = pool->destr;
+    const bool has_destr2 = pool->destr2;
+    for (size_t i = 0; i < pool->size; i++) {
+        void *mem = pool->data[i];
+        if (has_destr) {
+            pool->destr(mem);
+        }
+        if (has_destr2) {
+            pool->destr2(pool->destr2_data, mem);
+        }
+        cxFree(pool->base_allocator, mem);
+    }
+}
+
+static cx_allocator_class cx_mempool_pure_allocator_class = {
+    cx_mempool_malloc_pure,
+    cx_mempool_realloc_pure,
+    cx_mempool_calloc_pure,
+    cx_mempool_free_pure
+};
+
+static void cx_mempool_free_foreign(const struct cx_mempool_s *pool) {
+    for (size_t i = 0; i < pool->registered_size; i++) {
+        struct cx_mempool_foreign_memory_s info = pool->registered[i];
+        if (info.destr2_data == NULL) {
+            if (info.destr) {
+                info.destr(info.mem);
+            }
+        } else {
+            info.destr2(info.destr2_data, info.mem);
+        }
+    }
+}
+
+void cxMempoolFree(CxMempool *pool) {
+    if (pool == NULL) return;
+    if (pool->allocator->cl == &cx_mempool_simple_allocator_class) {
+        cx_mempool_free_all_simple(pool);
+    } else if (pool->allocator->cl == &cx_mempool_advanced_allocator_class) {
+        cx_mempool_free_all_advanced(pool);
+    } else {
+        cx_mempool_free_all_pure(pool);
+    }
+    cx_mempool_free_foreign(pool);
+    cxFree(pool->base_allocator, pool->data);
+    cxFree(pool->base_allocator, pool->registered);
+    cxFree(pool->base_allocator, (void*) pool->allocator);
+    cxFree(pool->base_allocator, pool);
 }
 
 void cxMempoolSetDestructor(
@@ -169,18 +484,26 @@ void cxMempoolSetDestructor(
     *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
 }
 
+void cxMempoolSetDestructor2(
+        void *ptr,
+        cx_destructor_func2 func,
+        void *data
+) {
+    struct cx_mempool_memory2_s *info =
+        (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s));
+    info->destructor = func;
+    info->data = data;
+}
+
 void cxMempoolRemoveDestructor(void *ptr) {
     *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL;
 }
 
-struct cx_mempool_foreign_mem_s {
-    cx_destructor_func destr;
-    void* mem;
-};
-
-static void cx_mempool_destr_foreign_mem(void* ptr) {
-    struct cx_mempool_foreign_mem_s *fm = ptr;
-    fm->destr(fm->mem);
+void cxMempoolRemoveDestructor2(void *ptr) {
+    struct cx_mempool_memory2_s *info =
+        (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s));
+    info->destructor = NULL;
+    info->data = NULL;
 }
 
 int cxMempoolRegister(
@@ -188,98 +511,151 @@ int cxMempoolRegister(
         void *memory,
         cx_destructor_func destr
 ) {
-    struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc(
-            pool,
-            sizeof(struct cx_mempool_foreign_mem_s)
-    );
-    if (fm == NULL) return 1;
+    if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) {
+        return 1; // LCOV_EXCL_LINE
+    }
 
-    fm->mem = memory;
-    fm->destr = destr;
-    *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem;
+    pool->registered[pool->registered_size++] =
+        (struct cx_mempool_foreign_memory_s) {
+            .mem = memory,
+            .destr = destr,
+            .destr2_data = NULL
+        };
 
     return 0;
 }
 
-static cx_allocator_class cx_mempool_allocator_class = {
-        cx_mempool_malloc,
-        cx_mempool_realloc,
-        cx_mempool_calloc,
-        cx_mempool_free
-};
+int cxMempoolRegister2(
+        CxMempool *pool,
+        void *memory,
+        cx_destructor_func2 destr,
+        void *data
+) {
+    if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) {
+        return 1; // LCOV_EXCL_LINE
+    }
+
+    pool->registered[pool->registered_size++] =
+        (struct cx_mempool_foreign_memory_s) {
+            .mem = memory,
+            .destr2 = destr,
+            .destr2_data = data
+        };
+
+    return 0;
+}
 
 CxMempool *cxMempoolCreate(
         size_t capacity,
-        cx_destructor_func destr
+        enum cx_mempool_type type
 ) {
+    if (capacity == 0) capacity = 16;
     size_t poolsize;
-    if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) {
+    if (cx_szmul(capacity, sizeof(void*), &poolsize)) {
+        // LCOV_EXCL_START
         errno = EOVERFLOW;
         return NULL;
-    }
-
-    struct cx_mempool_s *pool =
-            malloc(sizeof(struct cx_mempool_s));
-    if (pool == NULL) return NULL;
+    }  // LCOV_EXCL_STOP
 
-    CxAllocator *provided_allocator = malloc(sizeof(CxAllocator));
+    CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator));
     if (provided_allocator == NULL) { // LCOV_EXCL_START
-        free(pool);
         return NULL;
     } // LCOV_EXCL_STOP
-    provided_allocator->cl = &cx_mempool_allocator_class;
-    provided_allocator->data = pool;
 
+    CxMempool *pool = cxCallocDefault(1, sizeof(CxMempool));
+    if (pool == NULL) {  // LCOV_EXCL_START
+        cxFreeDefault(provided_allocator);
+        return NULL;
+    }  // LCOV_EXCL_STOP
+
+    provided_allocator->data = pool;
+    *((const CxAllocator**)&pool->base_allocator) = cxDefaultAllocator;
     pool->allocator = provided_allocator;
+    if (type == CX_MEMPOOL_TYPE_SIMPLE) {
+        provided_allocator->cl = &cx_mempool_simple_allocator_class;
+    } else if (type == CX_MEMPOOL_TYPE_ADVANCED) {
+        provided_allocator->cl = &cx_mempool_advanced_allocator_class;
+    } else {
+        provided_allocator->cl = &cx_mempool_pure_allocator_class;
+    }
 
-    pool->data = malloc(poolsize);
+    pool->data = cxMallocDefault(poolsize);
     if (pool->data == NULL) { // LCOV_EXCL_START
-        free(provided_allocator);
-        free(pool);
+        cxFreeDefault(provided_allocator);
+        cxFreeDefault(pool);
         return NULL;
     } // LCOV_EXCL_STOP
 
     pool->size = 0;
     pool->capacity = capacity;
-    pool->auto_destr = destr;
 
     return pool;
 }
 
+void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc) {
+    pool->destr = fnc;
+}
+
+void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data) {
+    pool->destr2 = fnc;
+    pool->destr2_data = data;
+}
+
+static void cx_mempool_free_transferred_allocator(void *base_al, void *al) {
+    cxFree(base_al, al);
+}
+
 int cxMempoolTransfer(
         CxMempool *source,
         CxMempool *dest
 ) {
-    // safety check
+    // safety checks
     if (source == dest) return 1;
+    if (source->allocator->cl != dest->allocator->cl) return 1;
+    if (source->base_allocator->cl != dest->base_allocator->cl) return 1;
 
     // ensure enough capacity in the destination pool
-    if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) {
+    if (cx_mempool_ensure_capacity(dest, dest->size + source->size)) {
+        return 1; // LCOV_EXCL_LINE
+    }
+    if (cx_mempool_ensure_registered_capacity(dest,
+            dest->registered_size + source->registered_size)) {
         return 1; // LCOV_EXCL_LINE
     }
 
     // allocate a replacement allocator for the source pool
-    CxAllocator *new_source_allocator = malloc(sizeof(CxAllocator));
+    CxAllocator *new_source_allocator =
+        cxMalloc(source->base_allocator, sizeof(CxAllocator));
     if (new_source_allocator == NULL) { // LCOV_EXCL_START
         return 1;
     } // LCOV_EXCL_STOP
-    new_source_allocator->cl = &cx_mempool_allocator_class;
+    new_source_allocator->cl = source->allocator->cl;
     new_source_allocator->data = source;
 
     // transfer all the data
-    memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size);
+    memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size);
     dest->size += source->size;
 
+    // transfer all registered memory
+    memcpy(&dest->registered[dest->registered_size], source->registered,
+           sizeof(struct cx_mempool_foreign_memory_s) * source->size);
+    dest->registered_size += source->registered_size;
+
     // register the old allocator with the new pool
     // we have to remove const-ness for this, but that's okay here
+    // also register the base allocator, s.t. the pool knows how to free it
     CxAllocator *transferred_allocator = (CxAllocator*) source->allocator;
     transferred_allocator->data = dest;
-    cxMempoolRegister(dest, transferred_allocator, free);
+    cxMempoolRegister2(dest, transferred_allocator,
+        cx_mempool_free_transferred_allocator, (void*)source->base_allocator);
 
     // prepare the source pool for re-use
     source->allocator = new_source_allocator;
-    memset(source->data, 0, source->size * sizeof(source->data[0]));
+    memset(source->data, 0, source->size * sizeof(void*));
+    memset(source->registered, 0,
+        source->registered_size * sizeof(struct cx_mempool_foreign_memory_s));
     source->size = 0;
+    source->registered_size = 0;
 
     return 0;
 }
@@ -289,17 +665,19 @@ int cxMempoolTransferObject(
         CxMempool *dest,
         const void *obj
 ) {
-    // safety check
+    // safety checks
     if (source == dest) return 1;
+    if (source->allocator->cl != dest->allocator->cl) return 1;
+    if (source->base_allocator->cl != dest->base_allocator->cl) 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) {
+            // 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
+            }
             // remove from the source pool
             size_t last_index = source->size - 1;
             if (i != last_index) {
@@ -312,6 +690,27 @@ int cxMempoolTransferObject(
             return 0;
         }
     }
+    // search in the registered objects
+    for (size_t i = 0; i < source->registered_size; i++) {
+        struct cx_mempool_foreign_memory_s *mem = &source->registered[i];
+        if (mem->mem == obj) {
+            // first, make sure that the dest pool can take the object
+            if (cx_mempool_ensure_registered_capacity(dest,
+                    dest->registered_size + 1)) {
+                return 1; // LCOV_EXCL_LINE
+            }
+            dest->registered[dest->registered_size++] = *mem;
+            // remove from the source pool
+            size_t last_index = source->registered_size - 1;
+            if (i != last_index) {
+                source->registered[i] = source->registered[last_index];
+                memset(&source->registered[last_index], 0,
+                    sizeof(struct cx_mempool_foreign_memory_s));
+            }
+            source->registered_size--;
+            return 0;
+        }
+    }
     // not found
     return 1;
 }
index a699a0fcd111443f532b2b8599141d3e79ed49cc..9d18673aa7748f2a9660b31d6a00dfb34aac526e 100644 (file)
@@ -68,7 +68,7 @@ int cx_vfprintf(
         return (int) wfc(buf, 1, ret, stream);
     } else {
         int len = ret + 1;
-        char *newbuf = malloc(len);
+        char *newbuf = cxMallocDefault(len);
         if (!newbuf) { // LCOV_EXCL_START
             va_end(ap2);
             return -1;
@@ -79,7 +79,7 @@ int cx_vfprintf(
         if (ret > 0) {
             ret = (int) wfc(newbuf, 1, ret, stream);
         }
-        free(newbuf);
+        cxFreeDefault(newbuf);
     }
     return ret;
 }
@@ -121,7 +121,7 @@ cxmutstr cx_vasprintf_a(
         if (s.ptr) {
             ret = vsnprintf(s.ptr, len, fmt, ap2);
             if (ret < 0) {
-                free(s.ptr);
+                cxFree(a, s.ptr);
                 s.ptr = NULL;
             } else {
                 s.length = (size_t) ret;
index 23f79b318a65c92d3ada6caa4edc5854bd9670a0..4593602c84a89162cdfedac781f412636550a192 100644 (file)
@@ -287,7 +287,7 @@ static int cx_properties_read_init_file(
         cx_attr_unused CxProperties *prop,
         CxPropertiesSource *src
 ) {
-    src->data_ptr = malloc(src->data_size);
+    src->data_ptr = cxMallocDefault(src->data_size);
     if (src->data_ptr == NULL) return 1;
     return 0;
 }
@@ -296,7 +296,7 @@ static void cx_properties_read_clean_file(
         cx_attr_unused CxProperties *prop,
         CxPropertiesSource *src
 ) {
-    free(src->data_ptr);
+    cxFreeDefault(src->data_ptr);
 }
 
 CxPropertiesSource cxPropertiesStringSource(cxstring str) {
index 2efc035dd336be4c87d34603afa3daabd1fd6402..bee37da64b61c4c5960fd17a60d55dab67663765 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include "cx/streams.h"
+#include "cx/allocator.h"
 
 #ifndef CX_STREAM_BCOPY_BUF_SIZE
 #define CX_STREAM_BCOPY_BUF_SIZE 8192
@@ -57,7 +58,7 @@ size_t cx_stream_bncopy(
         lbuf = buf;
     } else {
         if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE;
-        lbuf = malloc(bufsize);
+        lbuf = cxMallocDefault(bufsize);
         if (lbuf == NULL) return 0;
     }
 
@@ -74,7 +75,7 @@ size_t cx_stream_bncopy(
     }
 
     if (lbuf != buf) {
-        free(lbuf);
+        cxFreeDefault(lbuf);
     }
 
     return ncp;
index ab3d46613bb44963c218cdafc851de7cb8754f79..61d88f35acd65dd9afe38320a519ca1a63c9ec03 100644 (file)
@@ -65,7 +65,7 @@ cxstring cx_strn(
 
 void cx_strfree(cxmutstr *str) {
     if (str == NULL) return;
-    free(str->ptr);
+    cxFreeDefault(str->ptr);
     str->ptr = NULL;
     str->length = 0;
 }
@@ -286,8 +286,9 @@ cxstring cx_strstr(
     // check needle length and use appropriate prefix table
     // if the pattern exceeds static prefix table, allocate on the heap
     const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
-    register size_t *ptable = useheap ? calloc(needle.length + 1,
-                                               sizeof(size_t)) : s_prefix_table;
+    register size_t *ptable = useheap
+        ? cxCallocDefault(needle.length + 1, sizeof(size_t))
+        : s_prefix_table;
 
     // keep counter in registers
     register size_t i, j;
@@ -325,7 +326,7 @@ cxstring cx_strstr(
 
     // if prefix table was allocated on the heap, free it
     if (useheap) {
-        free(ptable);
+        cxFreeDefault(ptable);
     }
 
     return result;
index 73f3b3592496b853ca3b45553c5ce37fd953d0bc..1b2b38ec0174e02addefb4f3d15ccee36831095c 100644 (file)
@@ -305,7 +305,7 @@ static void cx_tree_iter_next(void *it) {
 
     if (children == NULL) {
         // search for the next node
-        void *next;
+        void *next = NULL;
         cx_tree_iter_search_next:
         // check if there is a sibling, but only if we are not a (subtree-)root
         if (iter->exiting) {
@@ -326,7 +326,7 @@ static void cx_tree_iter_next(void *it) {
                     // invalidate the iterator and free the node stack
                     iter->node = iter->node_next = NULL;
                     iter->stack_capacity = iter->depth = 0;
-                    free(iter->stack);
+                    cxFreeDefault(iter->stack);
                     iter->stack = NULL;
                 } else {
                     // the parent node can be obtained from the top of stack
@@ -386,7 +386,7 @@ CxTreeIterator cx_tree_iterator(
     iter.node = root;
     if (root != NULL) {
         iter.stack_capacity = 16;
-        iter.stack = malloc(sizeof(void *) * 16);
+        iter.stack = cxMallocDefault(sizeof(void *) * 16);
         iter.stack[0] = root;
         iter.counter = 1;
         iter.depth = 1;
@@ -416,7 +416,7 @@ static void cx_tree_visitor_enqueue_siblings(
     node = tree_next(node);
     while (node != NULL) {
         struct cx_tree_visitor_queue_s *q;
-        q = malloc(sizeof(struct cx_tree_visitor_queue_s));
+        q = cxMallocDefault(sizeof(struct cx_tree_visitor_queue_s));
         q->depth = iter->queue_last->depth;
         q->node = node;
         iter->queue_last->next = q;
@@ -445,7 +445,7 @@ static void cx_tree_visitor_next(void *it) {
     }
     if (children != NULL) {
         struct cx_tree_visitor_queue_s *q;
-        q = malloc(sizeof(struct cx_tree_visitor_queue_s));
+        q = cxMallocDefault(sizeof(struct cx_tree_visitor_queue_s));
         q->depth = iter->depth + 1;
         q->node = children;
         if (iter->queue_last == NULL) {
@@ -474,7 +474,7 @@ static void cx_tree_visitor_next(void *it) {
             assert(iter->queue_last == q);
             iter->queue_last = NULL;
         }
-        free(q);
+        cxFreeDefault(q);
     }
 
     // increment the node counter
index d36014dc5d14d4e506be2b72664863b637db4567..07177bdbfc47d63228df23d5426d6dd7a4beee7f 100644 (file)
@@ -83,7 +83,7 @@ void* ui_get_subdocument(void *document) {
 }
 
 void* ui_document_new(size_t size) {
-    CxMempool *mp = cxMempoolCreate(256, NULL);
+    CxMempool *mp = cxMempoolCreateSimple(256);
     const CxAllocator *a = mp->allocator;
     UiContext *ctx = uic_context(NULL, mp);