# HG changeset patch # User Mike Becker # Date 1765750668 -3600 # Node ID 0ecb13118cac3781ab5ba358947c68faa3490cd8 # Parent f5883f6e42e7ecca0531f6faa6920699d588bcc4 next step of simplifying the array API - relates to #619 diff -r f5883f6e42e7 -r 0ecb13118cac CHANGELOG --- a/CHANGELOG Sun Dec 14 21:14:34 2025 +0100 +++ b/CHANGELOG Sun Dec 14 23:17:48 2025 +0100 @@ -2,6 +2,7 @@ ------------------------ * adds cx_system_page_size() to allocator.h + * adds a new (optional) capacity parameter to cxJsonCreateArr() and cxJsonObjPutArr() * adds cxJsonFromString(), cxJsonToString(), and cxJsonToPrettyString() * adds cxJsonClone() * adds cxJsonCompare() @@ -35,6 +36,7 @@ * removes the sort_members feature from CxJsonWriter * removes the source and sink API from properties.h * removes the flush feature from CxBuffer + * removes the ability to remove elements from the iterators created with cxIterator() and cxIteratorPtr() * removes several unnecessary convenience functions Version 3.2 - 2025-11-30 diff -r f5883f6e42e7 -r 0ecb13118cac docs/Writerside/topics/about.md --- a/docs/Writerside/topics/about.md Sun Dec 14 21:14:34 2025 +0100 +++ b/docs/Writerside/topics/about.md Sun Dec 14 23:17:48 2025 +0100 @@ -29,6 +29,7 @@ ### Version 4.0 - preview {collapsible="true"} * adds cx_system_page_size() to allocator.h +* adds a new (optional) capacity parameter to cxJsonCreateArr() and cxJsonObjPutArr() * adds cxJsonFromString(), cxJsonToString(), and cxJsonToPrettyString() * adds cxJsonClone() * adds cxJsonCompare() @@ -62,6 +63,7 @@ * removes the sort_members feature from CxJsonWriter * removes the source and sink API from properties.h * removes the flush feature from CxBuffer +* removes the ability to remove elements from the iterators created with cxIterator() and cxIteratorPtr() * removes several unnecessary convenience functions ### Version 3.2 - 2025-11-30 {collapsible="true"} diff -r f5883f6e42e7 -r 0ecb13118cac docs/Writerside/topics/iterator.h.md --- a/docs/Writerside/topics/iterator.h.md Sun Dec 14 21:14:34 2025 +0100 +++ b/docs/Writerside/topics/iterator.h.md Sun Dec 14 23:17:48 2025 +0100 @@ -27,21 +27,23 @@ #include 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); CxIterator cxIteratorPtr(const void *array, - size_t elem_count, - bool remove_keeps_order); + size_t elem_count); ``` The `cxIterator()` function creates an iterator over the elements of `array` where each element is `elem_size` bytes large and the array contains a total of `elem_count` elements. -The `cxIteratorPtr()` function is equivalent to `cxIterator()`, except it assumes `sizeof(void*)` as the `elem_size`. +The `cxIteratorPtr()` on the other hand assumes `sizeof(void*)` as the `elem_size` and automatically dereferences the pointers to the array elements. +That means, values returned by the iterator created with `cxIteratorPtr()` are the pointers stored in the array, +while iterators created `cxIterator()` yield the pointers to the elements in the array. + +Iterators created with the above functions are not allowed to remove elements because they are unable to update the size of the array they are iterating over. The UCX collections also define functions for creating iterators over their items. -You can read more about them in the respective Sections of the documentation. +You can read more about them in the respective sections of the documentation. ## Using an Iterator diff -r f5883f6e42e7 -r 0ecb13118cac docs/Writerside/topics/json.h.md --- a/docs/Writerside/topics/json.h.md Sun Dec 14 21:14:34 2025 +0100 +++ b/docs/Writerside/topics/json.h.md Sun Dec 14 23:17:48 2025 +0100 @@ -201,7 +201,8 @@ CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); -CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); +CxJsonValue* cxJsonCreateArr( + const CxAllocator* allocator, size_t capacity); CxJsonValue* cxJsonCreateNumber( const CxAllocator* allocator, double num); @@ -237,7 +238,8 @@ CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, AnyStr name); -CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, AnyStr name); +CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, + AnyStr name, size_t capacity); CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, AnyStr name, double num); @@ -259,10 +261,16 @@ you can use `cxJsonArrAddValues()` or `cxJsonObjPut()`, respectively. However, it is usually more convenient to use one of the other functions, as they automatically create the JSON value for. +> The `capacity` argument for `cxJsonCreateArr()` is optional. +> When you specify zero, no memory for the data is allocated up-front, and the array +> is reallocated every time you add elements. +> If you can at least roughly estimate the number of elements the array will have, +> you can specify an initial capacity to save allocations. + ```C #include -CxJsonValue* arr = cxJsonCreateArr(NULL); +CxJsonValue* arr = cxJsonCreateArr(NULL, 2); // this is equivalent... CxJsonValue* x = cxJsonCreateInteger(NULL, 47); @@ -281,11 +289,11 @@ cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); cxJsonObjPutInteger(obj, "int", 47); -CxJsonValue *strings = cxJsonObjPutArr(obj, "strings"); +CxJsonValue *strings = cxJsonObjPutArr(obj, "strings", 2); cxJsonArrAddStrings(strings, (const char*[]) {"hello", "world"}, 2); CxJsonValue *nested = cxJsonObjPutObj(obj, "nested"); -CxJsonValue *objects = cxJsonObjPutArr(nested, "objects"); +CxJsonValue *objects = cxJsonObjPutArr(nested, "objects", 2); CxJsonValue *obj_in_arr[2] = { cxJsonCreateObj(NULL), cxJsonCreateObj(NULL), @@ -298,16 +306,16 @@ cxJsonArrAddValues(objects, obj_in_arr, 2); -cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"), +cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats", 3), (double[]){3.1415, 47.11, 8.15}, 3); -cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"), +cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals", 3), (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); -CxJsonValue *ints = cxJsonObjPutArr(nested, "ints"); +CxJsonValue *ints = cxJsonObjPutArr(nested, "ints", 3); cxJsonArrAddIntegers(ints, (int64_t[]){4, 8, 15}, 3); -CxJsonValue *nested_array = cxJsonCreateArr(NULL); +CxJsonValue *nested_array = cxJsonCreateArr(NULL, 2); cxJsonArrAddValues(ints, &nested_array, 1); cxJsonArrAddIntegers(nested_array, (int64_t[]){16, 23}, 2); cxJsonArrAddIntegers(ints, (int64_t[]){42}, 1); diff -r f5883f6e42e7 -r 0ecb13118cac src/array_list.c --- a/src/array_list.c Sun Dec 14 21:14:34 2025 +0100 +++ b/src/array_list.c Sun Dec 14 23:17:48 2025 +0100 @@ -32,6 +32,32 @@ #include #include +// LOW LEVEL ARRAY LIST FUNCTIONS + +/** + * Intelligently calculates a new capacity, reserving some more + * elements than required to prevent too many allocations. + * + * @param current_capacity the current capacity of the array + * @param needed_capacity the required capacity of the array + * @return the new capacity + */ +static size_t cx_array_grow_capacity( + size_t current_capacity, + size_t needed_capacity +) { + if (current_capacity >= needed_capacity) { + return current_capacity; + } + size_t cap = needed_capacity; + size_t alignment; + if (cap < 128) alignment = 16; + else if (cap < 1024) alignment = 64; + else if (cap < 8192) alignment = 512; + else alignment = 1024; + return cap - (cap % alignment) + alignment; +} + // Default array reallocator static void *cx_array_default_realloc( @@ -102,141 +128,33 @@ }; } -// LOW LEVEL ARRAY LIST FUNCTIONS - -/** - * Intelligently calculates a new capacity, reserving some more - * elements than required to prevent too many allocations. - * - * @param current_capacity the current capacity of the array - * @param needed_capacity the required capacity of the array - * @return the new capacity - */ -static size_t cx_array_grow_capacity( - size_t current_capacity, - size_t needed_capacity -) { - if (current_capacity >= needed_capacity) { - return current_capacity; - } - size_t cap = needed_capacity; - size_t alignment; - if (cap < 128) alignment = 16; - else if (cap < 1024) alignment = 64; - else if (cap < 8192) alignment = 512; - else alignment = 1024; - return cap - (cap % alignment) + alignment; +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); } -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); - - // 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; +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 } -#endif - else { - errno = EINVAL; - return 1; - } - - // 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; - } - - // 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->capacity = capacity; return 0; } -int cx_array_init_(CxArray *array, const CxAllocator *allocator, size_t elem_size, size_t capacity) { - array->size = 0; - array->capacity = capacity; - array->data = cxCalloc(allocator, capacity, elem_size); - return array->data == NULL; +int cx_array_move_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; } -static size_t cx_array_increase_capacity(size_t old_capacity) { - // TODO: make this strategy better - if (old_capacity < 1024) { - return old_capacity + 64; - } else { - return old_capacity + 128; - } -} - -int cx_array_add_(CxArray *array, const CxAllocator *allocator, size_t elem_size, void *element) { +int cx_array_add_(const CxAllocator *allocator, CxArray *array, size_t elem_size, void *element) { if (array->size >= array->capacity) { - size_t newcap = cx_array_increase_capacity(array->capacity); + size_t newcap = cx_array_grow_capacity(array->capacity, array->capacity + 1); if (cxReallocateArray(allocator, &array->data, newcap, elem_size)) { return -1; } @@ -249,6 +167,50 @@ return 0; } +int cx_array_insert_array_(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 insert position + char *arl_data = array->data; + char *insert_pos = arl_data + index * elem_size; + + // do we need to move some elements? + if (index < array->size) { + size_t elems_to_move = array->size - index; + char *target = insert_pos + n * elem_size; + memmove(target, insert_pos, elems_to_move * elem_size); + } + + // place the new elements, if any + // otherwise, this function just reserved the memory (a.k.a emplace) + if (other != NULL) { + memcpy(insert_pos, other, n * elem_size); + } + array->size += n; + + return 0; +} + +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; @@ -831,42 +793,17 @@ 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_array_(list->collection.allocator, &wrap, + list->collection.elem_size, index, array, n)) { + return 0; } - - // determine insert position - char *arl_data = arl->data; - char *insert_pos = arl_data + index * list->collection.elem_size; - - // do we need to move some elements? - if (index < list->collection.size) { - size_t elems_to_move = list->collection.size - index; - char *target = insert_pos + n * list->collection.elem_size; - memmove(target, insert_pos, elems_to_move * list->collection.elem_size); - } - - // place the new elements, if any - if (array != NULL) { - memcpy(insert_pos, array, n * list->collection.elem_size); - } - list->collection.size += n; - + arl->data = wrap.data; + arl->capacity = wrap.capacity; + list->collection.size = wrap.size; return n; } diff -r f5883f6e42e7 -r 0ecb13118cac src/cx/array_list.h --- a/src/cx/array_list.h Sun Dec 14 21:14:34 2025 +0100 +++ b/src/cx/array_list.h Sun Dec 14 23:17:48 2025 +0100 @@ -63,25 +63,65 @@ } CxArray; cx_attr_nonnull -CX_EXPORT int cx_array_init_(CxArray *array, const CxAllocator *allocator, size_t elem_size, size_t capacity); +CX_EXPORT int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); + +#define cx_array_init(array, capacity) cx_array_init_(cxDefaultAllocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +#define cx_array_init_a(allocator, array, capacity) cx_array_init_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +cx_attr_nonnull +CX_EXPORT int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); -#define cx_array_init(array, capacity) cx_array_init_((CxArray*)&array, cxDefaultAllocator, sizeof((array).data[0]), capacity) +#define cx_array_reserve(array, capacity) cx_array_reserve_(cxDefaultAllocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +#define cx_array_reserve_a(allocator, array, capacity) cx_array_reserve_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) -#define cx_array_init_a(array, allocator, capacity) cx_array_init_((CxArray*)&array, allocator, sizeof((array).data[0]), capacity) +cx_attr_nonnull +CX_EXPORT int cx_array_move_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); + +#define cx_array_move_to_new(array, capacity) cx_array_move_to_new_(cxDefaultAllocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +#define cx_array_move_to_new_a(allocator, array, capacity) cx_array_move_to_new_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) cx_attr_nonnull -CX_EXPORT int cx_array_add_(CxArray *array, const CxAllocator *allocator, size_t elem_size, void *element); +CX_EXPORT int cx_array_add_(const CxAllocator *allocator, CxArray *array, size_t elem_size, void *element); + +#define cx_array_add(array, element) cx_array_add_(cxDefaultAllocator, (CxArray*)&(array), sizeof((array).data[0]), element) + +#define cx_array_add_a(allocator, array, element) cx_array_add_(allocator, (CxArray*)&(array), sizeof((array).data[0]), element) + +cx_attr_nonnull_arg(1, 2) +CX_EXPORT int cx_array_insert_array_(const CxAllocator *allocator, CxArray *array, + size_t elem_size, size_t index, const void *other, size_t n); + +#define cx_array_insert_array(array, index, other, n) \ + cx_array_insert_array_(cxDefaultAllocator, (CxArray*)&(array), sizeof((array).data[0]), index, other, n) + +#define cx_array_insert_array_a(allocator, array, index, other, n) \ + cx_array_insert_array_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, other, n) -#define cx_array_add(array, element) cx_array_add_((CxArray*)&array, cxDefaultAllocator, sizeof((array).data[0]), element) +#define cx_array_add_array(array, other, n) \ + cx_array_insert_array_(cxDefaultAllocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, other, n) + +#define cx_array_add_array_a(allocator, array, other, n) \ + cx_array_insert_array_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, other, n) -#define cx_array_add_a(array, allocator, element) cx_array_add_((CxArray*)&array, cxDefaultAllocator, sizeof((array).data[0]), element) +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT CxIterator cx_array_iterator_(CxArray *array, size_t elem_size); + +#define cx_array_iterator(array) cx_array_iterator_((CxArray*)&(array), sizeof((array).data[0])) + +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT CxIterator cx_array_iterator_ptr_(CxArray *array); + +#define cx_array_iterator_ptr(array) cx_array_iterator_ptr_((CxArray*)&(array)) cx_attr_nonnull CX_EXPORT void cx_array_free_(const CxAllocator *allocator, CxArray *array); -#define cx_array_free(array) cx_array_free_(cxDefaultAllocator, (CxArray*)&array) +#define cx_array_free(array) cx_array_free_(cxDefaultAllocator, (CxArray*)&(array)) -#define cx_array_free_a(allocator, array) cx_array_free_(allocator, (CxArray*)&array) +#define cx_array_free_a(allocator, array) cx_array_free_(allocator, (CxArray*)&(array)) /** * Declares variables for an array that can be used with the convenience macros. @@ -104,7 +144,6 @@ * @param size_type the type of the size (should be uint8_t, uint16_t, uint32_t, or size_t) * * @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() @@ -133,7 +172,6 @@ * @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() @@ -260,45 +298,6 @@ */ CX_EXPORT CxArrayReallocator cx_array_reallocator( const struct cx_allocator_s *allocator, const void *stack_ptr); - -/** - * 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. - * - * 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(). - * - * 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 This function will reserve the minimum required capacity to hold - * the additional elements and does not perform an overallocation. - * - * @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() - */ -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); - /** * Copies elements from one array to another. * @@ -333,7 +332,6 @@ * @retval zero success * @retval non-zero failure * @see cx_array_reallocator() - * @see cx_array_reserve() */ cx_attr_nonnull_arg(1, 2, 3, 6) CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width, @@ -376,67 +374,6 @@ cx_array_simple_copy_a(NULL, array, index, src, count) /** - * Convenience macro that uses cx_array_reserve() with a default layout and - * the specified reallocator. - * - * @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 - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_reserve() - */ -#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) - -/** - * Convenience macro that uses cx_array_reserve() with a default layout and - * the default reallocator. - * - * @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 - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_reserve_a() - */ -#define cx_array_simple_reserve(array, count) \ - cx_array_simple_reserve_a(NULL, array, count) - - -/** - * Convenience macro that uses cx_array_add() with a default layout and - * the specified reallocator. - * - * @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) - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add() - */ -#define cx_array_simple_add_a(reallocator, array, elem) \ - cx_array_simple_copy_a(reallocator, array, array##_size, &(elem), 1) - -/** - * Convenience macro that uses cx_array_add() with a default layout and - * the default reallocator. - * - * @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 diff -r f5883f6e42e7 -r 0ecb13118cac src/cx/iterator.h --- a/src/cx/iterator.h Sun Dec 14 21:14:34 2025 +0100 +++ b/src/cx/iterator.h Sun Dec 14 23:17:48 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" diff -r f5883f6e42e7 -r 0ecb13118cac src/cx/json.h --- a/src/cx/json.h Sun Dec 14 21:14:34 2025 +0100 +++ b/src/cx/json.h Sun Dec 14 23:17:48 2025 +0100 @@ -183,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. */ @@ -209,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 { @@ -241,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. */ @@ -326,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. @@ -627,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. @@ -839,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. @@ -1237,7 +1228,7 @@ */ cx_attr_nonnull CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) { - return value->array.data_size; + return value->array.size; } /** diff -r f5883f6e42e7 -r 0ecb13118cac src/iterator.c --- a/src/iterator.c Sun Dec 14 21:14:34 2025 +0100 +++ b/src/iterator.c Sun Dec 14 23:17:48 2025 +0100 @@ -29,6 +29,7 @@ #include "cx/iterator.h" #include +#include 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; } diff -r f5883f6e42e7 -r 0ecb13118cac src/json.c --- a/src/json.c Sun Dec 14 21:14:34 2025 +0100 +++ b/src/json.c Sun Dec 14 23:17:48 2025 +0100 @@ -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_move_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,22 @@ 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->states.data = json->states_internal; + json->states.capacity = cx_nmemb(json->states_internal); + json->states.data[0] = JP_STATE_VALUE_BEGIN; + json->states.size = 1; - json->vbuf = json->vbuf_internal; - json->vbuf_capacity = cx_nmemb(json->vbuf_internal); + json->vbuf.data = json->vbuf_internal; + json->vbuf.capacity = cx_nmemb(json->vbuf_internal); } 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; @@ -584,9 +593,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 +616,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_move_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 + } } @@ -648,8 +665,8 @@ 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--; + json->vbuf.size--; + json->states.size--; return_rec(CX_JSON_NO_ERROR); } else { return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); @@ -708,7 +725,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); @@ -716,7 +733,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 @@ -731,7 +748,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); @@ -752,7 +769,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); @@ -777,17 +794,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; } @@ -796,7 +813,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; } @@ -842,11 +859,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: { @@ -875,14 +891,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; } @@ -1000,13 +1025,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) { @@ -1020,8 +1040,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; @@ -1056,23 +1076,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; } @@ -1105,11 +1125,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) { @@ -1465,8 +1481,8 @@ case CX_JSON_OBJECT: return cxMapCompare(json->object, other->object); case CX_JSON_ARRAY: - if (json->array.data_size != other->array.data_size) return -1; - for (size_t i = 0; i < json->array.data_size; i++) { + 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; } @@ -1527,17 +1543,10 @@ return_value(obj); } case CX_JSON_ARRAY: { - const size_t elem_count = source->array.data_size; - CxArrayReallocator reallocator = cx_array_reallocator(allocator, NULL); - CxJsonValue *arr = cxJsonCreateArr(allocator); + const size_t elem_count = source->array.size; + CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count); if (arr == NULL) return NULL; // LCOV_EXCL_LINE - if (cx_array_simple_reserve_a(&reallocator, arr->array.data, elem_count)) { - // LCOV_EXCL_START - cxJsonValueFree(arr); - return NULL; - // LCOV_EXCL_STOP - } - arr->array.data_size = elem_count; + 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) { diff -r f5883f6e42e7 -r 0ecb13118cac src/tree.c --- a/src/tree.c Sun Dec 14 21:14:34 2025 +0100 +++ b/src/tree.c Sun Dec 14 23:17:48 2025 +0100 @@ -28,8 +28,6 @@ #include "cx/tree.h" -#include "cx/array_list.h" - #include #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); } diff -r f5883f6e42e7 -r 0ecb13118cac tests/test_iterator.c --- a/tests/test_iterator.c Sun Dec 14 21:14:34 2025 +0100 +++ b/tests/test_iterator.c Sun Dec 14 23:17:48 2025 +0100 @@ -35,7 +35,7 @@ size_t size = cx_nmemb(array); for (unsigned i = 0 ; i < size ; i++) array[i] = i; - CxIterator iter = cxIterator(array, sizeof(unsigned), size, true); + CxIterator iter = cxIterator(array, sizeof(unsigned), size); CX_TEST_DO { CX_TEST_ASSERT(iter.index == 0); CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned)); @@ -47,7 +47,7 @@ } CX_TEST(test_iterator_create_null) { - CxIterator iter = cxIterator(NULL, sizeof(unsigned), 47, true); + CxIterator iter = cxIterator(NULL, sizeof(unsigned), 47); CX_TEST_DO { CX_TEST_ASSERT(iter.index == 0); CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned)); @@ -63,7 +63,7 @@ size_t size = cx_nmemb(array); for (unsigned i = 0 ; i < size ; i++) array[i] = i; - CxIterator iter = cxIterator(array, sizeof(unsigned), size, true); + CxIterator iter = cxIterator(array, sizeof(unsigned), size); CX_TEST_DO { CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned)); CX_TEST_ASSERT(iter.elem_count == size); @@ -88,7 +88,7 @@ ptr_array[i] = &array[i]; } - CxIterator iter = cxIteratorPtr(ptr_array, size, true); + CxIterator iter = cxIteratorPtr(ptr_array, size); CX_TEST_DO { CX_TEST_ASSERT(iter.elem_size == sizeof(void*)); CX_TEST_ASSERT(iter.elem_count == size); @@ -104,100 +104,12 @@ } } -CX_TEST(test_iterator_with_slow_remove) { - unsigned array[20]; - size_t size = cx_nmemb(array); - for (unsigned i = 0 ; i < size ; i++) array[i] = i; - - size_t elem_counts[] = { - 20, 20, 19, 19, 18, 18, 17, 17, 16, 16, - 15, 15, 14, 14, 13, 13, 12, 12, 11, 11 - }; - size_t indices[] = { - 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, - 6, 6, 7, 7, 8, 8, 9, 9, 10 - }; - unsigned expected_result[] = { - 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 - }; - - CxIterator iter = cxIterator(array, sizeof(unsigned), size, true); - CX_TEST_DO { - unsigned expected = 0; - cx_foreach(unsigned *, e, iter) { - CX_TEST_ASSERT(*e == expected); - CX_TEST_ASSERT(iter.index == indices[expected]); - CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned)); - CX_TEST_ASSERT(iter.elem_count == elem_counts[expected]); - CX_TEST_ASSERT(iter.src_handle == array); - CX_TEST_ASSERT(iter.elem_handle == &array[indices[expected]]); - expected++; - if (expected % 2 == 0) { - cxIteratorFlagRemoval(iter); - } - } - CX_TEST_ASSERT(expected == 20); - CX_TEST_ASSERT(iter.index == 10); - CX_TEST_ASSERT(iter.elem_count == 10); - for (unsigned i = 0 ; i < 9 ; i++) { - CX_TEST_ASSERT(array[i] == expected_result[i]); - } - } -} - -CX_TEST(test_iterator_with_fast_remove) { - unsigned array[20]; - size_t size = cx_nmemb(array); - for (unsigned i = 0 ; i < size ; i++) array[i] = i; - - size_t elem_counts[] = { - 20, 20, 19, 19, 18, 18, 17, 17, 16, 16, - 15, 15, 14, 14, 13, 13, 12, 12, 11, 11 - }; - size_t indices[] = { - 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, - 6, 6, 7, 7, 8, 8, 9, 9, 10 - }; - unsigned expected_result[] = { - 0, 19, 18, 17, 16, 15, 14, 13, 12, 11 - }; - unsigned expected_visits[] = { - 0, 1, 19, 2, 18, 3, 17, 4, 16, 5, - 15, 6, 14, 7, 13, 8, 12, 9, 11, 10 - }; - - CxIterator iter = cxIterator(array, sizeof(unsigned), size, false); - CX_TEST_DO { - unsigned expected = 0; - cx_foreach(unsigned *, e, iter) { - CX_TEST_ASSERT(*e == expected_visits[expected]); - CX_TEST_ASSERT(iter.index == indices[expected]); - CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned)); - CX_TEST_ASSERT(iter.elem_count == elem_counts[expected]); - CX_TEST_ASSERT(iter.src_handle == array); - CX_TEST_ASSERT(iter.elem_handle == &array[indices[expected]]); - expected++; - if (expected % 2 == 0) { - cxIteratorFlagRemoval(iter); - } - } - CX_TEST_ASSERT(expected == 20); - CX_TEST_ASSERT(iter.index == 10); - CX_TEST_ASSERT(iter.elem_count == 10); - for (unsigned i = 0 ; i < 9 ; i++) { - CX_TEST_ASSERT(array[i] == expected_result[i]); - } - } -} - CxTestSuite *cx_test_suite_iterator(void) { CxTestSuite *suite = cx_test_suite_new("iterator"); cx_test_register(suite, test_iterator_create); cx_test_register(suite, test_iterator_iterate); cx_test_register(suite, test_iterator_iterate_pointers); - cx_test_register(suite, test_iterator_with_slow_remove); - cx_test_register(suite, test_iterator_with_fast_remove); return suite; } diff -r f5883f6e42e7 -r 0ecb13118cac tests/test_json.c --- a/tests/test_json.c Sun Dec 14 21:14:34 2025 +0100 +++ b/tests/test_json.c Sun Dec 14 23:17:48 2025 +0100 @@ -38,12 +38,12 @@ CxJson json; CX_TEST_DO { cxJsonInit(&json, NULL); - CX_TEST_ASSERT(json.states == json.states_internal); - CX_TEST_ASSERT(json.states_size == 1); - CX_TEST_ASSERT(json.states_capacity >= 8); - CX_TEST_ASSERT(json.vbuf == json.vbuf_internal); - CX_TEST_ASSERT(json.vbuf_size == 0); - CX_TEST_ASSERT(json.vbuf_capacity >= 8); + CX_TEST_ASSERT(json.states.data == json.states_internal); + CX_TEST_ASSERT(json.states.size == 1); + CX_TEST_ASSERT(json.states.capacity >= 8); + CX_TEST_ASSERT(json.vbuf.data == json.vbuf_internal); + CX_TEST_ASSERT(json.vbuf.size == 0); + CX_TEST_ASSERT(json.vbuf.capacity >= 8); cxJsonDestroy(&json); } } @@ -174,7 +174,7 @@ CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(value); CX_TEST_ASSERT(cxJsonIsArray(value)); - CX_TEST_ASSERT(value->array.data_size == 3); + CX_TEST_ASSERT(value->array.size == 3); for(int i=0;i<3;i++) { CxJsonValue *v = cxJsonArrGet(value, i); CX_TEST_ASSERT(v); @@ -192,7 +192,7 @@ CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(value); CX_TEST_ASSERT(cxJsonIsArray(value)); - CX_TEST_ASSERT(value->array.data_size == 2); + CX_TEST_ASSERT(value->array.size == 2); CxJsonValue *s0 = cxJsonArrGet(value, 0); CxJsonValue *s1 = cxJsonArrGet(value, 1); CX_TEST_ASSERT(s0 && s1); @@ -210,7 +210,7 @@ CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(value); CX_TEST_ASSERT(cxJsonIsArray(value)); - CX_TEST_ASSERT(value->array.data_size == 5); + CX_TEST_ASSERT(value->array.size == 5); CxJsonValue *m0 = cxJsonArrGet(value, 0); CxJsonValue *m1 = cxJsonArrGet(value, 1); CxJsonValue *m2 = cxJsonArrGet(value, 2); @@ -888,8 +888,8 @@ CX_TEST_ASSERT(cxJsonIsInteger(d10)); CX_TEST_ASSERT(cxJsonAsInteger(d10) == 47); - CX_TEST_ASSERT(json.states != json.states_internal); - CX_TEST_ASSERT(json.states_capacity > cx_nmemb(json.states_internal)); + CX_TEST_ASSERT(json.states.data != json.states_internal); + CX_TEST_ASSERT(json.states.capacity > cx_nmemb(json.states_internal)); cxJsonValueFree(d1); cxJsonDestroy(&json); @@ -1088,7 +1088,7 @@ cx_testing_allocator_init(&talloc); const CxAllocator *allocator = &talloc.base; CX_TEST_DO { - CxJsonValue *arr = cxJsonCreateArr(allocator); + CxJsonValue *arr = cxJsonCreateArr(allocator, 0); cxJsonArrAddIntegers(arr, (int64_t[]){ 0, 3, 6, 9, 12, 15}, 6); CX_TEST_ASSERT(cxJsonArrSize(arr) == 6); CxJsonValue *e = cxJsonArrGet(arr, 3); @@ -1234,7 +1234,7 @@ { cxJsonObjPutLiteral(obj, "bool", CX_JSON_FALSE); cxJsonObjPutInteger(obj, "int", 47); - CxJsonValue *strings = cxJsonObjPutArr(obj, "strings"); + CxJsonValue *strings = cxJsonObjPutArr(obj, "strings", 0); CX_TEST_ASSERT(strings != NULL); CX_TEST_ASSERT(cxJsonIsArray(strings)); const char* str[] = {"hello", "world"}; @@ -1245,11 +1245,11 @@ CX_TEST_ASSERT(cxJsonIsObject(nested)); cxJsonObjPutString(nested, "string", "test"); - cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats"), + cxJsonArrAddNumbers(cxJsonObjPutArr(nested, "floats", 0), (double[]){3.1415, 47.11, 8.15}, 3); - cxJsonArrAddIntegers(cxJsonObjPutArr(nested, "ints"), + cxJsonArrAddIntegers(cxJsonObjPutArr(nested, "ints", 0), (int64_t[]){4, 8, 15, 16, 23, 42}, 6); - cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals"), + cxJsonArrAddLiterals(cxJsonObjPutArr(nested, "literals", 0), (CxJsonLiteral[]){CX_JSON_TRUE, CX_JSON_NULL, CX_JSON_FALSE}, 3); } @@ -1554,7 +1554,7 @@ CX_TEST_ASSERT(value); CX_TEST_ASSERT(cxJsonIsArray(value)); - CxJsonValue *value2 = cxJsonCreateArr(NULL); + CxJsonValue *value2 = cxJsonCreateArr(NULL, 16); int64_t ints[] = { 0, 1, 2, 3, 4, 5 }; cxJsonArrAddIntegers(value2, ints, 6); CX_TEST_ASSERT(cxJsonCompare(value, value2) == 0); @@ -1665,7 +1665,7 @@ cxJsonObjPutInteger(a[9], buf, i); - CxJsonValue *arr = cxJsonCreateArr(NULL); + CxJsonValue *arr = cxJsonCreateArr(NULL, TEST_CLONE_OBJECTS_LARGE_ARR); int64_t *ints = calloc(TEST_CLONE_OBJECTS_LARGE_ARR, sizeof(int64_t)); for(int n=0;n