# HG changeset patch # User Mike Becker # Date 1565428369 -7200 # Node ID 7fd13b9f8f602627b2baf21dc8ed84c29c5c9844 # Parent 135ce35d51080d292b650ec785ab97afec7cf855 improves array append/prepend/set interface diff -r 135ce35d5108 -r 7fd13b9f8f60 src/array.c --- a/src/array.c Sat Aug 10 09:47:59 2019 +0200 +++ b/src/array.c Sat Aug 10 11:12:49 2019 +0200 @@ -55,6 +55,18 @@ #define ucx_array_sort_impl ucx_mergesort #endif +static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) { + size_t required_capacity = array->capacity; + while (reqcap > required_capacity) { + if (required_capacity * 2 < required_capacity) + return 1; + required_capacity <<= 1; + } + if (ucx_array_reserve(array, required_capacity)) { + return 1; + } +} + UcxArray ucx_array_new(size_t capacity, size_t elemsize) { return ucx_array_new_a(capacity, elemsize, ucx_default_allocator()); } @@ -127,62 +139,84 @@ array->capacity = array->size = 0; } -int ucx_array_append(UcxArray *array, void *data) { - if (array->size == array->capacity) { - if (ucx_array_reserve(array, array->capacity*2)) { - return 1; - } +int ucx_array_append_from(UcxArray *array, void *data, size_t count) { + if (ucx_array_ensurecap(array, array->size + count)) + return 1; + + void* dest = ucx_array_at(*array, array->size); + if (data) { + memcpy(dest, data, array->elemsize*count); + } else { + memset(dest, 0, array->elemsize*count); + } + array->size += count; + + return 0; +} + +int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) { + if (ucx_array_ensurecap(array, array->size + count)) + return 1; + + if (array->size > 0) { + void *dest = ucx_array_at(*array, count); + memmove(dest, array->data, array->elemsize*array->size); } - void* dest = ucx_array_at(*array, array->size++); if (data) { - memcpy(dest, data, array->elemsize); + memcpy(array->data, data, array->elemsize*count); } else { - memset(dest, 0, array->elemsize); + memset(array->data, 0, array->elemsize*count); + } + array->size += count; + + return 0; +} + +int ucx_array_set_from(UcxArray *array, size_t index, + void *data, size_t count) { + if (ucx_array_ensurecap(array, index + count)) + return 1; + + if (index+count > array->size) { + array->size = index+count; + } + + void *dest = ucx_array_at(*array, index); + if (data) { + memcpy(dest, data, array->elemsize*count); + } else { + memset(dest, 0, array->elemsize*count); } return 0; } -int ucx_array_prepend(UcxArray *array, void *data) { - if (array->size == array->capacity) { - if (ucx_array_reserve(array, array->capacity*2)) { - return 1; - } - } - - array->size++; - - if (array->size > 1) { - void *dest = ucx_array_at(*array,1); - memmove(dest, array->data, array->elemsize*array->size); - } - - if (data) { - memcpy(array->data, data, array->elemsize); - } else { - memset(array->data, 0, array->elemsize); - } - - 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_set(UcxArray *array, size_t index, void *data) { - if (index >= array->size) { - if (ucx_array_reserve(array, index+1)) { - return 1; - } - array->size = index+1; - } - - void *dest = ucx_array_at(*array, index); - if (data) { - memcpy(dest, data, array->elemsize); - } else { - memset(dest, 0, array->elemsize); - } - - return 0; +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) { diff -r 135ce35d5108 -r 7fd13b9f8f60 src/ucx/array.h --- a/src/ucx/array.h Sat Aug 10 09:47:59 2019 +0200 +++ b/src/ucx/array.h Sat Aug 10 11:12:49 2019 +0200 @@ -131,18 +131,86 @@ void ucx_array_destroy(UcxArray *array); /** + * Inserts elements at the end of the array. + * + * This is an O(1) operation. + * The array will automatically grow, if the capacity is exceeded. + * If a pointer to data is provided, the data is copied into the array with + * memcpy(). Otherwise the new elements are completely zeroed. + * + * @param array a pointer the array where to append the data + * @param data a pointer to the data to insert (may be NULL) + * @param count number of elements to copy from data (if data is + * NULL, zeroed elements are appended) + * @return zero on success, non-zero if a reallocation was necessary but failed + * @see ucx_array_set_from() + * @see ucx_array_append() + */ +int ucx_array_append_from(UcxArray *array, void *data, size_t count); + + +/** + * Inserts elements 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_from() instead. + * + * @param array a pointer the array where to prepend the data + * @param data a pointer to the data to insert (may be NULL) + * @param count number of elements to copy from data (if data is + * NULL, zeroed elements are inserted) + * @return zero on success, non-zero if a reallocation was necessary but failed + * @see ucx_array_append_from() + * @see ucx_array_set_from() + * @see ucx_array_prepend() + */ +int ucx_array_prepend_from(UcxArray *array, void *data, size_t count); + + +/** + * Sets elements starting at the specified index. + * + * If the any index is out of bounds, the array automatically grows. + * The pointer to the data may be NULL, in which case the elements are zeroed. + * + * @param array a pointer the array where to set the data + * @param index the index of the element to set + * @param data a pointer to the data to insert (may be NULL) + * @param count number of elements to copy from data (if data is + * NULL, the memory in the array is zeroed) + * @return zero on success, non-zero if a reallocation was necessary but failed + * @see ucx_array_append_from() + * @see ucx_array_set() + */ +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 a pointer to data is provided, the data is copied into the array with - * memcpy(). Otherwise the new element is completely zeroed. + * 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 data a pointer to the data to insert (may be NULL) + * @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() */ -int ucx_array_append(UcxArray *array, void *data); +#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, ...); /** @@ -153,24 +221,51 @@ * ucx_array_append() instead. * * @param array a pointer the array where to prepend the data - * @param data a pointer to the data to insert (may be NULL) + * @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() */ -int ucx_array_prepend(UcxArray *array, void *data); +#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 index is out of bounds, the array automatically grows. - * The pointer to the data may be NULL, in which case the element is zeroed. + * 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 data a pointer to the data to insert (may be NULL) + * @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() */ -int ucx_array_set(UcxArray *array, size_t index, void *data); +#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. diff -r 135ce35d5108 -r 7fd13b9f8f60 test/array_tests.c --- a/test/array_tests.c Sat Aug 10 09:47:59 2019 +0200 +++ b/test/array_tests.c Sat Aug 10 11:12:49 2019 +0200 @@ -56,35 +56,138 @@ ucx_array_destroy(&array); } -UCX_TEST(test_ucx_array_append) { +UCX_TEST(test_ucx_array_append_from) { UcxArray array = ucx_array_new(16, sizeof(int)); int *elements; int x = 42; - ucx_array_append(&array, &x); + ucx_array_append_from(&array, &x, 1); UCX_TEST_BEGIN elements = array.data; UCX_TEST_ASSERT(elements[0] == 42, "failed"); - x = 13; - ucx_array_append(&array, &x); + int y[2] = {13, 37}; + ucx_array_append_from(&array, y, 2); elements = array.data; - UCX_TEST_ASSERT(array.size == 2, "incorrect size after append"); + 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_array_append(&array, NULL); + ucx_array_append_from(&array, NULL, 2); elements = array.data; - UCX_TEST_ASSERT(array.size == 3, "incorrect size after NULL append"); - UCX_TEST_ASSERT(elements[2] == 0, "element is not zeroed"); + UCX_TEST_ASSERT(array.size == 5, "incorrect size after NULL append"); + UCX_TEST_ASSERT(elements[3] == 0, "element is not zeroed"); + UCX_TEST_ASSERT(elements[4] == 0, "element is not zeroed"); UCX_TEST_ASSERT(elements[0] == 42, "NULL append corrupted previously inserted data"); UCX_TEST_ASSERT(elements[1] == 13, "NULL append corrupted previously inserted data"); + UCX_TEST_ASSERT(elements[2] == 37, + "NULL append corrupted previously inserted data"); + + UCX_TEST_END + + ucx_array_destroy(&array); +} + +UCX_TEST(test_ucx_array_prepend_from) { + int *elems; + UcxArray array = ucx_array_new(16, sizeof(int)); + + int x = 42; + ucx_array_prepend_from(&array, &x, 1); + UCX_TEST_BEGIN + + elems = array.data; + UCX_TEST_ASSERT(elems[0] == 42, "failed"); + + int y[2] = {13, 37}; + ucx_array_prepend_from(&array, y, 2); + + 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_array_prepend_from(&array, NULL, 2); + + elems = array.data; + UCX_TEST_ASSERT(array.size == 5, "incorrect size after NULL prepend"); + UCX_TEST_ASSERT(elems[0] == 0, "element is not zeroed"); + UCX_TEST_ASSERT(elems[1] == 0, "element is not zeroed"); + UCX_TEST_ASSERT(elems[2] == 13, + "NULL prepend corrupted previously inserted data"); + UCX_TEST_ASSERT(elems[3] == 37, + "NULL prepend corrupted previously inserted data"); + UCX_TEST_ASSERT(elems[4] == 42, + "NULL prepend corrupted previously inserted data"); + + UCX_TEST_END + + ucx_array_destroy(&array); +} + +UCX_TEST(test_ucx_array_set_from) { + int *elems; + UcxArray array = ucx_array_new(16, sizeof(int)); + + int x = 42; + + UCX_TEST_BEGIN + + ucx_array_set_from(&array, 7, &x, 1); + + 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"); + + int y[2] = {13, 37}; + ucx_array_set_from(&array, 27, y, 2); + + 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_array_set_from(&array, 7, NULL, 2); + + elems = array.data; + UCX_TEST_ASSERT(elems[7] == 0, "not zeroed on NULL set"); + UCX_TEST_ASSERT(elems[8] == 0, "not zeroed on NULL set"); + + UCX_TEST_END + + ucx_array_destroy(&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 @@ -95,32 +198,22 @@ int *elems; UcxArray array = ucx_array_new(16, sizeof(int)); - int x = 42; - ucx_array_prepend(&array, &x); + ucx_array_prepend(&array, 42); UCX_TEST_BEGIN elems = array.data; UCX_TEST_ASSERT(elems[0] == 42, "failed"); - x = 13; - ucx_array_prepend(&array, &x); + ucx_array_prepend(&array, 37); + ucx_array_prepend(&array, 13); elems = array.data; - UCX_TEST_ASSERT(array.size == 2, "incorrect size after prepend"); + UCX_TEST_ASSERT(array.size == 3, "incorrect size after prepend"); UCX_TEST_ASSERT(elems[0] == 13, "failed"); - UCX_TEST_ASSERT(elems[1] == 42, + UCX_TEST_ASSERT(elems[1] == 37, "failed"); + UCX_TEST_ASSERT(elems[2] == 42, "prepend corrupted previously inserted data"); - ucx_array_prepend(&array, NULL); - - elems = array.data; - UCX_TEST_ASSERT(array.size == 3, "incorrect size after NULL prepend"); - UCX_TEST_ASSERT(elems[0] == 0, "element is not zeroed"); - UCX_TEST_ASSERT(elems[1] == 13, - "NULL prepend corrupted previously inserted data"); - UCX_TEST_ASSERT(elems[2] == 42, - "NULL prepend corrupted previously inserted data"); - UCX_TEST_END ucx_array_destroy(&array); @@ -129,32 +222,25 @@ UCX_TEST(test_ucx_array_set) { int *elems; UcxArray array = ucx_array_new(16, sizeof(int)); - - - int x = 42; UCX_TEST_BEGIN - ucx_array_set(&array, 7, &x); + 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.size == 8, "array not resized on set"); UCX_TEST_ASSERT(array.capacity == 16, "capacity changed unnecessarily"); - x = 13; - ucx_array_set(&array, 27, &x); + 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(array.size == 28, "array not resized on set"); - UCX_TEST_ASSERT(array.capacity == 28, "capacity not grown"); - - ucx_array_set(&array, 7, NULL); - - elems = array.data; - UCX_TEST_ASSERT(elems[7] == 0, "not zeroed on NULL set"); - + 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_destroy(&array); @@ -260,12 +346,8 @@ UCX_TEST(test_ucx_array_at) { UcxArray array = ucx_array_new(16, sizeof(int)); - int x = 42; - ucx_array_append(&array, &x); - x = 13; - ucx_array_append(&array, &x); - x = 5; - ucx_array_append(&array, &x); + int x[3] = {42, 13, 5}; + ucx_array_append_from(&array, x, 3); UCX_TEST_BEGIN @@ -484,10 +566,10 @@ void* oldptr = array.data; - ucx_array_append(&array, &x); + ucx_array_append(&array, 5); UCX_TEST_ASSERT(array.capacity == 4 && array.data == oldptr, "array should not grow too early"); - ucx_array_append(&array, &x); + 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"); diff -r 135ce35d5108 -r 7fd13b9f8f60 test/array_tests.h --- a/test/array_tests.h Sat Aug 10 09:47:59 2019 +0200 +++ b/test/array_tests.h Sat Aug 10 11:12:49 2019 +0200 @@ -39,6 +39,9 @@ UCX_TEST(test_ucx_array_free); UCX_TEST(test_ucx_array_new); UCX_TEST(test_ucx_array_at); +UCX_TEST(test_ucx_array_append_from); +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_prepend); UCX_TEST(test_ucx_array_set); diff -r 135ce35d5108 -r 7fd13b9f8f60 test/main.c --- a/test/main.c Sat Aug 10 09:47:59 2019 +0200 +++ b/test/main.c Sat Aug 10 11:12:49 2019 +0200 @@ -146,6 +146,9 @@ ucx_test_register(suite, test_ucx_array_free); ucx_test_register(suite, test_ucx_array_new); ucx_test_register(suite, test_ucx_array_at); + ucx_test_register(suite, test_ucx_array_append_from); + 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_prepend); ucx_test_register(suite, test_ucx_array_set);