Thu, 07 Nov 2019 10:10:36 +0100
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);