removes some bugs by redesigning the array API

Thu, 07 Nov 2019 10:10:36 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 07 Nov 2019 10:10:36 +0100
changeset 369
28a8ccc442b0
parent 368
97c53f7ef5e4
child 370
07ac32b385e4

removes some bugs by redesigning the array API

src/array.c file | annotate | diff | comparison | revisions
src/ucx/array.h file | annotate | diff | comparison | revisions
test/array_tests.c file | annotate | diff | comparison | revisions
test/array_tests.h file | annotate | diff | comparison | revisions
test/main.c file | annotate | diff | comparison | revisions
--- a/src/array.c	Wed Nov 06 21:01:25 2019 +0100
+++ b/src/array.c	Thu Nov 07 10:10:36 2019 +0100
@@ -70,7 +70,7 @@
 }
 
 int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
-    size_t elmsize, size_t index, ...) {
+    size_t elmsize, size_t index, void* data) {
     
     if(!alloc || !capacity || !array) {
         errno = EINVAL;
@@ -104,16 +104,18 @@
     
     char* dest = *array;
     dest += elmsize*index;
-
-    va_list ap;
-    va_start(ap, index);
-    int elem = va_arg(ap, int);    
-    memcpy(dest, &elem, elmsize);
-    va_end(ap);
+    memcpy(dest, data, elmsize);
     
     return 0;
 }
 
+int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t index, void* data) {
+    
+    return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*),
+            index, &data);
+}
+
 UcxArray* ucx_array_new(size_t capacity, size_t elemsize) {
     return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
 }
@@ -254,33 +256,6 @@
     return 0;
 }
 
-int ucx_array_appendv(UcxArray *array, ...) {
-    va_list ap;
-    va_start(ap, array);
-    int elem = va_arg(ap, int);
-    int ret = ucx_array_append_from(array, &elem, 1);
-    va_end(ap);
-    return ret;
-}
-
-int ucx_array_prependv(UcxArray *array, ...) {
-    va_list ap;
-    va_start(ap, array);
-    int elem = va_arg(ap, int);
-    int ret = ucx_array_prepend_from(array, &elem, 1);
-    va_end(ap);
-    return ret;
-}
-
-int ucx_array_setv(UcxArray *array, size_t index, ...) {
-    va_list ap;
-    va_start(ap, index);
-    int elem = va_arg(ap, int);
-    int ret = ucx_array_set_from(array, index, &elem, 1);
-    va_end(ap);
-    return ret;
-}
-
 int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
     
     if (array1->elemsize != array2->elemsize)
@@ -486,3 +461,7 @@
         }
     }
 }
+
+int ucx_array_grow(UcxArray* array, size_t count) {
+    return ucx_array_reserve(array, array->size+count);
+}
--- a/src/ucx/array.h	Wed Nov 06 21:01:25 2019 +0100
+++ b/src/ucx/array.h	Thu Nov 07 10:10:36 2019 +0100
@@ -71,6 +71,7 @@
 
 /**
  * Sets an element in an arbitrary user defined array.
+ * The data is copied from the specified data location.
  * 
  * If the capacity is insufficient, the array is automatically reallocated and
  * the possibly new pointer is stored in the <code>array</code> argument.
@@ -82,7 +83,7 @@
  * @param capacity a pointer to the capacity
  * @param elmsize the size of each element
  * @param idx the index of the element to set
- * @param data the element data
+ * @param data a pointer to the element data
  * @return zero on success or non-zero on error (errno will be set)
  */
 #define ucx_array_util_set(array, capacity, elmsize, idx, data) \
@@ -90,22 +91,8 @@
                          elmsize, idx, data)
 
 /**
- * Convenience macro for ucx_array_util_set() which automatically computes
- * <code>sizeof(data)</code>.
- * 
- * @param array a pointer to location of the array pointer
- * @param capacity a pointer to the capacity
- * @param idx the index of the element to set
- * @param data the element data
- * @return zero on success or non-zero on error (errno will be set)
- * @see ucx_array_util_set()
- */
-#define UCX_ARRAY_UTIL_SET(array, capacity, idx, data) \
-    ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \
-                         sizeof(data), idx, data)
-
-/**
  * Sets an element in an arbitrary user defined array.
+ * The data is copied from the specified data location.
  * 
  * If the capacity is insufficient, the array is automatically reallocated
  * using the specified allocator and the possibly new pointer is stored in
@@ -119,27 +106,53 @@
  * @param capacity a pointer to the capacity
  * @param elmsize the size of each element
  * @param idx the index of the element to set
- * @param ... the element data
+ * @param data a pointer to the element data
  * @return zero on success or non-zero on error (errno will be set)
  */
 int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
