From 03fe914116e9d4ea084bc4215888a82103d79b9a Mon Sep 17 00:00:00 2001 From: Valtteri Koskivuori Date: Thu, 16 Nov 2023 21:08:27 +0200 Subject: [PATCH] Implement T_arr_copy() & T_arr_join() + tests Copy can be used to make a 'deep' copy of a dynamic array. It obviously won't know about any memory that underlying elements may point to, but doing a copy of the actual data may sometimes be useful. Join appends array b to array a, and then frees array b. This is useful when adding batches of multiple elements to an array. --- src/utils/dyn_array.h | 13 +++++++++ tests/test_dyn_array.h | 66 ++++++++++++++++++++++++++++++++++++++++++ tests/tests.h | 2 ++ 3 files changed, 81 insertions(+) diff --git a/src/utils/dyn_array.h b/src/utils/dyn_array.h index 6f147753..747e6332 100644 --- a/src/utils/dyn_array.h +++ b/src/utils/dyn_array.h @@ -61,6 +61,13 @@ static inline size_t grow_x_2(size_t capacity, size_t elem_size) { a->items = new; \ a->capacity = a->count; \ } \ + static inline struct T##_arr T##_arr_copy(const struct T##_arr a) { \ + if (!a.items) return (struct T##_arr){ 0 }; \ + struct T##_arr c = a; \ + c.items = malloc(a.count * sizeof(*a.items)); \ + memcpy(c.items, a.items, a.count * sizeof(*a.items)); \ + return c; \ + } \ static inline void T##_arr_free(struct T##_arr *a) { \ if (!a) return; \ if (a->elem_free) { \ @@ -71,6 +78,12 @@ static inline size_t grow_x_2(size_t capacity, size_t elem_size) { a->items = NULL; \ a->capacity = 0; \ a->count = 0; \ + } \ + static inline void T##_arr_join(struct T##_arr *a, struct T##_arr *b) { \ + if (!a || !b) return; \ + for (size_t i = 0; i < b->count; ++i) \ + T##_arr_add(a, b->items[i]); \ + T##_arr_free(b); \ } dyn_array_def(int); diff --git a/tests/test_dyn_array.h b/tests/test_dyn_array.h index e0c4560a..4a90e03f 100644 --- a/tests/test_dyn_array.h +++ b/tests/test_dyn_array.h @@ -199,3 +199,69 @@ bool dyn_array_trim_expand(void) { return true; } + +bool dyn_array_copy(void) { + struct int_arr arr = { 0 }; + + for (int i = 0; i < dyn_test_count; ++i) { + int_arr_add(&arr, i); + } + + struct int_arr no_copy = arr; + test_assert(no_copy.count == arr.count); + test_assert(no_copy.capacity == arr.capacity); + test_assert(no_copy.elem_free == arr.elem_free); + test_assert(no_copy.grow_fn == arr.grow_fn); + test_assert(no_copy.items == arr.items); + + struct int_arr copy = int_arr_copy(arr); + test_assert(copy.count == arr.count); + test_assert(copy.capacity == arr.capacity); + test_assert(copy.elem_free == arr.elem_free); + test_assert(copy.grow_fn == arr.grow_fn); + test_assert(copy.items != arr.items); + + int_arr_free(&arr); + for (int i = 0; i < dyn_test_count; ++i) { + test_assert(copy.items[i] == i); + } + int_arr_free(©); + + return true; +} + +bool dyn_array_join(void) { + struct int_arr a = { 0 }; + for (int i = 0; i < dyn_test_count / 2; ++i) { + int_arr_add(&a, i); + } + + struct int_arr b = { 0 }; + for (int i = 0; i < dyn_test_count / 2; ++i) { + int_arr_add(&b, i + (dyn_test_count / 2)); + } + + test_assert(a.count == dyn_test_count / 2); + test_assert(b.count == dyn_test_count / 2); + + struct int_arr combined = { 0 }; + int_arr_join(&combined, &a); + test_assert(combined.count == dyn_test_count / 2); + test_assert(a.count == 0); + test_assert(a.capacity == 0); + test_assert(!a.items); + for (int i = 0; i < dyn_test_count / 2; ++i) { + test_assert(combined.items[i] == i); + } + + int_arr_join(&combined, &b); + test_assert(combined.count == dyn_test_count); + test_assert(b.count == 0); + test_assert(b.capacity == 0); + test_assert(!b.items); + for (int i = 0; i < dyn_test_count; ++i) { + test_assert(combined.items[i] == i); + } + + return true; +} diff --git a/tests/tests.h b/tests/tests.h index cb728b91..c504b1c2 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -149,6 +149,8 @@ static test tests[] = { {"dyn_array::custom", dyn_array_custom}, {"dyn_array::trim", dyn_array_trim}, {"dyn_array::trim_expand", dyn_array_trim_expand}, + {"dyn_array::copy", dyn_array_copy}, + {"dyn_array::join", dyn_array_join} }; #define testCount (sizeof(tests) / sizeof(test))