Sun, 14 Dec 2025 21:14:34 +0100
first draft for simplifying the low-level array API - relates to #619
| src/array_list.c | file | annotate | diff | comparison | revisions | |
| src/cx/array_list.h | file | annotate | diff | comparison | revisions | |
| tests/test_list.c | file | annotate | diff | comparison | revisions |
--- a/src/array_list.c Sun Dec 14 17:30:17 2025 +0100 +++ b/src/array_list.c Sun Dec 14 21:14:34 2025 +0100 @@ -218,6 +218,43 @@ 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; +} + +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) { + if (array->size >= array->capacity) { + size_t newcap = cx_array_increase_capacity(array->capacity); + if (cxReallocateArray(allocator, &array->data, newcap, elem_size)) { + return -1; + } + array->capacity = newcap; + } + char *dst = array->data; + dst += elem_size * array->size; + memcpy(dst, element, elem_size); + array->size++; + return 0; +} + +void cx_array_free_(const CxAllocator *allocator, CxArray *array) { + cxFree(allocator, array->data); + array->data = NULL; + array->size = array->capacity = 0; +} + int cx_array_copy( void **target, void *size,
--- a/src/cx/array_list.h Sun Dec 14 17:30:17 2025 +0100 +++ b/src/cx/array_list.h Sun Dec 14 21:14:34 2025 +0100 @@ -49,6 +49,40 @@ */ CX_EXPORT extern const unsigned cx_array_swap_sbo_size; +#define CX_ARRAY(type, name) \ + struct { \ + type *data; \ + size_t size; \ + size_t capacity; \ + } name + +typedef struct cx_array_s { + void *data; + size_t size; + size_t capacity; +} CxArray; + +cx_attr_nonnull +CX_EXPORT int cx_array_init_(CxArray *array, const CxAllocator *allocator, 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_init_a(array, allocator, capacity) cx_array_init_((CxArray*)&array, allocator, 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); + +#define cx_array_add(array, element) cx_array_add_((CxArray*)&array, cxDefaultAllocator, sizeof((array).data[0]), element) + +#define cx_array_add_a(array, allocator, element) cx_array_add_((CxArray*)&array, cxDefaultAllocator, sizeof((array).data[0]), element) + +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_a(allocator, array) cx_array_free_(allocator, (CxArray*)&array) + /** * Declares variables for an array that can be used with the convenience macros. * @@ -372,32 +406,6 @@ #define cx_array_simple_reserve(array, count) \ cx_array_simple_reserve_a(NULL, array, count) -/** - * 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. - * - * 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. - * - * @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 - * @retval zero success - * @retval non-zero failure - */ -#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) /** * Convenience macro that uses cx_array_add() with a default layout and
--- a/tests/test_list.c Sun Dec 14 17:30:17 2025 +0100 +++ b/tests/test_list.c Sun Dec 14 21:14:34 2025 +0100 @@ -38,143 +38,41 @@ #include <errno.h> CX_TEST(test_array_add) { - CX_ARRAY_DECLARE(int, arr); - arr = cxCallocDefault(5, sizeof(int)); - arr[0] = 2; - arr[1] = 3; - arr[2] = 5; - arr[3] = 7; - arr[4] = 11; - arr_size = 3; - arr_capacity = 5; - int elem = 8, elem2 = 47; - int result; - CX_TEST_DO { - result = cx_array_simple_add(arr, elem); - CX_TEST_ASSERT(result == 0); - CX_TEST_ASSERT(arr[0] == 2); - CX_TEST_ASSERT(arr[1] == 3); - CX_TEST_ASSERT(arr[2] == 5); - CX_TEST_ASSERT(arr[3] == 8); - CX_TEST_ASSERT(arr[4] == 11); - CX_TEST_ASSERT(arr_size == 4); - CX_TEST_ASSERT(arr_capacity == 5); - - arr_size = 5; - result = cx_array_simple_add(arr, elem2); - CX_TEST_ASSERT(result == 0); - CX_TEST_ASSERT(arr[0] == 2); - CX_TEST_ASSERT(arr[1] == 3); - CX_TEST_ASSERT(arr[2] == 5); - CX_TEST_ASSERT(arr[3] == 8); - CX_TEST_ASSERT(arr[4] == 11); - CX_TEST_ASSERT(arr[5] == 47); - CX_TEST_ASSERT(arr_size == 6); - CX_TEST_ASSERT(arr_capacity >= 6); - } - cxFreeDefault(arr); -} - -CX_TEST(test_array_add8) { - CX_ARRAY_DECLARE_SIZED(int, arr, uint8_t); - arr = cxCallocDefault(5, sizeof(int)); - arr[0] = 2; - arr[1] = 3; - arr[2] = 5; - arr[3] = 7; - arr[4] = 11; - arr_size = 3; - arr_capacity = 5; + CX_ARRAY(int, arr); + cx_array_init(arr, 5); + arr.data[0] = 2; + arr.data[1] = 3; + arr.data[2] = 5; + arr.data[3] = 7; + arr.data[4] = 11; + arr.size = 3; + arr.capacity = 5; int elem = 8, elem2 = 47; int result; CX_TEST_DO { - result = cx_array_simple_add(arr, elem); - CX_TEST_ASSERT(result == 0); - CX_TEST_ASSERT(arr[0] == 2); - CX_TEST_ASSERT(arr[1] == 3); - CX_TEST_ASSERT(arr[2] == 5); - CX_TEST_ASSERT(arr[3] == 8); - CX_TEST_ASSERT(arr[4] == 11); - CX_TEST_ASSERT(arr_size == 4); - CX_TEST_ASSERT(arr_capacity == 5); - - arr_size = 5; - result = cx_array_simple_add(arr, elem2); - CX_TEST_ASSERT(result == 0); - CX_TEST_ASSERT(arr[0] == 2); - CX_TEST_ASSERT(arr[1] == 3); - CX_TEST_ASSERT(arr[2] == 5); - CX_TEST_ASSERT(arr[3] == 8); - CX_TEST_ASSERT(arr[4] == 11); - CX_TEST_ASSERT(arr[5] == 47); - CX_TEST_ASSERT(arr_size == 6); - CX_TEST_ASSERT(arr_capacity >= 6); - - result = cx_array_simple_copy(arr, 260, &elem, 1); - CX_TEST_ASSERT(result != 0); - CX_TEST_ASSERT(errno == EOVERFLOW); - CX_TEST_ASSERT(arr_size == 6); - CX_TEST_ASSERT(arr_capacity < 128); - } - cxFreeDefault(arr); -} - -CX_TEST(test_array_add16) { - CX_ARRAY_DECLARE_SIZED(char, arr, uint16_t); - cx_array_initialize(arr, 300); - arr_size = 270; - int result; - CX_TEST_DO { - char elem = 'A'; - result = cx_array_simple_add(arr, elem); + result = cx_array_add(arr, &elem); CX_TEST_ASSERT(result == 0); - CX_TEST_ASSERT(arr[270] == 'A'); - CX_TEST_ASSERT(arr_size == 271); - CX_TEST_ASSERT(arr_capacity == 300); - - char *hello = "Hello"; - result = cx_array_simple_copy(arr, 9000, hello, 5); + CX_TEST_ASSERT(arr.data[0] == 2); + CX_TEST_ASSERT(arr.data[1] == 3); + CX_TEST_ASSERT(arr.data[2] == 5); + CX_TEST_ASSERT(arr.data[3] == 8); + CX_TEST_ASSERT(arr.data[4] == 11); + CX_TEST_ASSERT(arr.size == 4); + CX_TEST_ASSERT(arr.capacity == 5); + + arr.size = 5; + result = cx_array_add(arr, &elem2); CX_TEST_ASSERT(result == 0); - CX_TEST_ASSERT(arr[9000] == 'H'); - CX_TEST_ASSERT(arr[9001] == 'e'); - CX_TEST_ASSERT(arr[9002] == 'l'); - CX_TEST_ASSERT(arr[9003] == 'l'); - CX_TEST_ASSERT(arr[9004] == 'o'); - CX_TEST_ASSERT(arr_size == 9005); - CX_TEST_ASSERT(arr_capacity == 9*1024); - - // does not fit into 16-bit sized array - result = cx_array_simple_copy(arr, 65532, hello, 5); - CX_TEST_ASSERT(result != 0); - CX_TEST_ASSERT(errno == EOVERFLOW); - CX_TEST_ASSERT(arr_size == 9005); - CX_TEST_ASSERT(arr_capacity == 9*1024); + CX_TEST_ASSERT(arr.data[0] == 2); + CX_TEST_ASSERT(arr.data[1] == 3); + CX_TEST_ASSERT(arr.data[2] == 5); + CX_TEST_ASSERT(arr.data[3] == 8); + CX_TEST_ASSERT(arr.data[4] == 11); + CX_TEST_ASSERT(arr.data[5] == 47); + CX_TEST_ASSERT(arr.size == 6); + CX_TEST_ASSERT(arr.capacity >= 6); } - cxFreeDefault(arr); -} - -CX_TEST(test_array_copy_unsupported_width) { - CX_ARRAY_DECLARE_SIZED(int, arr, uint16_t); - cx_array_initialize(arr, 16); - int result; - CX_TEST_DO { - int elem = 5; - result = cx_array_copy( - (void **) &(arr), - &(arr_size), - &(arr_capacity), - 12, // unsupported width - 5, - &elem, sizeof(int), - 1, - cx_array_default_reallocator - ); - CX_TEST_ASSERT(result != 0); - CX_TEST_ASSERT(errno == EINVAL); - CX_TEST_ASSERT(arr_size == 0); - CX_TEST_ASSERT(arr_capacity == 16); - } - cxFreeDefault(arr); + cx_array_free(arr); } CX_TEST(test_array_copy_overlap) { @@ -3450,9 +3348,6 @@ CxTestSuite *suite = cx_test_suite_new("array_list"); cx_test_register(suite, test_array_add); - cx_test_register(suite, test_array_add8); - cx_test_register(suite, test_array_add16); - cx_test_register(suite, test_array_copy_unsupported_width); cx_test_register(suite, test_array_copy_overlap); cx_test_register(suite, test_array_reserve); cx_test_register(suite, test_array_reserve_unsupported_width);