-    size_t elmsize, size_t idx, ...);
-
+    size_t elmsize, size_t idx, void* data);
 
 /**
- * Convenience macro for ucx_array_util_set_a() which automatically computes
- * <code>sizeof(data)</code>.
+ * Stores a pointer in an arbitrary user defined array.
+ * The element size of the array must be sizeof(void*).
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated and
+ * the possibly new pointer is stored in the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *  
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param idx the index of the element to set
+ * @param ptr the pointer to store
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+#define ucx_array_util_setptr(array, capacity, idx, ptr) \
+    ucx_array_util_setptr_a(ucx_default_allocator(), (void**)(array), \
+                            capacity, idx, ptr)
+
+/**
+ * Stores a pointer in an arbitrary user defined array.
+ * The element size of the array must be sizeof(void*).
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated
+ * using the specified allocator and the possibly new pointer is stored in
+ * the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>. 
  * 
  * @param alloc the allocator that shall be used to reallocate the array
  * @param array a pointer to location of the array pointer
  * @param capacity a pointer to the capacity
  * @param idx the index of the element to set
- * @param data the element data
+ * @param ptr the pointer to store
  * @return zero on success or non-zero on error (errno will be set)
- * @see ucx_array_util_set_a()
  */
-#define UCX_ARRAY_UTIL_SET_A(alloc, array, capacity, idx, data) \
-    ucx_array_util_set_a(alloc, capacity, sizeof(data), idx, data)
+int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t idx, void* ptr);
+
 
 /**
  * Creates a new UCX array with the given capacity and element size.
@@ -291,88 +304,6 @@
 int ucx_array_set_from(UcxArray *array, size_t index, void *data, size_t count);
 
 /**
- * Inserts an element at the end of the array.
- * 
- * This is an O(1) operation.
- * The array will automatically grow, if the capacity is exceeded.
- * If the type of the argument has a different size than the element size of
- * this array, the behavior is undefined.
- * 
- * @param array a pointer the array where to append the data
- * @param elem the value to insert
- * @return zero on success, non-zero if a reallocation was necessary but failed
- * @see ucx_array_append_from()
- * @see ucx_array_set()
- */
-#define ucx_array_append(array, elem) ucx_array_appendv(array, elem)
-
-/**
- * For internal use.
- * Use ucx_array_append()
- * 
- * @param array
- * @param ... 
- * @return 
- * @see ucx_array_append()
- */
-int ucx_array_appendv(UcxArray *array, ...);
-
-
-/**
- * Inserts an element at the beginning of the array.
- * 
- * This is an expensive operation, because the contents must be moved.
- * If there is no particular reason to prepend data, you should use
- * ucx_array_append() instead.
- * 
- * @param array a pointer the array where to prepend the data
- * @param elem the value to insert
- * @return zero on success, non-zero if a reallocation was necessary but failed
- * @see ucx_array_append()
- * @see ucx_array_set_from()
- * @see ucx_array_prepend_from()
- */
-#define ucx_array_prepend(array, elem) ucx_array_prependv(array, elem)
-
-/**
- * For internal use.
- * Use ucx_array_prepend()
- * 
- * @param array
- * @param ... 
- * @return 
- * @see ucx_array_prepend()
- */
-int ucx_array_prependv(UcxArray *array, ...);
-
-
-/**
- * Sets an element at the specified index.
- * 
- * If the any index is out of bounds, the array automatically grows.
- * 
- * @param array a pointer the array where to set the data
- * @param index the index of the element to set
- * @param elem the value to set
- * @return zero on success, non-zero if a reallocation was necessary but failed
- * @see ucx_array_append()
- * @see ucx_array_set_from()
- */
-#define ucx_array_set(array, index, elem) ucx_array_setv(array, index, elem)
-
-/**
- * For internal use.
- * Use ucx_array_set()
- * 
- * @param array
- * @param index
- * @param ... 
- * @return 
- * @see ucx_array_set()
- */
-int ucx_array_setv(UcxArray *array, size_t index, ...);
-
-/**
  * Concatenates two arrays.
  * 
  * The contents of the second array are appended to the first array in one
@@ -507,6 +438,18 @@
  */
 int ucx_array_reserve(UcxArray* array, size_t capacity);
 
