first draft for simplifying the low-level array API - relates to #619

Sun, 14 Dec 2025 21:14:34 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 14 Dec 2025 21:14:34 +0100
changeset 1606
f5883f6e42e7
parent 1605
55b13f583356
child 1607
0ecb13118cac

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);

mercurial