Thu, 18 Dec 2025 17:50:15 +0100
update ucx
--- a/application/main.c Wed Dec 17 18:31:20 2025 +0100 +++ b/application/main.c Thu Dec 18 17:50:15 2025 +0100 @@ -783,7 +783,7 @@ ui_tab(obj, "Tab 9") { #ifdef UI_WEBVIEW ui_webview(obj, .fill = TRUE, .value = doc->web); - cxstring html = CX_STR("<html><body><h1>Hello Toolkit</h1><p>Toolkit WebView</p></body></html>"); + cxstring html = cx_str("<html><body><h1>Hello Toolkit</h1><p>Toolkit WebView</p></body></html>"); ui_webview_load_content(doc->web, "mypage", html.ptr, html.length, NULL, NULL); #else ui_label(obj, .label = "Webview is not supported");
--- a/ucx/array_list.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/array_list.c Thu Dec 18 17:50:15 2025 +0100 @@ -32,76 +32,6 @@ #include <string.h> #include <errno.h> -// Default array reallocator - -static void *cx_array_default_realloc( - void *array, - cx_attr_unused size_t old_capacity, - size_t new_capacity, - size_t elem_size, - cx_attr_unused CxArrayReallocator *alloc -) { - size_t n; - // LCOV_EXCL_START - if (cx_szmul(new_capacity, elem_size, &n)) { - errno = EOVERFLOW; - return NULL; - } // LCOV_EXCL_STOP - return cxReallocDefault(array, n); -} - -CxArrayReallocator cx_array_default_reallocator_impl = { - cx_array_default_realloc, NULL, NULL -}; - -CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl; - -// Stack-aware array reallocator - -static void *cx_array_advanced_realloc( - void *array, - size_t old_capacity, - size_t new_capacity, - size_t elem_size, - cx_attr_unused CxArrayReallocator *alloc -) { - // check for overflow - size_t n; - // LCOV_EXCL_START - if (cx_szmul(new_capacity, elem_size, &n)) { - errno = EOVERFLOW; - return NULL; - } // LCOV_EXCL_STOP - - // retrieve the pointer to the actual allocator - const CxAllocator *al = alloc->allocator; - - // check if the array is still located on the stack - void *newmem; - if (array == alloc->stack_ptr) { - newmem = cxMalloc(al, n); - if (newmem != NULL && array != NULL) { - memcpy(newmem, array, old_capacity*elem_size); - } - } else { - newmem = cxRealloc(al, array, n); - } - return newmem; -} - -struct cx_array_reallocator_s cx_array_reallocator( - const struct cx_allocator_s *allocator, - const void *stack_ptr -) { - if (allocator == NULL) { - allocator = cxDefaultAllocator; - } - return (struct cx_array_reallocator_s) { - cx_array_advanced_realloc, - allocator, stack_ptr, - }; -} - // LOW LEVEL ARRAY LIST FUNCTIONS /** @@ -128,295 +58,139 @@ return cap - (cap % alignment) + alignment; } -int cx_array_reserve( - void **array, - void *size, - void *capacity, - unsigned width, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -) { - // assert pointers - assert(array != NULL); - assert(size != NULL); - assert(capacity != NULL); +int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) { + memset(array, 0, sizeof(CxArray)); + return cx_array_reserve_(allocator, array, elem_size, capacity); +} + +void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size) { + array->data = (void*) data; + array->capacity = capacity; + array->size = size; +} + +int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) { + if (cxReallocateArray(allocator, &array->data, capacity, elem_size)) { + return -1; // LCOV_EXCL_LINE + } + array->capacity = capacity; + if (array->size > capacity) { + array->size = capacity; + } + return 0; +} - // default reallocator - if (reallocator == NULL) { - reallocator = cx_array_default_reallocator; +int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) { + CxArray heap_array; + if (cx_array_init_(allocator, &heap_array, elem_size, capacity)) { + return -1; // LCOV_EXCL_LINE + } + heap_array.size = array->size; + memcpy(heap_array.data, array->data, elem_size * array->size); + *array = heap_array; + return 0; +} + +int cx_array_insert_(const CxAllocator *allocator, CxArray *array, + size_t elem_size, size_t index, const void *other, size_t n) { + // out of bounds and special case check + if (index > array->size) return -1; + if (n == 0) return 0; + + // guarantee enough capacity + if (array->capacity < array->size + n) { + const size_t new_capacity = cx_array_grow_capacity(array->capacity,array->size + n); + if (cxReallocateArray(allocator, &array->data, new_capacity, elem_size)) { + return -1; // LCOV_EXCL_LINE + } + array->capacity = new_capacity; } - // determine size and capacity - size_t oldcap; - size_t oldsize; - size_t max_size; - if (width == 0 || width == sizeof(size_t)) { - oldcap = *(size_t*) capacity; - oldsize = *(size_t*) size; - max_size = SIZE_MAX; - } else if (width == sizeof(uint16_t)) { - oldcap = *(uint16_t*) capacity; - oldsize = *(uint16_t*) size; - max_size = UINT16_MAX; - } else if (width == sizeof(uint8_t)) { - oldcap = *(uint8_t*) capacity; - oldsize = *(uint8_t*) size; - max_size = UINT8_MAX; - } -#if CX_WORDSIZE == 64 - else if (width == sizeof(uint32_t)) { - oldcap = *(uint32_t*) capacity; - oldsize = *(uint32_t*) size; - max_size = UINT32_MAX; - } -#endif - else { - errno = EINVAL; - return 1; + // determine insert position + char *dst = array->data; + dst += index * elem_size; + + // do we need to move some elements? + if (index < array->size) { + size_t elems_to_move = array->size - index; + char *target = dst + n * elem_size; + memmove(target, dst, elems_to_move * elem_size); } - // assert that the array is allocated when it has capacity - assert(*array != NULL || oldcap == 0); - - // check for overflow - if (elem_count > max_size - oldsize) { - errno = EOVERFLOW; - return 1; + // place the new elements, if any + // otherwise, this function just reserved the memory (a.k.a emplace) + if (other != NULL) { + memcpy(dst, other, n * elem_size); } - - // determine new capacity - size_t newcap = oldsize + elem_count; - - // reallocate if possible - if (newcap > oldcap) { - void *newmem = reallocator->realloc( - *array, oldcap, newcap, elem_size, reallocator - ); - if (newmem == NULL) { - return 1; // LCOV_EXCL_LINE - } - - // store new pointer - *array = newmem; - - // store new capacity - if (width == 0 || width == sizeof(size_t)) { - *(size_t*) capacity = newcap; - } else if (width == sizeof(uint16_t)) { - *(uint16_t*) capacity = (uint16_t) newcap; - } else if (width == sizeof(uint8_t)) { - *(uint8_t*) capacity = (uint8_t) newcap; - } -#if CX_WORDSIZE == 64 - else if (width == sizeof(uint32_t)) { - *(uint32_t*) capacity = (uint32_t) newcap; - } -#endif - } + array->size += n; return 0; } -int cx_array_copy( - void **target, - void *size, - void *capacity, - unsigned width, - size_t index, - const void *src, +int cx_array_insert_sorted_s_( + const CxAllocator *allocator, + CxArray *array, size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator + const void *sorted_data, + size_t n, + bool allow_duplicates, + cx_compare_func2 cmp_func, + void *context ) { // assert pointers - assert(target != NULL); - assert(size != NULL); - assert(capacity != NULL); - assert(src != NULL); - - // default reallocator - if (reallocator == NULL) { - reallocator = cx_array_default_reallocator; - } - - // determine size and capacity - size_t oldcap; - size_t oldsize; - size_t max_size; - if (width == 0 || width == sizeof(size_t)) { - oldcap = *(size_t*) capacity; - oldsize = *(size_t*) size; - max_size = SIZE_MAX; - } else if (width == sizeof(uint16_t)) { - oldcap = *(uint16_t*) capacity; - oldsize = *(uint16_t*) size; - max_size = UINT16_MAX; - } else if (width == sizeof(uint8_t)) { - oldcap = *(uint8_t*) capacity; - oldsize = *(uint8_t*) size; - max_size = UINT8_MAX; - } -#if CX_WORDSIZE == 64 - else if (width == sizeof(uint32_t)) { - oldcap = *(uint32_t*) capacity; - oldsize = *(uint32_t*) size; - max_size = UINT32_MAX; - } -#endif - else { - errno = EINVAL; - return 1; - } - - // assert that the array is allocated when it has capacity - assert(*target != NULL || oldcap == 0); - - // check for overflow - if (index > max_size || elem_count > max_size - index) { - errno = EOVERFLOW; - return 1; - } - - // check if resize is required - const size_t minsize = index + elem_count; - const size_t newsize = oldsize < minsize ? minsize : oldsize; - - // reallocate if necessary - const size_t newcap = cx_array_grow_capacity(oldcap, newsize); - if (newcap > oldcap) { - // check if we need to repair the src pointer - uintptr_t targetaddr = (uintptr_t) *target; - uintptr_t srcaddr = (uintptr_t) src; - bool repairsrc = targetaddr <= srcaddr - && srcaddr < targetaddr + oldcap * elem_size; - - // perform reallocation - void *newmem = reallocator->realloc( - *target, oldcap, newcap, elem_size, reallocator - ); - if (newmem == NULL) { - return 1; // LCOV_EXCL_LINE - } - - // repair src pointer, if necessary - if (repairsrc) { - src = ((char *) newmem) + (srcaddr - targetaddr); - } - - // store new pointer - *target = newmem; - } - - // determine target pointer - char *start = *target; - start += index * elem_size; - - // copy elements and set new size - // note: no overflow check here, b/c we cannot get here w/o allocation - memmove(start, src, elem_count * elem_size); - - // if any of size or capacity changed, store them back - if (newsize != oldsize || newcap != oldcap) { - if (width == 0 || width == sizeof(size_t)) { - *(size_t*) capacity = newcap; - *(size_t*) size = newsize; - } else if (width == sizeof(uint16_t)) { - *(uint16_t*) capacity = (uint16_t) newcap; - *(uint16_t*) size = (uint16_t) newsize; - } else if (width == sizeof(uint8_t)) { - *(uint8_t*) capacity = (uint8_t) newcap; - *(uint8_t*) size = (uint8_t) newsize; - } -#if CX_WORDSIZE == 64 - else if (width == sizeof(uint32_t)) { - *(uint32_t*) capacity = (uint32_t) newcap; - *(uint32_t*) size = (uint32_t) newsize; - } -#endif - } - - // return successfully - return 0; -} - -static int cx_array_insert_sorted_impl( - void **target, - size_t *size, - size_t *capacity, - cx_compare_func cmp_func, - const void *sorted_data, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator, - bool allow_duplicates -) { - // assert pointers - assert(target != NULL); - assert(size != NULL); - assert(capacity != NULL); + assert(allocator != NULL); + assert(array != NULL); assert(cmp_func != NULL); assert(sorted_data != NULL); - // default reallocator - if (reallocator == NULL) { - reallocator = cx_array_default_reallocator; - } - // corner case - if (elem_count == 0) return 0; + if (n == 0) return 0; // overflow check // LCOV_EXCL_START - if (elem_count > SIZE_MAX - *size) { + if (n > SIZE_MAX - array->size) { errno = EOVERFLOW; return 1; } // LCOV_EXCL_STOP // store some counts - const size_t old_size = *size; - const size_t old_capacity = *capacity; + const size_t old_size = array->size; + const size_t old_capacity = array->capacity; // the necessary capacity is the worst case assumption, including duplicates - const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + elem_count); + const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + n); // if we need more than we have, try a reallocation if (needed_capacity > old_capacity) { - void *new_mem = reallocator->realloc( - *target, old_capacity, needed_capacity, elem_size, reallocator - ); - if (new_mem == NULL) { - // give it up right away, there is no contract - // that requires us to insert as much as we can - return 1; // LCOV_EXCL_LINE + if (cxReallocateArray(allocator, &array->data, needed_capacity, elem_size)) { + return -1; // LCOV_EXCL_LINE } - *target = new_mem; - *capacity = needed_capacity; + array->capacity = needed_capacity; } // now we have guaranteed that we can insert everything - size_t new_size = old_size + elem_count; - *size = new_size; + size_t new_size = old_size + n; + array->size = new_size; // declare the source and destination indices/pointers size_t si = 0, di = 0; const char *src = sorted_data; - char *dest = *target; + char *dest = array->data; // find the first insertion point - di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func); + di = cx_array_binary_search_sup_c(dest, old_size, elem_size, src, cmp_func, context); dest += di * elem_size; // move the remaining elements in the array completely to the right // we will call it the "buffer" for parked elements size_t buf_size = old_size - di; size_t bi = new_size - buf_size; - char *bptr = ((char *) *target) + bi * elem_size; + char *bptr = ((char *) array->data) + bi * elem_size; memmove(bptr, dest, buf_size * elem_size); // while there are both source and buffered elements left, // copy them interleaving - while (si < elem_count && bi < new_size) { + while (si < n && bi < new_size) { // determine how many source elements can be inserted. // the first element that shall not be inserted is the smallest element // that is strictly larger than the first buffered element @@ -430,8 +204,8 @@ // Therefore, the buffer can never contain an element that is smaller // than any element in the source and the infimum exists. size_t copy_len, bytes_copied; - copy_len = cx_array_binary_search_inf( - src, elem_count - si, elem_size, bptr, cmp_func + copy_len = cx_array_binary_search_inf_c( + src, n - si, elem_size, bptr, cmp_func, context ); copy_len++; @@ -450,17 +224,17 @@ // for being a duplicate of the bptr const char *end_of_src = src + (copy_len - 1) * elem_size; size_t skip_len = 0; - while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) { + while (copy_len > 0 && cmp_func(bptr, end_of_src, context) == 0) { end_of_src -= elem_size; skip_len++; copy_len--; } - char *last = dest == *target ? NULL : dest - elem_size; + char *last = dest == array->data ? NULL : dest - elem_size; // then iterate through the source chunk // and skip all duplicates with the last element in the array size_t more_skipped = 0; for (unsigned j = 0; j < copy_len; j++) { - if (last != NULL && cmp_func(last, src) == 0) { + if (last != NULL && cmp_func(last, src, context) == 0) { // duplicate - skip src += elem_size; si++; @@ -479,20 +253,21 @@ si += skip_len; skip_len += more_skipped; // reduce the actual size by the number of skipped elements - *size -= skip_len; + array->size -= skip_len; } } // when all source elements are in place, we are done - if (si >= elem_count) break; + if (si >= n) break; // determine how many buffered elements need to be restored - copy_len = cx_array_binary_search_sup( + copy_len = cx_array_binary_search_sup_c( bptr, new_size - bi, elem_size, src, - cmp_func + cmp_func, + context ); // restore the buffered elements @@ -505,24 +280,24 @@ } // still source elements left? - if (si < elem_count) { + if (si < n) { if (allow_duplicates) { // duplicates allowed or nothing inserted yet: simply copy everything - memcpy(dest, src, elem_size * (elem_count - si)); + memcpy(dest, src, elem_size * (n - si)); } else { // we must check the remaining source elements one by one // to skip the duplicates. // Note that no source element can equal the last element in the // destination, because that would have created an insertion point // and a buffer, s.t. the above loop already handled the duplicates - while (si < elem_count) { + while (si < n) { // find a chain of elements that can be copied size_t copy_len = 1, skip_len = 0; { const char *left_src = src; - while (si + copy_len + skip_len < elem_count) { + while (si + copy_len + skip_len < n) { const char *right_src = left_src + elem_size; - int d = cmp_func(left_src, right_src); + int d = cmp_func(left_src, right_src, context); if (d < 0) { if (skip_len > 0) { // new larger element found; @@ -545,13 +320,13 @@ src += bytes_copied + skip_len * elem_size; si += copy_len + skip_len; di += copy_len; - *size -= skip_len; + array->size -= skip_len; } } } // buffered elements need to be moved when we skipped duplicates - size_t total_skipped = new_size - *size; + size_t total_skipped = new_size - array->size; if (bi < new_size && total_skipped > 0) { // move the remaining buffer to the end of the array memmove(dest, bptr, elem_size * (new_size - bi)); @@ -560,41 +335,43 @@ return 0; } -int cx_array_insert_sorted( - void **target, - size_t *size, - size_t *capacity, +int cx_array_insert_sorted_( + const CxAllocator *allocator, + CxArray *array, + size_t elem_size, cx_compare_func cmp_func, const void *sorted_data, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator + size_t n, + bool allow_duplicates ) { - return cx_array_insert_sorted_impl(target, size, capacity, - cmp_func, sorted_data, elem_size, elem_count, reallocator, true); + cx_compare_func_wrapper wrapper = {cmp_func}; + return cx_array_insert_sorted_s_(allocator, array, elem_size, sorted_data, + n, allow_duplicates, cx_acmp_wrap, &wrapper); } -int cx_array_insert_unique( - void **target, - size_t *size, - size_t *capacity, - cx_compare_func cmp_func, - const void *sorted_data, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -) { - return cx_array_insert_sorted_impl(target, size, capacity, - cmp_func, sorted_data, elem_size, elem_count, reallocator, false); +CxIterator cx_array_iterator_(CxArray *array, size_t elem_size) { + return cxIterator(array->data, elem_size, array->size); +} + +CxIterator cx_array_iterator_ptr_(CxArray *array) { + return cxIteratorPtr(array->data, array->size); } +void cx_array_free_(const CxAllocator *allocator, CxArray *array) { + cxFree(allocator, array->data); + array->data = NULL; + array->size = array->capacity = 0; +} + + // implementation that finds ANY index static size_t cx_array_binary_search_inf_impl( const void *arr, size_t size, size_t elem_size, const void *elem, - cx_compare_func cmp_func + cx_compare_func2 cmp_func, + void *context ) { // special case: empty array if (size == 0) return 0; @@ -606,7 +383,7 @@ const char *array = arr; // check the first array element - result = cmp_func(elem, array); + result = cmp_func(elem, array, context); if (result < 0) { return size; } else if (result == 0) { @@ -617,7 +394,7 @@ if (size == 1) return 0; // check the last array element - result = cmp_func(elem, array + elem_size * (size - 1)); + result = cmp_func(elem, array + elem_size * (size - 1), context); if (result >= 0) { return size - 1; } @@ -626,12 +403,12 @@ // so start the binary search size_t left_index = 1; size_t right_index = size - 1; - size_t pivot_index; + size_t pivot_index = 0; while (left_index <= right_index) { pivot_index = left_index + (right_index - left_index) / 2; const char *arr_elem = array + pivot_index * elem_size; - result = cmp_func(elem, arr_elem); + result = cmp_func(elem, arr_elem, context); if (result == 0) { // found it! return pivot_index; @@ -648,6 +425,74 @@ return result < 0 ? (pivot_index - 1) : pivot_index; } +size_t cx_array_binary_search_inf_c( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func2 cmp_func, + void *context +) { + size_t index = cx_array_binary_search_inf_impl( + arr, size, elem_size, elem, cmp_func, context); + // in case of equality, report the largest index + const char *e = ((const char *) arr) + (index + 1) * elem_size; + while (index + 1 < size && cmp_func(e, elem, context) == 0) { + e += elem_size; + index++; + } + return index; +} + +size_t cx_array_binary_search_c( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func2 cmp_func, + void *context +) { + size_t index = cx_array_binary_search_inf_c( + arr, size, elem_size, elem, cmp_func, context + ); + if (index < size && cmp_func(((const char *) arr) + index * elem_size, + elem, context) == 0) { + return index; + } else { + return size; + } +} + +size_t cx_array_binary_search_sup_c( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func2 cmp_func, + void *context +) { + size_t index = cx_array_binary_search_inf_impl( + arr, size, elem_size, elem, cmp_func, context + ); + const char *e = ((const char *) arr) + index * elem_size; + if (index == size) { + // no infimum means the first element is supremum + return 0; + } else if (cmp_func(e, elem, context) == 0) { + // found an equal element, search the smallest index + e -= elem_size; // e now contains the element at index-1 + while (index > 0 && cmp_func(e, elem, context) == 0) { + e -= elem_size; + index--; + } + return index; + } else { + // we already have the largest index of the infimum (by design) + // the next element is the supremum (or there is no supremum) + return index + 1; + } +} + size_t cx_array_binary_search_inf( const void *arr, size_t size, @@ -655,15 +500,8 @@ const void *elem, cx_compare_func cmp_func ) { - size_t index = cx_array_binary_search_inf_impl( - arr, size, elem_size, elem, cmp_func); - // in case of equality, report the largest index - const char *e = ((const char *) arr) + (index + 1) * elem_size; - while (index + 1 < size && cmp_func(e, elem) == 0) { - e += elem_size; - index++; - } - return index; + cx_compare_func_wrapper wrapper = {cmp_func}; + return cx_array_binary_search_inf_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper); } size_t cx_array_binary_search( @@ -673,15 +511,8 @@ const void *elem, cx_compare_func cmp_func ) { - size_t index = cx_array_binary_search_inf( - arr, size, elem_size, elem, cmp_func - ); - if (index < size && - cmp_func(((const char *) arr) + index * elem_size, elem) == 0) { - return index; - } else { - return size; - } + cx_compare_func_wrapper wrapper = {cmp_func}; + return cx_array_binary_search_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper); } size_t cx_array_binary_search_sup( @@ -691,26 +522,8 @@ const void *elem, cx_compare_func cmp_func ) { - size_t index = cx_array_binary_search_inf_impl( - arr, size, elem_size, elem, cmp_func - ); - const char *e = ((const char *) arr) + index * elem_size; - if (index == size) { - // no infimum means the first element is supremum - return 0; - } else if (cmp_func(e, elem) == 0) { - // found an equal element, search the smallest index - e -= elem_size; // e now contains the element at index-1 - while (index > 0 && cmp_func(e, elem) == 0) { - e -= elem_size; - index--; - } - return index; - } else { - // we already have the largest index of the infimum (by design) - // the next element is the supremum (or there is no supremum) - return index + 1; - } + cx_compare_func_wrapper wrapper = {cmp_func}; + return cx_array_binary_search_sup_c(arr, size, elem_size, elem, cx_acmp_wrap, &wrapper); } #ifndef CX_ARRAY_SWAP_SBO_SIZE @@ -763,7 +576,6 @@ struct cx_list_s base; void *data; size_t capacity; - CxArrayReallocator reallocator; } cx_array_list; static void cx_arl_destructor(struct cx_list_s *list) { @@ -794,42 +606,47 @@ const void *array, size_t n ) { - // out of bounds and special case check - if (index > list->collection.size || n == 0) return 0; - - // get a correctly typed pointer to the list cx_array_list *arl = (cx_array_list *) list; - - // guarantee enough capacity - if (arl->capacity < list->collection.size + n) { - const size_t new_capacity = cx_array_grow_capacity(arl->capacity,list->collection.size + n); - if (cxReallocateArray( - list->collection.allocator, - &arl->data, new_capacity, - list->collection.elem_size) - ) { - return 0; // LCOV_EXCL_LINE - } - arl->capacity = new_capacity; + CxArray wrap = { + arl->data, list->collection.size, arl->capacity + }; + if (cx_array_insert_(list->collection.allocator, &wrap, + list->collection.elem_size, index, array, n)) { + return 0; } + arl->data = wrap.data; + arl->capacity = wrap.capacity; + list->collection.size = wrap.size; + return n; +} - // determine insert position - char *arl_data = arl->data; - char *insert_pos = arl_data + index * list->collection.elem_size; +static size_t cx_arl_insert_sorted_impl( + struct cx_list_s *list, + const void *sorted_data, + size_t n, + bool allow_duplicates +) { + cx_array_list *arl = (cx_array_list *) list; + CxArray wrap = { + arl->data, list->collection.size, arl->capacity + }; - // 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); + if (cx_array_insert_sorted_s_( + list->collection.allocator, + &wrap, + list->collection.elem_size, + sorted_data, + n, + allow_duplicates, + cx_list_compare_wrapper, + list + )) { + // array list implementation is "all or nothing" + return 0; // LCOV_EXCL_LINE } - - // place the new elements, if any - if (array != NULL) { - memcpy(insert_pos, array, n * list->collection.elem_size); - } - list->collection.size += n; - + arl->data = wrap.data; + arl->capacity = wrap.capacity; + list->collection.size = wrap.size; return n; } @@ -838,24 +655,7 @@ const void *sorted_data, size_t n ) { - // get a correctly typed pointer to the list - cx_array_list *arl = (cx_array_list *) list; - - if (cx_array_insert_sorted( - &arl->data, - &list->collection.size, - &arl->capacity, - list->collection.cmpfunc, - sorted_data, - list->collection.elem_size, - n, - &arl->reallocator - )) { - // array list implementation is "all or nothing" - return 0; // LCOV_EXCL_LINE - } else { - return n; - } + return cx_arl_insert_sorted_impl(list, sorted_data, n, true); } static size_t cx_arl_insert_unique( @@ -863,24 +663,7 @@ const void *sorted_data, size_t n ) { - // get a correctly typed pointer to the list - cx_array_list *arl = (cx_array_list *) list; - - if (cx_array_insert_unique( - &arl->data, - &list->collection.size, - &arl->capacity, - list->collection.cmpfunc, - sorted_data, - list->collection.elem_size, - n, - &arl->reallocator - )) { - // array list implementation is "all or nothing" - return 0; // LCOV_EXCL_LINE - } else { - return n; - } + return cx_arl_insert_sorted_impl(list, sorted_data, n, false); } static void *cx_arl_insert_element( @@ -959,24 +742,21 @@ ); } + // calculate how many elements would need to be moved + size_t remaining = list->collection.size - index - remove; + // short-circuit removal of last elements - if (index + remove == list->collection.size) { + if (remaining == 0) { list->collection.size -= remove; return remove; } // just move the elements to the left - cx_array_copy( - &arl->data, - &list->collection.size, - &arl->capacity, - 0, - index, - ((char *) arl->data) + (index + remove) * list->collection.elem_size, - list->collection.elem_size, - list->collection.size - index - remove, - &arl->reallocator - ); + char *first_remaining = arl->data; + first_remaining += (index + remove) * list->collection.elem_size; + char *dst_move = arl->data; + dst_move += index * list->collection.elem_size; + memmove(dst_move, first_remaining, remaining * list->collection.elem_size); // decrease the size list->collection.size -= remove; @@ -1037,18 +817,18 @@ bool remove ) { assert(list != NULL); - assert(list->collection.cmpfunc != NULL); if (list->collection.size == 0) return 0; char *cur = ((const cx_array_list *) list)->data; // optimize with binary search, when sorted if (list->collection.sorted) { - size_t i = cx_array_binary_search( + size_t i = cx_array_binary_search_c( cur, list->collection.size, list->collection.elem_size, elem, - list->collection.cmpfunc + cx_list_compare_wrapper, + list ); if (remove && i < list->collection.size) { cx_arl_remove(list, i, 1, NULL); @@ -1058,7 +838,7 @@ // fallback: linear search for (size_t i = 0; i < list->collection.size; i++) { - if (0 == list->collection.cmpfunc(elem, cur)) { + if (0 == cx_list_compare_wrapper(elem, cur, list)) { if (remove) { cx_arl_remove(list, i, 1, NULL); } @@ -1069,12 +849,19 @@ return list->collection.size; } +// TODO: remove this hack once we have a solution for qsort() / qsort_s() +static _Thread_local CxList *cx_hack_for_qsort_list; +static int cx_hack_cmp_for_qsort(const void *l, const void *r) { + return cx_list_compare_wrapper(l, r, cx_hack_for_qsort_list); +} + static void cx_arl_sort(struct cx_list_s *list) { - assert(list->collection.cmpfunc != NULL); + // TODO: think about if we can somehow use qsort()_s + cx_hack_for_qsort_list = list; qsort(((cx_array_list *) list)->data, list->collection.size, list->collection.elem_size, - list->collection.cmpfunc + cx_hack_cmp_for_qsort ); } @@ -1082,12 +869,11 @@ const struct cx_list_s *list, const struct cx_list_s *other ) { - assert(list->collection.cmpfunc != NULL); if (list->collection.size == other->collection.size) { const char *left = ((const cx_array_list *) list)->data; const char *right = ((const cx_array_list *) other)->data; for (size_t i = 0; i < list->collection.size; i++) { - int d = list->collection.cmpfunc(left, right); + int d = cx_list_compare_wrapper(left, right, (void*)list); if (d != 0) { return d; } @@ -1200,7 +986,6 @@ CxList *cxArrayListCreate( const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size, size_t initial_capacity ) { @@ -1211,7 +996,7 @@ cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list)); if (list == NULL) return NULL; cx_list_init((CxList*)list, &cx_array_list_class, - allocator, comparator, elem_size); + allocator, elem_size); list->capacity = initial_capacity; // allocate the array after the real elem_size is known @@ -1222,8 +1007,5 @@ return NULL; } // LCOV_EXCL_STOP - // configure the reallocator - list->reallocator = cx_array_reallocator(allocator, NULL); - return (CxList *) list; }
--- a/ucx/buffer.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/buffer.c Thu Dec 18 17:50:15 2025 +0100 @@ -45,11 +45,11 @@ int cxBufferInit( CxBuffer *buffer, + const CxAllocator *allocator, void *space, size_t capacity, - const CxAllocator *allocator, int flags -) { + ) { if (allocator == NULL) { allocator = cxDefaultAllocator; } @@ -82,17 +82,17 @@ } CxBuffer *cxBufferCreate( + const CxAllocator *allocator, void *space, size_t capacity, - const CxAllocator *allocator, int flags -) { + ) { if (allocator == NULL) { allocator = cxDefaultAllocator; } CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); if (buf == NULL) return NULL; // LCOV_EXCL_LINE - if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { + if (0 == cxBufferInit(buf, allocator, space, capacity, flags)) { return buf; } else { // LCOV_EXCL_START
--- a/ucx/compare.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/compare.c Thu Dec 18 17:50:15 2025 +0100 @@ -29,6 +29,7 @@ #include "cx/compare.h" #include <math.h> +#include <string.h> int cx_vcmp_int(int a, int b) { if (a == b) { @@ -289,3 +290,21 @@ return p1 < p2 ? -1 : 1; } } + +int cx_acmp_memcmp( + const void *ptr1, + const void *ptr2, + void *size +) { + size_t n = *(size_t*)size; + return memcmp(ptr1, ptr2, n); +} + +int cx_acmp_wrap( + const void *ptr1, + const void *ptr2, + void *w +) { + cx_compare_func_wrapper *wrapper = w; + return wrapper->cmp(ptr1, ptr2); +}
--- a/ucx/cx/allocator.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/allocator.h Thu Dec 18 17:50:15 2025 +0100 @@ -142,7 +142,7 @@ * @return either the specified @p target, a pointer to the allocated memory, * or @c NULL, if any error occurred */ -typedef void*(cx_clone_func)(void *target, const void *source, +typedef void*(*cx_clone_func)(void *target, const void *source, const CxAllocator *allocator, void *data); /** @@ -157,13 +157,9 @@ CX_EXPORT unsigned long cx_system_page_size(void); /** - * Reallocate a previously allocated block and changes the pointer in-place, - * if necessary. + * Reallocate a previously allocated block. * - * @note This will use stdlib reallocate and @em not the cxDefaultAllocator. - * - * @par Error handling - * @c errno will be set by realloc() on failure. + * Internal function - do not use. * * @param mem pointer to the pointer to allocated block * @param n the new size in bytes @@ -175,16 +171,9 @@ CX_EXPORT int cx_reallocate_(void **mem, size_t n); /** - * Reallocate a previously allocated block and changes the pointer in-place, - * if necessary. - * - * The size is calculated by multiplying @p nemb and @p size. + * Reallocate a previously allocated block. * - * @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. + * Internal function - do not use. * * @param mem pointer to the pointer to allocated block * @param nmemb the number of elements @@ -272,6 +261,8 @@ * * @note Re-allocating a block allocated by a different allocator is undefined. * + * @attention This function is bug-prone. Consider using cxReallocate(). + * * @param allocator the allocator * @param mem pointer to the previously allocated block * @param n the new size in bytes @@ -282,8 +273,8 @@ CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n); /** - * Reallocate the previously allocated block in @p mem, making the new block - * @p n bytes long. + * Reallocate the previously allocated block in @p mem. + * * This function may return the same pointer passed to it if moving * the memory was not necessary. * @@ -293,6 +284,8 @@ * * @note Re-allocating a block allocated by a different allocator is undefined. * + * @attention This function is bug-prone. Consider using cxReallocateArray(). + * * @param allocator the allocator * @param mem pointer to the previously allocated block * @param nmemb the number of elements @@ -305,14 +298,9 @@ void *mem, size_t nmemb, size_t size); /** - * Reallocate a previously allocated block and changes the pointer in-place, - * if necessary. - * This function acts like cxRealloc() using the pointer pointed to by @p mem. + * Reallocate a previously allocated block. * - * @note Re-allocating a block allocated by a different allocator is undefined. - * - * @par Error handling - * @c errno will be set if the underlying realloc function does so. + * Internal function - do not use. * * @param allocator the allocator * @param mem pointer to the pointer to allocated block @@ -343,16 +331,9 @@ cxReallocate_(allocator, (void**)(mem), n) /** - * Reallocate a previously allocated block and changes the pointer in-place, - * if necessary. - * This function acts like cxReallocArray() using the pointer pointed to - * by @p mem. + * Reallocate a previously allocated block. * - * @note Re-allocating a block allocated by a different allocator is undefined. - * - * @par Error handling - * @c errno will be set, if the underlying realloc function does so or the - * multiplication of @p nmemb and @p size overflows. + * Internal function - do not use. * * @param allocator the allocator * @param mem pointer to the pointer to allocated block @@ -388,7 +369,7 @@ cxReallocateArray_(allocator, (void**) (mem), nmemb, size) /** - * Allocate @p nmemb elements of @p n bytes each, all initialized to zero. + * Allocate @p nmemb elements of @p size bytes each, all initialized to zero. * * @param allocator the allocator * @param nmemb the number of elements @@ -411,35 +392,123 @@ CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n); /** + * Allocate @p n bytes of memory. + * * Convenience macro that invokes cxMalloc() with the cxDefaultAllocator. + * + * @param n (@c size_t) the number of bytes + * @return (@c void*) a pointer to the allocated memory */ -#define cxMallocDefault(...) cxMalloc(cxDefaultAllocator, __VA_ARGS__) +#define cxMallocDefault(n) cxMalloc(cxDefaultAllocator, n) + /** + * Allocate @p n bytes of memory and sets every byte to zero. + * * Convenience macro that invokes cxZalloc() with the cxDefaultAllocator. + * + * @param n (@c size_t) the number of bytes + * @return (@c void*) a pointer to the allocated memory */ -#define cxZallocDefault(...) cxZalloc(cxDefaultAllocator, __VA_ARGS__) +#define cxZallocDefault(n) cxZalloc(cxDefaultAllocator, n) + /** + * Allocate @p nmemb elements of @p size bytes each, all initialized to zero. + * * Convenience macro that invokes cxCalloc() with the cxDefaultAllocator. + * + * @param nmemb (@c size_t) the number of elements + * @param size (@c size_t) the size of each element in bytes + * @return (@c void*) a pointer to the allocated memory */ -#define cxCallocDefault(...) cxCalloc(cxDefaultAllocator, __VA_ARGS__) +#define cxCallocDefault(nmemb, size) cxCalloc(cxDefaultAllocator, nmemb, size) + /** + * Reallocate the previously allocated block in @p mem. + * + * This function may return the same pointer passed to it if moving + * the memory was not necessary. + * * Convenience macro that invokes cxRealloc() with the cxDefaultAllocator. + * + * @attention This function is bug-prone. Consider using cxReallocateDefault(). + * + * @param mem (@c void*) pointer to the previously allocated block + * @param n (@c size_t) the new size in bytes + * @return (@c void*) a pointer to the reallocated memory */ -#define cxReallocDefault(...) cxRealloc(cxDefaultAllocator, __VA_ARGS__) +#define cxReallocDefault(mem, n) cxRealloc(cxDefaultAllocator, mem, n) + /** + * Reallocate a previously allocated block and changes the pointer in-place, + * if necessary. + * This function acts like cxRealloc() using the pointer pointed to by @p mem. + * * Convenience macro that invokes cxReallocate() with the cxDefaultAllocator. - */ -#define cxReallocateDefault(...) cxReallocate(cxDefaultAllocator, __VA_ARGS__) -/** - * Convenience macro that invokes cxReallocateArray() with the cxDefaultAllocator. + * + * @note Re-allocating a block allocated by a different allocator is undefined. + * + * @par Error handling + * @c errno will be set if the underlying realloc function does so. + * + * @param mem (@c void**) pointer to the pointer to allocated block + * @param n (@c size_t) the new size in bytes + * @retval zero success + * @retval non-zero failure */ -#define cxReallocateArrayDefault(...) cxReallocateArray(cxDefaultAllocator, __VA_ARGS__) +#define cxReallocateDefault(mem, n) cxReallocate(cxDefaultAllocator, mem, n) + /** - * Convenience macro that invokes cxReallocArray() with the cxDefaultAllocator. + * Reallocate a previously allocated block and changes the pointer in-place, + * if necessary. + * This function acts like cxReallocArray() using the pointer pointed to + * by @p mem. + * + * Convenience macro that invokes cxReallocateArray() with the cxDefaultAllocator. + * + * @note Re-allocating a block allocated by a different allocator is undefined. + * + * @par Error handling + * @c errno will be set, if the underlying realloc function does so or the + * multiplication of @p nmemb and @p size overflows. + * + * @param mem (@c void**) pointer to the pointer to allocated block + * @param nmemb (@c size_t) the number of elements + * @param size (@c size_t) the size of each element + * @retval zero success + * @retval non-zero failure */ -#define cxReallocArrayDefault(...) cxReallocArray(cxDefaultAllocator, __VA_ARGS__) +#define cxReallocateArrayDefault(mem, nmemb, size) \ + cxReallocateArray(cxDefaultAllocator, mem, nmemb, size) + /** + * Reallocate the previously allocated block in @p mem. + * + * Convenience macro that invokes cxReallocArray() with the cxDefaultAllocator. + * + * This function may return the same pointer passed to it if moving + * the memory was not necessary. + * + * The size is calculated by multiplying @p nemb and @p size. + * If that multiplication overflows, this function returns @c NULL, and @c errno + * will be set. + * + * @note Re-allocating a block allocated by a different allocator is undefined. + * + * @attention This function is bug-prone. Consider using cxReallocateArrayDefault(). + * + * @param mem (@c void*) pointer to the previously allocated block + * @param nmemb (@c size_t) the number of elements + * @param size (@c size_t) the size of each element + * @return (@c void*) a pointer to the reallocated memory + */ +#define cxReallocArrayDefault(mem, nmemb, size) cxReallocArray(cxDefaultAllocator, mem, nmemb, size) + +/** + * Free a block of memory. + * * Convenience function that invokes cxFree() with the cxDefaultAllocator. + * + * @param mem the memory to deallocate */ CX_EXPORT void cxFreeDefault(void *mem);
--- a/ucx/cx/array_list.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/array_list.h Thu Dec 18 17:50:15 2025 +0100 @@ -50,624 +50,581 @@ CX_EXPORT extern const unsigned cx_array_swap_sbo_size; /** - * Declares variables for an array that can be used with the convenience macros. - * - * @par Examples - * @code - * // integer array with at most 255 elements - * CX_ARRAY_DECLARE_SIZED(int, myarray, uint8_t) - * - * // array of MyObject* pointers where size and capacity are stored as unsigned int - * CX_ARRAY_DECLARE_SIZED(MyObject*, objects, unsigned int) - * - * // initializing code - * cx_array_initialize(myarray, 16); // reserve space for 16 - * cx_array_initialize(objects, 100); // reserve space for 100 - * @endcode + * Declares a typed array with size and capacity. * - * @param type the type of the data + * @param type the type of the elements * @param name the name of the array - * @param size_type the type of the size (should be uint8_t, uint16_t, uint32_t, or size_t) + */ +#define CX_ARRAY(type, name) \ + struct { \ + type *data; \ + size_t size; \ + size_t capacity; \ + } name + +/** + * Internal structure for arrays. * - * @see cx_array_initialize() - * @see cx_array_simple_add() - * @see cx_array_simple_copy() - * @see cx_array_simple_add_sorted() - * @see cx_array_simple_insert_sorted() + * A generalization of array structures declared with CX_ARRAY(). */ -#define CX_ARRAY_DECLARE_SIZED(type, name, size_type) \ - type * name; \ - /** Array size. */ size_type name##_size; \ - /** Array capacity. */ size_type name##_capacity +typedef struct cx_array_s { + /** The array data. */ + void *data; + /** The number of elements. */ + size_t size; + /** The maximum number of elements. */ + size_t capacity; +} CxArray; /** - * Declares variables for an array that can be used with the convenience macros. - * - * The size and capacity variables will have type @c size_t. - * Use #CX_ARRAY_DECLARE_SIZED() to specify a different type. + * Initializes an array by allocating memory. * - * @par Examples - * @code - * // int array - * CX_ARRAY_DECLARE(int, myarray) + * Internal function - do not use manually. * - * // initializing code - * cx_array_initialize(myarray, 32); // reserve space for 32 - * @endcode - * - * @param type the type of the data - * @param name the name of the array - * - * @see cx_array_initialize() - * @see cx_array_simple_add() - * @see cx_array_simple_copy() - * @see cx_array_simple_add_sorted() - * @see cx_array_simple_insert_sorted() + * @param allocator the allocator for the array + * @param array a pointer to the array structure + * @param elem_size size of one element + * @param capacity the initial maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -#define CX_ARRAY_DECLARE(type, name) CX_ARRAY_DECLARE_SIZED(type, name, size_t) +cx_attr_nonnull +CX_EXPORT int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); /** - * Initializes an array with the given capacity. + * Initializes an array by allocating memory. * - * The type of the capacity depends on the type used during declaration. + * The size is set to zero. + * + * @attention If the array was already initialized, this will leak memory. + * Use cx_array_reserve() to change the capacity of an initialized array. * - * @par Examples - * @code - * CX_ARRAY_DECLARE_SIZED(int, arr1, uint8_t) - * CX_ARRAY_DECLARE(int, arr2) // size and capacity are implicitly size_t + * @param allocator (@c CxAllocator*) the allocator for the array + * @param array the name of the array + * @param capacity (@c size_t) the initial maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + */ +#define cx_array_init_a(allocator, array, capacity) cx_array_init_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +/** + * Initializes an array by allocating memory. * - * // initializing code - * cx_array_initialize(arr1, 500); // error: maximum for uint8_t is 255 - * cx_array_initialize(arr2, 500); // OK - * @endcode + * The size is set to zero. * - * - * The memory for the array is allocated with the cxDefaultAllocator. + * @attention If the array was already initialized, this will leak memory. * * @param array the name of the array - * @param capacity the initial capacity - * @see cx_array_initialize_a() - * @see CX_ARRAY_DECLARE_SIZED() - * @see CX_ARRAY_DECLARE() + * @param capacity (@c size_t) the initial maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -#define cx_array_initialize(array, capacity) \ - array##_capacity = capacity; \ - array##_size = 0; \ - array = cxMallocDefault(sizeof(array[0]) * capacity) +#define cx_array_init(array, capacity) cx_array_init_a(cxDefaultAllocator, array, capacity) /** - * Initializes an array with the given capacity using the specified allocator. + * Initializes an array with fixed size memory. + * + * Internal function - do not use manually. * - * @par Example - * @code - * CX_ARRAY_DECLARE(int, myarray) + * @param array a pointer to the array structure + * @param data the fixed size array + * @param capacity the capacity of the fixed size array + * @param size the number of initialized elements in the fixed size array + */ +cx_attr_nonnull +CX_EXPORT void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size); + +/** + * Initializes an array with fixed size memory. * + * This is useful, for example, when you want to work with memory on the stack + * and only want to move to the heap when the stack memory is not enough. * - * const CxAllocator *al = // ... - * cx_array_initialize_a(al, myarray, 128); - * // ... - * cxFree(al, myarray); // remember to free with the same allocator - * @endcode + * With the @p num_initialized argument you can specify how many elements in the + * fixed size array are already correctly initialized, which determines the + * initial size of the array. + * + * The capacity is determined automatically by the compiler. + * + * @attention When you add elements to an array that was initialized with fixed + * size memory, you MUST check the capacity before adding the element and invoke + * cx_array_copy_to_new() when you intend to exceed the capacity. * - * @param allocator (@c CxAllocator*) the allocator - * @param array the name of the array - * @param capacity the initial capacity - * @see cx_array_initialize() - * @see CX_ARRAY_DECLARE_SIZED() - * @see CX_ARRAY_DECLARE() + * @attention When you pass a pointer to an array that does not have a fixed + * size, the behavior is unspecified. + * + * @param array the name of the array to initialize + * @param fixed_size_array (@c void*) the fixed size array + * @param num_initialized (@c size_t) the number of already initialized elements in the fixed size array + * @see cx_array_copy_to_new() */ -#define cx_array_initialize_a(allocator, array, capacity) \ - array##_capacity = capacity; \ - array##_size = 0; \ - array = cxMalloc(allocator, sizeof(array[0]) * capacity) +#define cx_array_init_fixed(array, fixed_size_array, num_initialized) \ + cx_array_init_fixed_((CxArray*)&(array), fixed_size_array, cx_nmemb(fixed_size_array), num_initialized) /** - * Defines a reallocation mechanism for arrays. - * You can create your own, use cx_array_reallocator(), or - * use the #cx_array_default_reallocator. + * Changes the capacity of an array. + * + * Internal function - do not use. + * + * @param allocator the allocator + * @param array a pointer to the array structure + * @param elem_size the size of one element + * @param capacity the new capacity + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -struct cx_array_reallocator_s { - /** - * Reallocates space for the given array. - * - * Implementations are not required to free the original array. - * This allows reallocation of static or stack memory by allocating heap memory - * and copying the array contents; namely when @c stack_ptr in this struct - * is not @c NULL and @p array equals @c stack_ptr. - * - * @param array the array to reallocate - * @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 - */ - void *(*realloc)( void *array, size_t old_capacity, size_t new_capacity, - size_t elem_size, struct cx_array_reallocator_s *alloc); - - /** - * The allocator that shall be used for the reallocations. - */ - const CxAllocator *allocator; - /** - * Optional pointer to stack memory - * if the array is originally located on the stack. - */ - const void *stack_ptr; -}; +cx_attr_nonnull +CX_EXPORT int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); /** - * Typedef for the array reallocator struct. + * Changes the capacity of an array. + * + * If required, the size is reduced to fit into the new capacity. + * + * @param allocator (@c CxAllocator*) the allocator for the array + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -typedef struct cx_array_reallocator_s CxArrayReallocator; - -/** - * A default array reallocator that is based on the cxDefaultAllocator. - */ -CX_EXPORT extern CxArrayReallocator *cx_array_default_reallocator; +#define cx_array_reserve_a(allocator, array, capacity) \ + cx_array_reserve_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) /** - * Creates a new array reallocator. - * - * When @p allocator is @c NULL, the cxDefaultAllocator will be used. + * Changes the capacity of an array. * - * When @p stack_ptr is not @c NULL, the reallocator is supposed to be used - * @em only for the specific array initially located at @p stack_ptr. - * When reallocation is needed, the reallocator checks if the array is - * still located at @p stack_ptr and copies the contents to the heap. + * If required, the size is reduced to fit into the new capacity. * - * @note Invoking this function with both arguments being @c NULL will return a - * reallocator that behaves like #cx_array_default_reallocator. + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + */ +#define cx_array_reserve(array, capacity) \ + cx_array_reserve_a(cxDefaultAllocator, array, capacity) + +/** + * Copies the array to a new memory region. * - * @param allocator the allocator this reallocator shall be based on - * @param stack_ptr the address of the array when the array is initially located - * on the stack or shall not reallocate in place - * @return an array reallocator + * Internal function - do not use. + * + * @param allocator the allocator for new new memory + * @param array a pointer to the array structure + * @param elem_size the size of one element + * @param capacity the new capacity + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -CX_EXPORT CxArrayReallocator cx_array_reallocator( - const struct cx_allocator_s *allocator, const void *stack_ptr); +cx_attr_nonnull +CX_EXPORT int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); /** - * Reserves memory for additional elements. - * - * This function checks if the @p capacity of the array is sufficient to hold - * at least @p size plus @p elem_count elements. If not, a reallocation is - * performed with the specified @p reallocator. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. + * Copies the array to a new memory region. * - * This function can be useful to replace subsequent calls to cx_array_copy() - * with one single cx_array_reserve() and then - after guaranteeing a - * sufficient capacity - use simple memmove() or memcpy(). + * This is useful when you have initialized the array with a fixed size memory + * using cx_array_init_fixed(), and now you want to increase the capacity. * - * The @p width in bytes refers to the size and capacity. - * Both must have the same width. - * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit - * architecture. If set to zero, the native word width is used. + * @attention When the original memory does not belong to stack memory, and + * you do not have another reference to this memory, it will leak. * - * @note This function will reserve the minimum required capacity to hold - * the additional elements and does not perform an overallocation. + * @param allocator (@c CxAllocator*) the allocator for the new memory + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + * @see cx_array_init_fixed() + */ +#define cx_array_copy_to_new_a(allocator, array, capacity) \ + cx_array_copy_to_new_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +/** + * Copies the array to a new memory region. * - * @param array a pointer to the target array - * @param size a pointer to the size of the array - * @param capacity a pointer to the capacity of the array - * @param width the width in bytes for the @p size and @p capacity or zero for default - * @param elem_size the size of one element - * @param elem_count the number of expected additional elements - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) - * @retval zero success - * @retval non-zero failure - * @see cx_array_reallocator() + * This is useful when you have initialized the array with a fixed size memory + * using cx_array_init_fixed(), and now you want to increase the capacity. + * + * @attention When the original memory does not belong to stack memory, and + * you do not have another reference to this memory, it will leak. + * + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + * @see cx_array_init_fixed() */ -cx_attr_nonnull_arg(1, 2, 3) -CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity, - unsigned width, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +#define cx_array_copy_to_new(array, capacity) \ + cx_array_copy_to_new_a(cxDefaultAllocator, array, capacity) /** - * Copies elements from one array to another. - * - * The elements are copied to the @p target array at the specified @p index, - * overwriting possible elements. The @p index does not need to be in range of - * the current array @p size. If the new index plus the number of elements added - * extends the array's size, the remaining @p capacity is used. + * Inserts data into an array. * - * If the @p capacity is also insufficient to hold the new data, a reallocation - * attempt is made with the specified @p reallocator. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. - * - * The @p width in bytes refers to the size and capacity. - * Both must have the same width. - * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit - * architecture. If set to zero, the native word width is used. - * - * @note When this function does reallocate the array, it may allocate more - * space than required to avoid further allocations in the near future. + * Internal function - do not use. * - * @param target a pointer to the target array - * @param size a pointer to the size of the target array - * @param capacity a pointer to the capacity of the target array - * @param width the width in bytes for the @p size and @p capacity or zero for default - * @param index the index where the copied elements shall be placed - * @param src the source array + * @param allocator the allocator to use for a possible reallocation + * @param array a pointer to the array structure * @param elem_size the size of one element - * @param elem_count the number of elements to copy - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) + * @param index the index where to insert the @p other data + * @param other a pointer to an array of data that shall be inserted + * @param n the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure - * @see cx_array_reallocator() - * @see cx_array_reserve() + * @retval non-zero a re-allocation was necessary but failed */ -cx_attr_nonnull_arg(1, 2, 3, 6) -CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width, - size_t index, const void *src, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +cx_attr_nonnull_arg(1, 2) +CX_EXPORT int cx_array_insert_(const CxAllocator *allocator, CxArray *array, + size_t elem_size, size_t index, const void *other, size_t n); /** - * Convenience macro that uses cx_array_copy() with a default layout and - * the specified reallocator. + * Appends an element to an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param index (@c size_t) the index where the copied elements shall be placed - * @param src (@c void*) the source array - * @param count (@c size_t) the number of elements to copy + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the element shall be added + * @param element (@c void*) a pointer to the element that shall be added * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_copy() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_copy_a(reallocator, array, index, src, count) \ - cx_array_copy((void**)&(array), &(array##_size), &(array##_capacity), \ - sizeof(array##_size), index, src, sizeof((array)[0]), count, \ - reallocator) +#define cx_array_add_a(allocator, array, element) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, element, 1) /** - * Convenience macro that uses cx_array_copy() with a default layout and - * the default reallocator. + * Appends an element to an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param index (@c size_t) the index where the copied elements shall be placed - * @param src (@c void*) the source array - * @param count (@c size_t) the number of elements to copy + * @param array the name of the array where the element shall be added + * @param element (@c void*) a pointer to the element that shall be added * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_copy_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_copy(array, index, src, count) \ - cx_array_simple_copy_a(NULL, array, index, src, count) +#define cx_array_add(array, element) \ + cx_array_add_a(cxDefaultAllocator, array, element) /** - * Convenience macro that uses cx_array_reserve() with a default layout and - * the specified reallocator. + * Inserts an element into an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param count (@c size_t) the number of expected @em additional elements + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the element shall be inserted + * @param index (@c size_t) the index where to insert the @p element + * @param element (@c void*) a pointer to the element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_reserve() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_reserve_a(reallocator, array, count) \ - cx_array_reserve((void**)&(array), &(array##_size), &(array##_capacity), \ - sizeof(array##_size), sizeof((array)[0]), count, \ - reallocator) +#define cx_array_insert_a(allocator, array, index, element) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, element, 1) /** - * Convenience macro that uses cx_array_reserve() with a default layout and - * the default reallocator. + * Inserts an element into an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param count (@c size_t) the number of expected additional elements + * @param array the name of the array where the element shall be inserted + * @param index (@c size_t) the index where to insert the @p element + * @param element (@c void*) a pointer to the element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_reserve_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_reserve(array, count) \ - cx_array_simple_reserve_a(NULL, array, count) +#define cx_array_insert(array, index, element) \ + cx_array_insert_a(cxDefaultAllocator, array, index, element) + +/** + * Inserts data into an array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param index (@c size_t) the index where to insert the @p other data + * @param other (@c void*) a pointer to an array of data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed + */ +#define cx_array_insert_array_a(allocator, array, index, other, n) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, other, n) /** - * Adds an element to an array with the possibility of allocating more space. - * - * The element @p elem is added to the end of the @p target array which contains - * @p size elements, already. The @p capacity must point to a variable denoting - * the current maximum number of elements the array can hold. + * Inserts data into an array. * - * If the capacity is insufficient to hold the new element, an attempt to - * increase the @p capacity is made and the new capacity is written back. - * - * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. - * It is important, however, that @p size and @p capacity are pointers to - * variables of the same type. + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * @param target (@c void**) a pointer to the target array - * @param size (@c SIZE_TYPE*) a pointer to the size of the target array - * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array - * @param elem_size (@c size_t) the size of one element - * @param elem (@c void*) a pointer to the element to add - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use + * @param array the name of the array where the elements shall be inserted + * @param index (@c size_t) the index where to insert the @p other data + * @param other (@c void*) a pointer to an array of data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \ - cx_array_copy((void**)(target), size, capacity, sizeof(*(size)), \ - *(size), elem, elem_size, 1, reallocator) +#define cx_array_insert_array(array, index, other, n) \ + cx_array_insert_array_a(cxDefaultAllocator, array, index, other, n) + +/** + * Appends data to an array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be added + * @param other (@c void*) a pointer to an array of data that shall be added + * @param n (@c size_t) the number of elements that shall be added + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed + */ +#define cx_array_add_array_a(allocator, array, other, n) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, other, n) /** - * Convenience macro that uses cx_array_add() with a default layout and - * the specified reallocator. + * Appends data to an array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) + * @param array the name of the array where the elements shall be added + * @param other (@c void*) a pointer to an array of data that shall be added + * @param n (@c size_t) the number of elements that shall be added * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_add_a(reallocator, array, elem) \ - cx_array_simple_copy_a(reallocator, array, array##_size, &(elem), 1) +#define cx_array_add_array(array, other, n) \ + cx_array_add_array_a(cxDefaultAllocator, array, other, n) /** - * Convenience macro that uses cx_array_add() with a default layout and - * the default reallocator. + * Inserts sorted data into a sorted array. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_a() - */ -#define cx_array_simple_add(array, elem) \ - cx_array_simple_add_a(cx_array_default_reallocator, array, elem) - -/** - * Inserts a sorted array into another sorted array. - * - * If either the target or the source array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * Internal function - do not use. * - * If the capacity is insufficient to hold the new data, a reallocation - * attempt is made. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. - * - * @param target a pointer to the target array - * @param size a pointer to the size of the target array - * @param capacity a pointer to the capacity of the target array - * @param cmp_func the compare function for the elements - * @param src the source array + * @param allocator the allocator to use for a possible reallocation + * @param array a pointer to the array structure * @param elem_size the size of one element - * @param elem_count the number of elements to insert - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) + * @param cmp_func + * @param sorted_data a pointer to an array of data that shall be inserted + * @param n the number of elements that shall be inserted + * @param allow_duplicates @c false if duplicates shall be skipped during insertion * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -cx_attr_nonnull_arg(1, 2, 3, 5) -CX_EXPORT int cx_array_insert_sorted(void **target, size_t *size, size_t *capacity, - cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +cx_attr_nonnull +CX_EXPORT int cx_array_insert_sorted_(const CxAllocator *allocator, CxArray *array, + size_t elem_size, cx_compare_func cmp_func, const void *sorted_data, size_t n, + bool allow_duplicates); /** * Inserts an element into a sorted array. * - * If the target array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * If the capacity is not enough to hold the new data, a reallocation - * attempt is made. - * - * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. - * It is important, however, that @p size and @p capacity are pointers to - * variables of the same type. + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param target (@c void**) a pointer to the target array - * @param size (@c SIZE_TYPE*) a pointer to the size of the target array - * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array - * @param elem_size (@c size_t) the size of one element - * @param elem (@c void*) a pointer to the element to add - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_add_sorted(target, size, capacity, elem_size, elem, cmp_func, reallocator) \ - cx_array_insert_sorted((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator) +#define cx_array_insert_sorted_a(allocator, array, cmp_func, element) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, element, 1, true) /** - * Convenience macro for cx_array_add_sorted() with a default - * layout and the specified reallocator. + * Inserts an element into a sorted array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_sorted() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_add_sorted_a(reallocator, array, elem, cmp_func) \ - cx_array_add_sorted(&array, &(array##_size), &(array##_capacity), \ - sizeof((array)[0]), &(elem), cmp_func, reallocator) +#define cx_array_insert_sorted(array, cmp_func, element) \ + cx_array_insert_sorted_a(cxDefaultAllocator, array, cmp_func, element) + +/** + * Inserts sorted data into a sorted array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed + */ +#define cx_array_insert_sorted_array_a(allocator, array, cmp_func, sorted_data, n) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, sorted_data, n, true) /** - * Convenience macro for cx_array_add_sorted() with a default - * layout and the default reallocator. + * Inserts sorted data into a sorted array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_sorted_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_add_sorted(array, elem, cmp_func) \ - cx_array_simple_add_sorted_a(NULL, array, elem, cmp_func) +#define cx_array_insert_sorted_array(array, cmp_func, sorted_data, n) \ + cx_array_insert_sorted_array_a(cxDefaultAllocator, array, cmp_func, sorted_data, n) /** - * Convenience macro for cx_array_insert_sorted() with a default - * layout and the specified reallocator. + * Inserts an element into a sorted array if it is not already contained. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. + * + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_sorted() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_insert_sorted_a(reallocator, array, src, n, cmp_func) \ - cx_array_insert_sorted((void**)(&array), &(array##_size), &(array##_capacity), \ - cmp_func, src, sizeof((array)[0]), n, reallocator) +#define cx_array_insert_unique_a(allocator, array, cmp_func, element) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, element, 1, false) /** - * Convenience macro for cx_array_insert_sorted() with a default - * layout and the default reallocator. + * Inserts an element into a sorted array if it is not already contained. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_sorted_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_insert_sorted(array, src, n, cmp_func) \ - cx_array_simple_insert_sorted_a(NULL, array, src, n, cmp_func) - +#define cx_array_insert_unique(array, cmp_func, element) \ + cx_array_insert_unique_a(cxDefaultAllocator, array, cmp_func, element) /** - * Inserts a sorted array into another sorted array, avoiding duplicates. - * - * If either the target or the source array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * Inserts sorted data into a sorted array, skipping duplicates. * - * If the capacity is insufficient to hold the new data, a reallocation - * attempt is made. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param target a pointer to the target array - * @param size a pointer to the size of the target array - * @param capacity a pointer to the capacity of the target array - * @param cmp_func the compare function for the elements - * @param src the source array - * @param elem_size the size of one element - * @param elem_count the number of elements to insert - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -cx_attr_nonnull_arg(1, 2, 3, 5) -CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capacity, - cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +#define cx_array_insert_unique_array_a(allocator, array, cmp_func, sorted_data, n) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, sorted_data, n, false) /** - * Inserts an element into a sorted array if it does not exist. - * - * If the target array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * Inserts sorted data into a sorted array, skipping duplicates. * - * If the capacity is insufficient to hold the new data, a reallocation - * attempt is made. + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. - * It is important, however, that @p size and @p capacity are pointers to - * variables of the same type. + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param target (@c void**) a pointer to the target array - * @param size (@c SIZE_TYPE*) a pointer to the size of the target array - * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array - * @param elem_size (@c size_t) the size of one element - * @param elem (@c void*) a pointer to the element to add - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @retval zero success (also when the element was already present) - * @retval non-zero failure + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_add_unique(target, size, capacity, elem_size, elem, cmp_func, reallocator) \ - cx_array_insert_unique((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator) +#define cx_array_insert_unique_array(array, cmp_func, sorted_data, n) \ + cx_array_insert_unique_array_a(cxDefaultAllocator, array, cmp_func, sorted_data, n) + +/** + * Creates an iterator over the elements of an array. + * + * Internal function - do not use. + * + * @param array a pointer to the array structure + * @param elem_size the size of one element + * @return an iterator over the elements + */ +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT CxIterator cx_array_iterator_(CxArray *array, size_t elem_size); /** - * Convenience macro for cx_array_add_unique() with a default - * layout and the specified reallocator. + * Creates an iterator over the elements of an array. + * + * The iterator will yield pointers to the elements. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_unique() + * @param array the name of the array + * @return an iterator over the elements + * @see cx_array_iterator_ptr() */ -#define cx_array_simple_add_unique_a(reallocator, array, elem, cmp_func) \ - cx_array_add_unique(&array, &(array##_size), &(array##_capacity), \ - sizeof((array)[0]), &(elem), cmp_func, reallocator) +#define cx_array_iterator(array) \ + cx_array_iterator_((CxArray*)&(array), sizeof((array).data[0])) /** - * Convenience macro for cx_array_add_unique() with a default - * layout and the default reallocator. + * Creates an iterator over the elements of an array containing pointers. + * + * Internal function - do not use. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_unique_a() + * @param array the name of the array + * @return an iterator over the elements */ -#define cx_array_simple_add_unique(array, elem, cmp_func) \ - cx_array_simple_add_unique_a(NULL, array, elem, cmp_func) +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT CxIterator cx_array_iterator_ptr_(CxArray *array); /** - * Convenience macro for cx_array_insert_unique() with a default - * layout and the specified reallocator. + * Creates an iterator over the elements of an array containing pointers. + * + * The iterator will yield the elements themselves, which are supposed to + * be pointers. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_unique() + * @param array the name of the array + * @return an iterator over the elements + * @see cx_array_iterator() */ -#define cx_array_simple_insert_unique_a(reallocator, array, src, n, cmp_func) \ - cx_array_insert_unique((void**)(&array), &(array##_size), &(array##_capacity), \ - cmp_func, src, sizeof((array)[0]), n, reallocator) +#define cx_array_iterator_ptr(array) \ + cx_array_iterator_ptr_((CxArray*)&(array)) /** - * Convenience macro for cx_array_insert_unique() with a default - * layout and the default reallocator. + * Deallocates an array. + * + * Internal function - do not use. + * + * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array + * @param array a pointer to the array structure + */ +cx_attr_nonnull +CX_EXPORT void cx_array_free_(const CxAllocator *allocator, CxArray *array); + +/** + * Deallocates an array. + * + * The structure is reset to zero and can be re-initialized with + * cx_array_inita(). * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_unique_a() + * @param array the name of the array */ -#define cx_array_simple_insert_unique(array, src, n, cmp_func) \ - cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func) +#define cx_array_free(array) cx_array_free_(cxDefaultAllocator, (CxArray*)&(array)) + +/** + * Deallocates an array. + * + * The structure is reset to zero and can be re-initialized with + * cx_array_init_a(). + * + * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array + * @param array the name of the array + */ +#define cx_array_free_a(allocator, array) cx_array_free_(allocator, (CxArray*)&(array)) + /** * Searches the largest lower bound in a sorted array. @@ -750,6 +707,91 @@ CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size, size_t elem_size, const void *elem, cx_compare_func cmp_func); + +/** + * Searches the largest lower bound in a sorted array. + * + * In other words, this function returns the index of the largest element + * in @p arr that is less or equal to @p elem with respect to @p cmp_func. + * When no such element exists, @p size is returned. + * + * When such an element exists more than once, the largest index of all those + * elements is returned. + * + * If @p elem is contained in the array, this is identical to + * #cx_array_binary_search(). + * + * If the array is not sorted with respect to the @p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @param context the context for the compare function + * @return the index of the largest lower bound, or @p size + * @see cx_array_binary_search_sup() + * @see cx_array_binary_search() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_array_binary_search_inf_c(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context); + +/** + * Searches an item in a sorted array. + * + * When such an element exists more than once, the largest index of all those + * elements is returned. + * + * If the array is not sorted with respect to the @p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @param context the context for the compare function + * @return the index of the element in the array, or @p size if the element + * cannot be found + * @see cx_array_binary_search_inf() + * @see cx_array_binary_search_sup() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_array_binary_search_c(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context); + +/** + * Searches the smallest upper bound in a sorted array. + * + * In other words, this function returns the index of the smallest element + * in @p arr that is greater or equal to @p elem with respect to @p cmp_func. + * When no such element exists, @p size is returned. + * + * When such an element exists more than once, the smallest index of all those + * elements is returned. + * + * If @p elem is contained in the array, this is identical to + * #cx_array_binary_search(). + * + * If the array is not sorted with respect to the @p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @param context the context for the compare function + * @return the index of the smallest upper bound, or @p size + * @see cx_array_binary_search_inf() + * @see cx_array_binary_search() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_array_binary_search_sup_c(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context); + /** * Swaps two array elements. * @@ -766,13 +808,10 @@ * * 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 allocator the allocator for allocating the list memory * (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) * @param elem_size the size of each element in bytes * @param initial_capacity the initial number of elements the array can store * @return the created list @@ -781,25 +820,7 @@ cx_attr_malloc cx_attr_dealloc(cxListFree, 1) CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size, size_t initial_capacity); - -/** - * Allocates an array list for storing elements with @p elem_size bytes each. - * - * The list will use the cxDefaultAllocator and @em NO compare function. - * If you want to call functions that need a compare function, you have to - * set it immediately after creation or use cxArrayListCreate(). - * - * 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(). - * - * @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 - * @return the created list - */ -#define cxArrayListCreateSimple(elem_size, initial_capacity) \ - cxArrayListCreate(NULL, NULL, elem_size, initial_capacity) + size_t elem_size, size_t initial_capacity); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/buffer.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/buffer.h Thu Dec 18 17:50:15 2025 +0100 @@ -168,18 +168,18 @@ * space will be leaking after the copy-on-write operation. * * @param buffer the buffer to initialize + * @param allocator the allocator this buffer shall use for automatic + * memory management + * (if @c NULL, the cxDefaultAllocator will be used) * @param space pointer to the memory area, or @c NULL to allocate * new memory * @param capacity the capacity of the buffer - * @param allocator the allocator this buffer shall use for automatic - * memory management - * (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 */ cx_attr_nonnull_arg(1) -CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity, - const CxAllocator *allocator, int flags); +CX_EXPORT int cxBufferInit(CxBuffer *buffer, const CxAllocator *allocator, + void *space, size_t capacity, int flags); /** * Destroys the buffer contents. @@ -219,18 +219,18 @@ * Then this function will allocate the space and enforce * the #CX_BUFFER_FREE_CONTENTS flag. * + * @param allocator the allocator to use for allocating the structure and the automatic + * memory management within the buffer + * (if @c NULL, the cxDefaultAllocator will be used) * @param space pointer to the memory area, or @c NULL to allocate * new memory * @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, 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 */ cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard -CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity, - const CxAllocator *allocator, int flags); +CX_EXPORT CxBuffer *cxBufferCreate(const CxAllocator *allocator, void *space, + size_t capacity, int flags); /** * Shifts the contents of the buffer by the given offset.
--- a/ucx/cx/collection.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/collection.h Thu Dec 18 17:50:15 2025 +0100 @@ -58,10 +58,6 @@ */ const CxAllocator *allocator; /** - * The comparator function for the elements. - */ - cx_compare_func cmpfunc; - /** * The size of each element. */ size_t elem_size; @@ -70,6 +66,19 @@ */ size_t size; /** + * A two-argument comparator function for the elements. + */ + cx_compare_func simple_cmp; + /** + * A three-argument comparator function for the elements. + * If specified, this function has precedence over the @c simple_cmp function. + */ + cx_compare_func2 advanced_cmp; + /** + * A pointer to custom data for the @c advanced_cmp function + */ + void *cmp_data; + /** * An optional simple destructor for the collection's elements. * * @attention Read the documentation of the particular collection implementation @@ -139,6 +148,25 @@ */ #define cxCollectionStoresPointers(c) ((c)->collection.store_pointer) + +/** + * Convenience macro for adding indirection to an element if the collection is storing pointers. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param elem the pointer that shall be taken the address from, if the collection is storing pointers + * @return if the collection is storing pointers, takes the address of @p elem, otherwise returns @p elem + */ +#define cx_ref(c, elem) (cxCollectionStoresPointers(c) ? ((void*)&(elem)) : (elem)) + +/** + * Convenience macro for dereferencing an element if the collection is storing pointers. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param elem a pointer to the collection element + * @return if the collection is storing pointers, dereferences @p elem, otherwise returns @p elem + */ +#define cx_deref(c, elem) (cxCollectionStoresPointers(c) ? *((void**)(elem)) : (elem)) + /** * Indicates whether the collection can guarantee that the stored elements are currently sorted. * @@ -154,29 +182,89 @@ #define cxCollectionSorted(c) ((c)->collection.sorted || (c)->collection.size == 0) /** - * Sets the compare function for a collection. + * Sets a simple compare function for a collection. + * + * Erases a possible advanced compare function. + * If you want to set both, because you want to access the simple function + * in your advanced function, you must set the simple function first. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param func (@c cx_compare_func) the compare function + */ +#define cxSetCompareFunc(c, func) \ + (c)->collection.simple_cmp = (cx_compare_func)(func); \ + (c)->collection.advanced_cmp = NULL + +/** + * Sets an advanced compare function that supports custom data for a collection. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param func (@c cx_compare_func2) the compare function + * @param data (@c void*) the pointer to custom data that is passed to the compare function + */ +#define cxSetAdvancedCompareFunc(c, func, data) \ + (c)->collection.advanced_cmp = (cx_compare_func2) func; \ + (c)->collection.destructor_data = data + +/** + * Invokes the simple comparator function for two elements. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. * * @param c a pointer to a struct that contains #CX_COLLECTION_BASE - * @param func (@c cx_compare_func) the compare function that shall be used by @c c + * @param left (@c void*) pointer to data + * @param right (@c void*) pointer to data + */ +#define cx_invoke_simple_compare_func(c, left, right) \ + (c)->collection.simple_cmp(left, right) + +/** + * Invokes the advanced comparator function for two elements. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param left (@c void*) pointer to data + * @param right (@c void*) pointer to data */ -#define cxCollectionCompareFunc(c, func) (c)->collection.cmpfunc = (func) +#define cx_invoke_advanced_compare_func(c, left, right) \ + (c)->collection.advanced_cmp(left, right, (c)->collection.cmp_data) + + +/** + * Invokes the configured comparator function for two elements. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param left (@c void*) pointer to data + * @param right (@c void*) pointer to data + */ +#define cx_invoke_compare_func(c, left, right) \ + (((c)->collection.advanced_cmp) ? \ + cx_invoke_advanced_compare_func(c,left,right) : \ + cx_invoke_simple_compare_func(c,left,right)) /** * Sets a simple destructor function for this collection. * * @param c a pointer to a struct that contains #CX_COLLECTION_BASE - * @param destr the destructor function + * @param destr (@c cx_destructor_func) the destructor function */ -#define cxDefineDestructor(c, destr) \ +#define cxSetDestructor(c, destr) \ (c)->collection.simple_destructor = (cx_destructor_func) destr /** - * Sets a simple destructor function for this collection. + * Sets an advanced destructor function for this collection. * * @param c a pointer to a struct that contains #CX_COLLECTION_BASE - * @param destr the destructor function + * @param destr (@c cx_destructor_func2) the destructor function + * @param data (@c void*) the additional data the advanced destructor is invoked with */ -#define cxDefineAdvancedDestructor(c, destr, data) \ +#define cxSetAdvancedDestructor(c, destr, data) \ (c)->collection.advanced_destructor = (cx_destructor_func2) destr; \ (c)->collection.destructor_data = data
--- a/ucx/cx/compare.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/compare.h Thu Dec 18 17:50:15 2025 +0100 @@ -57,6 +57,13 @@ typedef int (*cx_compare_func)(const void *left, const void *right); /** + * A comparator function comparing two arbitrary values. + * + * Functions with this signature allow specifying a pointer to custom data. + */ +typedef int (*cx_compare_func2)(const void *left, const void *right, void *data); + +/** * Compares two integers of type int. * * @note the parameters deliberately have type @c void* to be @@ -527,6 +534,41 @@ cx_attr_nonnull cx_attr_nodiscard CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2); +/** + * A @c cx_compare_func2 compatible wrapper for @c memcmp(). + * + * @param ptr1 pointer one + * @param ptr2 pointer two + * @param n (@c size_t*) a pointer to the length + * @return the result of @c memcmp() + */ +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_acmp_memcmp(const void *ptr1, const void *ptr2, void *n); + +/** Wraps a compare function for cx_acmp_wrap. */ +typedef struct { + /** The wrapped compare function */ + cx_compare_func cmp; +} cx_compare_func_wrapper; + +/** + * A @c cx_compare_func2 wrapper for a @c cx_compare_func(). + * + * This is not strictly compatible with a @c cx_compare_func2 because + * ISO C does not define conversions between function and object pointers. + * + * But it works on all tested platforms to cast a pointer to this function to + * a @c cx_compare_func2. + * + * @param ptr1 pointer one + * @param ptr2 pointer two + * @param cmp_wrapper a pointer to a @c cx_compare_func_wrapper + * @return the result of the invoked compare function + * @see cx_compare_func_wrapper_s + */ +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_acmp_wrap(const void *ptr1, const void *ptr2, void* cmp_wrapper); + #ifdef __cplusplus } // extern "C" #endif
--- a/ucx/cx/hash_map.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/hash_map.h Thu Dec 18 17:50:15 2025 +0100 @@ -88,22 +88,6 @@ size_t itemsize, size_t buckets); /** - * Creates a new hash map with a default number of buckets. - * - * If @p elem_size is #CX_STORE_POINTERS, the created map stores pointers instead of - * copies of the added elements. - * - * @note Iterators provided by this hash map implementation provide the remove operation. - * The index value of an iterator is incremented when the iterator advanced without - * removing an entry. - * In other words, when the iterator is finished, @c index==size . - * - * @param itemsize (@c size_t) the size of one element - * @return (@c CxMap*) a pointer to the new hash map - */ -#define cxHashMapCreateSimple(itemsize) cxHashMapCreate(NULL, itemsize, 0) - -/** * Increases the number of buckets, if necessary. * * The load threshold is @c 0.75*buckets. If the element count exceeds the load
--- a/ucx/cx/iterator.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/iterator.h Thu Dec 18 17:50:15 2025 +0100 @@ -214,26 +214,15 @@ * use cxIteratorPtr() to create an iterator which directly * yields the stored pointers. * - * While the iterator is in use, the array may only be altered by removing - * elements through #cxIteratorFlagRemoval(). Every other change to the array - * will bring this iterator to an undefined state. - * - * When @p remove_keeps_order is set to @c false, removing an element will only - * move the last element to the position of the removed element, instead of - * moving all subsequent elements by one. Usually, when the order of elements is - * not important, this parameter should be set to @c false. - * * @param array a pointer to the array (can be @c NULL) * @param elem_size the size of one array element * @param elem_count the number of elements in the array - * @param remove_keeps_order @c true if the order of elements must be preserved - * when removing an element * @return an iterator for the specified array * @see cxIteratorPtr() */ cx_attr_nodiscard CX_EXPORT CxIterator cxIterator(const void *array, - size_t elem_size, size_t elem_count, bool remove_keeps_order); + size_t elem_size, size_t elem_count); /** * Creates an iterator for the specified plain pointer array. @@ -243,25 +232,13 @@ * hand, an iterator created with cxIterator() would return the * addresses of those pointers within the array). * - * While the iterator is in use, the array may only be altered by removing - * elements through #cxIteratorFlagRemoval(). Every other change to the array - * will bring this iterator to an undefined state. - * - * When @p remove_keeps_order is set to @c false, removing an element will only - * move the last element to the position of the removed element, instead of - * moving all subsequent elements by one. Usually, when the order of elements is - * not important, this parameter should be set to @c false. - * * @param array a pointer to the array (can be @c NULL) * @param elem_count the number of elements in the array - * @param remove_keeps_order @c true if the order of elements must be preserved - * when removing an element * @return an iterator for the specified array * @see cxIterator() */ cx_attr_nodiscard -CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count, - bool remove_keeps_order); +CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/json.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/json.h Thu Dec 18 17:50:15 2025 +0100 @@ -43,8 +43,6 @@ #include "array_list.h" #include "map.h" -#include <string.h> - #ifdef __cplusplus extern "C" { #endif @@ -185,10 +183,6 @@ typedef struct cx_json_value_s CxJsonValue; /** - * Type alias for the JSON array struct. - */ -typedef struct cx_json_array_s CxJsonArray; -/** * Type alias for the map representing a JSON object. * The map contains pointers of type @c CxJsonValue. */ @@ -211,16 +205,6 @@ typedef enum cx_json_literal CxJsonLiteral; /** - * JSON array structure. - */ -struct cx_json_array_s { - /** - * The array data. - */ - CX_ARRAY_DECLARE(CxJsonValue*, data); -}; - -/** * Structure for a JSON value. */ struct cx_json_value_s { @@ -243,7 +227,7 @@ /** * The array data if the type is #CX_JSON_ARRAY. */ - CxJsonArray array; + CX_ARRAY(CxJsonValue*, array); /** * The object data if the type is #CX_JSON_OBJECT. */ @@ -298,6 +282,7 @@ * The allocator used for produced JSON values. */ const CxAllocator *allocator; + /** * The input buffer. */ @@ -327,12 +312,12 @@ /** * State stack. */ - CX_ARRAY_DECLARE_SIZED(int, states, unsigned); + CX_ARRAY(int, states); /** * Value buffer stack. */ - CX_ARRAY_DECLARE_SIZED(CxJsonValue*, vbuf, unsigned); + CX_ARRAY(CxJsonValue*, vbuf); /** * Internally reserved memory for the state stack. @@ -477,28 +462,28 @@ /** * Produces a compact string representation of the specified JSON value. * + * @param allocator the allocator for the string * @param value the JSON value - * @param allocator the allocator for the string * @return the produced string * @see cxJsonWrite() * @see cxJsonWriterCompact() * @see cxJsonToPrettyString() */ -cx_attr_nonnull_arg(1) -CX_EXPORT cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator); +cx_attr_nonnull_arg(2) +CX_EXPORT cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value); /** * Produces a pretty string representation of the specified JSON value. * + * @param allocator the allocator for the string * @param value the JSON value - * @param allocator the allocator for the string * @return the produced string * @see cxJsonWrite() * @see cxJsonWriterPretty() * @see cxJsonToString() */ -cx_attr_nonnull_arg(1) -CX_EXPORT cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator); +cx_attr_nonnull_arg(2) +CX_EXPORT cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value); /** * Initializes the JSON interface. @@ -628,13 +613,16 @@ /** * Creates a new (empty) JSON array. * + * Optionally, this function already allocates memory with the given capacity. + * * @param allocator the allocator to use + * @param capacity optional capacity or zero if it's unknown how many elements the array will have * @return the new JSON array or @c NULL if allocation fails * @see cxJsonObjPutArr() * @see cxJsonArrAddValues() */ cx_attr_nodiscard -CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); +CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity); /** * Creates a new JSON number value. @@ -840,23 +828,25 @@ * * @param obj the target JSON object * @param name the name of the new value + * @param capacity optional initial capacity * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateArr() */ cx_attr_nonnull -CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name); +CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity); /** * Creates a new JSON array and adds it to an object. * * @param obj (@c CxJsonValue*) the target JSON object * @param name (any string) the name of the new value + * @param capacity (@c size_t) optional initial capacity * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateArr() */ -#define cxJsonObjPutArr(obj, name) cx_json_obj_put_arr(obj, cx_strcast(name)) +#define cxJsonObjPutArr(obj, name, capacity) cx_json_obj_put_arr(obj, cx_strcast(name), capacity) /** * Creates a new JSON number and adds it to an object. @@ -1238,7 +1228,7 @@ */ cx_attr_nonnull CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) { - return value->array.data_size; + return value->array.size; } /** @@ -1366,6 +1356,66 @@ */ #define cxJsonObjRemove(value, name) cx_json_obj_remove(value, cx_strcast(name)) +/** + * Performs a deep comparison of two JSON values. + * + * The order of object members is ignored during comparison. + * + * @param json the JSON value + * @param other the other JSON value that the JSON value is compared to + * @retval zero the values are equal (except for ordering of object members) + * @retval non-zero the values differ + */ +CX_EXPORT int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other); + + +/** + * Creates a deep copy of the specified JSON value. + * + * If you need a @c cx_clone_func compatible version, see cxJsonCloneFunc(). + * + * @note when you are cloning @c NULL, you will get a pointer to a statically + * allocated value which represents nothing. + * + * @param value the value to be cloned + * @param allocator the allocator for the new value + * @return the new value or @c NULL if any allocation was unsuccessful + * @see cxJsonCloneFunc() + */ +cx_attr_nodiscard +CX_EXPORT CxJsonValue* cxJsonClone(const CxJsonValue* value, + const CxAllocator* allocator); + + +/** + * A @c cx_clone_func compatible version of cxJsonClone(). + * + * Internal function - use cxJsonCloneFunc() to get a properly casted function pointer. + * + * @param target the target memory or @c NULL + * @param source the value to be cloned + * @param allocator the allocator for the new value + * @param data unused + * @return the new value or @c NULL if any allocation was unsuccessful + * @see cxJsonClone() + */ +cx_attr_nodiscard +CX_EXPORT CxJsonValue* cx_json_clone_func( + CxJsonValue* target, const CxJsonValue* source, + const CxAllocator* allocator, void *data); + +/** + * A @c cx_clone_func compatible version of cxJsonClone(). + * + * @param target (@c CxJsonValue*) the target memory or @c NULL + * @param source (@c CxJsonValue*) the value to be cloned + * @param allocator (@c CxAllocator*) the allocator for the new value + * @param data unused + * @return the new value or @c NULL if any allocation was unsuccessful + * @see cxJsonClone() + */ +#define cxJsonCloneFunc ((cx_clone_func) cx_json_clone_func) + #ifdef __cplusplus } #endif
--- a/ucx/cx/kv_list.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/kv_list.h Thu Dec 18 17:50:15 2025 +0100 @@ -49,7 +49,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(). * * After creating the list, it can also be used as a map after converting the pointer * to a CxMap pointer with cxKvListAsMap(). @@ -58,9 +58,6 @@ * * @param allocator the allocator for allocating the list nodes * (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) * @param elem_size the size of each element in bytes * @return the created list * @see cxKvListAsMap() @@ -68,14 +65,14 @@ */ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size); + size_t elem_size); /** * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. * * 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(). * * This function creates the list with cxKvListCreate() and immediately applies * cxKvListAsMap(). If you want to use the returned object as a list, you can call @@ -83,9 +80,6 @@ * * @param allocator the allocator for allocating the list nodes * (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) * @param elem_size the size of each element in bytes * @return the created list wrapped into the CxMap interface * @see cxKvListAsMap() @@ -93,52 +87,7 @@ */ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1) CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size); - -/** - * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. - * - * The list will use cxDefaultAllocator and no comparator function. If you want - * to call functions that need a comparator, you must either set one immediately - * after list creation or use cxKvListCreate(). - * - * 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(). - * - * After creating the list, it can also be used as a map after converting the pointer - * to a CxMap pointer with cxKvListAsMap(). - * When you want to use the list interface again, you can also convert the map pointer back - * with cxKvListAsList(). - * - * @param elem_size (@c size_t) the size of each element in bytes - * @return (@c CxList*) the created list - * @see cxKvListAsMap() - * @see cxKvListAsList() - */ -#define cxKvListCreateSimple(elem_size) cxKvListCreate(NULL, NULL, elem_size) - -/** - * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. - * - * The list will use cxDefaultAllocator and no comparator function. If you want - * to call functions that need a comparator, you must either set one immediately - * after list creation or use cxKvListCreate(). - * - * 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(). - * - * This macro behaves as if the list was created with cxKvListCreateSimple() and - * immediately followed up by cxKvListAsMap(). - * If you want to use the returned object as a list, you can call cxKvListAsList() later. - * - * @param elem_size (@c size_t) the size of each element in bytes - * @return (@c CxMap*) the created list wrapped into the CxMap interface - * @see cxKvListAsMap() - * @see cxKvListAsList() - */ -#define cxKvListCreateAsMapSimple(elem_size) cxKvListCreateAsMap(NULL, NULL, elem_size) + size_t elem_size); /** * Converts a map pointer belonging to a key-value-List back to the original list pointer. @@ -152,7 +101,7 @@ /** * Converts a map pointer belonging to a key-value-List back to the original list pointer. * - * @param list a list created by cxKvListCreate() or cxKvListCreateSimple() + * @param list a list created by cxKvListCreate() * @return a map pointer that lets you use the list as if it was a map */ cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
--- a/ucx/cx/linked_list.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/linked_list.h Thu Dec 18 17:50:15 2025 +0100 @@ -87,36 +87,16 @@ * * 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 allocator the allocator for allocating the list nodes * (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) * @param elem_size the size of each element in bytes * @return the created list */ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size); - -/** - * Allocates a linked list for storing elements with @p elem_size bytes each. - * - * The list will use cxDefaultAllocator and no comparator function. If you want - * to call functions that need a comparator, you must either set one immediately - * after list creation or use cxLinkedListCreate(). - * - * 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(). - * - * @param elem_size (@c size_t) the size of each element in bytes - * @return (@c CxList*) the created list - */ -#define cxLinkedListCreateSimple(elem_size) \ - cxLinkedListCreate(NULL, NULL, elem_size) + size_t elem_size); /** * Instructs the linked list to reserve extra data in each node. @@ -162,16 +142,34 @@ * @param start a pointer to the start node * @param loc_advance the location of the pointer to advance * @param loc_data the location of the @c data pointer within your node struct - * @param cmp_func a compare function to compare @p elem against the node data * @param elem a pointer to the element to find * @param found_index an optional pointer where the index of the found node * (given that @p start has index 0) is stored + * @param cmp_func a compare function to compare @p elem against the node data * @return a pointer to the found node or @c NULL if no matching node was found */ -cx_attr_nonnull_arg(1, 4, 5) +cx_attr_nonnull_arg(1, 4, 6) CX_EXPORT void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance, - ptrdiff_t loc_data, cx_compare_func cmp_func, const void *elem, - size_t *found_index); + ptrdiff_t loc_data, const void *elem, size_t *found_index, + cx_compare_func cmp_func); + +/** + * Finds the node containing an element within a linked list. + * + * @param start a pointer to the start node + * @param loc_advance the location of the pointer to advance + * @param loc_data the location of the @c data pointer within your node struct + * @param elem a pointer to the element to find + * @param found_index an optional pointer where the index of the found node + * (given that @p start has index 0) is stored + * @param cmp_func a compare function to compare @p elem against the node data + * @param context additional context for the compare function + * @return a pointer to the found node or @c NULL if no matching node was found + */ +cx_attr_nonnull_arg(1, 4, 6) +CX_EXPORT void *cx_linked_list_find_c(const void *start, ptrdiff_t loc_advance, + ptrdiff_t loc_data, const void *elem, size_t *found_index, + cx_compare_func2 cmp_func, void *context); /** * Finds the first node in a linked list. @@ -454,16 +452,6 @@ /** * Sorts a linked list based on a comparison function. * - * This function can work with linked lists of the following structure: - * @code - * typedef struct node node; - * struct node { - * node* prev; - * node* next; - * my_payload data; - * } - * @endcode - * * @note This is a recursive function with at most logarithmic recursion depth. * * @param begin a pointer to the beginning node pointer (required) @@ -477,6 +465,23 @@ CX_EXPORT void cx_linked_list_sort(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func cmp_func); +/** + * Sorts a linked list based on a comparison function. + * + * @note This is a recursive function with at most logarithmic recursion depth. + * + * @param begin a pointer to the beginning node pointer (required) + * @param end a pointer to the end node pointer (optional) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if not present) + * @param loc_next the location of a @c next pointer within your node struct (required) + * @param loc_data the location of the @c data pointer within your node struct + * @param cmp_func the compare function defining the sort order + * @param context additional context for the compare function + */ +cx_attr_nonnull_arg(1, 6) +CX_EXPORT void cx_linked_list_sort_c(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context); + /** * Compares two lists element wise. @@ -496,6 +501,23 @@ ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func); /** + * Compares two lists element wise. + * + * @attention Both lists must have the same structure. + * + * @param begin_left the beginning of the left list (@c NULL denotes an empty list) + * @param begin_right the beginning of the right list (@c NULL denotes an empty list) + * @param loc_advance the location of the pointer to advance + * @param loc_data the location of the @c data pointer within your node struct + * @param cmp_func the function to compare the elements + * @return the first non-zero result of invoking @p cmp_func or: negative if the left list is smaller than the + * right list, positive if the left list is larger than the right list, zero if both lists are equal. + */ +cx_attr_nonnull_arg(5) +CX_EXPORT int cx_linked_list_compare_c(const void *begin_left, const void *begin_right, + ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context); + +/** * Reverses the order of the nodes in a linked list. * * @param begin a pointer to the beginning node pointer (required)
--- a/ucx/cx/list.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/list.h Thu Dec 18 17:50:15 2025 +0100 @@ -60,10 +60,6 @@ * The list class definition. */ const cx_list_class *cl; - /** - * The actual implementation in case the list class is delegating. - */ - const cx_list_class *climpl; }; /** @@ -302,19 +298,18 @@ * @code * CxList *myCustomListCreate( * const CxAllocator *allocator, - * cx_compare_func comparator, * size_t elem_size * ) { * if (allocator == NULL) { * allocator = cxDefaultAllocator; * } * - * MyCustomList *list = cxCalloc(allocator, 1, sizeof(MyCustomList)); + * MyCustomList *list = cxZalloc(allocator, sizeof(MyCustomList)); * if (list == NULL) return NULL; * * // initialize * cx_list_init((CxList*)list, &my_custom_list_class, - * allocator, comparator, elem_size); + * allocator, elem_size); * * // ... some more custom stuff ... * @@ -325,13 +320,24 @@ * @param list the list to initialize * @param cl the list class * @param allocator the allocator for the elements - * @param comparator a compare function for the elements * @param elem_size the size of one element */ cx_attr_nonnull_arg(1, 2, 3) CX_EXPORT void cx_list_init(struct cx_list_s *list, struct cx_list_class_s *cl, const struct cx_allocator_s *allocator, - cx_compare_func comparator, size_t elem_size); + size_t elem_size); + +/** + * A @c cx_compare_func2 compatible wrapper for the compare functions of a list. + * + * @param left first element + * @param right second element + * @param list the list which is comparing the elements + * @return the comparison result + */ +cx_attr_nonnull +CX_EXPORT int cx_list_compare_wrapper( + const void *left, const void *right, void *list); /** * Returns the number of elements currently stored in the list. @@ -984,7 +990,7 @@ * @param data optional additional data that is passed to the clone function * @retval zero when all elements were successfully cloned * @retval non-zero when an allocation error occurred - * @see cxListCloneSimple() + * @see cxListCloneShallow() */ cx_attr_nonnull_arg(1, 2, 3) CX_EXPORT int cxListClone(CxList *dst, const CxList *src, @@ -1007,7 +1013,7 @@ * @param data optional additional data that is passed to the clone function * @retval zero when the elements were successfully cloned * @retval non-zero when an allocation error occurred - * @see cxListDifferenceSimple() + * @see cxListDifferenceShallow() */ cx_attr_nonnull_arg(1, 2, 3, 4) CX_EXPORT int cxListDifference(CxList *dst, @@ -1031,7 +1037,7 @@ * @param data optional additional data that is passed to the clone function * @retval zero when the elements were successfully cloned * @retval non-zero when an allocation error occurred - * @see cxListIntersectionSimple() + * @see cxListIntersectionShallow() */ cx_attr_nonnull_arg(1, 2, 3, 4) CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *other, @@ -1056,7 +1062,7 @@ * @param data optional additional data that is passed to the clone function * @retval zero when the elements were successfully cloned * @retval non-zero when an allocation error occurred - * @see cxListUnionSimple() + * @see cxListUnionShallow() */ cx_attr_nonnull_arg(1, 2, 3, 4) CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other, @@ -1082,7 +1088,7 @@ * @see cxListClone() */ cx_attr_nonnull -CX_EXPORT int cxListCloneSimple(CxList *dst, const CxList *src); +CX_EXPORT int cxListCloneShallow(CxList *dst, const CxList *src); /** * Clones elements from a list only if they are not present in another list. @@ -1104,7 +1110,7 @@ * @see cxListDifference() */ cx_attr_nonnull -CX_EXPORT int cxListDifferenceSimple(CxList *dst, +CX_EXPORT int cxListDifferenceShallow(CxList *dst, const CxList *minuend, const CxList *subtrahend); /** @@ -1127,7 +1133,7 @@ * @see cxListIntersection() */ cx_attr_nonnull -CX_EXPORT int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other); +CX_EXPORT int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other); /** * Performs a deep clone of one list into another, skipping duplicates. @@ -1151,7 +1157,7 @@ * @see cxListUnion() */ cx_attr_nonnull -CX_EXPORT int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other); +CX_EXPORT int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other); /** * Asks the list to reserve enough memory for a given total number of elements.
--- a/ucx/cx/map.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/map.h Thu Dec 18 17:50:15 2025 +0100 @@ -183,9 +183,9 @@ * 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. + * Returns a map entry where the pointer to the key is @c NULL if allocation fails. */ - void *(*put)(CxMap *map, CxHashKey key, void *value); + CxMapEntry (*put)(CxMap *map, CxHashKey key, void *value); /** * Returns an element. @@ -357,8 +357,6 @@ * @param map the map * @param key 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 * @see cxMapEmplace() */ cx_attr_nonnull @@ -379,8 +377,6 @@ * @param map (@c CxMap*) the map * @param key (any supported key type) 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 * @see CX_HASH_KEY() */ #define cxMapEmplace(map, key) cx_map_emplace(map, CX_HASH_KEY(key)) @@ -627,7 +623,7 @@ * @see cxMapClone() */ cx_attr_nonnull -CX_EXPORT int cxMapCloneSimple(CxMap *dst, const CxMap *src); +CX_EXPORT int cxMapCloneShallow(CxMap *dst, const CxMap *src); /** * Clones entries of a map if their key is not present in another map. @@ -642,7 +638,7 @@ * @retval non-zero when an allocation error occurred */ cx_attr_nonnull -CX_EXPORT int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend); +CX_EXPORT int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend); /** * Clones entries of a map if their key is not present in a list. @@ -663,7 +659,7 @@ * @see cxMapListDifference() */ cx_attr_nonnull -CX_EXPORT int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys); +CX_EXPORT int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys); /** @@ -679,7 +675,7 @@ * @retval non-zero when an allocation error occurred */ cx_attr_nonnull -CX_EXPORT int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other); +CX_EXPORT int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other); /** * Clones entries of a map only if their key is present in a list. @@ -699,7 +695,7 @@ * @retval non-zero when an allocation error occurred */ cx_attr_nonnull -CX_EXPORT int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys); +CX_EXPORT int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys); /** * Clones entries into a map if their key does not exist yet. @@ -718,7 +714,23 @@ * @retval non-zero when an allocation error occurred */ cx_attr_nonnull -CX_EXPORT int cxMapUnionSimple(CxMap *dst, const CxMap *src); +CX_EXPORT int cxMapUnionShallow(CxMap *dst, const CxMap *src); + + +/** + * Compares the entries of two maps. + * + * @param map the map + * @param other the other map that the first map is compared to + * @retval zero when both maps have the same key sets + * and the values are pairwise equivalent + * @retval negative when the first @p map has fewer keys than the @p other map + * @retval positive when the first @p map has more keys than the @p other map + * @retval non-zero (unspecified whether positive or negative) when the size + * of both maps is equal but a key or a value is different + */ +cx_attr_nonnull +CX_EXPORT int cxMapCompare(const CxMap *map, const CxMap *other); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/properties.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/properties.h Thu Dec 18 17:50:15 2025 +0100 @@ -73,7 +73,7 @@ */ char comment3; - /* + /** * The character, when appearing at the end of a line, continues that line. * This is '\' by default. */ @@ -336,15 +336,15 @@ /** * Internal function - use cxPropertiesLoad() instead. * - * @param config the parser config * @param allocator the allocator for the values * @param filename the file name * @param target the target map + * @param config the parser config * @return status code */ -cx_attr_nonnull_arg(4) -CX_EXPORT CxPropertiesStatus cx_properties_load(CxPropertiesConfig config, - const CxAllocator *allocator, cxstring filename, CxMap *target); +cx_attr_nonnull_arg(3) +CX_EXPORT CxPropertiesStatus cx_properties_load(const CxAllocator *allocator, + cxstring filename, CxMap *target, CxPropertiesConfig config); /** * Loads properties from a file and inserts them into a map. @@ -357,10 +357,10 @@ * @note When the parser finds an error, all successfully parsed keys before the error * are added to the map nonetheless. * - * @param config the parser config * @param allocator the allocator for the values that will be stored in the map * @param filename (any string) the absolute or relative path to the file * @param target (@c CxMap*) the map where the properties shall be added + * @param config the parser config * @retval CX_PROPERTIES_NO_ERROR (zero) at least one key/value pair was found * @retval CX_PROPERTIES_NO_DATA the file is syntactically OK, but does not contain properties * @retval CX_PROPERTIES_INCOMPLETE_DATA unexpected end of file @@ -371,8 +371,8 @@ * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed * @see cxPropertiesLoadDefault() */ -#define cxPropertiesLoad(config, allocator, filename, target) \ - cx_properties_load(config, allocator, cx_strcast(filename), target) +#define cxPropertiesLoad(allocator, filename, target, config) \ + cx_properties_load(allocator, cx_strcast(filename), target, config) /** * Loads properties from a file and inserts them into a map with a default config. @@ -399,7 +399,7 @@ * @see cxPropertiesLoad() */ #define cxPropertiesLoadDefault(allocator, filename, target) \ - cx_properties_load(cx_properties_config_default, allocator, cx_strcast(filename), target) + cx_properties_load(allocator, cx_strcast(filename), target, cx_properties_config_default) #ifdef __cplusplus
--- a/ucx/cx/streams.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/streams.h Thu Dec 18 17:50:15 2025 +0100 @@ -36,8 +36,8 @@ * @copyright 2-Clause BSD License */ -#ifndef UCX_STREAMS_H -#define UCX_STREAMS_H +#ifndef Ucx_strEAMS_H +#define Ucx_strEAMS_H #include "common.h" @@ -117,4 +117,4 @@ } #endif -#endif // UCX_STREAMS_H +#endif // Ucx_strEAMS_H
--- a/ucx/cx/string.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/string.h Thu Dec 18 17:50:15 2025 +0100 @@ -33,12 +33,14 @@ * @copyright 2-Clause BSD License */ -#ifndef UCX_STRING_H -#define UCX_STRING_H +#ifndef Ucx_strING_H +#define Ucx_strING_H #include "common.h" #include "allocator.h" +#include <string.h> + /** Expands a UCX string as printf arguments. */ #define CX_SFMT(s) (int) (s).length, (s).ptr @@ -137,30 +139,6 @@ */ typedef struct cx_strtok_ctx_s CxStrtokCtx; -#ifdef __cplusplus -extern "C" { - -/** - * A literal initializer for an UCX string structure. - * - * @param literal the string literal - */ -#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1} - -#else // __cplusplus - -/** - * A literal initializer for an UCX string structure. - * - * The argument MUST be a string (const char*) @em literal. - * - * @param literal the string literal - */ -#define CX_STR(literal) ((cxstring){literal, sizeof(literal) - 1}) - -#endif - - /** * Wraps a mutable string that must be zero-terminated. * @@ -179,7 +157,12 @@ * @see cx_mutstrn() */ cx_attr_nodiscard cx_attr_cstr_arg(1) -CX_EXPORT cxmutstr cx_mutstr(char *cstring); +CX_INLINE cxmutstr cx_mutstr(char *cstring) { + cxmutstr str; + str.ptr = cstring; + str.length = cstring == NULL ? 0 : strlen(cstring); + return str; +} /** * Wraps a string that does not need to be zero-terminated. @@ -198,7 +181,12 @@ * @see cx_mutstr() */ cx_attr_nodiscard cx_attr_access_rw(1, 2) -CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length); +CX_INLINE cxmutstr cx_mutstrn(char *cstring, size_t length) { + cxmutstr str; + str.ptr = cstring; + str.length = length; + return str; +} /** * Wraps a string that must be zero-terminated. @@ -218,7 +206,12 @@ * @see cx_strn() */ cx_attr_nodiscard cx_attr_cstr_arg(1) -CX_EXPORT cxstring cx_str(const char *cstring); +CX_INLINE cxstring cx_str(const char *cstring) { + cxstring str; + str.ptr = cstring; + str.length = cstring == NULL ? 0 : strlen(cstring); + return str; +} /** @@ -238,10 +231,14 @@ * @see cx_str() */ cx_attr_nodiscard cx_attr_access_r(1, 2) -CX_EXPORT cxstring cx_strn(const char *cstring, size_t length); +CX_INLINE cxstring cx_strn(const char *cstring, size_t length) { + cxstring str; + str.ptr = cstring; + str.length = length; + return str; +} #ifdef __cplusplus -} // extern "C" cx_attr_nodiscard CX_CPPDECL cxstring cx_strcast(cxmutstr str) { return cx_strn(str.ptr, str.length); @@ -2087,4 +2084,4 @@ } // extern "C" #endif -#endif //UCX_STRING_H +#endif //Ucx_strING_H
--- a/ucx/hash_map.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/hash_map.c Thu Dec 18 17:50:15 2025 +0100 @@ -78,7 +78,7 @@ cxFree(map->collection.allocator, map); } -static void *cx_hash_map_put( +static CxMapEntry cx_hash_map_put( CxMap *map, CxHashKey key, void *value @@ -117,7 +117,7 @@ allocator, sizeof(struct cx_hash_map_element_s) + map->collection.elem_size ); - if (e == NULL) return NULL; // LCOV_EXCL_LINE + if (e == NULL) return (CxMapEntry){NULL, NULL}; // LCOV_EXCL_LINE // write the value if (value == NULL) { @@ -132,7 +132,7 @@ void *kd = cxMalloc(allocator, key.len); if (kd == NULL) { // LCOV_EXCL_START cxFree(allocator, e); - return NULL; + return (CxMapEntry){NULL, NULL}; } // LCOV_EXCL_STOP memcpy(kd, key.data, key.len); e->key.data = kd; @@ -152,8 +152,8 @@ map->collection.size++; } - // return pointer to the element - return elm->data; + // return the entry + return (CxMapEntry){&elm->key, elm->data}; } static void cx_hash_map_unlink( @@ -414,8 +414,7 @@ buckets = 16; } - struct cx_hash_map_s *map = cxCalloc(allocator, 1, - sizeof(struct cx_hash_map_s)); + struct cx_hash_map_s *map = cxZalloc(allocator, sizeof(struct cx_hash_map_s)); if (map == NULL) return NULL; // initialize hash map members @@ -433,9 +432,12 @@ if (itemsize > 0) { map->base.collection.elem_size = itemsize; + map->base.collection.advanced_cmp = cx_acmp_memcmp; + map->base.collection.cmp_data = &map->base.collection.elem_size; } else { map->base.collection.elem_size = sizeof(void *); map->base.collection.store_pointer = true; + map->base.collection.simple_cmp = cx_cmp_ptr; } return (CxMap *) map;
--- a/ucx/iterator.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/iterator.c Thu Dec 18 17:50:15 2025 +0100 @@ -29,6 +29,7 @@ #include "cx/iterator.h" #include <string.h> +#include <assert.h> static bool cx_iter_valid(const void *it) { const struct cx_iterator_s *iter = it; @@ -45,51 +46,14 @@ return *(void**)iter->elem_handle; } -static void cx_iter_next_fast(void *it) { +static void cx_iter_next(void *it) { struct cx_iterator_s *iter = it; - if (iter->base.remove) { - iter->base.remove = false; - iter->elem_count--; - // only move the last element when we are not currently aiming - // at the last element already - if (iter->index < iter->elem_count) { - void *last = ((char *) iter->src_handle) - + iter->elem_count * iter->elem_size; - memcpy(iter->elem_handle, last, iter->elem_size); - } - } else { - iter->index++; - iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size; - } + assert(!iter->base.remove); + iter->index++; + iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size; } -static void cx_iter_next_slow(void *it) { - struct cx_iterator_s *iter = it; - if (iter->base.remove) { - iter->base.remove = false; - iter->elem_count--; - - // number of elements to move - size_t remaining = iter->elem_count - iter->index; - if (remaining > 0) { - memmove( - iter->elem_handle, - ((char *) iter->elem_handle) + iter->elem_size, - remaining * iter->elem_size - ); - } - } else { - iter->index++; - iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size; - } -} - -CxIterator cxIterator( - const void *array, - size_t elem_size, - size_t elem_count, - bool remove_keeps_order -) { +CxIterator cxIterator(const void *array, size_t elem_size, size_t elem_count) { CxIterator iter; iter.index = 0; @@ -99,19 +63,18 @@ iter.elem_count = array == NULL ? 0 : elem_count; iter.base.valid = cx_iter_valid; iter.base.current = cx_iter_current; - iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast; + iter.base.next = cx_iter_next; + iter.base.valid_impl = NULL; + iter.base.current_impl = NULL; + iter.base.next_impl = NULL; iter.base.remove = false; iter.base.allow_remove = true; return iter; } -CxIterator cxIteratorPtr( - const void *array, - size_t elem_count, - bool remove_keeps_order -) { - CxIterator iter = cxIterator(array, sizeof(void*), elem_count, remove_keeps_order); +CxIterator cxIteratorPtr(const void *array, size_t elem_count) { + CxIterator iter = cxIterator(array, sizeof(void*), elem_count); iter.base.current = cx_iter_current_ptr; return iter; }
--- a/ucx/json.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/json.c Thu Dec 18 17:50:15 2025 +0100 @@ -111,8 +111,8 @@ ttype = CX_JSON_TOKEN_STRING; } else { cxstring s = cx_strcast(str); - if (!cx_strcmp(s, CX_STR("true")) || !cx_strcmp(s, CX_STR("false")) - || !cx_strcmp(s, CX_STR("null"))) { + if (!cx_strcmp(s, "true") || !cx_strcmp(s, "false") + || !cx_strcmp(s, "null")) { ttype = CX_JSON_TOKEN_LITERAL; } else { ttype = token_numbertype(str.ptr, str.length); @@ -410,7 +410,7 @@ size_t capa = str.length + 32; char *space = cxMallocDefault(capa); if (space == NULL) return cx_mutstrn(NULL, 0); - cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND); cxBufferWrite(str.ptr, 1, i, &buf); all_printable = false; } @@ -453,10 +453,10 @@ } static CxJsonObject json_create_object_map(const CxAllocator *allocator) { - // TODO: we might want to add a comparator that is sorting the elements by their key - CxMap *map = cxKvListCreateAsMap(allocator, NULL, CX_STORE_POINTERS); + CxMap *map = cxKvListCreateAsMap(allocator, CX_STORE_POINTERS); if (map == NULL) return NULL; // LCOV_EXCL_LINE - cxDefineDestructor(map, cxJsonValueFree); + cxSetCompareFunc(map, cxJsonCompare); + cxSetDestructor(map, cxJsonValueFree); return map; } @@ -472,20 +472,20 @@ v->type = type; v->allocator = json->allocator; if (type == CX_JSON_ARRAY) { - cx_array_initialize_a(json->allocator, v->array.data, 16); - if (v->array.data == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE + if (cx_array_init_a(json->allocator, v->array, 16)) { + goto create_json_value_exit_error; // LCOV_EXCL_LINE + } } else if (type == CX_JSON_OBJECT) { v->object = json_create_object_map(json->allocator); if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE } // add the new value to a possible parent - if (json->vbuf_size > 0) { - CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; + if (json->vbuf.size > 0) { + CxJsonValue *parent = json->vbuf.data[json->vbuf.size - 1]; assert(parent != NULL); if (parent->type == CX_JSON_ARRAY) { - CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); - if (cx_array_simple_add_a(&value_realloc, parent->array.data, v)) { + if (cx_array_add_a(json->allocator, parent->array, &v)) { goto create_json_value_exit_error; // LCOV_EXCL_LINE } } else if (parent->type == CX_JSON_OBJECT) { @@ -503,10 +503,19 @@ // add the new value to the stack, if it is an array or object if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { - CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); - if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { - goto create_json_value_exit_error; // LCOV_EXCL_LINE + if (json->vbuf.size >= json->vbuf.capacity) { + int alloc_error; + if (json->vbuf.data == json->vbuf_internal) { + alloc_error = cx_array_copy_to_new(json->vbuf, json->vbuf.size+1); + } else { + alloc_error = cx_array_reserve(json->vbuf, json->vbuf.size+1); + } + if (alloc_error) { + goto create_json_value_exit_error; // LCOV_EXCL_LINE + } } + json->vbuf.data[json->vbuf.size] = v; + json->vbuf.size++; } // if currently no value is parsed, this is now the value of interest @@ -540,22 +549,18 @@ memset(json, 0, sizeof(CxJson)); json->allocator = allocator; - json->states = json->states_internal; - json->states_capacity = cx_nmemb(json->states_internal); - json->states[0] = JP_STATE_VALUE_BEGIN; - json->states_size = 1; - - json->vbuf = json->vbuf_internal; - json->vbuf_capacity = cx_nmemb(json->vbuf_internal); + cx_array_init_fixed(json->states, json->states_internal, 1); + json->states.data[0] = JP_STATE_VALUE_BEGIN; + cx_array_init_fixed(json->vbuf, json->vbuf_internal, 0); } void cxJsonDestroy(CxJson *json) { cxBufferDestroy(&json->buffer); - if (json->states != json->states_internal) { - cxFreeDefault(json->states); + if (json->states.data != json->states_internal) { + cx_array_free(json->states); } - if (json->vbuf != json->vbuf_internal) { - cxFreeDefault(json->vbuf); + if (json->vbuf.data != json->vbuf_internal) { + cx_array_free(json->vbuf); } cxJsonValueFree(json->parsed); json->parsed = NULL; @@ -574,8 +579,8 @@ // reinitialize the buffer cxBufferDestroy(&json->buffer); if (buf == NULL) buf = ""; // buffer must not be initialized with NULL - cxBufferInit(&json->buffer, (char*) buf, size, - NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE); + cxBufferInit(&json->buffer, NULL, (char*) buf, + size, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE); json->buffer.size = size; return 0; } else { @@ -584,9 +589,9 @@ } static void json_add_state(CxJson *json, int state) { - // we have guaranteed the necessary space with cx_array_simple_reserve() + // we have guaranteed the necessary space // therefore, we can safely add the state in the simplest way possible - json->states[json->states_size++] = state; + json->states.data[json->states.size++] = state; } #define return_rec(code) \ @@ -607,13 +612,21 @@ } // pop the current state - assert(json->states_size > 0); - int state = json->states[--json->states_size]; + assert(json->states.size > 0); + int state = json->states.data[--json->states.size]; - // guarantee that at least two more states fit on the stack - CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); - if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { - return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE + // guarantee that at least two more states fit into the array + const size_t required_states_depth = json->states.size + 2; + if (required_states_depth >= json->states.capacity) { + int alloc_error; + if (json->states.data == json->states_internal) { + alloc_error = cx_array_copy_to_new(json->states, required_states_depth); + } else { + alloc_error = cx_array_reserve(json->states, required_states_depth); + } + if (alloc_error) { + return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE + } } @@ -645,6 +658,16 @@ json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); return_rec(CX_JSON_NO_ERROR); } + case CX_JSON_TOKEN_END_ARRAY: { + if (state == JP_STATE_VALUE_BEGIN_AR) { + // discard the array from the value buffer + json->vbuf.size--; + json->states.size--; + return_rec(CX_JSON_NO_ERROR); + } else { + return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); + } + } case CX_JSON_TOKEN_STRING: { if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE @@ -678,9 +701,9 @@ if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE } - if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { + if (0 == cx_strcmp(token.content, "true")) { vbuf->literal = CX_JSON_TRUE; - } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { + } else if (0 == cx_strcmp(token.content, "false")) { vbuf->literal = CX_JSON_FALSE; } else { vbuf->literal = CX_JSON_NULL; @@ -698,7 +721,7 @@ return_rec(CX_JSON_NO_ERROR); } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { // discard the array from the value buffer - json->vbuf_size--; + json->vbuf.size--; return_rec(CX_JSON_NO_ERROR); } else { return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); @@ -706,7 +729,7 @@ } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { // discard the obj from the value buffer - json->vbuf_size--; + json->vbuf.size--; return_rec(CX_JSON_NO_ERROR); } else { // expect string @@ -721,7 +744,7 @@ } assert(json->uncompleted_member_name.ptr == NULL); json->uncompleted_member_name = name; - assert(json->vbuf_size > 0); + assert(json->vbuf.size > 0); // next state json_add_state(json, JP_STATE_OBJ_COLON); @@ -742,7 +765,7 @@ return_rec(CX_JSON_NO_ERROR); } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { // discard the obj from the value buffer - json->vbuf_size--; + json->vbuf.size--; return_rec(CX_JSON_NO_ERROR); } else { return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); @@ -767,17 +790,17 @@ CxJsonStatus result; do { result = json_parse(json); - if (result == CX_JSON_NO_ERROR && json->states_size == 1) { + if (result == CX_JSON_NO_ERROR && json->states.size == 1) { // final state reached - assert(json->states[0] == JP_STATE_VALUE_END); - assert(json->vbuf_size == 0); + assert(json->states.data[0] == JP_STATE_VALUE_END); + assert(json->vbuf.size == 0); // write output value *value = json->parsed; json->parsed = NULL; // re-initialize state machine - json->states[0] = JP_STATE_VALUE_BEGIN; + json->states.data[0] = JP_STATE_VALUE_BEGIN; return CX_JSON_NO_ERROR; } @@ -786,7 +809,7 @@ // the parser might think there is no data // but when we did not reach the final state, // we know that there must be more to come - if (result == CX_JSON_NO_DATA && json->states_size > 1) { + if (result == CX_JSON_NO_DATA && json->states.size > 1) { return CX_JSON_INCOMPLETE_DATA; } @@ -832,11 +855,10 @@ break; } case CX_JSON_ARRAY: { - CxJsonArray array = value->array; - for (size_t i = 0; i < array.data_size; i++) { - cxJsonValueFree(array.data[i]); + for (size_t i = 0; i < value->array.size; i++) { + cxJsonValueFree(value->array.data[i]); } - cxFree(value->allocator, array.data); + cx_array_free_a(value->allocator, value->array); break; } case CX_JSON_STRING: { @@ -865,14 +887,23 @@ return v; } -CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { +CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity) { if (allocator == NULL) allocator = cxDefaultAllocator; CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); if (v == NULL) return NULL; v->allocator = allocator; v->type = CX_JSON_ARRAY; - cx_array_initialize_a(allocator, v->array.data, 16); - if (v->array.data == NULL) { cxFree(allocator, v); return NULL; } + if (capacity > 0) { + if (cx_array_init_a(allocator, v->array, capacity)) { + // LCOV_EXCL_START + cxFree(allocator, v); + return NULL; + // LCOV_EXCL_STOP + } + } else { + v->array.data = NULL; + v->array.size = v->array.capacity = 0; + } return v; } @@ -990,13 +1021,8 @@ } int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { - CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); assert(arr->type == CX_JSON_ARRAY); - return cx_array_simple_copy_a(&value_realloc, - arr->array.data, - arr->array.data_size, - val, count - ); + return cx_array_add_array_a(arr->allocator, arr->array, val, count); } int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { @@ -1010,8 +1036,8 @@ return v; } -CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name) { - CxJsonValue* v = cxJsonCreateArr(obj->allocator); +CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity) { + CxJsonValue* v = cxJsonCreateArr(obj->allocator, capacity); if (v == NULL) return NULL; if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } return v; @@ -1046,23 +1072,23 @@ } CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { - if (index >= value->array.data_size) { + if (index >= value->array.size) { return &cx_json_value_nothing; } return value->array.data[index]; } CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { - if (index >= value->array.data_size) { + if (index >= value->array.size) { return NULL; } CxJsonValue *ret = value->array.data[index]; // TODO: replace with a low level cx_array_remove() - size_t count = value->array.data_size - index - 1; + size_t count = value->array.size - index - 1; if (count > 0) { memmove(value->array.data + index, value->array.data + index + 1, count * sizeof(CxJsonValue*)); } - value->array.data_size--; + value->array.size--; return ret; } @@ -1095,11 +1121,7 @@ } CxIterator cxJsonArrIter(const CxJsonValue *value) { - return cxIteratorPtr( - value->array.data, - value->array.data_size, - true // arrays need to keep order - ); + return cx_array_iterator_ptr(value->array); } CxMapIterator cxJsonObjIter(const CxJsonValue *value) { @@ -1413,8 +1435,8 @@ static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) { if (allocator == NULL) allocator = cxDefaultAllocator; CxBuffer buffer; - if (cxBufferInit(&buffer, NULL, 128, allocator, - CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) { + if (cxBufferInit(&buffer, allocator, NULL, 128, + CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) { return (cxmutstr){NULL, 0}; } if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0) @@ -1432,12 +1454,121 @@ } -cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator) { +cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value) { CxJsonWriter writer = cxJsonWriterCompact(); return cx_json_to_string(value, allocator, &writer); } -cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator) { +cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value) { CxJsonWriter writer = cxJsonWriterPretty(true); return cx_json_to_string(value, allocator, &writer); } + +int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other) { + if (json == other) return 0; + if (json == NULL || other == NULL) return -1; + if (json->type != other->type) { + if (!cxJsonIsNumber(json)) return -1; + if (!cxJsonIsNumber(other)) return -1; + } + switch (json->type) { + case CX_JSON_NOTHING: + return 0; + case CX_JSON_OBJECT: + return cxMapCompare(json->object, other->object); + case CX_JSON_ARRAY: + if (json->array.size != other->array.size) return -1; + for (size_t i = 0; i < json->array.size; i++) { + const int d = cxJsonCompare(json->array.data[i], other->array.data[i]); + if (d != 0) return d; + } + return 0; + case CX_JSON_STRING: + return cx_strcmp(json->string, other->string); + case CX_JSON_INTEGER: + if (other->type == CX_JSON_INTEGER) { + return cx_vcmp_int64(json->integer, other->integer); + } else { + return cx_vcmp_double(cxJsonAsDouble(json), other->number); + } + case CX_JSON_NUMBER: + return cx_vcmp_double(json->number, cxJsonAsDouble(other)); + case CX_JSON_LITERAL: + return json->literal == other->literal ? 0 : -1; + default: + // LCOV_EXCL_START + // unreachable + assert(false); + return -1; + // LCOV_EXCL_STOP + } +} + +CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) { + return cx_json_clone_func(NULL, value, allocator, NULL); +} + +CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source, + const CxAllocator* allocator, cx_attr_unused void *data) { + if (source == NULL || source->type == CX_JSON_NOTHING) { + return &cx_json_value_nothing; + } + if (allocator == NULL) allocator = cxDefaultAllocator; + +#define return_value(v) { \ + CxJsonValue *ret = v; \ + if (target == NULL) { \ + return ret; \ + } else { \ + *target = *ret; \ + cxFree(allocator, ret); \ + return target; \ + } \ + } + + switch (source->type) { + case CX_JSON_OBJECT: { + CxJsonValue *obj = cxJsonCreateObj(allocator); + if (obj == NULL) return NULL; // LCOV_EXCL_LINE + if (cxMapClone(obj->object, source->object, cxJsonCloneFunc, allocator, NULL)) { + // LCOV_EXCL_START + cxJsonValueFree(obj); + return NULL; + // LCOV_EXCL_STOP + } + return_value(obj); + } + case CX_JSON_ARRAY: { + const size_t elem_count = source->array.size; + CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count); + if (arr == NULL) return NULL; // LCOV_EXCL_LINE + arr->array.size = elem_count; + for (size_t i = 0 ; i < elem_count ; i++) { + CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL); + if (e == NULL) { + // LCOV_EXCL_START + cxJsonValueFree(arr); + return NULL; + // LCOV_EXCL_STOP + } + arr->array.data[i] = e; + } + return_value(arr); + } + case CX_JSON_STRING: + return_value(cxJsonCreateString(allocator, source->string)); + case CX_JSON_INTEGER: + return_value(cxJsonCreateInteger(allocator, source->integer)); + case CX_JSON_NUMBER: + return_value(cxJsonCreateNumber(allocator, source->number)); + case CX_JSON_LITERAL: + return_value(cxJsonCreateLiteral(allocator, source->literal)); + default: + // LCOV_EXCL_START + // unreachable + assert(false); + return NULL; + // LCOV_EXCL_STOP + } +#undef return_value +}
--- a/ucx/kv_list.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/kv_list.c Thu Dec 18 17:50:15 2025 +0100 @@ -221,11 +221,12 @@ size_t index; cx_linked_list *ll = &kv_list->list; - char *node = cx_linked_list_find( + char *node = cx_linked_list_find_c( ll->begin, ll->loc_next, ll->loc_data, - list->collection.cmpfunc, elem, - &index + elem, &index, + cx_list_compare_wrapper, + list ); if (node == NULL) { return list->collection.size; @@ -348,7 +349,7 @@ return 0; } -static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) { +static CxMapEntry cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) { cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; // if the hash has not yet been computed, do it now if (key.hash == 0) { @@ -359,8 +360,8 @@ cx_kvl_map_remove(map, key, NULL); // now reserve new memory in the map - void **map_data = kv_list->map_methods->put(map, key, NULL); - if (map_data == NULL) return NULL; // LCOV_EXCL_LINE + CxMapEntry map_entry = kv_list->map_methods->put(map, key, NULL); + if (map_entry.key == NULL) return (CxMapEntry){NULL, NULL}; // LCOV_EXCL_LINE // insert the data into the list (which most likely destroys the sorted property) kv_list->list.base.collection.sorted = false; @@ -369,20 +370,20 @@ kv_list->list.base.collection.store_pointer ? &value : value); if (node_data == NULL) { // LCOV_EXCL_START // non-destructively remove the key again - kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data); - return NULL; + void *dummy; + kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy); + return (CxMapEntry){NULL, NULL}; } // LCOV_EXCL_STOP // write the node pointer to the map entry - *map_data = node_data; + *(void**)map_entry.value = node_data; // copy the key to the node data CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data); - *key_ptr = key; + *key_ptr = *map_entry.key; - // we must return node_data here and not map_data, - // because the node_data is the actual element of this collection - return node_data; + // we must return an entry that points to the node data! + return (CxMapEntry ){key_ptr, node_data}; } static void *cx_kvl_iter_current_entry(const void *it) { @@ -552,7 +553,6 @@ CxList *cxKvListCreate( const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size ) { if (allocator == NULL) { @@ -560,7 +560,7 @@ } // create a normal linked list and a normal hash map, first - CxList *list = cxLinkedListCreate(allocator, comparator, elem_size); + CxList *list = cxLinkedListCreate(allocator, elem_size); if (list == NULL) return NULL; // LCOV_EXCL_LINE cx_linked_list *ll = (cx_linked_list*)list; cx_linked_list_extra_data(ll, sizeof(CxHashKey)); @@ -600,23 +600,17 @@ // remember the base methods and override them kv_list->map_methods = map->cl; map->cl = &cx_kv_map_class; - if (list->climpl == NULL) { - kv_list->list_methods = list->cl; - list->cl = &cx_kv_list_class; - } else { - kv_list->list_methods = list->climpl; - list->climpl = &cx_kv_list_class; - } + kv_list->list_methods = list->cl; + list->cl = &cx_kv_list_class; return list; } CxMap *cxKvListCreateAsMap( const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size ) { - CxList *list = cxKvListCreate(allocator, comparator, elem_size); + CxList *list = cxKvListCreate(allocator, elem_size); return list == NULL ? NULL : cxKvListAsMap(list); } @@ -649,14 +643,14 @@ return 1; } - // add the key to the map; - if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) { - return 1; // LCOV_EXCL_LINE - } + // add the key to the map + const CxMapEntry entry = kv_list->map_methods->put( + &kv_list->map->map_base.base, key, node_data); + if (entry.key == NULL) return 1; // LCOV_EXCL_LINE // write the key to the list's node CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); - *loc_key = key; + *loc_key = *entry.key; return 0; } @@ -698,22 +692,23 @@ cx_kv_list *kv_list = (cx_kv_list*)list; // reserve memory in the map - void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL); - if (map_data == NULL) return 1; // LCOV_EXCL_LINE + CxMapEntry map_entry = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL); + if (map_entry.key == NULL) return 1; // LCOV_EXCL_LINE // insert the node void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index, kv_list->list.base.collection.store_pointer ? &value : value); if (node_data == NULL) { // LCOV_EXCL_START // non-destructively remove the key again - kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data); + void *dummy; + kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy); return 1; } // LCOV_EXCL_STOP - *map_data = node_data; + *(void**)map_entry.value = node_data; // write the key to the node CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); - *loc_key = key; + *loc_key = *map_entry.key; return 0; }
--- a/ucx/linked_list.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/linked_list.c Thu Dec 18 17:50:15 2025 +0100 @@ -65,13 +65,14 @@ return (void *) cur; } -void *cx_linked_list_find( +void *cx_linked_list_find_c( const void *start, ptrdiff_t loc_advance, ptrdiff_t loc_data, - cx_compare_func cmp_func, const void *elem, - size_t *found_index + size_t *found_index, + cx_compare_func2 cmp_func, + void *context ) { assert(start != NULL); assert(loc_advance >= 0); @@ -82,7 +83,7 @@ size_t index = 0; do { void *current = ll_data(node); - if (cmp_func(current, elem) == 0) { + if (cmp_func(current, elem, context) == 0) { if (found_index != NULL) { *found_index = index; } @@ -94,6 +95,19 @@ return NULL; } +void *cx_linked_list_find( + const void *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + const void *elem, + size_t *found_index, + cx_compare_func cmp_func +) { + cx_compare_func_wrapper wrapper = {cmp_func}; + return cx_linked_list_find_c(start, loc_advance, loc_data, + elem, found_index, cx_acmp_wrap, &wrapper); +} + void *cx_linked_list_first( const void *node, ptrdiff_t loc_prev @@ -259,7 +273,8 @@ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, - cx_compare_func cmp_func, + cx_compare_func2 cmp_func, + void *context, bool allow_duplicates ) { assert(begin != NULL); @@ -276,7 +291,7 @@ // determine the new start { - int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument); + int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument, context); if (d <= 0) { // the new chain starts with the original chain new_begin = new_end = source_original; @@ -302,7 +317,7 @@ // now successively compare the elements and add them to the correct chains while (source_original != NULL && source_argument != NULL) { - int d = cmp_func(source_original, source_argument); + int d = cmp_func(source_original, source_argument, context); if (d <= 0) { // the original is not larger, add it to the chain cx_linked_list_link(new_end, source_original, loc_prev, loc_next); @@ -327,7 +342,7 @@ } else { // the original is larger, append the source argument to the chain // check if we must discard the source argument as duplicate - if (!allow_duplicates && cmp_func(new_end, source_argument) == 0) { + if (!allow_duplicates && cmp_func(new_end, source_argument, context) == 0) { if (dup_end == NULL) { dup_begin = dup_end = source_argument; } else { @@ -356,7 +371,7 @@ } else { // otherwise we must check one-by-one while (source_argument != NULL) { - if (cmp_func(new_end, source_argument) == 0) { + if (cmp_func(new_end, source_argument, context) == 0) { if (dup_end == NULL) { dup_begin = dup_end = source_argument; } else { @@ -402,9 +417,10 @@ void *insert_begin, cx_compare_func cmp_func ) { + cx_compare_func_wrapper wrapper = {cmp_func}; cx_linked_list_insert_sorted_chain_impl( begin, end, loc_prev, loc_next, - insert_begin, cmp_func, true); + insert_begin, cx_acmp_wrap, &wrapper, true); } int cx_linked_list_insert_unique( @@ -428,9 +444,10 @@ void *insert_begin, cx_compare_func cmp_func ) { + cx_compare_func_wrapper wrapper = {cmp_func}; return cx_linked_list_insert_sorted_chain_impl( begin, end, loc_prev, loc_next, - insert_begin, cmp_func, false); + insert_begin, cx_acmp_wrap, &wrapper, false); } size_t cx_linked_list_remove_chain( @@ -511,6 +528,8 @@ #endif static void cx_linked_list_sort_merge( + void **begin, + void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, @@ -518,9 +537,8 @@ void *ls, void *le, void *re, - cx_compare_func cmp_func, - void **begin, - void **end + cx_compare_func2 cmp_func, + void *context ) { void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE]; void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ? @@ -532,7 +550,7 @@ rc = le; size_t n = 0; while (lc && lc != le && rc != re) { - if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) { + if (cmp_func(ll_data(lc), ll_data(rc), context) <= 0) { sorted[n] = lc; lc = ll_next(lc); } else { @@ -566,13 +584,14 @@ } } -void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function +void cx_linked_list_sort_c( // NOLINT(misc-no-recursion) - purposely recursive function void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, - cx_compare_func cmp_func + cx_compare_func2 cmp_func, + void *context ) { assert(begin != NULL); assert(loc_next >= 0); @@ -590,7 +609,7 @@ // check how many elements are already sorted lc = ls; size_t ln = 1; - while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) { + while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc), context) > 0) { lc = ll_next(lc); ln++; } @@ -602,7 +621,7 @@ size_t rn = 1; rc = le; // skip already sorted elements - while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) { + while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc), context) > 0) { rc = ll_next(rc); rn++; } @@ -610,27 +629,65 @@ // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them void *sorted_begin, *sorted_end; - cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + cx_linked_list_sort_merge(&sorted_begin, &sorted_end, + loc_prev, loc_next, loc_data, ln + rn, ls, le, re, cmp_func, - &sorted_begin, &sorted_end); + context); // Something left? Sort it! size_t remainder_length = cx_linked_list_size(re, loc_next); if (remainder_length > 0) { void *remainder = re; - cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func); + cx_linked_list_sort_c(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func, context); // merge sorted list with (also sorted) remainder - cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + cx_linked_list_sort_merge(&sorted_begin, &sorted_end, + loc_prev, loc_next, loc_data, ln + rn + remainder_length, sorted_begin, remainder, NULL, cmp_func, - &sorted_begin, &sorted_end); + context); } *begin = sorted_begin; if (end) *end = sorted_end; } } +void cx_linked_list_sort( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + cx_compare_func cmp_func +) { + cx_compare_func_wrapper wrapper = {cmp_func}; + cx_linked_list_sort_c(begin, end, loc_prev, loc_next, loc_data, cx_acmp_wrap, &wrapper); +} + +int cx_linked_list_compare_c( + const void *begin_left, + const void *begin_right, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func2 cmp_func, + void *context +) { + const void *left = begin_left, *right = begin_right; + + while (left != NULL && right != NULL) { + const void *left_data = ll_data(left); + const void *right_data = ll_data(right); + int result = cmp_func(left_data, right_data, context); + if (result != 0) return result; + left = ll_advance(left); + right = ll_advance(right); + } + + if (left != NULL) { return 1; } + else if (right != NULL) { return -1; } + else { return 0; } +} + int cx_linked_list_compare( const void *begin_left, const void *begin_right, @@ -638,20 +695,9 @@ ptrdiff_t loc_data, cx_compare_func cmp_func ) { - const void *left = begin_left, *right = begin_right; - - while (left != NULL && right != NULL) { - const void *left_data = ll_data(left); - const void *right_data = ll_data(right); - int result = cmp_func(left_data, right_data); - if (result != 0) return result; - left = ll_advance(left); - right = ll_advance(right); - } - - if (left != NULL) { return 1; } - else if (right != NULL) { return -1; } - else { return 0; } + cx_compare_func_wrapper wrapper = {cmp_func}; + return cx_linked_list_compare_c(begin_left, begin_right, + loc_advance, loc_data, cx_acmp_wrap, &wrapper); } void cx_linked_list_reverse( @@ -798,13 +844,11 @@ } } -static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func; -static _Thread_local off_t cx_ll_insert_sorted_loc_data; - -static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) { - const char *left = (const char*)l + cx_ll_insert_sorted_loc_data; - const char *right = (const char*)r + cx_ll_insert_sorted_loc_data; - return cx_ll_insert_sorted_cmp_func(left, right); +static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r, void *c) { + cx_linked_list *list = c; + const char *left = (const char*)l + list->loc_data; + const char *right = (const char*)r + list->loc_data; + return cx_list_compare_wrapper(left, right, list); } static size_t cx_ll_insert_sorted_impl( @@ -839,29 +883,19 @@ } CX_LL_PTR(prev, ll->loc_next) = NULL; - // invoke the low level function - cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; - cx_ll_insert_sorted_loc_data = ll->loc_data; - if (allow_duplicates) { - cx_linked_list_insert_sorted_chain( - &ll->begin, - &ll->end, - ll->loc_prev, - ll->loc_next, - chain, - cx_ll_insert_sorted_cmp_helper - ); - list->collection.size += inserted; - } else { - void *duplicates = cx_linked_list_insert_unique_chain( - &ll->begin, - &ll->end, - ll->loc_prev, - ll->loc_next, - chain, - cx_ll_insert_sorted_cmp_helper - ); - list->collection.size += inserted; + // invoke the low-level function + void *duplicates = cx_linked_list_insert_sorted_chain_impl( + &ll->begin, + &ll->end, + ll->loc_prev, + ll->loc_next, + chain, + cx_ll_insert_sorted_cmp_helper, + list, + allow_duplicates + ); + list->collection.size += inserted; + if (!allow_duplicates) { // free the nodes that did not make it into the list while (duplicates != NULL) { void *next = CX_LL_PTR(duplicates, ll->loc_next); @@ -1090,12 +1124,12 @@ size_t index; cx_linked_list *ll = (cx_linked_list *) list; - char *node = cx_linked_list_find( + char *node = cx_linked_list_find_c( ll->begin, ll->loc_next, ll->loc_data, - list->collection.cmpfunc, elem, - &index - ); + elem, &index, + cx_list_compare_wrapper, + list); if (node == NULL) { return list->collection.size; } @@ -1111,9 +1145,9 @@ static void cx_ll_sort(struct cx_list_s *list) { cx_linked_list *ll = (cx_linked_list *) list; - cx_linked_list_sort(&ll->begin, &ll->end, + cx_linked_list_sort_c(&ll->begin, &ll->end, ll->loc_prev, ll->loc_next, ll->loc_data, - list->collection.cmpfunc); + cx_list_compare_wrapper, list); } static void cx_ll_reverse(struct cx_list_s *list) { @@ -1129,9 +1163,9 @@ cx_linked_list *right = (cx_linked_list *) other; assert(left->loc_next == right->loc_next); assert(left->loc_data == right->loc_data); - return cx_linked_list_compare(left->begin, right->begin, + return cx_linked_list_compare_c(left->begin, right->begin, left->loc_next, left->loc_data, - list->collection.cmpfunc); + cx_list_compare_wrapper, (void*)list); } static bool cx_ll_iter_valid(const void *it) { @@ -1272,7 +1306,6 @@ CxList *cxLinkedListCreate( const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size ) { if (allocator == NULL) { @@ -1287,7 +1320,7 @@ list->loc_extra = -1; list->extra_data_len = 0; cx_list_init((CxList*)list, &cx_linked_list_class, - allocator, comparator, elem_size); + allocator, elem_size); return (CxList *) list; }
--- a/ucx/list.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/list.c Thu Dec 18 17:50:15 2025 +0100 @@ -31,198 +31,29 @@ #include <string.h> #include <assert.h> -// <editor-fold desc="Store Pointers Functionality"> - -static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; - -static int cx_pl_cmpfunc( - const void *l, - const void *r -) { - // l and r are guaranteed to be non-NULL pointing to the list's memory - void *const *lptr = l; - void *const *rptr = r; - const void *left = *lptr; - const void *right = *rptr; - if (left == NULL) { - // NULL is smaller than any value except NULL - return right == NULL ? 0 : -1; - } else if (right == NULL) { - // any value is larger than NULL - return 1; +int cx_list_compare_wrapper(const void *l, const void *r, void *c) { + CxList *list = c; + const void *left; + const void *right; + if (cxCollectionStoresPointers(list)) { + left = *(void**)l; + right = *(void**)r; + // for historic reasons, we are handling the NULL case here + // because every UCX compare function does not support NULL arguments + if (left == NULL) { + if (right == NULL) return 0; + return -1; + } else if (right == NULL) { + return 1; + } + } else { + left = l; + right = r; } - return cx_pl_cmpfunc_impl(left, right); -} - -static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) { - // cast away const - this is the hacky thing - struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; - cx_pl_cmpfunc_impl = l->cmpfunc; - l->cmpfunc = cx_pl_cmpfunc; -} - -static void cx_pl_unhack_cmpfunc(const struct cx_list_s *list) { - // cast away const - this is the hacky thing - struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; - l->cmpfunc = cx_pl_cmpfunc_impl; -} - -static void cx_pl_destructor(struct cx_list_s *list) { - list->climpl->deallocate(list); -} - -static void *cx_pl_insert_element( - struct cx_list_s *list, - size_t index, - const void *element -) { - return list->climpl->insert_element(list, index, &element); -} - -static size_t cx_pl_insert_array( - struct cx_list_s *list, - size_t index, - const void *array, - size_t n -) { - return list->climpl->insert_array(list, index, array, n); -} - -static size_t cx_pl_insert_sorted( - struct cx_list_s *list, - const void *array, - size_t n -) { - cx_pl_hack_cmpfunc(list); - size_t result = list->climpl->insert_sorted(list, array, n); - cx_pl_unhack_cmpfunc(list); - return result; -} - -static size_t cx_pl_insert_unique( - struct cx_list_s *list, - const void *array, - size_t n -) { - cx_pl_hack_cmpfunc(list); - size_t result = list->climpl->insert_unique(list, array, n); - cx_pl_unhack_cmpfunc(list); - return result; -} - -static int cx_pl_insert_iter( - struct cx_iterator_s *iter, - const void *elem, - int prepend -) { - struct cx_list_s *list = iter->src_handle; - return list->climpl->insert_iter(iter, &elem, prepend); + return cx_invoke_compare_func(list, left, right); } -static size_t cx_pl_remove( - struct cx_list_s *list, - size_t index, - size_t num, - void *targetbuf -) { - return list->climpl->remove(list, index, num, targetbuf); -} - -static void cx_pl_clear(struct cx_list_s *list) { - list->climpl->clear(list); -} - -static int cx_pl_swap( - struct cx_list_s *list, - size_t i, - size_t j -) { - return list->climpl->swap(list, i, j); -} - -static void *cx_pl_at( - const struct cx_list_s *list, - size_t index -) { - void **ptr = list->climpl->at(list, index); - return ptr == NULL ? NULL : *ptr; -} - -static size_t cx_pl_find_remove( - struct cx_list_s *list, - const void *elem, - bool remove -) { - cx_pl_hack_cmpfunc(list); - size_t ret = list->climpl->find_remove(list, &elem, remove); - cx_pl_unhack_cmpfunc(list); - return ret; -} - -static void cx_pl_sort(struct cx_list_s *list) { - cx_pl_hack_cmpfunc(list); - list->climpl->sort(list); - cx_pl_unhack_cmpfunc(list); -} - -static int cx_pl_compare( - const struct cx_list_s *list, - const struct cx_list_s *other -) { - cx_pl_hack_cmpfunc(list); - int ret = list->climpl->compare(list, other); - cx_pl_unhack_cmpfunc(list); - return ret; -} - -static void cx_pl_reverse(struct cx_list_s *list) { - list->climpl->reverse(list); -} - -static void *cx_pl_iter_current(const void *it) { - const struct cx_iterator_s *iter = it; - void **ptr = iter->base.current_impl(it); - return ptr == NULL ? NULL : *ptr; -} - -static int cx_pl_change_capacity(struct cx_list_s *list, size_t cap) { - if (list->climpl->change_capacity == NULL) { - return 0; - } else { - return list->climpl->change_capacity(list, cap); - } -} - -static struct cx_iterator_s cx_pl_iterator( - const struct cx_list_s *list, - size_t index, - bool backwards -) { - struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards); - iter.base.current_impl = iter.base.current; - iter.base.current = cx_pl_iter_current; - return iter; -} - -static cx_list_class cx_pointer_list_class = { - cx_pl_destructor, - cx_pl_insert_element, - cx_pl_insert_array, - cx_pl_insert_sorted, - cx_pl_insert_unique, - cx_pl_insert_iter, - cx_pl_remove, - cx_pl_clear, - cx_pl_swap, - cx_pl_at, - cx_pl_find_remove, - cx_pl_sort, - cx_pl_compare, - cx_pl_reverse, - cx_pl_change_capacity, - cx_pl_iterator, -}; -// </editor-fold> +#define cx_list_compare_wrapper(l, r, c) cx_list_compare_wrapper(l, r, (void*)c) // <editor-fold desc="empty list implementation"> @@ -283,27 +114,24 @@ CxList cx_empty_list = { { NULL, - NULL, 0, 0, NULL, NULL, NULL, + NULL, + NULL, + NULL, false, true, }, &cx_empty_list_class, - NULL }; CxList *const cxEmptyList = &cx_empty_list; // </editor-fold> -#define invoke_list_func(name, list, ...) \ - ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \ - (list, __VA_ARGS__) - size_t cx_list_default_insert_array( struct cx_list_s *list, size_t index, @@ -313,8 +141,7 @@ const char *src = data; size_t i = 0; for (; i < n; i++) { - if (NULL == invoke_list_func( - insert_element, list, index + i, src) + if (NULL == list->cl->insert_element(list, index + i, src) ) { return i; // LCOV_EXCL_LINE } @@ -335,7 +162,6 @@ if (n == 0) return 0; size_t elem_size = list->collection.elem_size; - cx_compare_func cmp = list->collection.cmpfunc; const char *src = sorted_data; // track indices and number of inserted items @@ -343,19 +169,19 @@ // search the list for insertion points while (di < list->collection.size) { - const void *list_elm = invoke_list_func(at, list, di); + const void *list_elm = list->cl->at(list, di); // compare the current list element with the first source element // if less, skip the list elements // if equal, skip the list elements and optionally the source elements { - int d = cmp(list_elm, src); + int d = cx_list_compare_wrapper(list_elm, src, list); if (d <= 0) { if (!allow_duplicates && d == 0) { src += elem_size; si++; processed++; // we also count duplicates for the return value - while (si < n && cmp(list_elm, src) == 0) { + while (si < n && cx_list_compare_wrapper(list_elm, src, list) == 0) { src += elem_size; si++; processed++; @@ -375,7 +201,7 @@ while (++si < n) { if (!allow_duplicates) { // skip duplicates within the source - if (cmp(next, next + elem_size) == 0) { + if (cx_list_compare_wrapper(next, next + elem_size, list) == 0) { next += elem_size; skip++; continue; @@ -389,7 +215,7 @@ } next += elem_size; // once we become larger than the list elem, break - if (cmp(list_elm, next) <= 0) { + if (cx_list_compare_wrapper(list_elm, next, list) <= 0) { break; } // otherwise, we can insert one more @@ -398,11 +224,11 @@ // insert the elements at location si if (ins == 1) { - if (NULL == invoke_list_func(insert_element, list, di, src)) { + if (NULL == list->cl->insert_element(list, di, src)) { return processed; // LCOV_EXCL_LINE } } else { - size_t r = invoke_list_func(insert_array, list, di, src, ins); + size_t r = list->cl->insert_array(list, di, src, ins); if (r < ins) { return processed + r; // LCOV_EXCL_LINE } @@ -420,13 +246,13 @@ // insert remaining items if (si < n) { if (allow_duplicates) { - processed += invoke_list_func(insert_array, list, di, src, n - si); + processed += list->cl->insert_array(list, di, src, n - si); } else { - const void *last = di == 0 ? NULL : invoke_list_func(at, list, di - 1); + const void *last = di == 0 ? NULL : list->cl->at(list, di - 1); for (; si < n; si++) { // skip duplicates within the source - if (last == NULL || cmp(last, src) != 0) { - if (NULL == invoke_list_func(insert_element, list, di, src)) { + if (last == NULL || cx_list_compare_wrapper(last, src, list) != 0) { + if (NULL == list->cl->insert_element(list, di, src)) { return processed; // LCOV_EXCL_LINE } last = src; @@ -457,6 +283,12 @@ return cx_list_default_insert_sorted_impl(list, sorted_data, n, false); } +// TODO: remove this hack once we have a solution for qsort() / qsort_s() +static _Thread_local CxList *cx_hack_for_qsort_list; +static int cx_hack_cmp_for_qsort(const void *l, const void *r) { + return cx_list_compare_wrapper(l, r, cx_hack_for_qsort_list); +} + 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; @@ -466,19 +298,20 @@ // copy elements from source array char *loc = tmp; for (size_t i = 0; i < list_size; i++) { - void *src = invoke_list_func(at, list, i); + void *src = list->cl->at(list, i); memcpy(loc, src, elem_size); loc += elem_size; } // qsort - qsort(tmp, list_size, elem_size, - list->collection.cmpfunc); + // TODO: qsort_s() is not as nice as we thought + cx_hack_for_qsort_list = list; + qsort(tmp, list_size, elem_size, cx_hack_cmp_for_qsort); // copy elements back loc = tmp; for (size_t i = 0; i < list_size; i++) { - void *dest = invoke_list_func(at, list, i); + void *dest = list->cl->at(list, i); memcpy(dest, loc, elem_size); loc += elem_size; } @@ -496,8 +329,8 @@ void *tmp = cxMallocDefault(elem_size); if (tmp == NULL) return 1; // LCOV_EXCL_LINE - void *ip = invoke_list_func(at, list, i); - void *jp = invoke_list_func(at, list, j); + void *ip = list->cl->at(list, i); + void *jp = list->cl->at(list, j); memcpy(tmp, ip, elem_size); memcpy(ip, jp, elem_size); @@ -512,22 +345,24 @@ struct cx_list_s *list, struct cx_list_class_s *cl, const struct cx_allocator_s *allocator, - cx_compare_func comparator, size_t elem_size ) { list->cl = cl; list->collection.allocator = allocator; - list->collection.cmpfunc = comparator; + list->collection.size = 0; + list->collection.sorted = false; // should be set by the implementation if (elem_size > 0) { list->collection.elem_size = elem_size; + list->collection.simple_cmp = NULL; + list->collection.advanced_cmp = cx_acmp_memcmp; + list->collection.cmp_data = &list->collection.elem_size; + list->collection.store_pointer = false; } else { list->collection.elem_size = sizeof(void *); - if (list->collection.cmpfunc == NULL) { - list->collection.cmpfunc = cx_cmp_ptr; - } + list->collection.simple_cmp = cx_cmp_ptr; + list->collection.advanced_cmp = NULL; + list->collection.cmp_data = NULL; list->collection.store_pointer = true; - list->climpl = list->cl; - list->cl = &cx_pointer_list_class; } } @@ -535,33 +370,28 @@ const CxList *list, const CxList *other ) { + // check if we cannot use the list internal function bool cannot_optimize = false; // if one is storing pointers but the other is not cannot_optimize |= list->collection.store_pointer ^ other->collection.store_pointer; - // if one class is wrapped but the other is not - cannot_optimize |= (list->climpl == NULL) ^ (other->climpl == NULL); - - // if the compare functions do not match or both are NULL - if (!cannot_optimize) { - cx_compare_func list_cmp = (cx_compare_func) (list->climpl != NULL ? - list->climpl->compare : list->cl->compare); - cx_compare_func other_cmp = (cx_compare_func) (other->climpl != NULL ? - other->climpl->compare : other->cl->compare); - cannot_optimize |= list_cmp != other_cmp; - cannot_optimize |= list_cmp == NULL; - } + // check if the lists are incompatible or this list does not implement compare + cx_compare_func list_cmp = (cx_compare_func) list->cl->compare; + cx_compare_func other_cmp = (cx_compare_func) other->cl->compare; + cannot_optimize |= list_cmp != other_cmp; + cannot_optimize |= list_cmp == NULL; if (cannot_optimize) { // lists are definitely different - cannot use internal compare function if (list->collection.size == other->collection.size) { - CxIterator left = list->cl->iterator(list, 0, false); - CxIterator right = other->cl->iterator(other, 0, false); + CxIterator left = cxListIterator(list); + CxIterator right = cxListIterator(other); for (size_t i = 0; i < list->collection.size; i++) { void *leftValue = cxIteratorCurrent(left); void *rightValue = cxIteratorCurrent(right); - int d = list->collection.cmpfunc(leftValue, rightValue); + // values are already unwrapped, invoke immediately + int d = cx_invoke_compare_func(list, leftValue, rightValue); if (d != 0) { return d; } @@ -584,7 +414,7 @@ int cxListAdd(CxList *list, const void *elem) { list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, elem) == NULL; + return list->cl->insert_element(list, list->collection.size, cx_ref(list, elem)) == NULL; } size_t cxListAddArray(CxList *list, const void *array, size_t n) { @@ -594,7 +424,7 @@ int cxListInsert(CxList *list, size_t index, const void *elem) { list->collection.sorted = false; - return list->cl->insert_element(list, index, elem) == NULL; + return list->cl->insert_element(list, index, cx_ref(list, elem)) == NULL; } void *cxListEmplaceAt(CxList *list, size_t index) { @@ -621,11 +451,6 @@ iter.index = 0; // replace the valid function to abort iteration when c is reached iter.base.valid = cx_list_emplace_iterator_valid; - // if we are storing pointers, we want to return the pure pointers. - // therefore, we must unwrap the "current" method - if (list->collection.store_pointer) { - iter.base.current = iter.base.current_impl; - } return iter; } @@ -636,15 +461,13 @@ int cxListInsertSorted(CxList *list, const void *elem) { assert(cxCollectionSorted(list)); list->collection.sorted = true; - const void *data = list->collection.store_pointer ? &elem : elem; - return list->cl->insert_sorted(list, data, 1) == 0; + return list->cl->insert_sorted(list, cx_ref(list, elem), 1) == 0; } int cxListInsertUnique(CxList *list, const void *elem) { if (cxCollectionSorted(list)) { list->collection.sorted = true; - const void *data = list->collection.store_pointer ? &elem : elem; - return list->cl->insert_unique(list, data, 1) == 0; + return list->cl->insert_unique(list, cx_ref(list, elem), 1) == 0; } else { if (cxListContains(list, elem)) { return 0; @@ -673,8 +496,7 @@ const char *source = array; for (size_t i = 0 ; i < n; i++) { // note: this also checks elements added in a previous iteration - const void *data = list->collection.store_pointer ? - *((const void**)source) : source; + const void *data = cx_deref(list, source); if (!cxListContains(list, data)) { if (cxListAdd(list, data)) { return i; // LCOV_EXCL_LINE @@ -687,15 +509,15 @@ } int cxListInsertAfter(CxIterator *iter, const void *elem) { - CxList* list = (CxList*)iter->src_handle; + CxList* list = iter->src_handle; list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 0); + return list->cl->insert_iter(iter, cx_ref(list, elem), 0); } int cxListInsertBefore(CxIterator *iter, const void *elem) { - CxList* list = (CxList*)iter->src_handle; + CxList* list = iter->src_handle; list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 1); + return list->cl->insert_iter(iter, cx_ref(list, elem), 1); } int cxListRemove(CxList *list, size_t index) { @@ -734,15 +556,17 @@ } void *cxListAt(const CxList *list, size_t index) { - return list->cl->at(list, index); + void *result = list->cl->at(list, index); + if (result == NULL) return NULL; + return cx_deref(list, result); } void *cxListFirst(const CxList *list) { - return list->cl->at(list, 0); + return cxListAt(list, 0); } void *cxListLast(const CxList *list) { - return list->cl->at(list, list->collection.size - 1); + return cxListAt(list, list->collection.size - 1); } int cxListSet(CxList *list, size_t index, const void *elem) { @@ -751,8 +575,7 @@ } if (list->collection.store_pointer) { - // For pointer collections, always use climpl - void **target = list->climpl->at(list, index); + void **target = list->cl->at(list, index); *target = (void *)elem; } else { void *target = list->cl->at(list, index); @@ -762,32 +585,48 @@ return 0; } +static void *cx_pl_iter_current(const void *it) { + const struct cx_iterator_s *iter = it; + void **ptr = iter->base.current_impl(it); + return ptr == NULL ? NULL : *ptr; +} + +CX_INLINE CxIterator cx_pl_iter_wrap(const CxList *list, CxIterator iter) { + if (cxCollectionStoresPointers(list)) { + iter.base.current_impl = iter.base.current; + iter.base.current = cx_pl_iter_current; + return iter; + } else { + return iter; + } +} + CxIterator cxListIteratorAt(const CxList *list, size_t index) { if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, index, false); + return cx_pl_iter_wrap(list, list->cl->iterator(list, index, false)); } CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index) { if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, index, true); + return cx_pl_iter_wrap(list, list->cl->iterator(list, index, true)); } CxIterator cxListIterator(const CxList *list) { if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, 0, false); + return cx_pl_iter_wrap(list, list->cl->iterator(list, 0, false)); } CxIterator cxListBackwardsIterator(const CxList *list) { if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, list->collection.size - 1, true); + return cx_pl_iter_wrap(list, list->cl->iterator(list, list->collection.size - 1, true)); } size_t cxListFind(const CxList *list, const void *elem) { - return list->cl->find_remove((CxList*)list, elem, false); + return list->cl->find_remove((CxList*)list, cx_ref(list, elem), false); } bool cxListContains(const CxList* list, const void* elem) { - return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size; + return list->cl->find_remove((CxList*)list, cx_ref(list, elem), false) < list->collection.size; } bool cxListIndexValid(const CxList *list, size_t index) { @@ -795,7 +634,7 @@ } size_t cxListFindRemove(CxList *list, const void *elem) { - return list->cl->find_remove(list, elem, true); + return list->cl->find_remove(list, cx_ref(list, elem), true); } void cxListSort(CxList *list) { @@ -829,14 +668,14 @@ list->collection.advanced_destructor = destr2_bak; } -static void* cx_list_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { +static void* cx_list_shallow_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { size_t elem_size = *(size_t*)data; if (dst == NULL) dst = cxMalloc(al, elem_size); if (dst != NULL) memcpy(dst, src, elem_size); return dst; } -#define use_simple_clone_func(list) cx_list_simple_clone_func, NULL, (void*)&((list)->collection.elem_size) +#define use_shallow_clone_func(list) cx_list_shallow_clone_func, NULL, (void*)&((list)->collection.elem_size) int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { @@ -902,8 +741,7 @@ int d; if (cxIteratorValid(sub_iter)) { sub_elem = cxIteratorCurrent(sub_iter); - cx_compare_func cmp = subtrahend->collection.cmpfunc; - d = cmp(sub_elem, min_elem); + d = cx_list_compare_wrapper(sub_elem, min_elem, subtrahend); } else { // no more elements in the subtrahend, // i.e., the min_elem is larger than any elem of the subtrahend @@ -971,7 +809,7 @@ while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) { void *src_elem = cxIteratorCurrent(src_iter); void *other_elem = cxIteratorCurrent(other_iter); - int d = src->collection.cmpfunc(src_elem, other_elem); + int d = cx_list_compare_wrapper(src_elem, other_elem, src); if (d == 0) { // is contained, clone it void **dst_mem = cxListEmplace(dst); @@ -1041,7 +879,7 @@ } else { src_elem = cxIteratorCurrent(src_iter); other_elem = cxIteratorCurrent(other_iter); - d = src->collection.cmpfunc(src_elem, other_elem); + d = cx_list_compare_wrapper(src_elem, other_elem, src); } void *clone_from; if (d < 0) { @@ -1097,20 +935,20 @@ return 0; } -int cxListCloneSimple(CxList *dst, const CxList *src) { - return cxListClone(dst, src, use_simple_clone_func(src)); +int cxListCloneShallow(CxList *dst, const CxList *src) { + return cxListClone(dst, src, use_shallow_clone_func(src)); } -int cxListDifferenceSimple(CxList *dst, const CxList *minuend, const CxList *subtrahend) { - return cxListDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); +int cxListDifferenceShallow(CxList *dst, const CxList *minuend, const CxList *subtrahend) { + return cxListDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend)); } -int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other) { - return cxListIntersection(dst, src, other, use_simple_clone_func(src)); +int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other) { + return cxListIntersection(dst, src, other, use_shallow_clone_func(src)); } -int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other) { - return cxListUnion(dst, src, other, use_simple_clone_func(src)); +int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other) { + return cxListUnion(dst, src, other, use_shallow_clone_func(src)); } int cxListReserve(CxList *list, size_t capacity) {
--- a/ucx/map.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/map.c Thu Dec 18 17:50:15 2025 +0100 @@ -70,12 +70,14 @@ CxMap cx_empty_map = { { NULL, - NULL, 0, 0, NULL, NULL, NULL, + NULL, + NULL, + NULL, false, true }, @@ -110,11 +112,13 @@ } int cx_map_put(CxMap *map, CxHashKey key, void *value) { - return map->cl->put(map, key, value) == NULL; + return map->cl->put(map, key, value).key == NULL; } void *cx_map_emplace(CxMap *map, CxHashKey key) { - return map->cl->put(map, key, NULL); + const CxMapEntry entry = map->cl->put(map, key, NULL); + if (entry.key == NULL) return NULL; + return entry.value; } void *cx_map_get(const CxMap *map, CxHashKey key) { @@ -140,14 +144,14 @@ map->collection.advanced_destructor = destr2_bak; } -static void* cx_map_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { +static void* cx_map_shallow_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { size_t elem_size = *(size_t*)data; if (dst == NULL) dst = cxMalloc(al, elem_size); if (dst != NULL) memcpy(dst, src, elem_size); return dst; } -#define use_simple_clone_func(map) cx_map_simple_clone_func, NULL, (void*)&((map)->collection.elem_size) +#define use_shallow_clone_func(map) cx_map_shallow_clone_func, NULL, (void*)&((map)->collection.elem_size) int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { @@ -303,26 +307,55 @@ return 0; } -int cxMapCloneSimple(CxMap *dst, const CxMap *src) { - return cxMapClone(dst, src, use_simple_clone_func(src)); +int cxMapCloneShallow(CxMap *dst, const CxMap *src) { + return cxMapClone(dst, src, use_shallow_clone_func(src)); +} + +int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) { + return cxMapDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend)); } -int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) { - return cxMapDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); +int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListDifference(dst, src, keys, use_shallow_clone_func(src)); } -int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys) { - return cxMapListDifference(dst, src, keys, use_simple_clone_func(src)); +int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other) { + return cxMapIntersection(dst, src, other, use_shallow_clone_func(src)); +} + +int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListIntersection(dst, src, keys, use_shallow_clone_func(src)); +} + +int cxMapUnionShallow(CxMap *dst, const CxMap *src) { + return cxMapUnion(dst, src, use_shallow_clone_func(src)); } -int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other) { - return cxMapIntersection(dst, src, other, use_simple_clone_func(src)); -} +int cxMapCompare(const CxMap *map, const CxMap *other) { + // compare map sizes + const size_t size_left = cxMapSize(map); + const size_t size_right = cxMapSize(other); + if (size_left < size_right) { + return -1; + } else if (size_left > size_right) { + return 1; + } -int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys) { - return cxMapListIntersection(dst, src, keys, use_simple_clone_func(src)); -} + // iterate through the first map + CxMapIterator iter = cxMapIterator(map); + cx_foreach(const CxMapEntry *, entry, iter) { + const void *value_left = entry->value; + const void *value_right = cxMapGet(other, *entry->key); + // if the other map does not have the key, we are done + if (value_right == NULL) { + return -1; + } + // compare the values + const int d = cx_invoke_compare_func(map, value_left, value_right); + if (d != 0) { + return d; + } + } -int cxMapUnionSimple(CxMap *dst, const CxMap *src) { - return cxMapUnion(dst, src, use_simple_clone_func(src)); + return 0; }
--- a/ucx/properties.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/properties.c Thu Dec 18 17:50:15 2025 +0100 @@ -68,8 +68,8 @@ if (cxBufferEof(&prop->input)) { // destroy a possible previously initialized buffer cxBufferDestroy(&prop->input); - cxBufferInit(&prop->input, (void*) buf, len, - NULL, CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&prop->input, NULL, (void*) buf, + len, CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_AUTO_EXTEND); prop->input.size = len; } else { if (cxBufferAppend(buf, 1, len, &prop->input) < len) return -1; @@ -82,7 +82,7 @@ char *buf, size_t capacity ) { - cxBufferInit(&prop->buffer, buf, capacity, NULL, CX_BUFFER_COPY_ON_EXTEND); + cxBufferInit(&prop->buffer, NULL, buf, capacity, CX_BUFFER_COPY_ON_EXTEND); } CxPropertiesStatus cxPropertiesNext( @@ -190,7 +190,7 @@ assert(cxBufferEof(&prop->buffer)); if (prop->buffer.space == NULL) { // initialize a rescue buffer, if the user did not provide one - cxBufferInit(&prop->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&prop->buffer, NULL, NULL, 256, CX_BUFFER_AUTO_EXTEND); } else { // from a previous rescue there might be already read data // reset the buffer to avoid unnecessary buffer extension @@ -251,7 +251,7 @@ if (current_buffer != &prop->buffer) { // move value to the rescue buffer if (prop->buffer.space == NULL) { - cxBufferInit(&prop->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&prop->buffer, NULL, NULL, 256, CX_BUFFER_AUTO_EXTEND); } prop->buffer.size = 0; prop->buffer.pos = 0; @@ -312,8 +312,8 @@ #endif const unsigned cx_properties_load_buf_size = CX_PROPERTIES_LOAD_BUF_SIZE; -CxPropertiesStatus cx_properties_load(CxPropertiesConfig config, - const CxAllocator *allocator, cxstring filename, CxMap *target) { +CxPropertiesStatus cx_properties_load(const CxAllocator *allocator, + cxstring filename, CxMap *target, CxPropertiesConfig config) { if (allocator == NULL) { allocator = cxDefaultAllocator; }
--- a/ucx/streams.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/streams.c Thu Dec 18 17:50:15 2025 +0100 @@ -29,12 +29,12 @@ #include "cx/streams.h" #include "cx/allocator.h" -#ifndef CX_STREAM_BCOPY_BUF_SIZE -#define CX_STREAM_BCOPY_BUF_SIZE 8192 +#ifndef cx_strEAM_BCOPY_BUF_SIZE +#define cx_strEAM_BCOPY_BUF_SIZE 8192 #endif -#ifndef CX_STREAM_COPY_BUF_SIZE -#define CX_STREAM_COPY_BUF_SIZE 1024 +#ifndef cx_strEAM_COPY_BUF_SIZE +#define cx_strEAM_COPY_BUF_SIZE 1024 #endif size_t cx_stream_bncopy( @@ -57,7 +57,7 @@ if (bufsize == 0) return 0; lbuf = buf; } else { - if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE; + if (bufsize == 0) bufsize = cx_strEAM_BCOPY_BUF_SIZE; lbuf = cxMallocDefault(bufsize); if (lbuf == NULL) return 0; } @@ -88,7 +88,7 @@ cx_write_func wfnc, size_t n ) { - char buf[CX_STREAM_COPY_BUF_SIZE]; + char buf[cx_strEAM_COPY_BUF_SIZE]; return cx_stream_bncopy(src, dest, rfnc, wfnc, - buf, CX_STREAM_COPY_BUF_SIZE, n); + buf, cx_strEAM_COPY_BUF_SIZE, n); }
--- a/ucx/string.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/string.c Thu Dec 18 17:50:15 2025 +0100 @@ -25,9 +25,9 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifdef MEMRCHR_NEED_GNU + +// for memrchr in glibc #define _GNU_SOURCE -#endif #include "cx/string.h" @@ -46,28 +46,6 @@ #define cx_strcasecmp_impl strncasecmp #endif -cxmutstr cx_mutstr(char *cstring) { - return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)}; -} - -cxmutstr cx_mutstrn( - char *cstring, - size_t length -) { - return (cxmutstr) {cstring, length}; -} - -cxstring cx_str(const char *cstring) { - return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)}; -} - -cxstring cx_strn( - const char *cstring, - size_t length -) { - return (cxstring) {cstring, length}; -} - void cx_strfree(cxmutstr *str) { if (str == NULL) return; cxFreeDefault(str->ptr); @@ -264,10 +242,10 @@ return (cxmutstr) {(char *) result.ptr, result.length}; } -#ifndef CX_STRSTR_SBO_SIZE -#define CX_STRSTR_SBO_SIZE 128 +#ifndef cx_strSTR_SBO_SIZE +#define cx_strSTR_SBO_SIZE 128 #endif -const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE; +const unsigned cx_strstr_sbo_size = cx_strSTR_SBO_SIZE; cxstring cx_strstr( cxstring haystack, @@ -291,11 +269,11 @@ */ // local prefix table - size_t s_prefix_table[CX_STRSTR_SBO_SIZE]; + size_t s_prefix_table[cx_strSTR_SBO_SIZE]; // 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; + const bool useheap = needle.length >= cx_strSTR_SBO_SIZE; register size_t *ptable = useheap ? cxCallocDefault(needle.length + 1, sizeof(size_t)) : s_prefix_table;
--- a/ucx/tree.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/tree.c Thu Dec 18 17:50:15 2025 +0100 @@ -28,8 +28,6 @@ #include "cx/tree.h" -#include "cx/array_list.h" - #include <assert.h> #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) @@ -352,7 +350,16 @@ } } else { // node has children, push the first child onto the stack and enter it - cx_array_simple_add(iter->stack, children); + if (iter->stack_size >= iter->stack_capacity) { + const size_t newcap = iter->stack_capacity + 8; + if (cxReallocArrayDefault(&iter->stack, newcap, sizeof(void*))) { + // we cannot return an error in this function + abort(); // LCOV_EXCL_LINE + } + iter->stack_capacity = newcap; + } + iter->stack[iter->stack_size] = children; + iter->stack_size++; iter->node = children; iter->counter++; } @@ -717,7 +724,7 @@ } // otherwise, create iterator and hand over to other function - CxIterator iter = cxIterator(src, elem_size, num, false); + CxIterator iter = cxIterator(src, elem_size, num); return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc, cfunc, cdata, failed, root, loc_parent, loc_children, loc_last_child, @@ -902,7 +909,7 @@ size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n) { if (n == 0) return 0; if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0; - CxIterator iter = cxIterator(data, elem_size, n, false); + CxIterator iter = cxIterator(data, elem_size, n); return cxTreeInsertIter(tree, cxIteratorRef(iter), n); }
--- a/ui/cocoa/button.m Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/cocoa/button.m Thu Dec 18 17:50:15 2025 +0100 @@ -353,19 +353,19 @@ static char* create_linkbutton_jsonvalue(const char *label, const char *uri, UiBool include_null, UiBool visited, UiBool set_visited) { CxJsonValue *obj = cxJsonCreateObj(NULL); if(label) { - cxJsonObjPutString(obj, CX_STR("label"), label); + cxJsonObjPutString(obj, cx_str("label"), label); } else if(include_null) { - cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL); + cxJsonObjPutLiteral(obj, cx_str("label"), CX_JSON_NULL); } if(uri) { - cxJsonObjPutString(obj, CX_STR("uri"), uri); + cxJsonObjPutString(obj, cx_str("uri"), uri); } else if(include_null) { - cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL); + cxJsonObjPutLiteral(obj, cx_str("uri"), CX_JSON_NULL); } if(set_visited) { - cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE); + cxJsonObjPutLiteral(obj, cx_str("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE); } CxJsonWriter writer = cxJsonWriterCompact();
--- a/ui/common/context.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/context.c Thu Dec 18 17:50:15 2025 +0100 @@ -59,13 +59,14 @@ memset(ctx, 0, sizeof(UiContext)); ctx->mp = mp; ctx->allocator = mp->allocator; - ctx->destroy_handler = cxArrayListCreate(ctx->allocator, NULL, sizeof(UiDestroyHandler), 16); + ctx->destroy_handler = cxArrayListCreate(ctx->allocator, sizeof(UiDestroyHandler), 16); ctx->obj = toplevel; ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16); - ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, CX_STORE_POINTERS); - ctx->state_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiStateWidget)); - ctx->states = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32); + ctx->documents = cxLinkedListCreate(mp->allocator, CX_STORE_POINTERS); + ctx->state_widgets = cxLinkedListCreate(mp->allocator, sizeof(UiStateWidget)); + ctx->states = cxArrayListCreate(mp->allocator, sizeof(int), 32); + cxSetCompareFunc(ctx->states, cx_cmp_int); ctx->attach_document = uic_context_attach_document; ctx->detach_document2 = uic_context_detach_document; @@ -173,7 +174,7 @@ void uic_context_detach_all(UiContext *ctx) { // copy list - CxList *ls = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + CxList *ls = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS); CxIterator i = cxListIterator(ctx->documents); cx_foreach(void *, doc, i) { cxListAdd(ls, doc); @@ -590,7 +591,7 @@ enable = (ui_enablefunc)ui_set_enabled; } // get states - CxList *states = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); + CxList *states = cxArrayListCreate(cxDefaultAllocator, sizeof(int), 16); va_list ap; va_start(ap, enable); int state; @@ -608,7 +609,7 @@ if(enable == NULL) { enable = (ui_enablefunc)ui_set_enabled; } - CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), nstates); + CxList *ls = cxArrayListCreate(cxDefaultAllocator, sizeof(int), nstates); for(int i=0;i<nstates;i++) { cxListAdd(ls, states+i); }
--- a/ui/common/menu.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/menu.c Thu Dec 18 17:50:15 2025 +0100 @@ -58,7 +58,7 @@ } void uic_menu_init(void) { - global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + global_builder.current = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS); current_builder = &global_builder; } @@ -270,7 +270,7 @@ UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder)); builder->menus_begin = NULL; builder->menus_end = NULL; - builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + builder->current = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS); builder->ref = 1; current_builder = builder; *out_builder = builder;
--- a/ui/common/message.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/message.c Thu Dec 18 17:50:15 2025 +0100 @@ -46,7 +46,7 @@ handler->handler.callback = callback; handler->in = in; handler->out = out; - handler->outbuf = cxBufferCreate(NULL, 4096, NULL, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND); + handler->outbuf = cxBufferCreate(NULL, NULL, 4096, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND); handler->stop = 0; pthread_mutex_init(&handler->queue_lock, NULL); pthread_mutex_init(&handler->avlbl_lock, NULL);
--- a/ui/common/object.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/object.c Thu Dec 18 17:50:15 2025 +0100 @@ -46,7 +46,7 @@ void ui_register_object_creation_callback(ui_object_callback func, void *userdata) { if(!creation_callbacks) { - creation_callbacks = cxLinkedListCreateSimple(sizeof(objcallback)); + creation_callbacks = cxLinkedListCreate(NULL, sizeof(objcallback)); } objcallback cb = { func, userdata }; cxListAdd(creation_callbacks, &cb); @@ -54,7 +54,7 @@ void ui_register_object_destruction_callback(ui_object_callback func, void *userdata) { if(!destruction_callbacks) { - destruction_callbacks = cxLinkedListCreateSimple(sizeof(objcallback)); + destruction_callbacks = cxLinkedListCreate(NULL, sizeof(objcallback)); } objcallback cb = { func, userdata }; cxListAdd(destruction_callbacks, &cb);
--- a/ui/common/properties.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/properties.c Thu Dec 18 17:50:15 2025 +0100 @@ -101,7 +101,7 @@ } CxBuffer buf; - cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, cxDefaultAllocator, NULL, 128, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // add base dir char *homeenv = getenv(UI_ENV_HOME); @@ -336,7 +336,7 @@ static char* uic_concat_path(const char *base, const char *p, const char *ext) { size_t baselen = strlen(base); - CxBuffer *buf = cxBufferCreate(NULL, 32, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 32, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); if(baselen > 0) { cxBufferWrite(base, 1, baselen, buf); if(base[baselen - 1] != '/') {
--- a/ui/common/toolbar.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/toolbar.c Thu Dec 18 17:50:15 2025 +0100 @@ -42,7 +42,7 @@ void uic_toolbar_init(void) { toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); for(int i=0;i<8;i++) { - toolbar_defaults[i] = cxLinkedListCreateSimple(CX_STORE_POINTERS); + toolbar_defaults[i] = cxLinkedListCreate(NULL, CX_STORE_POINTERS); } }
--- a/ui/common/types.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/types.c Thu Dec 18 17:50:15 2025 +0100 @@ -100,7 +100,7 @@ /* --------------------------- UiList --------------------------- */ void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) { - list->data = cxArrayListCreate(ctx->mp->allocator, NULL, CX_STORE_POINTERS, 32); + list->data = cxArrayListCreate(ctx->mp->allocator, CX_STORE_POINTERS, 32); list->first = ui_list_first; list->next = ui_list_next; list->get = ui_list_get; @@ -217,7 +217,7 @@ va_list ap; va_start(ap, ctx); - CxList *cols = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(UiColumn), 32); + CxList *cols = cxArrayListCreate(cxDefaultAllocator, sizeof(UiColumn), 32); int type; while((type = va_arg(ap, int)) != -1) { char *name = va_arg(ap, char*);
--- a/ui/common/utils.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/common/utils.c Thu Dec 18 17:50:15 2025 +0100 @@ -37,7 +37,7 @@ UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { cxstring *pathelms; - size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms); if (nelm == 0) { *ret_nelm = 0; @@ -104,7 +104,7 @@ size_t capa = str.length + 32; char *space = cxMallocDefault(capa); if (space == NULL) return cx_mutstrn(NULL, 0); - cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND); cxBufferWrite(str.ptr, 1, i, &buf); all_printable = false; }
--- a/ui/gtk/button.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/button.c Thu Dec 18 17:50:15 2025 +0100 @@ -787,24 +787,24 @@ static char* create_linkbutton_jsonvalue(const char *label, const char *uri, gboolean include_null, gboolean visited, gboolean set_visited) { CxJsonValue *obj = cxJsonCreateObj(NULL); if(label) { - cxJsonObjPutString(obj, CX_STR("label"), label); + cxJsonObjPutString(obj, cx_str("label"), label); } else if(include_null) { - cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL); + cxJsonObjPutLiteral(obj, cx_str("label"), CX_JSON_NULL); } if(uri) { - cxJsonObjPutString(obj, CX_STR("uri"), uri); + cxJsonObjPutString(obj, cx_str("uri"), uri); } else if(include_null) { - cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL); + cxJsonObjPutLiteral(obj, cx_str("uri"), CX_JSON_NULL); } if(set_visited) { - cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE); + cxJsonObjPutLiteral(obj, cx_str("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE); } CxJsonWriter writer = cxJsonWriterCompact(); CxBuffer buf; - cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, NULL, 128, CX_BUFFER_AUTO_EXTEND); cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer); cxJsonValueFree(obj); cxBufferTerminate(&buf);
--- a/ui/gtk/container.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/container.c Thu Dec 18 17:50:15 2025 +0100 @@ -1162,7 +1162,7 @@ ct->orientation = orientation; ct->max = max; ct->initial_position = init; - ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); + ct->children = cxArrayListCreate(NULL, CX_STORE_POINTERS, 4); return ct; } @@ -1246,7 +1246,7 @@ static void update_itemlist(UiList *list, int c) { UiGtkItemListContainer *ct = list->obj; - CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS); + CxMap *new_items = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32); new_items->collection.advanced_destructor = remove_item; new_items->collection.destructor_data = ct; @@ -1331,7 +1331,7 @@ container->create_ui = args->create_ui; container->userdata = args->userdata; container->subcontainer = args->subcontainer; - container->current_items = cxHashMapCreateSimple(CX_STORE_POINTERS); + container->current_items = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32); container->current_items->collection.advanced_destructor = remove_item; container->current_items->collection.destructor_data = container; container->margin = args->sub_margin;
--- a/ui/gtk/dnd.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/dnd.c Thu Dec 18 17:50:15 2025 +0100 @@ -184,7 +184,7 @@ UiDnD* ui_create_dnd(void) { UiDnD *dnd = malloc(sizeof(UiDnD)); memset(dnd, 0, sizeof(UiDnD)); - dnd->providers = cxArrayListCreateSimple(sizeof(void*), 16); + dnd->providers = cxArrayListCreate(NULL, sizeof(void*), 16); dnd->selected_action = 0; dnd->delete = FALSE; return dnd;
--- a/ui/gtk/icon.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/icon.c Thu Dec 18 17:50:15 2025 +0100 @@ -46,7 +46,7 @@ #endif void ui_image_init(void) { - image_map = cxHashMapCreateSimple(CX_STORE_POINTERS); + image_map = cxHashMapCreate(NULL, CX_STORE_POINTERS, 16); icon_theme = ICONTHEME_GET_DEFAULT(); }
--- a/ui/gtk/list.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/list.c Thu Dec 18 17:50:15 2025 +0100 @@ -822,20 +822,19 @@ view->selection.count = 0; view->selection.rows = NULL; - CX_ARRAY_DECLARE(int, newselection); - cx_array_initialize(newselection, 8); - size_t nitems = g_list_model_get_n_items(G_LIST_MODEL(view->liststore)); + int *newselection = calloc(nitems, sizeof(int)); + int selection_size = 0; for(size_t i=0;i<nitems;i++) { if(gtk_selection_model_is_selected(view->selectionmodel, i)) { int s = (int)i; - cx_array_simple_add(newselection, s); + newselection[selection_size++] = s; } } - if(newselection_size > 0) { - view->selection.count = newselection_size; + if(selection_size > 0) { + view->selection.count = selection_size; view->selection.rows = newselection; } else { free(newselection); @@ -2234,7 +2233,7 @@ uisublist.numitems = 0; uisublist.header = sublist->header ? strdup(sublist->header) : NULL; uisublist.separator = sublist->separator; - uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS); + uisublist.widgets = cxLinkedListCreate(NULL, CX_STORE_POINTERS); uisublist.listbox = uilistbox; uisublist.userdata = sublist->userdata; uisublist.index = cxListSize(sublists); @@ -2285,7 +2284,7 @@ uilistbox->onactivatedata = args->onactivatedata; uilistbox->onbuttonclick = args->onbuttonclick; uilistbox->onbuttonclickdata = args->onbuttonclickdata; - uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4); + uilistbox->sublists = cxArrayListCreate(NULL, sizeof(UiListBoxSubList), 4); uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy; uilistbox->sublists->collection.destructor_data = obj; uilistbox->first_row = NULL; @@ -2373,7 +2372,7 @@ } cxListFree(uilistbox->sublists); - CxList *new_sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), list->count(list)); + CxList *new_sublists = cxArrayListCreate(NULL, sizeof(UiListBoxSubList), list->count(list)); uilistbox->sublists = new_sublists; UiSubList *sublist = list->first(list);
--- a/ui/gtk/menu.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/menu.c Thu Dec 18 17:50:15 2025 +0100 @@ -130,7 +130,7 @@ gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget); if(i->states) { - CxList *states = cxArrayListCreateSimple(sizeof(int), i->nstates); + CxList *states = cxArrayListCreate(NULL, sizeof(int), i->nstates); cxListAddArray(states, i->states, i->nstates); uic_add_state_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, states); cxListFree(states); @@ -220,7 +220,7 @@ // binding object is a list of all connected UiActiveMenuItemList. CxList *bindings = list->obj; if(!bindings) { - bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS); + bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, CX_STORE_POINTERS); list->obj = bindings; } cxListAdd(bindings, ls); @@ -463,7 +463,7 @@ g_object_unref(action); if(i->states) { - CxList *groups = cxArrayListCreateSimple(sizeof(int), i->nstates); + CxList *groups = cxArrayListCreate(NULL, sizeof(int), i->nstates); cxListAddArray(groups, i->states, i->nstates); uic_add_state_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups); cxListFree(groups); @@ -569,7 +569,7 @@ static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item, GSimpleAction *action) { UiMenuRadioGroup *group = cxZalloc(obj->ctx->allocator, sizeof(UiMenuRadioGroup)); - group->callbacks = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(UiCallbackData), 8); + group->callbacks = cxArrayListCreate(obj->ctx->allocator, sizeof(UiCallbackData), 8); group->var = uic_create_var(ui_global_context(), item->varname, UI_VAR_INTEGER); group->obj = obj; group->action = action; @@ -577,7 +577,7 @@ UiInteger *i = group->var->value; CxList *bindings = i->obj; if(!bindings) { - bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS); + bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, CX_STORE_POINTERS); i->obj = bindings; i->set = ui_action_set_state; i->get = ui_action_get_state; @@ -711,7 +711,7 @@ // binding object is a list of all connected UiActiveMenuItemList. CxList *bindings = list->obj; if(!bindings) { - bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS); + bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, CX_STORE_POINTERS); list->obj = bindings; } cxListAdd(bindings, ls);
--- a/ui/gtk/text.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/text.c Thu Dec 18 17:50:15 2025 +0100 @@ -756,7 +756,7 @@ // TODO: move to common static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { cxstring *pathelms; - size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms); if (nelm == 0) { *ret_nelm = 0; @@ -1042,7 +1042,7 @@ gtk_box_append(GTK_BOX(pathtf->hbox), button); - if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), CX_STR("/"))) { + if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), cx_str("/"))) { GtkWidget *path_separator = gtk_label_new("/"); gtk_widget_add_css_class(path_separator, "pathbar-button-inactive"); gtk_box_append(GTK_BOX(pathtf->hbox), path_separator);
--- a/ui/gtk/toolkit.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/gtk/toolkit.c Thu Dec 18 17:50:15 2025 +0100 @@ -489,7 +489,7 @@ } if(style_classes) { cxstring *cls = NULL; - size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), CX_STR(" "), 128, &cls); + size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), cx_str(" "), 128, &cls); for(int i=0;i<numClasses;i++) { cxmutstr m = cx_strdup(cls[i]); #if GTK_MAJOR_VERSION >= 4
--- a/ui/motif/button.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/motif/button.c Thu Dec 18 17:50:15 2025 +0100 @@ -304,7 +304,7 @@ if(!rb) { // first button in the radiobutton group // create a list for all buttons and use the list as value obj - rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); + rb = cxArrayListCreate(NULL, CX_STORE_POINTERS, 4); value->obj = rb; value->get = ui_radiobutton_get; value->set = ui_radiobutton_set; @@ -374,7 +374,7 @@ if(!rb) { // first button in the radiobutton group // create a list for all buttons and use the list as value obj - rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); + rb = cxArrayListCreate(NULL, CX_STORE_POINTERS, 4); value->obj = rb; value->get = ui_radiobutton_get; value->set = ui_radiobutton_set;
--- a/ui/motif/container.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/motif/container.c Thu Dec 18 17:50:15 2025 +0100 @@ -448,7 +448,7 @@ tabview->select = ui_motif_tabview_select; tabview->add = ui_motif_tabview_add_tab; tabview->remove = ui_motif_tabview_remove; - tabview->tabs = cxArrayListCreate(obj->ctx->allocator, cx_cmp_ptr, sizeof(UiTab), 8); + tabview->tabs = cxArrayListCreate(obj->ctx->allocator, sizeof(UiTab), 8); tabview->current_index = -1; UiTabViewContainer *ct = ui_malloc(obj->ctx, sizeof(UiTabViewContainer));
--- a/ui/motif/menu.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/motif/menu.c Thu Dec 18 17:50:15 2025 +0100 @@ -244,7 +244,7 @@ // binding object is a list of all connected UiActiveMenuItemList. CxList *bindings = list->obj; if(!bindings) { - bindings = cxLinkedListCreate(ls->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS); + bindings = cxLinkedListCreate(ls->var->from_ctx->mp->allocator, CX_STORE_POINTERS); list->obj = bindings; } cxListAdd(bindings, ls);
--- a/ui/motif/pathbar.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/motif/pathbar.c Thu Dec 18 17:50:15 2025 +0100 @@ -170,7 +170,7 @@ static cxmutstr concat_path_s(cxstring base, cxstring path) { if(!path.ptr) { - path = CX_STR(""); + path = cx_str(""); } int add_separator = 0; @@ -186,7 +186,7 @@ cxmutstr url; if(add_separator) { - url = cx_strcat(3, base, CX_STR("/"), path); + url = cx_strcat(3, base, cx_str("/"), path); } else { url = cx_strcat(2, base, path); }
--- a/ui/server/button.c Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/server/button.c Thu Dec 18 17:50:15 2025 +0100 @@ -74,7 +74,7 @@ UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { UiCallbackWidget *widget = cxZalloc(obj->ctx->allocator, sizeof(UiCallbackWidget)); widget->widget.obj = obj->widget->obj; - widget->widget.type = CX_STR("button"); + widget->widget.type = cx_str("button"); widget->widget.args = ui_button_args_to_string(obj->ctx, args); widget->widget.serialize = (ui_serialize_func)ui_button_serialize; widget->callback = args->onclick;
--- a/ui/winui/text.cpp Wed Dec 17 18:31:20 2025 +0100 +++ b/ui/winui/text.cpp Thu Dec 18 17:50:15 2025 +0100 @@ -327,7 +327,7 @@ static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { cxstring *pathelms; - size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms); if (nelm == 0) { *ret_nelm = 0;