+/**
+ * Resizes the capacity, if the specified number of elements would not fit.
+ * 
+ * A call to ucx_array_grow(array, count) is effectively the same as
+ * ucx_array_reserve(array, array->size+count).
+ * 
+ * @param array a pointer to the array
+ * @param count the number of elements that should additionally fit
+ * into the array
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_grow(UcxArray* array, size_t count);
 
 
 #ifdef	__cplusplus
--- a/test/array_tests.c	Wed Nov 06 21:01:25 2019 +0100
+++ b/test/array_tests.c	Thu Nov 07 10:10:36 2019 +0100
@@ -212,124 +212,6 @@
     ucx_array_free(array);
 }
 
-UCX_TEST(test_ucx_array_append) {
-    UcxArray *array = ucx_array_new(16, sizeof(int));
-    int *elements;
-    
-    ucx_array_append(array, 42);
-    UCX_TEST_BEGIN
-    
-    elements = array->data;
-    UCX_TEST_ASSERT(elements[0] == 42, "failed");
-    
-    ucx_array_append(array, 13);
-    ucx_array_append(array, 37);
-    
-    elements = array->data;
-    UCX_TEST_ASSERT(array->size == 3, "incorrect size after append");
-    UCX_TEST_ASSERT(elements[1] == 13, "failed");
-    UCX_TEST_ASSERT(elements[2] == 37, "failed");
-    UCX_TEST_ASSERT(elements[0] == 42,
-            "append corrupted previously inserted data");
-    
-    UCX_TEST_END
-    
-    ucx_array_destroy(array);
-}
-
-UCX_TEST(test_ucx_array_append_struct) {
-    struct teststruct {
-        unsigned long long x;
-        unsigned long long y;
-        unsigned long long z;
-    };
-    
-    UcxArray *array = ucx_array_new(16, sizeof(struct teststruct));
-    struct teststruct *elements;
-    
-    struct teststruct data;
-    data.x = 13; data.y = 37; data.z = 47;
-    
-    ucx_array_append(array, data);
-    UCX_TEST_BEGIN
-    
-    elements = array->data;
-    UCX_TEST_ASSERT(elements[0].x == 13, "failed");
-    UCX_TEST_ASSERT(elements[0].y == 37, "failed");
-    UCX_TEST_ASSERT(elements[0].z == 47, "failed");
-    
-    data.x = 0; data.y = 8; data.z = 15;
-    ucx_array_append(array, data);
-    
-    elements = array->data;
-    UCX_TEST_ASSERT(array->size == 2, "incorrect size after append");
-    UCX_TEST_ASSERT(elements[1].x == 0, "failed");
-    UCX_TEST_ASSERT(elements[1].y == 8, "failed");
-    UCX_TEST_ASSERT(elements[1].z == 15, "failed");
-    
-    UCX_TEST_ASSERT(elements[0].x == 13,
-            "append corrupted previously inserted data");
-    UCX_TEST_ASSERT(elements[0].y == 37,
-            "append corrupted previously inserted data");
-    UCX_TEST_ASSERT(elements[0].z == 47,
-            "append corrupted previously inserted data");
-    
-    UCX_TEST_END
-    
-    ucx_array_destroy(array);
-}
-
-UCX_TEST(test_ucx_array_prepend) {
-    int *elems;
-    UcxArray *array = ucx_array_new(16, sizeof(int));
-    
-    ucx_array_prepend(array, 42);
-    UCX_TEST_BEGIN
-    
-    elems = array->data;
-    UCX_TEST_ASSERT(elems[0] == 42, "failed");
-    
-    ucx_array_prepend(array, 37);
-    ucx_array_prepend(array, 13);
-    
-    elems = array->data;
-    UCX_TEST_ASSERT(array->size == 3, "incorrect size after prepend");
-    UCX_TEST_ASSERT(elems[0] == 13, "failed");
-    UCX_TEST_ASSERT(elems[1] == 37, "failed");
-    UCX_TEST_ASSERT(elems[2] == 42,
-            "prepend corrupted previously inserted data");
-    
-    UCX_TEST_END
-    
-    ucx_array_free(array);
-}
-
-UCX_TEST(test_ucx_array_set) {
-    int *elems;
-    UcxArray *array = ucx_array_new(16, sizeof(int));
-
-    UCX_TEST_BEGIN
-
-    ucx_array_set(array, 7, 42);
-    
-    elems = array->data;
-    UCX_TEST_ASSERT(elems[7] == 42, "failed");
-    UCX_TEST_ASSERT(array->size == 8, "array not resized on set");
-    UCX_TEST_ASSERT(array->capacity == 16, "capacity changed unnecessarily");
-    
-    ucx_array_set(array, 27, 13);
-    ucx_array_set(array, 28, 37);
-    
-    elems = array->data;
-    UCX_TEST_ASSERT(elems[27] == 13, "failed");
-    UCX_TEST_ASSERT(elems[28] == 37, "failed");
-    UCX_TEST_ASSERT(array->size == 29, "array not resized on set");
-    UCX_TEST_ASSERT(array->capacity == 32, "capacity not grown");
-        
-    UCX_TEST_END
-    
-    ucx_array_free(array);
-}
 
 UCX_TEST(test_ucx_array_equals) {
     UcxArray *a1 = ucx_array_new(16, sizeof(int32_t));
@@ -643,32 +525,6 @@
     ucx_array_free(array);
 }
 
-UCX_TEST(test_ucx_array_autogrow) {
-    int *elems;
-    UcxArray *array = ucx_array_new(4, sizeof(int));
-    array->size = 3;
-    elems = array->data;
-    elems[0] = 47;
-    elems[1] = 11;
-    int x = 5;
-    
-    UCX_TEST_BEGIN
-
-    void* oldptr = array->data;
-    
-    ucx_array_append(array, 5);
-    UCX_TEST_ASSERT(array->capacity == 4 && array->data == oldptr,
-            "array should not grow too early");
-    ucx_array_append(array, 5);
-    elems = array->data;
-    UCX_TEST_ASSERT(array->capacity == 8, "array did not grow");
-    UCX_TEST_ASSERT(array->size == 5, "incorrect size after grow");
-    UCX_TEST_ASSERT(elems[3] == 5 && elems[4] == 5, "corrupt data");
-    
-    UCX_TEST_END
-    ucx_array_free(array);
-}
-
 UCX_TEST(test_ucx_array_shrink) {
     UcxArray *array = ucx_array_new(16, sizeof(int));
     array->size = 4;
@@ -713,19 +569,42 @@
     ucx_array_free(array);
 }
 
+UCX_TEST(test_ucx_array_grow) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    array->size = 12;
+    
+    UCX_TEST_BEGIN
+
+    UCX_TEST_ASSERT(!ucx_array_grow(array, 4), "failed");
+    UCX_TEST_ASSERT(array->capacity == 16, "shall be noop if contents fit");
+    /* subsequent calls shall also be noops */
+    UCX_TEST_ASSERT(!ucx_array_grow(array, 4), "failed");
+    UCX_TEST_ASSERT(array->capacity == 16, "shall be noop if contents fit");
+            
+    UCX_TEST_ASSERT(!ucx_array_grow(array, 6), "failed");
+    UCX_TEST_ASSERT(array->capacity == 18, "incorrect capacity after grow");    
+    
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
 UCX_TEST(test_ucx_array_util_set) {
     size_t capacity = 16;
     int* array = malloc(sizeof(int)*capacity);
+    int x;
 
     UCX_TEST_BEGIN
 
-    UCX_ARRAY_UTIL_SET(&array, &capacity, 7, 42);
+    x = 42;
+    ucx_array_util_set(&array, &capacity, sizeof(int), 7, &x);
     
     UCX_TEST_ASSERT(array[7] == 42, "failed");
     UCX_TEST_ASSERT(capacity == 16, "capacity changed unnecessarily");
     
-    UCX_ARRAY_UTIL_SET(&array, &capacity, 37, 13);
-    UCX_ARRAY_UTIL_SET(&array, &capacity, 38, 37);
+    x = 13;
+    ucx_array_util_set(&array, &capacity, sizeof(int), 37, &x);
+    x = 37;
+    ucx_array_util_set(&array, &capacity, sizeof(int), 38, &x);
     
     UCX_TEST_ASSERT(array[37] == 13, "failed");
     UCX_TEST_ASSERT(array[38] == 37, "failed");
@@ -735,3 +614,29 @@
     
     free(array);
 }
