# HG changeset patch
# User Olaf Wintermann
# Date 1757444207 -7200
# Node ID da79af4baec8de924695afb6382b382cfc80c476
# Parent 591377a27fa3c47ab4751092cafac792635c79fb
update ucx
diff -r 591377a27fa3 -r da79af4baec8 dav/assistant.c
--- a/dav/assistant.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/assistant.c Tue Sep 09 20:56:47 2025 +0200
@@ -31,7 +31,6 @@
#include
#include
-#include
#include
diff -r 591377a27fa3 -r da79af4baec8 dav/config.c
--- a/dav/config.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/config.c Tue Sep 09 20:56:47 2025 +0200
@@ -31,7 +31,7 @@
#include
#include
#include
-#include
+#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 dav/connect.c
--- a/dav/connect.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/connect.c Tue Sep 09 20:56:47 2025 +0200
@@ -37,7 +37,6 @@
#include
#include
-#include
DavSession* connect_to_repo(DavContext *ctx, DavCfgRepository *repo, const char *path, dav_auth_func authfunc, CmdArgs *a) {
cxmutstr decodedpw = dav_repository_get_decodedpassword(repo);
diff -r 591377a27fa3 -r da79af4baec8 dav/db.c
--- a/dav/db.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/db.c Tue Sep 09 20:56:47 2025 +0200
@@ -33,7 +33,6 @@
#include "db.h"
-#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 dav/main.c
--- a/dav/main.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/main.c Tue Sep 09 20:56:47 2025 +0200
@@ -39,10 +39,10 @@
#include
#endif
#include
-#include
#include
#include
#include
+#include
#include
diff -r 591377a27fa3 -r da79af4baec8 dav/scfg.c
--- a/dav/scfg.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/scfg.c Tue Sep 09 20:56:47 2025 +0200
@@ -32,7 +32,6 @@
#include
#include
#include
-#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 dav/sync.c
--- a/dav/sync.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/sync.c Tue Sep 09 20:56:47 2025 +0200
@@ -36,10 +36,10 @@
#include
#include
#include
-#include
#include
#include
#include
+#include
#ifndef _WIN32
// unix includes
diff -r 591377a27fa3 -r da79af4baec8 dav/tags.c
--- a/dav/tags.c Tue Sep 09 16:01:30 2025 +0200
+++ b/dav/tags.c Tue Sep 09 20:56:47 2025 +0200
@@ -32,7 +32,6 @@
#include
#include
-#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/davqlexec.c
--- a/libidav/davqlexec.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/davqlexec.c Tue Sep 09 20:56:47 2025 +0200
@@ -31,7 +31,6 @@
#include
#include
-#include
#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/davqlparser.c
--- a/libidav/davqlparser.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/davqlparser.c Tue Sep 09 20:56:47 2025 +0200
@@ -27,7 +27,6 @@
*/
#include "davqlparser.h"
-#include
#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/methods.c
--- a/libidav/methods.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/methods.c Tue Sep 09 20:56:47 2025 +0200
@@ -36,7 +36,6 @@
#include "session.h"
#include "xml.h"
-#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/pwdstore.c
--- a/libidav/pwdstore.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/pwdstore.c Tue Sep 09 20:56:47 2025 +0200
@@ -34,8 +34,8 @@
#include
#include
-#include
#include
+#include
#ifdef _WIN32
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/resource.c
--- a/libidav/resource.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/resource.c Tue Sep 09 20:56:47 2025 +0200
@@ -37,7 +37,6 @@
#include "methods.h"
#include "crypto.h"
#include
-#include
#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/utils.c
--- a/libidav/utils.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/utils.c Tue Sep 09 20:56:47 2025 +0200
@@ -34,7 +34,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/webdav.c
--- a/libidav/webdav.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/webdav.c Tue Sep 09 20:56:47 2025 +0200
@@ -36,7 +36,7 @@
#include "session.h"
#include "methods.h"
#include
-#include
+#include
#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 libidav/xml.c
--- a/libidav/xml.c Tue Sep 09 16:01:30 2025 +0200
+++ b/libidav/xml.c Tue Sep 09 20:56:47 2025 +0200
@@ -30,7 +30,6 @@
#include
#include
-#include
#include
#include "xml.h"
diff -r 591377a27fa3 -r da79af4baec8 test/crypto.c
--- a/test/crypto.c Tue Sep 09 16:01:30 2025 +0200
+++ b/test/crypto.c Tue Sep 09 20:56:47 2025 +0200
@@ -34,7 +34,6 @@
#include "crypto.h"
#include
-#include
#include
#include
#include
diff -r 591377a27fa3 -r da79af4baec8 ucx/allocator.c
--- a/ucx/allocator.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/allocator.c Tue Sep 09 20:56:47 2025 +0200
@@ -29,6 +29,7 @@
#include "cx/allocator.h"
#include
+#include
static void *cx_malloc_stdlib(
cx_attr_unused void *d,
@@ -60,18 +61,19 @@
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 @@
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,
diff -r 591377a27fa3 -r da79af4baec8 ucx/array_list.c
--- a/ucx/array_list.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/array_list.c Tue Sep 09 20:56:47 2025 +0200
@@ -36,16 +36,17 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// free dynamic memory, if it was needed
if (tmp != sbo_mem) {
- free(tmp);
+ cxFreeDefault(tmp);
}
}
@@ -638,50 +641,38 @@
// 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 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 @@
) {
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;
}
}
diff -r 591377a27fa3 -r da79af4baec8 ucx/buffer.c
--- a/ucx/buffer.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/buffer.c Tue Sep 09 20:56:47 2025 +0200
@@ -32,6 +32,24 @@
#include
#include
+#ifdef _WIN32
+#include
+#include
+static unsigned long system_page_size() {
+ static unsigned long ps = 0;
+ if (ps == 0) {
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ ps = sysinfo.dwPageSize;
+ }
+ return ps;
+}
+#define SYSTEM_PAGE_SIZE system_page_size()
+#else
+#include
+#define SYSTEM_PAGE_SIZE sysconf(_SC_PAGESIZE)
+#endif
+
static int buffer_copy_on_write(CxBuffer* buffer) {
if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
@@ -80,7 +98,7 @@
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;
@@ -90,7 +108,7 @@
if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
cxFree(buffer->allocator, buffer->bytes);
}
- free(buffer->flush);
+ cxFreeDefault(buffer->flush);
memset(buffer, 0, sizeof(CxBuffer));
}
@@ -139,6 +157,7 @@
npos = 0;
break;
default:
+ errno = EINVAL;
return -1;
}
@@ -146,11 +165,16 @@
npos += offset;
if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
- errno = EOVERFLOW;
+ // to be compliant with fseek() specification
+ // we return EINVAL on underflow
+ errno = EINVAL;
return -1;
}
if (npos > buffer->size) {
+ // not compliant with fseek() specification
+ // but this is the better behavior for CxBuffer
+ errno = EINVAL;
return -1;
} else {
buffer->pos = npos;
@@ -184,6 +208,28 @@
return 0;
}
+ unsigned long pagesize = SYSTEM_PAGE_SIZE;
+ // if page size is larger than 64 KB - for some reason - truncate to 64 KB
+ if (pagesize > 65536) pagesize = 65536;
+ if (newcap < pagesize) {
+ // when smaller as one page, map to the next power of two
+ newcap--;
+ newcap |= newcap >> 1;
+ newcap |= newcap >> 2;
+ newcap |= newcap >> 4;
+ // last operation only needed for pages larger 4096 bytes
+ // but if/else would be more expensive than just doing this
+ newcap |= newcap >> 8;
+ newcap++;
+ } else {
+ // otherwise, map to a multiple of the page size
+ newcap -= newcap % pagesize;
+ newcap += pagesize;
+ // note: if newcap is already page aligned,
+ // this gives a full additional page (which is good)
+ }
+
+
const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
if (buffer->flags & force_copy_flags) {
void *newspace = cxMalloc(buffer->allocator, newcap);
@@ -203,6 +249,28 @@
}
}
+void cxBufferShrink(
+ CxBuffer *buffer,
+ size_t reserve
+) {
+ // Ensure buffer is in a reallocatable state
+ const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
+ if (buffer->flags & force_copy_flags) {
+ // do nothing when we are not allowed to reallocate
+ return;
+ }
+
+ // calculate new capacity
+ size_t newCapacity = buffer->size + reserve;
+
+ // If new capacity is smaller than current capacity, resize the buffer
+ if (newCapacity < buffer->capacity) {
+ if (0 == cxReallocate(buffer->allocator, &buffer->bytes, newCapacity)) {
+ buffer->capacity = newCapacity;
+ }
+ }
+}
+
static size_t cx_buffer_flush_helper(
const CxBuffer *buffer,
const unsigned char *src,
@@ -399,10 +467,8 @@
}
int cxBufferTerminate(CxBuffer *buffer) {
- bool success = 0 == cxBufferPut(buffer, 0);
- if (success) {
- buffer->pos--;
- buffer->size--;
+ if (0 == cxBufferPut(buffer, 0)) {
+ buffer->size = buffer->pos - 1;
return 0;
} else {
return -1;
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/allocator.h
--- a/ucx/cx/allocator.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/allocator.h Tue Sep 09 20:56:47 2025 +0200
@@ -98,10 +98,17 @@
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 @@
* 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 @@
*
* 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 @@
* 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 @@
*
* 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.
@@ -213,6 +228,14 @@
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.
*
* @note Freeing a block of a different allocator is undefined.
@@ -414,6 +437,57 @@
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
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/array_list.h
--- a/ucx/cx/array_list.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/array_list.h Tue Sep 09 20:56:47 2025 +0200
@@ -123,7 +123,8 @@
* @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 @@
#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 @@
* 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 @@
* 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 @@
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 @@
/**
* 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 @@
* 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)
@@ -727,7 +729,7 @@
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * to cx_cmp_ptr().
*
* @param elem_size (@c size_t) the size of each element in bytes
* @param initial_capacity (@c size_t) the initial number of elements the array can store
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/buffer.h
--- a/ucx/cx/buffer.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/buffer.h Tue Sep 09 20:56:47 2025 +0200
@@ -222,7 +222,7 @@
* @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 @@
* @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
*/
@@ -474,10 +474,14 @@
*
* If the current capacity is not sufficient, the buffer will be extended.
*
+ * The new capacity will be a power of two until the system's page size is reached.
+ * Then, the new capacity will be a multiple of the page size.
+ *
* @param buffer the buffer
* @param capacity the minimum required capacity for this buffer
* @retval zero the capacity was already sufficient or successfully increased
* @retval non-zero on allocation failure
+ * @see cxBufferShrink()
*/
cx_attr_nonnull
cx_attr_export
@@ -487,6 +491,29 @@
);
/**
+ * Shrinks the capacity of the buffer to fit its current size.
+ *
+ * If @p reserve is larger than zero, the buffer is shrunk to its size plus
+ * the number of reserved bytes.
+ *
+ * If the current capacity is not larger than the size plus the reserved bytes,
+ * nothing happens.
+ *
+ * If the #CX_BUFFER_COPY_ON_WRITE or #CX_BUFFER_COPY_ON_EXTEND flag is set,
+ * this function does nothing.
+ *
+ * @param buffer the buffer
+ * @param reserve the number of bytes that shall remain reserved
+ * @see cxBufferMinimumCapacity()
+ */
+cx_attr_nonnull
+cx_attr_export
+void cxBufferShrink(
+ CxBuffer *buffer,
+ size_t reserve
+);
+
+/**
* Writes data to a CxBuffer.
*
* If automatic flushing is not enabled, the data is simply written into the
@@ -674,11 +701,10 @@
/**
* Writes a terminating zero to a buffer at the current position.
*
- * On successful write, @em neither the position @em nor the size of the buffer is
- * increased.
+ * If successful, sets the size to the current position and advances the position by one.
*
* The purpose of this function is to have the written data ready to be used as
- * a C string.
+ * a C string with the buffer's size being the length of that string.
*
* @param buffer the buffer to write to
* @return zero, if the terminator could be written, non-zero otherwise
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/common.h
--- a/ucx/cx/common.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/common.h Tue Sep 09 20:56:47 2025 +0200
@@ -46,7 +46,7 @@
* Repositories:
* https://sourceforge.net/p/ucx/code
* - or -
- * https://develop.uap-core.de/hg/ucx
+ * https://uap-core.de/hg/ucx
*
*
* LICENCE
@@ -131,6 +131,11 @@
#endif
/**
+ * Inform the compiler that falling through a switch case is intentional.
+ */
+#define cx_attr_fallthrough __attribute__((__fallthrough__))
+
+/**
* All pointer arguments must be non-NULL.
*/
#define cx_attr_nonnull __attribute__((__nonnull__))
@@ -150,7 +155,7 @@
*/
#define cx_attr_malloc __attribute__((__malloc__))
-#ifndef __clang__
+#if !defined(__clang__) && __GNUC__ >= 11
/**
* The pointer returned by the attributed function is supposed to be freed
* by @p freefunc.
@@ -240,20 +245,6 @@
*/
#define cx_attr_access_w(...) cx_attr_access(__write_only__, __VA_ARGS__)
-#if __STDC_VERSION__ >= 202300L
-
-/**
- * Do not warn about unused variable.
- */
-#define cx_attr_unused [[maybe_unused]]
-
-/**
- * Warn about discarded return value.
- */
-#define cx_attr_nodiscard [[nodiscard]]
-
-#else // no C23
-
/**
* Do not warn about unused variable.
*/
@@ -264,8 +255,6 @@
*/
#define cx_attr_nodiscard __attribute__((__warn_unused_result__))
-#endif // __STDC_VERSION__
-
// ---------------------------------------------------------------------------
// MSVC specifics
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/hash_map.h
--- a/ucx/cx/hash_map.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/hash_map.h Tue Sep 09 20:56:47 2025 +0200
@@ -77,7 +77,7 @@
* 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
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/json.h
--- a/ucx/cx/json.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/json.h Tue Sep 09 20:56:47 2025 +0200
@@ -1284,6 +1284,23 @@
CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
/**
+ * Removes an element from a JSON array.
+ *
+ * If the @p value is not a JSON array, the behavior is undefined.
+ *
+ * This function, in contrast to cxJsonArrayGet(), returns @c NULL
+ * when the index is out of bounds.
+ *
+ * @param value the JSON value
+ * @param index the index in the array
+ * @return the removed value from the specified index or @c NULL when the index was out of bounds
+ * @see cxJsonIsArray()
+ */
+cx_attr_nonnull
+cx_attr_export
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
+
+/**
* Returns an iterator over the JSON array elements.
*
* The iterator yields values of type @c CxJsonValue* .
@@ -1324,6 +1341,13 @@
cx_attr_export
CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name);
+/**
+ * @copydoc cxJsonObjRemove()
+ */
+cx_attr_nonnull
+cx_attr_export
+CxJsonValue *cx_json_obj_remove_cxstr(CxJsonValue *value, cxstring name);
+
#ifdef __cplusplus
} // extern "C"
@@ -1339,6 +1363,18 @@
return cx_json_obj_get_cxstr(value, cx_str(name));
}
+static inline CxJsonValue *cxJsonObjRemove(CxJsonValue *value, cxstring name) {
+ return cx_json_obj_remove_cxstr(value, name);
+}
+
+static inline CxJsonValue *cxJsonObjRemove(CxJsonValue *value, cxmutstr name) {
+ return cx_json_obj_remove_cxstr(value, cx_strcast(name));
+}
+
+static inline CxJsonValue *cxJsonObjRemove(CxJsonValue *value, const char *name) {
+ return cx_json_obj_remove_cxstr(value, cx_str(name));
+}
+
extern "C" {
#else
/**
@@ -1380,6 +1416,43 @@
static inline CxJsonValue *cx_json_obj_get_str(const CxJsonValue *value, const char *name) {
return cx_json_obj_get_cxstr(value, cx_str(name));
}
+
+/**
+ * Removes and returns a value corresponding to a key in a JSON object.
+ *
+ * If the @p value is not a JSON object, the behavior is undefined.
+ *
+ * This function, in contrast to cxJsonObjGet() returns @c NULL when the
+ * object does not contain @p name.
+ *
+ * @param value the JSON object
+ * @param name the key to look up
+ * @return the value corresponding to the key or @c NULL when the key is not part of the object
+ * @see cxJsonIsObject()
+ */
+#define cxJsonObjRemove(value, name) _Generic((name), \
+ cxstring: cx_json_obj_remove_cxstr, \
+ cxmutstr: cx_json_obj_remove_mutstr, \
+ char*: cx_json_obj_remove_str, \
+ const char*: cx_json_obj_remove_str) \
+ (value, name)
+
+/**
+ * @copydoc cxJsonObjRemove()
+ */
+cx_attr_nonnull
+static inline CxJsonValue *cx_json_obj_remove_mutstr(CxJsonValue *value, cxmutstr name) {
+ return cx_json_obj_remove_cxstr(value, cx_strcast(name));
+}
+
+/**
+ * @copydoc cxJsonObjRemove()
+ */
+cx_attr_nonnull
+cx_attr_cstr_arg(2)
+static inline CxJsonValue *cx_json_obj_remove_str(CxJsonValue *value, const char *name) {
+ return cx_json_obj_remove_cxstr(value, cx_str(name));
+}
#endif
#ifdef __cplusplus
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/linked_list.h
--- a/ucx/cx/linked_list.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/linked_list.h Tue Sep 09 20:56:47 2025 +0200
@@ -51,7 +51,7 @@
* 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)
@@ -77,7 +77,7 @@
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * to cx_cmp_ptr().
*
* @param elem_size (@c size_t) the size of each element in bytes
* @return (@c CxList*) the created list
@@ -393,7 +393,7 @@
* @li @p loc_next and @p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance)
* @li @p loc_next and @p begin (ancestor node is determined by list traversal, overall O(n) performance)
*
- * @remark The @c next and @c prev pointers of the removed node are not cleared by this function and may still be used
+ * @remark The @c next and @c prev pointers of the removed chain are not cleared by this function and may still be used
* to traverse to a former adjacent node in the list, or within the chain.
*
* @param begin a pointer to the beginning node pointer (optional)
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/list.h
--- a/ucx/cx/list.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/list.h Tue Sep 09 20:56:47 2025 +0200
@@ -80,8 +80,10 @@
/**
* 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 allocated memory or @c NULL if allocation fails.
*/
- int (*insert_element)(
+ void *(*insert_element)(
struct cx_list_s *list,
size_t index,
const void *data
@@ -181,7 +183,6 @@
* to another list of the same type.
* If set to @c NULL, comparison won't be optimized.
*/
- cx_attr_nonnull
int (*compare)(
const struct cx_list_s *list,
const struct cx_list_s *other
@@ -203,6 +204,22 @@
};
/**
+ * Common type for all list implementations.
+ */
+typedef struct cx_list_s CxList;
+
+/**
+ * A shared instance of an empty list.
+ *
+ * Writing to that list is not allowed.
+ *
+ * You can use this is a placeholder for initializing CxList pointers
+ * for which you do not want to reserve memory right from the beginning.
+ */
+cx_attr_export
+extern CxList *const cxEmptyList;
+
+/**
* Default implementation of an array insert.
*
* This function uses the element insert function for each element of the array.
@@ -336,11 +353,6 @@
);
/**
- * Common type for all list implementations.
- */
-typedef struct cx_list_s CxList;
-
-/**
* Returns the number of elements currently stored in the list.
*
* @param list the list
@@ -359,6 +371,7 @@
* @retval zero success
* @retval non-zero memory allocation failure
* @see cxListAddArray()
+ * @see cxListEmplace()
*/
cx_attr_nonnull
static inline int cxListAdd(
@@ -366,7 +379,7 @@
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;
}
/**
@@ -407,6 +420,7 @@
* @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(
@@ -415,7 +429,41 @@
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);
}
/**
@@ -571,8 +619,9 @@
/**
* 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
@@ -591,11 +640,93 @@
}
/**
+ * 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.
@@ -615,10 +746,11 @@
}
/**
- * 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
@@ -654,15 +786,15 @@
/**
* 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(
@@ -677,6 +809,8 @@
/**
* 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
@@ -690,22 +824,65 @@
}
/**
+ * 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
+ *
+ * @param list the list to set the element in
+ * @param index the index to set the element at
+ * @param elem element to set
+ * @retval zero on success
+ * @retval non-zero when index is out of bounds
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxListSet(
+ CxList *list,
+ size_t index,
+ const void *elem
+);
+
+/**
* Returns an iterator pointing to the item at the specified index.
*
* The returned iterator is position-aware.
*
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxIterator cxListIteratorAt(
const CxList *list,
size_t index
) {
+ if (list == NULL) list = cxEmptyList;
return list->cl->iterator(list, index, false);
}
@@ -714,18 +891,18 @@
*
* The returned iterator is position-aware.
*
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxIterator cxListBackwardsIteratorAt(
const CxList *list,
size_t index
) {
+ if (list == NULL) list = cxEmptyList;
return list->cl->iterator(list, index, true);
}
@@ -734,13 +911,12 @@
*
* The returned iterator is position-aware.
*
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
cx_attr_export
CxIterator cxListMutIteratorAt(
@@ -754,13 +930,12 @@
*
* The returned iterator is position-aware.
*
- * If the index is out of range, a past-the-end iterator will be returned.
+ * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
cx_attr_export
CxIterator cxListMutBackwardsIteratorAt(
@@ -773,14 +948,14 @@
*
* The returned iterator is position-aware.
*
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxIterator cxListIterator(const CxList *list) {
+ if (list == NULL) list = cxEmptyList;
return list->cl->iterator(list, 0, false);
}
@@ -789,14 +964,14 @@
*
* The returned iterator is position-aware.
*
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxIterator cxListMutIterator(CxList *list) {
+ if (list == NULL) list = cxEmptyList;
return cxListMutIteratorAt(list, 0);
}
@@ -806,14 +981,14 @@
*
* The returned iterator is position-aware.
*
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxIterator cxListBackwardsIterator(const CxList *list) {
+ if (list == NULL) list = cxEmptyList;
return list->cl->iterator(list, list->collection.size - 1, true);
}
@@ -822,14 +997,14 @@
*
* The returned iterator is position-aware.
*
- * If the list is empty, a past-the-end iterator will be returned.
+ * If the list is empty or @c NULL, a past-the-end iterator will be returned.
*
* @param list the list
* @return a new iterator
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxIterator cxListMutBackwardsIterator(CxList *list) {
+ if (list == NULL) list = cxEmptyList;
return cxListMutBackwardsIteratorAt(list, list->collection.size - 1);
}
@@ -842,6 +1017,7 @@
* @param elem the element to find
* @return the index of the element or the size of the list when the element is not found
* @see cxListIndexValid()
+ * @see cxListContains()
*/
cx_attr_nonnull
cx_attr_nodiscard
@@ -853,6 +1029,26 @@
}
/**
+ * Checks, if the list contains the specified element.
+ *
+ * The elements are compared with the list's comparator function.
+ *
+ * @param list the list
+ * @param elem the element to find
+ * @retval true if the element is contained
+ * @retval false if the element is not contained
+ * @see cxListFind()
+ */
+cx_attr_nonnull
+cx_attr_nodiscard
+static inline bool cxListContains(
+ const CxList* list,
+ const void* elem
+) {
+ return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size;
+}
+
+/**
* Checks if the specified index is within bounds.
*
* @param list the list
@@ -894,6 +1090,7 @@
*/
cx_attr_nonnull
static inline void cxListSort(CxList *list) {
+ if (list->collection.sorted) return;
list->cl->sort(list);
list->collection.sorted = true;
}
@@ -942,17 +1139,6 @@
cx_attr_export
void cxListFree(CxList *list);
-/**
- * A shared instance of an empty list.
- *
- * Writing to that list is not allowed.
- *
- * You can use this is a placeholder for initializing CxList pointers
- * for which you do not want to reserve memory right from the beginning.
- */
-cx_attr_export
-extern CxList *const cxEmptyList;
-
#ifdef __cplusplus
} // extern "C"
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/map.h
--- a/ucx/cx/map.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/map.h Tue Sep 09 20:56:47 2025 +0200
@@ -185,8 +185,11 @@
/**
* Add or overwrite an element.
+ * If the @p value is @c NULL, the implementation
+ * shall only allocate memory instead of adding an existing value to the map.
+ * Returns a pointer to the allocated memory or @c NULL if allocation fails.
*/
- int (*put)(
+ void *(*put)(
CxMap *map,
CxHashKey key,
void *value
@@ -277,12 +280,12 @@
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored values
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxMapIterator cxMapIteratorValues(const CxMap *map) {
+ if (map == NULL) map = cxEmptyMap;
return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
}
@@ -295,12 +298,12 @@
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored keys
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) {
+ if (map == NULL) map = cxEmptyMap;
return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
}
@@ -313,14 +316,14 @@
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored entries
* @see cxMapIteratorKeys()
* @see cxMapIteratorValues()
*/
-cx_attr_nonnull
cx_attr_nodiscard
static inline CxMapIterator cxMapIterator(const CxMap *map) {
+ if (map == NULL) map = cxEmptyMap;
return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
}
@@ -335,10 +338,9 @@
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored values
*/
-cx_attr_nonnull
cx_attr_nodiscard
cx_attr_export
CxMapIterator cxMapMutIteratorValues(CxMap *map);
@@ -352,10 +354,9 @@
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored keys
*/
-cx_attr_nonnull
cx_attr_nodiscard
cx_attr_export
CxMapIterator cxMapMutIteratorKeys(CxMap *map);
@@ -369,12 +370,11 @@
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
- * @param map the map to create the iterator for
+ * @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored entries
* @see cxMapMutIteratorKeys()
* @see cxMapMutIteratorValues()
*/
-cx_attr_nonnull
cx_attr_nodiscard
cx_attr_export
CxMapIterator cxMapMutIterator(CxMap *map);
@@ -387,7 +387,7 @@
CxHashKey const &key,
void *value
) {
- return map->cl->put(map, key, value);
+ return map->cl->put(map, key, value) == NULL;
}
cx_attr_nonnull
@@ -396,7 +396,7 @@
cxstring const &key,
void *value
) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
+ return map->cl->put(map, cx_hash_key_cxstr(key), value) == NULL;
}
cx_attr_nonnull
@@ -405,7 +405,7 @@
cxmutstr const &key,
void *value
) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
+ return map->cl->put(map, cx_hash_key_cxstr(key), value) == NULL;
}
cx_attr_nonnull
@@ -415,7 +415,40 @@
const char *key,
void *value
) {
- return map->cl->put(map, cx_hash_key_str(key), value);
+ return map->cl->put(map, cx_hash_key_str(key), value) == NULL;
+}
+
+cx_attr_nonnull
+static inline void *cxMapEmplace(
+ CxMap *map,
+ CxHashKey const &key
+) {
+ return map->cl->put(map, key, NULL);
+}
+
+cx_attr_nonnull
+static inline void *cxMapEmplace(
+ CxMap *map,
+ cxstring const &key
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), NULL);
+}
+
+cx_attr_nonnull
+static inline void *cxMapEmplace(
+ CxMap *map,
+ cxmutstr const &key
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), NULL);
+}
+
+cx_attr_nonnull
+cx_attr_cstr_arg(2)
+static inline void *cxMapEmplace(
+ CxMap *map,
+ const char *key
+) {
+ return map->cl->put(map, cx_hash_key_str(key), NULL);
}
cx_attr_nonnull
@@ -540,7 +573,7 @@
CxHashKey key,
void *value
) {
- return map->cl->put(map, key, value);
+ return map->cl->put(map, key, value) == NULL;
}
/**
@@ -552,7 +585,7 @@
cxstring key,
void *value
) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
+ return map->cl->put(map, cx_hash_key_cxstr(key), value) == NULL;
}
/**
@@ -564,7 +597,7 @@
cxmutstr key,
void *value
) {
- return map->cl->put(map, cx_hash_key_cxstr(key), value);
+ return map->cl->put(map, cx_hash_key_cxstr(key), value) == NULL;
}
/**
@@ -577,7 +610,7 @@
const char *key,
void *value
) {
- return map->cl->put(map, cx_hash_key_str(key), value);
+ return map->cl->put(map, cx_hash_key_str(key), value) == NULL;
}
/**
@@ -608,6 +641,77 @@
(map, key, value)
/**
+ * @copydoc cxMapEmplace()
+ */
+cx_attr_nonnull
+static inline void *cx_map_emplace(
+ CxMap *map,
+ CxHashKey key
+) {
+ return map->cl->put(map, key, NULL);
+}
+
+/**
+ * @copydoc cxMapEmplace()
+ */
+cx_attr_nonnull
+static inline void *cx_map_emplace_cxstr(
+ CxMap *map,
+ cxstring key
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), NULL);
+}
+
+/**
+ * @copydoc cxMapEmplace()
+ */
+cx_attr_nonnull
+static inline void *cx_map_emplace_mustr(
+ CxMap *map,
+ cxmutstr key
+) {
+ return map->cl->put(map, cx_hash_key_cxstr(key), NULL);
+}
+
+/**
+ * @copydoc cxMapEmplace()
+ */
+cx_attr_nonnull
+cx_attr_cstr_arg(2)
+static inline void *cx_map_emplace_str(
+ CxMap *map,
+ const char *key
+) {
+ return map->cl->put(map, cx_hash_key_str(key), NULL);
+}
+
+/**
+ * Allocates memory for a value in the map associated with the specified key.
+ *
+ * A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
+ *
+ * If the map is storing pointers, this function returns a @c void** pointer,
+ * meaning a pointer to that pointer.
+ *
+ * The @p key is always copied.
+ *
+ * @param map (@c CxMap*) the map
+ * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
+ * @return the pointer to the allocated memory or @c NULL if allocation fails
+ * @retval zero success
+ * @retval non-zero value on memory allocation failure
+ */
+#define cxMapEmplace(map, key) _Generic((key), \
+ CxHashKey: cx_map_emplace, \
+ cxstring: cx_map_emplace_cxstr, \
+ cxmutstr: cx_map_emplace_mustr, \
+ char*: cx_map_emplace_str, \
+ const char*: cx_map_emplace_str) \
+ (map, key)
+
+/**
* @copydoc cxMapGet()
*/
cx_attr_nonnull
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/mempool.h
--- a/ucx/cx/mempool.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/mempool.h Tue Sep 09 20:56:47 2025 +0200
@@ -43,31 +43,107 @@
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 @@
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,10 +244,29 @@
);
/**
+ * 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.
- * The destructor MUST NOT free the memory.
*
* @param memory the object allocated in the pool
*/
@@ -135,12 +275,25 @@
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,79 @@
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.
+ *
+ * The allocator of the source pool will also be transferred and registered with the destination pool
+ * and stays valid, as long as the destination pool is not destroyed.
+ *
+ * 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 allocation failure or incompatible pools
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolTransfer(
+ CxMempool *source,
+ CxMempool *dest
+);
+
+/**
+ * Transfers an object from one pool to another.
+ *
+ * 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.
+ *
+ * @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 the object was not found in the source pool, or the pools are incompatible
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolTransferObject(
+ CxMempool *source,
+ CxMempool *dest,
+ const void *obj
+);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/printf.h
--- a/ucx/cx/printf.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/printf.h Tue Sep 09 20:56:47 2025 +0200
@@ -229,7 +229,7 @@
cx_attr_cstr_arg(4)
cx_attr_export
int cx_sprintf_a(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char **str,
size_t *len,
const char *fmt,
@@ -274,7 +274,7 @@
cx_attr_access_rw(3)
cx_attr_export
int cx_vsprintf_a(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char **str,
size_t *len,
const char *fmt,
@@ -333,7 +333,7 @@
cx_attr_access_rw(4)
cx_attr_export
int cx_sprintf_sa(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char *buf,
size_t *len,
char **str,
@@ -388,7 +388,7 @@
cx_attr_cstr_arg(5)
cx_attr_export
int cx_vsprintf_sa(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char *buf,
size_t *len,
char **str,
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/properties.h
--- a/ucx/cx/properties.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/properties.h Tue Sep 09 20:56:47 2025 +0200
@@ -551,10 +551,12 @@
/**
* Creates a properties sink for an UCX map.
*
- * The values stored in the map will be pointers to strings allocated
- * by #cx_strdup_a().
- * The default stdlib allocator will be used, unless you specify a custom
- * allocator in the optional @c data of the sink.
+ * The values stored in the map will be pointers to freshly allocated,
+ * zero-terminated C strings (@c char*), which means the @p map should have been
+ * created with #CX_STORE_POINTERS.
+ *
+ * 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.
* @return the sink
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/string.h
--- a/ucx/cx/string.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/string.h Tue Sep 09 20:56:47 2025 +0200
@@ -39,6 +39,12 @@
#include "common.h"
#include "allocator.h"
+/** Expands a UCX string as printf arguments. */
+#define CX_SFMT(s) (int) (s).length, (s).ptr
+
+/** Format specifier for a UCX string */
+#define CX_PRIstr ".*s"
+
/**
* The maximum length of the "needle" in cx_strstr() that can use SBO.
*/
@@ -161,6 +167,8 @@
*
* The length is implicitly inferred by using a call to @c strlen().
*
+ * When @c NULL is passed, the length will be set to zero.
+ *
* @note the wrapped string will share the specified pointer to the string.
* If you do want a copy, use cx_strdup() on the return value of this function.
*
@@ -171,7 +179,6 @@
*
* @see cx_mutstrn()
*/
-cx_attr_nonnull
cx_attr_nodiscard
cx_attr_cstr_arg(1)
cx_attr_export
@@ -206,6 +213,8 @@
*
* The length is implicitly inferred by using a call to @c strlen().
*
+ * When @c NULL is passed, the length will be set to zero.
+ *
* @note the wrapped string will share the specified pointer to the string.
* If you do want a copy, use cx_strdup() on the return value of this function.
*
@@ -216,7 +225,6 @@
*
* @see cx_strn()
*/
-cx_attr_nonnull
cx_attr_nodiscard
cx_attr_cstr_arg(1)
cx_attr_export
@@ -257,6 +265,10 @@
static inline cxstring cx_strcast(cxstring str) {
return str;
}
+cx_attr_nodiscard
+static inline cxstring cx_strcast(const char *str) {
+ return cx_str(str);
+}
extern "C" {
#else
/**
@@ -281,6 +293,17 @@
}
/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast()
+ */
+cx_attr_nodiscard
+static inline cxstring cx_strcast_z(const char *str) {
+ return cx_str(str);
+}
+
+/**
* Casts a mutable string to an immutable string.
*
* Does nothing for already immutable strings.
@@ -294,12 +317,13 @@
*/
#define cx_strcast(str) _Generic((str), \
cxmutstr: cx_strcast_m, \
- cxstring: cx_strcast_c) \
- (str)
+ cxstring: cx_strcast_c, \
+ const char*: cx_strcast_z, \
+ char *: cx_strcast_z) (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.
@@ -334,6 +358,46 @@
);
/**
+ * Copies a string.
+ *
+ * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
+ * source string, including a zero-terminator.
+ *
+ * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
+ *
+ * @param alloc the allocator
+ * @param dest a pointer to the structure where to copy the contents to
+ * @param src the source string
+ *
+ * @retval zero success
+ * @retval non-zero if re-allocation failed
+ */
+cx_attr_nonnull_arg(1)
+cx_attr_export
+int cx_strcpy_a(
+ const CxAllocator *alloc,
+ cxmutstr *dest,
+ cxstring src
+);
+
+
+/**
+ * Copies a string.
+ *
+ * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
+ * source string, including a zero-terminator.
+ *
+ * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
+ *
+ * @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to
+ * @param src (@c cxstring) the source string
+ *
+ * @retval zero success
+ * @retval non-zero if re-allocation failed
+ */
+#define cx_strcpy(dest, src) cx_strcpy_a(cxDefaultAllocator, dest, src)
+
+/**
* Returns the accumulated length of all specified strings.
*
* If this sum overflows, errno is set to EOVERFLOW.
@@ -408,7 +472,7 @@
/**
* 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
@@ -428,7 +492,7 @@
/**
* 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
@@ -884,8 +948,8 @@
/**
* 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.
*
@@ -1016,7 +1080,7 @@
*
* 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,
@@ -1052,7 +1116,7 @@
/**
* 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,
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/tree.h
--- a/ucx/cx/tree.h Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/cx/tree.h Tue Sep 09 20:56:47 2025 +0200
@@ -120,6 +120,7 @@
size_t stack_size;
/**
* The current depth in the tree.
+ * The node with which the iteration starts has depth 1.
*/
size_t depth;
};
@@ -135,6 +136,7 @@
void *node;
/**
* The depth of the node.
+ * The first visited node has depth 1.
*/
size_t depth;
/**
@@ -211,7 +213,7 @@
*/
cx_attr_nonnull
static inline void cxTreeIteratorDispose(CxTreeIterator *iter) {
- free(iter->stack);
+ cxFreeDefault(iter->stack);
iter->stack = NULL;
}
@@ -224,7 +226,7 @@
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;
}
}
@@ -441,7 +443,7 @@
* 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
@@ -469,8 +471,8 @@
/**
* 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
@@ -956,7 +958,7 @@
* 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
@@ -1020,7 +1022,7 @@
* 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
@@ -1188,6 +1190,18 @@
size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
/**
+ * Determines the size of the entire tree.
+ *
+ * @param tree the tree
+ * @return the tree size, counting the root as one
+ */
+cx_attr_nonnull
+cx_attr_nodiscard
+static inline size_t cxTreeSize(CxTree *tree) {
+ return tree->size;
+}
+
+/**
* Determines the depth of the entire tree.
*
* @param tree the tree
diff -r 591377a27fa3 -r da79af4baec8 ucx/cx/utils.h
--- a/ucx/cx/utils.h Tue Sep 09 16:01:30 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * \file utils.h
- *
- * \brief General purpose utility functions.
- *
- * \author Mike Becker
- * \author Olaf Wintermann
- * \copyright 2-Clause BSD License
- */
-
-#ifndef UCX_UTILS_H
-#define UCX_UTILS_H
-
-#include "common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Convenience macro for a for loop that counts from zero to n-1.
- */
-#define cx_for_n(varname, n) for (size_t varname = 0 ; (varname) < (n) ; (varname)++)
-
-/**
- * Convenience macro for swapping two pointers.
- */
-#ifdef __cplusplus
-#define cx_swap_ptr(left, right) do {auto cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0)
-#else
-#define cx_swap_ptr(left, right) do {void *cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0)
-#endif
-
-// cx_szmul() definition
-
-#if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN)
-#define CX_SZMUL_BUILTIN
-
-/**
- * Alias for \c __builtin_mul_overflow.
- *
- * Performs a multiplication of size_t values and checks for overflow.
- *
- * @param a first operand
- * @param b second operand
- * @param result a pointer to a size_t, where the result should
- * be stored
- * @return zero, if no overflow occurred and the result is correct, non-zero
- * otherwise
- */
-#define cx_szmul(a, b, result) __builtin_mul_overflow(a, b, result)
-
-#else // no GNUC or clang bultin
-
-/**
- * Performs a multiplication of size_t values and checks for overflow.
- *
- * @param a first operand
- * @param b second operand
- * @param result a pointer to a size_t, where the result should
- * be stored
- * @return zero, if no overflow occurred and the result is correct, non-zero
- * otherwise
- */
-#define cx_szmul(a, b, result) cx_szmul_impl(a, b, result)
-
-/**
- * Performs a multiplication of size_t values and checks for overflow.
- *
- * This is a custom implementation in case there is no compiler builtin
- * available.
- *
- * @param a first operand
- * @param b second operand
- * @param result a pointer to a size_t where the result should be stored
- * @return zero, if no overflow occurred and the result is correct, non-zero
- * otherwise
- */
-int cx_szmul_impl(size_t a, size_t b, size_t *result);
-
-#endif // cx_szmul
-
-
-/**
- * Reads data from a stream and writes it to another stream.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @param buf a pointer to the copy buffer or \c NULL if a buffer
- * shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can
- * set this to zero to let the implementation decide
- * @param n the maximum number of bytes that shall be copied.
- * If this is larger than \p bufsize, the content is copied over multiple
- * iterations.
- * @return the total number of bytes copied
- */
-__attribute__((__nonnull__(1, 2, 3, 4)))
-size_t cx_stream_bncopy(
- void *src,
- void *dest,
- cx_read_func rfnc,
- cx_write_func wfnc,
- char *buf,
- size_t bufsize,
- size_t n
-);
-
-/**
- * Reads data from a stream and writes it to another stream.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @param buf a pointer to the copy buffer or \c NULL if a buffer
- * shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can
- * set this to zero to let the implementation decide
- * @return total number of bytes copied
- */
-#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \
- cx_stream_bncopy(src, dest, rfnc, wfnc, buf, bufsize, SIZE_MAX)
-
-/**
- * Reads data from a stream and writes it to another stream.
- *
- * The data is temporarily stored in a stack allocated buffer.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @param n the maximum number of bytes that shall be copied.
- * @return total number of bytes copied
- */
-__attribute__((__nonnull__))
-size_t cx_stream_ncopy(
- void *src,
- void *dest,
- cx_read_func rfnc,
- cx_write_func wfnc,
- size_t n
-);
-
-/**
- * Reads data from a stream and writes it to another stream.
- *
- * The data is temporarily stored in a stack allocated buffer.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @return total number of bytes copied
- */
-#define cx_stream_copy(src, dest, rfnc, wfnc) \
- cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // UCX_UTILS_H
diff -r 591377a27fa3 -r da79af4baec8 ucx/hash_key.c
--- a/ucx/hash_key.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/hash_key.c Tue Sep 09 20:56:47 2025 +0200
@@ -62,14 +62,14 @@
switch (len) {
case 3:
h ^= (data[i + 2] & 0xFF) << 16;
- __attribute__((__fallthrough__));
+ cx_attr_fallthrough;
case 2:
h ^= (data[i + 1] & 0xFF) << 8;
- __attribute__((__fallthrough__));
+ cx_attr_fallthrough;
case 1:
h ^= (data[i + 0] & 0xFF);
h *= m;
- __attribute__((__fallthrough__));
+ cx_attr_fallthrough;
default: // do nothing
;
}
diff -r 591377a27fa3 -r da79af4baec8 ucx/hash_map.c
--- a/ucx/hash_map.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/hash_map.c Tue Sep 09 20:56:47 2025 +0200
@@ -78,7 +78,7 @@
cxFree(map->collection.allocator, map);
}
-static int cx_hash_map_put(
+static void *cx_hash_map_put(
CxMap *map,
CxHashKey key,
void *value
@@ -105,7 +105,9 @@
memcmp(elm->key.data, key.data, key.len) == 0) {
// overwrite existing element, but call destructors first
cx_invoke_destructor(map, elm->data);
- if (map->collection.store_pointer) {
+ if (value == NULL) {
+ memset(elm->data, 0, map->collection.elem_size);
+ } else if (map->collection.store_pointer) {
memcpy(elm->data, &value, sizeof(void *));
} else {
memcpy(elm->data, value, map->collection.elem_size);
@@ -116,10 +118,12 @@
allocator,
sizeof(struct cx_hash_map_element_s) + map->collection.elem_size
);
- if (e == NULL) return -1;
+ if (e == NULL) return NULL;
// write the value
- if (map->collection.store_pointer) {
+ if (value == NULL) {
+ memset(e->data, 0, map->collection.elem_size);
+ } else if (map->collection.store_pointer) {
memcpy(e->data, &value, sizeof(void *));
} else {
memcpy(e->data, value, map->collection.elem_size);
@@ -127,7 +131,10 @@
// copy the key
void *kd = cxMalloc(allocator, key.len);
- if (kd == NULL) return -1;
+ if (kd == NULL) {
+ cxFree(allocator, e);
+ return NULL;
+ }
memcpy(kd, key.data, key.len);
e->key.data = kd;
e->key.len = key.len;
@@ -140,12 +147,14 @@
prev->next = e;
}
e->next = elm;
+ elm = e;
// increase the size
map->collection.size++;
}
- return 0;
+ // return pointer to the element
+ return elm->data;
}
static void cx_hash_map_unlink(
diff -r 591377a27fa3 -r da79af4baec8 ucx/json.c
--- a/ucx/json.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/json.c Tue Sep 09 20:56:47 2025 +0200
@@ -46,22 +46,17 @@
return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name));
}
-static CxJsonObjValue *json_find_objvalue(const CxJsonValue *obj, cxstring name) {
+static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) {
assert(obj->type == CX_JSON_OBJECT);
CxJsonObjValue kv_dummy;
kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length);
- size_t index = cx_array_binary_search(
+ return cx_array_binary_search(
obj->value.object.values,
obj->value.object.values_size,
sizeof(CxJsonObjValue),
&kv_dummy,
json_cmp_objvalue
);
- if (index == obj->value.object.values_size) {
- return NULL;
- } else {
- return &obj->value.object.values[index];
- }
}
static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) {
@@ -500,7 +495,7 @@
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 +626,10 @@
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 +979,67 @@
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;
}
@@ -1126,6 +1121,20 @@
return value->value.array.array[index];
}
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
+ if (index >= value->value.array.array_size) {
+ return NULL;
+ }
+ CxJsonValue *ret = value->value.array.array[index];
+ // TODO: replace with a low level cx_array_remove()
+ size_t count = value->value.array.array_size - index - 1;
+ if (count > 0) {
+ memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*));
+ }
+ value->value.array.array_size--;
+ return ret;
+}
+
CxIterator cxJsonArrIter(const CxJsonValue *value) {
return cxIteratorPtr(
value->value.array.array,
@@ -1142,11 +1151,25 @@
}
CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) {
- CxJsonObjValue *member = json_find_objvalue(value, name);
- if (member == NULL) {
+ size_t index = json_find_objvalue(value, name);
+ if (index >= value->value.object.values_size) {
return &cx_json_value_nothing;
} else {
- return member->value;
+ return value->value.object.values[index].value;
+ }
+}
+
+CxJsonValue *cx_json_obj_remove_cxstr(CxJsonValue *value, cxstring name) {
+ size_t index = json_find_objvalue(value, name);
+ if (index >= value->value.object.values_size) {
+ return NULL;
+ } else {
+ CxJsonObjValue kv = value->value.object.values[index];
+ cx_strfree_a(value->allocator, &kv.name);
+ // TODO: replace with cx_array_remove()
+ value->value.object.values_size--;
+ memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
+ return kv.value;
}
}
diff -r 591377a27fa3 -r da79af4baec8 ucx/linked_list.c
--- a/ucx/linked_list.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/linked_list.c Tue Sep 09 20:56:47 2025 +0200
@@ -401,7 +401,7 @@
) {
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 @@
*begin = sorted[0];
*end = sorted[length - 1];
if (sorted != sbo) {
- free(sorted);
+ cxFreeDefault(sorted);
}
}
@@ -565,6 +565,7 @@
// HIGH LEVEL LINKED LIST IMPLEMENTATION
typedef struct cx_linked_list_node cx_linked_list_node;
+// IMPORTANT: the layout must stay exactly like this, because kv_list.c uses that!
struct cx_linked_list_node {
cx_linked_list_node *prev;
cx_linked_list_node *next;
@@ -613,7 +614,9 @@
// 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 +662,26 @@
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;
@@ -920,6 +937,8 @@
const void *elem,
bool remove
) {
+ if (list->collection.size == 0) return 0;
+
size_t index;
cx_linked_list *ll = ((cx_linked_list *) list);
cx_linked_list_node *node = cx_linked_list_find(
@@ -1054,12 +1073,12 @@
}
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;
}
}
diff -r 591377a27fa3 -r da79af4baec8 ucx/list.c
--- a/ucx/list.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/list.c Tue Sep 09 20:56:47 2025 +0200
@@ -62,7 +62,7 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
memcpy(ip, jp, elem_size);
memcpy(jp, tmp, elem_size);
- free(tmp);
+ cxFreeDefault(tmp);
return 0;
}
@@ -476,6 +476,7 @@
CxList *list,
size_t index
) {
+ if (list == NULL) list = cxEmptyList;
CxIterator it = list->cl->iterator(list, index, false);
it.base.mutating = true;
return it;
@@ -485,6 +486,7 @@
CxList *list,
size_t index
) {
+ if (list == NULL) list = cxEmptyList;
CxIterator it = list->cl->iterator(list, index, true);
it.base.mutating = true;
return it;
@@ -494,3 +496,24 @@
if (list == NULL) return;
list->cl->deallocate(list);
}
+
+int cxListSet(
+ CxList *list,
+ size_t index,
+ const void *elem
+) {
+ if (index >= list->collection.size) {
+ return 1;
+ }
+
+ if (list->collection.store_pointer) {
+ // For pointer collections, always use climpl
+ void **target = list->climpl->at(list, index);
+ *target = (void *)elem;
+ } else {
+ void *target = list->cl->at(list, index);
+ memcpy(target, elem, list->collection.elem_size);
+ }
+
+ return 0;
+}
diff -r 591377a27fa3 -r da79af4baec8 ucx/map.c
--- a/ucx/map.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/map.c Tue Sep 09 20:56:47 2025 +0200
@@ -85,18 +85,21 @@
//
CxMapIterator cxMapMutIteratorValues(CxMap *map) {
+ if (map == NULL) map = cxEmptyMap;
CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
it.base.mutating = true;
return it;
}
CxMapIterator cxMapMutIteratorKeys(CxMap *map) {
+ if (map == NULL) map = cxEmptyMap;
CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
it.base.mutating = true;
return it;
}
CxMapIterator cxMapMutIterator(CxMap *map) {
+ if (map == NULL) map = cxEmptyMap;
CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
it.base.mutating = true;
return it;
diff -r 591377a27fa3 -r da79af4baec8 ucx/mempool.c
--- a/ucx/mempool.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/mempool.c Tue Sep 09 20:56:47 2025 +0200
@@ -31,44 +31,69 @@
#include
#include
-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
+) {
+ if (needed_capacity <= pool->capacity) return 0;
+ size_t newcap = pool->capacity >= 1000 ?
+ pool->capacity + 1000 : pool->capacity * 2;
+ size_t newmsize;
+ // LCOV_EXCL_START
+ if (pool->capacity > newcap
+ || cx_szmul(newcap, sizeof(void*), &newmsize)) {
+ errno = EOVERFLOW;
+ return 1;
+ } // 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 (pool->size >= pool->capacity) {
- size_t newcap = pool->capacity - (pool->capacity % 16) + 16;
- size_t newmsize;
- if (pool->capacity > newcap || cx_szmul(newcap,
- sizeof(struct cx_mempool_memory_s*), &newmsize)) {
- errno = EOVERFLOW;
- return NULL;
- }
- struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize);
- if (newdata == NULL) return NULL;
- pool->data = newdata;
- pool->capacity = newcap;
+ if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+ 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
@@ -78,53 +103,165 @@
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 void cx_mempool_free(
+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_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);
+ }
+ if (pool->destr) {
+ pool->destr(mem->c);
}
- free(mem);
+ 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];
@@ -137,19 +274,207 @@
abort(); // LCOV_EXCL_LINE
}
+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++) {
+ struct cx_mempool_memory2_s *mem = pool->data[i];
+ if (mem->destructor) {
+ mem->destructor(mem->data, 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_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;
- struct cx_mempool_memory_s *mem;
- for (size_t i = 0; i < pool->size; i++) {
- mem = pool->data[i];
- if (mem->destructor) {
- mem->destructor(mem->c);
- }
- free(mem);
+ 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);
}
- free(pool->data);
- free((void*) pool->allocator);
- free(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(
@@ -159,18 +484,26 @@
*(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(
@@ -178,60 +511,212 @@
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
+ }
+
+ pool->registered[pool->registered_size++] =
+ (struct cx_mempool_foreign_memory_s) {
+ .mem = memory,
+ .destr = destr,
+ .destr2_data = NULL
+ };
+
+ return 0;
+}
- fm->mem = memory;
- fm->destr = destr;
- *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem;
+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;
}
-static cx_allocator_class cx_mempool_allocator_class = {
- cx_mempool_malloc,
- cx_mempool_realloc,
- cx_mempool_calloc,
- cx_mempool_free
-};
-
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;
+ } // LCOV_EXCL_STOP
+
+ CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator));
+ if (provided_allocator == NULL) { // LCOV_EXCL_START
+ return NULL;
+ } // LCOV_EXCL_STOP
+
+ 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;
}
- struct cx_mempool_s *pool =
- malloc(sizeof(struct cx_mempool_s));
- if (pool == NULL) return NULL;
-
- CxAllocator *provided_allocator = malloc(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;
-
- pool->allocator = provided_allocator;
-
- 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 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)) {
+ 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 =
+ cxMalloc(source->base_allocator, sizeof(CxAllocator));
+ if (new_source_allocator == NULL) { // LCOV_EXCL_START
+ return 1;
+ } // LCOV_EXCL_STOP
+ new_source_allocator->cl = source->allocator->cl;
+ new_source_allocator->data = source;
+
+ // transfer all the data
+ if (source->size > 0) {
+ memcpy(&dest->data[dest->size], source->data,
+ sizeof(void*)*source->size);
+ dest->size += source->size;
+ }
+
+ // transfer all registered memory
+ if (source->registered_size > 0) {
+ memcpy(&dest->registered[dest->registered_size], source->registered,
+ sizeof(struct cx_mempool_foreign_memory_s)
+ * source->registered_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;
+ 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(void*));
+ memset(source->registered, 0,
+ source->registered_size * sizeof(struct cx_mempool_foreign_memory_s));
+ source->size = 0;
+ source->registered_size = 0;
+
+ return 0;
+}
+
+int cxMempoolTransferObject(
+ CxMempool *source,
+ CxMempool *dest,
+ const void *obj
+) {
+ // 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;
+
+ // 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) {
+ 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;
+ }
+ }
+ // 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;
+}
diff -r 591377a27fa3 -r da79af4baec8 ucx/printf.c
--- a/ucx/printf.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/printf.c Tue Sep 09 20:56:47 2025 +0200
@@ -68,7 +68,7 @@
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 @@
if (ret > 0) {
ret = (int) wfc(newbuf, 1, ret, stream);
}
- free(newbuf);
+ cxFreeDefault(newbuf);
}
return ret;
}
@@ -121,7 +121,7 @@
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;
@@ -132,7 +132,13 @@
return s;
}
-int cx_sprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, ... ) {
+int cx_sprintf_a(
+ const CxAllocator *alloc,
+ char **str,
+ size_t *len,
+ const char *fmt,
+ ...
+) {
va_list ap;
va_start(ap, fmt);
int ret = cx_vsprintf_a(alloc, str, len, fmt, ap);
@@ -140,7 +146,13 @@
return ret;
}
-int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap) {
+int cx_vsprintf_a(
+ const CxAllocator *alloc,
+ char **str,
+ size_t *len,
+ const char *fmt,
+ va_list ap
+) {
va_list ap2;
va_copy(ap2, ap);
int ret = vsnprintf(*str, *len, fmt, ap);
@@ -162,7 +174,14 @@
return ret;
}
-int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ... ) {
+int cx_sprintf_sa(
+ const CxAllocator *alloc,
+ char *buf,
+ size_t *len,
+ char **str,
+ const char *fmt,
+ ...
+) {
va_list ap;
va_start(ap, fmt);
int ret = cx_vsprintf_sa(alloc, buf, len, str, fmt, ap);
@@ -170,7 +189,14 @@
return ret;
}
-int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap) {
+int cx_vsprintf_sa(
+ const CxAllocator *alloc,
+ char *buf,
+ size_t *len,
+ char **str,
+ const char *fmt,
+ va_list ap
+) {
va_list ap2;
va_copy(ap2, ap);
int ret = vsnprintf(buf, *len, fmt, ap);
diff -r 591377a27fa3 -r da79af4baec8 ucx/properties.c
--- a/ucx/properties.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/properties.c Tue Sep 09 20:56:47 2025 +0200
@@ -287,7 +287,7 @@
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 @@
cx_attr_unused CxProperties *prop,
CxPropertiesSource *src
) {
- free(src->data_ptr);
+ cxFreeDefault(src->data_ptr);
}
CxPropertiesSource cxPropertiesStringSource(cxstring str) {
diff -r 591377a27fa3 -r da79af4baec8 ucx/streams.c
--- a/ucx/streams.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/streams.c Tue Sep 09 20:56:47 2025 +0200
@@ -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 @@
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 @@
}
if (lbuf != buf) {
- free(lbuf);
+ cxFreeDefault(lbuf);
}
return ncp;
diff -r 591377a27fa3 -r da79af4baec8 ucx/string.c
--- a/ucx/string.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/string.c Tue Sep 09 20:56:47 2025 +0200
@@ -42,7 +42,7 @@
#endif
cxmutstr cx_mutstr(char *cstring) {
- return (cxmutstr) {cstring, strlen(cstring)};
+ return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)};
}
cxmutstr cx_mutstrn(
@@ -53,7 +53,7 @@
}
cxstring cx_str(const char *cstring) {
- return (cxstring) {cstring, strlen(cstring)};
+ return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)};
}
cxstring cx_strn(
@@ -65,7 +65,7 @@
void cx_strfree(cxmutstr *str) {
if (str == NULL) return;
- free(str->ptr);
+ cxFreeDefault(str->ptr);
str->ptr = NULL;
str->length = 0;
}
@@ -80,6 +80,22 @@
str->length = 0;
}
+int cx_strcpy_a(
+ const CxAllocator *alloc,
+ cxmutstr *dest,
+ cxstring src
+) {
+ if (cxReallocate(alloc, &dest->ptr, src.length + 1)) {
+ return 1;
+ }
+
+ memcpy(dest->ptr, src.ptr, src.length);
+ dest->length = src.length;
+ dest->ptr[dest->length] = '\0';
+
+ return 0;
+}
+
size_t cx_strlen(
size_t count,
...
@@ -106,27 +122,16 @@
...
) {
if (count == 0) return str;
-
- cxstring strings_stack[8];
- cxstring *strings;
- if (count > 8) {
- strings = calloc(count, sizeof(cxstring));
- if (strings == NULL) {
- return (cxmutstr) {NULL, 0};
- }
- } else {
- strings = strings_stack;
- }
-
va_list ap;
va_start(ap, count);
+ va_list ap2;
+ va_copy(ap2, ap);
- // get all args and overall length
+ // compute overall length
bool overflow = false;
size_t slen = str.length;
for (size_t i = 0; i < count; i++) {
- cxstring s = va_arg (ap, cxstring);
- strings[i] = s;
+ cxstring s = va_arg(ap, cxstring);
if (slen > SIZE_MAX - str.length) overflow = true;
slen += s.length;
}
@@ -134,10 +139,8 @@
// abort in case of overflow
if (overflow) {
+ va_end(ap2);
errno = EOVERFLOW;
- if (strings != strings_stack) {
- free(strings);
- }
return (cxmutstr) { NULL, 0 };
}
@@ -149,9 +152,7 @@
newstr = cxRealloc(alloc, str.ptr, slen + 1);
}
if (newstr == NULL) {
- if (strings != strings_stack) {
- free(strings);
- }
+ va_end(ap2);
return (cxmutstr) {NULL, 0};
}
str.ptr = newstr;
@@ -160,19 +161,15 @@
size_t pos = str.length;
str.length = slen;
for (size_t i = 0; i < count; i++) {
- cxstring s = strings[i];
+ cxstring s = va_arg(ap2, cxstring);
memcpy(str.ptr + pos, s.ptr, s.length);
pos += s.length;
}
+ va_end(ap2);
// terminate string
str.ptr[str.length] = '\0';
- // free temporary array
- if (strings != strings_stack) {
- free(strings);
- }
-
return str;
}
@@ -289,8 +286,9 @@
// 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;
@@ -328,7 +326,7 @@
// if prefix table was allocated on the heap, free it
if (useheap) {
- free(ptable);
+ cxFreeDefault(ptable);
}
return result;
@@ -588,27 +586,6 @@
#endif
}
-#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
-#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
-#endif
-
-struct cx_strreplace_ibuf {
- size_t *buf;
- struct cx_strreplace_ibuf *next;
- unsigned int len;
-};
-
-static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
- // remember, the first data is on the stack!
- buf = buf->next;
- while (buf) {
- struct cx_strreplace_ibuf *next = buf->next;
- free(buf->buf);
- free(buf);
- buf = next;
- }
-}
-
cxmutstr cx_strreplacen_a(
const CxAllocator *allocator,
cxstring str,
@@ -616,108 +593,60 @@
cxstring replacement,
size_t replmax
) {
+ // special cases
+ if (search.length == 0 || search.length > str.length || replmax == 0) {
+ return cx_strdup_a(allocator, str);
+ }
- if (search.length == 0 || search.length > str.length || replmax == 0)
- return cx_strdup_a(allocator, str);
+ size_t in_len = str.length;
+ size_t search_len = search.length;
+ size_t repl_len = replacement.length;
- // Compute expected buffer length
- size_t ibufmax = str.length / search.length;
- size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
- if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) {
- ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE;
+ // first run, count the occurrences
+ // and remember where the first is
+ size_t occurrences = 1;
+ cxstring first = cx_strstr(str, search);
+ if (first.length == 0) {
+ // special case, no replacements
+ return cx_strdup_a(allocator, str);
+ }
+ cxstring tmp = cx_strsubs(first, search_len);
+ while (occurrences < replmax &&
+ (tmp = cx_strstr(tmp, search)).length > 0) {
+ occurrences++;
+ tmp = cx_strsubs(tmp, search_len);
}
- // First index buffer can be on the stack
- struct cx_strreplace_ibuf ibuf, *curbuf = &ibuf;
- size_t ibuf_sbo[CX_STRREPLACE_INDEX_BUFFER_SIZE];
- ibuf.buf = ibuf_sbo;
- ibuf.next = NULL;
- ibuf.len = 0;
+ // calculate necessary memory
+ signed long long diff_len = (signed long long) repl_len - search_len;
+ size_t out_len = in_len + diff_len * occurrences;
+ cxmutstr out = {
+ cxMalloc(allocator, out_len + 1),
+ out_len
+ };
+ if (out.ptr == NULL) return out;
- // Search occurrences
- cxstring searchstr = str;
- size_t found = 0;
- do {
- cxstring match = cx_strstr(searchstr, search);
- if (match.length > 0) {
- // Allocate next buffer in chain, if required
- if (curbuf->len == ibuflen) {
- struct cx_strreplace_ibuf *nextbuf =
- calloc(1, sizeof(struct cx_strreplace_ibuf));
- if (!nextbuf) {
- cx_strrepl_free_ibuf(&ibuf);
- return cx_mutstrn(NULL, 0);
- }
- nextbuf->buf = calloc(ibuflen, sizeof(size_t));
- if (!nextbuf->buf) {
- free(nextbuf);
- cx_strrepl_free_ibuf(&ibuf);
- return cx_mutstrn(NULL, 0);
- }
- curbuf->next = nextbuf;
- curbuf = nextbuf;
- }
-
- // Record match index
- found++;
- size_t idx = match.ptr - str.ptr;
- curbuf->buf[curbuf->len++] = idx;
- searchstr.ptr = match.ptr + search.length;
- searchstr.length = str.length - idx - search.length;
- } else {
- break;
- }
- } while (searchstr.length > 0 && found < replmax);
-
- // Allocate result string
- cxmutstr result;
- {
- long long adjlen = (long long) replacement.length - (long long) search.length;
- size_t rcount = 0;
- curbuf = &ibuf;
- do {
- rcount += curbuf->len;
- curbuf = curbuf->next;
- } while (curbuf);
- result.length = str.length + rcount * adjlen;
- result.ptr = cxMalloc(allocator, result.length + 1);
- if (!result.ptr) {
- cx_strrepl_free_ibuf(&ibuf);
- return cx_mutstrn(NULL, 0);
- }
+ // second run: perform the replacements
+ // but start where we found the first occurrence
+ const char *inp = str.ptr;
+ tmp = first;
+ char *outp = out.ptr;
+ while (occurrences-- > 0 && (tmp = cx_strstr(tmp, search)).length > 0) {
+ size_t copylen = tmp.ptr - inp;
+ memcpy(outp, inp, copylen);
+ outp += copylen;
+ memcpy(outp, replacement.ptr, repl_len);
+ outp += repl_len;
+ inp += copylen + search_len;
+ tmp = cx_strsubs(tmp, search_len);
}
- // Build result string
- curbuf = &ibuf;
- size_t srcidx = 0;
- char *destptr = result.ptr;
- do {
- for (size_t i = 0; i < curbuf->len; i++) {
- // Copy source part up to next match
- size_t idx = curbuf->buf[i];
- size_t srclen = idx - srcidx;
- if (srclen > 0) {
- memcpy(destptr, str.ptr + srcidx, srclen);
- destptr += srclen;
- srcidx += srclen;
- }
+ // add the remaining string
+ size_t copylen = in_len - (inp - str.ptr);
+ memcpy(outp, inp, copylen);
+ out.ptr[out_len] = '\0';
- // Copy the replacement and skip the source pattern
- srcidx += search.length;
- memcpy(destptr, replacement.ptr, replacement.length);
- destptr += replacement.length;
- }
- curbuf = curbuf->next;
- } while (curbuf);
- memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
-
- // Result is guaranteed to be zero-terminated
- result.ptr[result.length] = '\0';
-
- // Free index buffer
- cx_strrepl_free_ibuf(&ibuf);
-
- return result;
+ return out;
}
CxStrtokCtx cx_strtok_(
diff -r 591377a27fa3 -r da79af4baec8 ucx/tree.c
--- a/ucx/tree.c Tue Sep 09 16:01:30 2025 +0200
+++ b/ucx/tree.c Tue Sep 09 20:56:47 2025 +0200
@@ -226,14 +226,14 @@
int ret_elem = sfunc(elem, node);
if (ret_elem == 0) {
// if found, exit the search
- *result = (void *) elem;
+ *result = elem;
ret = 0;
break;
} else if (ret_elem > 0 && ret_elem < ret) {
// new distance is better
*result = elem;
ret = ret_elem;
- } else {
+ } else if (ret_elem < 0 || ret_elem > ret) {
// not contained or distance is worse, skip entire subtree
cxTreeIteratorContinue(iter);
}
@@ -305,12 +305,12 @@
if (children == NULL) {
// search for the next node
- void *next;
+ void *next = NULL;
cx_tree_iter_search_next:
- // check if there is a sibling
+ // check if there is a sibling, but only if we are not a (subtree-)root
if (iter->exiting) {
next = iter->node_next;
- } else {
+ } else if (iter->depth > 1) {
next = tree_next(iter->node);
iter->node_next = next;
}
@@ -326,7 +326,7 @@
// 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 @@
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 @@
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 @@
}
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 @@
assert(iter->queue_last == q);
iter->queue_last = NULL;
}
- free(q);
+ cxFreeDefault(q);
}
// increment the node counter