+
+
+UCX_TEST(test_ucx_array_util_setptr) {
+    size_t capacity = 16;
+    double** array = malloc(sizeof(double*)*capacity);
+    double x, y, z;
+
+    UCX_TEST_BEGIN
+
+    ucx_array_util_setptr(&array, &capacity, 7, &x);
+    
+    UCX_TEST_ASSERT(array[7] == &x, "failed");
+    UCX_TEST_ASSERT(capacity == 16, "capacity changed unnecessarily");
+    
+    ucx_array_util_setptr(&array, &capacity, 37, &y);
+    ucx_array_util_setptr(&array, &capacity, 38, &z);
+    
+    UCX_TEST_ASSERT(array[37] == &y, "failed");
+    UCX_TEST_ASSERT(array[38] == &z, "failed");
+    UCX_TEST_ASSERT(capacity == 64, "capacity not grown");
+        
+    UCX_TEST_END
+    
+    free(array);
+}
+
--- a/test/array_tests.h	Wed Nov 06 21:01:25 2019 +0100
+++ b/test/array_tests.h	Thu Nov 07 10:10:36 2019 +0100
@@ -43,11 +43,6 @@
 UCX_TEST(test_ucx_array_append_from_struct);
 UCX_TEST(test_ucx_array_prepend_from);
 UCX_TEST(test_ucx_array_set_from);
-UCX_TEST(test_ucx_array_append);
-UCX_TEST(test_ucx_array_append_struct);
-UCX_TEST(test_ucx_array_prepend);
-UCX_TEST(test_ucx_array_set);
-UCX_TEST(test_ucx_array_autogrow);
 UCX_TEST(test_ucx_array_equals);
 UCX_TEST(test_ucx_array_concat);
 UCX_TEST(test_ucx_array_find);
@@ -58,7 +53,9 @@
 UCX_TEST(test_ucx_array_shrink);
 UCX_TEST(test_ucx_array_resize);
 UCX_TEST(test_ucx_array_reserve);
+UCX_TEST(test_ucx_array_grow);
 UCX_TEST(test_ucx_array_util_set);
+UCX_TEST(test_ucx_array_util_setptr);
 
 #ifdef	__cplusplus
 }
--- a/test/main.c	Wed Nov 06 21:01:25 2019 +0100
+++ b/test/main.c	Thu Nov 07 10:10:36 2019 +0100
@@ -151,11 +151,6 @@
         ucx_test_register(suite, test_ucx_array_append_from_struct);
         ucx_test_register(suite, test_ucx_array_prepend_from);
         ucx_test_register(suite, test_ucx_array_set_from);
-        ucx_test_register(suite, test_ucx_array_append);
-        ucx_test_register(suite, test_ucx_array_append_struct);
-        ucx_test_register(suite, test_ucx_array_prepend);
-        ucx_test_register(suite, test_ucx_array_set);
-        ucx_test_register(suite, test_ucx_array_autogrow);
         ucx_test_register(suite, test_ucx_array_equals);
         ucx_test_register(suite, test_ucx_array_concat);
         ucx_test_register(suite, test_ucx_array_find);
@@ -166,7 +161,9 @@
         ucx_test_register(suite, test_ucx_array_shrink);
         ucx_test_register(suite, test_ucx_array_resize);
         ucx_test_register(suite, test_ucx_array_reserve);
+        ucx_test_register(suite, test_ucx_array_grow);
         ucx_test_register(suite, test_ucx_array_util_set);
+        ucx_test_register(suite, test_ucx_array_util_setptr);
         
         /* UcxList Tests */
         ucx_test_register(suite, test_ucx_list_append);

mercurial