Bläddra i källkod

Added an elem_copyer to SCDArray, fixed a lot of bugs.

Aaro Saila 5 dagar sedan
förälder
incheckning
57662fe527
3 ändrade filer med 212 tillägg och 85 borttagningar
  1. 4 1
      .clang-format
  2. 46 18
      include/scufcont/DArray.h
  3. 162 66
      tests/cmocka/DArray_tests.c

+ 4 - 1
.clang-format

@@ -20,4 +20,7 @@ AllowShortFunctionsOnASingleLine: None
 AllowShortIfStatementsOnASingleLine: Never
 AllowShortLambdasOnASingleLine: None
 
-TypenameMacros: [ SCDARRAY_DESTROYER_FUNC_TYPE_NAME ]
+TypenameMacros: [
+  SCDARRAY_DESTROYER_FUNC_TYPE_NAME,
+  SCDARRAY_COPYER_FUNC_TYPE_NAME
+]

+ 46 - 18
include/scufcont/DArray.h

@@ -47,6 +47,7 @@
 
 #define SCDARRAY_HEADER(type, type_name) \
     SCDARRAY_DESTROYER(type, type_name); \
+    SCDARRAY_COPYER(type, type_name);    \
     SCDARRAY_STRUCT(type, type_name);    \
     SCDARRAY_SIGS(type, type_name)
 
@@ -60,6 +61,11 @@
 #define SCDARRAY_DESTROYER(type, type_name) \
     typedef void (*SCDARRAY_DESTROYER_FUNC_TYPE_NAME(type_name))(type *const elem)
 
+#define SCDARRAY_COPYER_FUNC_TYPE_NAME(type_name) SCDArray_##type_name##_copyer_func
+
+#define SCDARRAY_COPYER(type, type_name) \
+    typedef void (*SCDARRAY_COPYER_FUNC_TYPE_NAME(type_name))(type * dst, type * src)
+
 // SCDARRAY_DESTROYER(char *, charp);
 
 #define SCDARRAY_STRUCT(type, type_name)                           \
@@ -68,14 +74,16 @@
         size_t len;                                                \
         size_t capacity;                                           \
         SCDARRAY_DESTROYER_FUNC_TYPE_NAME(type_name) elem_destroy; \
+        SCDARRAY_COPYER_FUNC_TYPE_NAME(type_name) elem_copy;       \
     } SCDArray_##type_name
 
 // SCDARRAY_STRUCT(char *, charp)
 
-#define SCDARRAY_ALLOC_SIG(type, type_name, func_name)            \
-    ScufContResult func_name(                                     \
-        SCDArray_##type_name *const darr,                         \
-        SCDARRAY_DESTROYER_FUNC_TYPE_NAME(type_name) elem_destroy \
+#define SCDARRAY_ALLOC_SIG(type, type_name, func_name)             \
+    ScufContResult func_name(                                      \
+        SCDArray_##type_name *const darr,                          \
+        SCDARRAY_DESTROYER_FUNC_TYPE_NAME(type_name) elem_destroy, \
+        SCDARRAY_COPYER_FUNC_TYPE_NAME(type_name) elem_copy        \
     )
 
 /**
@@ -93,6 +101,7 @@
         darr->len = 0;                                 \
         darr->capacity = 1;                            \
         darr->elem_destroy = elem_destroy;             \
+        darr->elem_copy = elem_copy;                   \
         return SCUFCONT_SUCCESS;                       \
     }
 
@@ -106,17 +115,25 @@
   @return SCUFCONT_OUT_OF_MEMORY
   @return SCUFCONT_SUCCESS
 */
-#define SCDARRAY_ALLOC_COPY(type, type_name, func_name)           \
-    SCDARRAY_ALLOC_COPY_SIG(type, type_name, func_name) {         \
-        dst->data = (type *) calloc(src->capacity, sizeof(type)); \
-        if (dst->data == NULL) {                                  \
-            return SCUFCONT_OUT_OF_MEMORY;                        \
-        }                                                         \
-        dst->len = src->len;                                      \
-        dst->capacity = src->capacity;                            \
-        dst->elem_destroy = src->elem_destroy;                    \
-        memcpy(dst->data, src->data, src->len);                   \
-        return SCUFCONT_SUCCESS;                                  \
+#define SCDARRAY_ALLOC_COPY(type, type_name, func_name)             \
+    SCDARRAY_ALLOC_COPY_SIG(type, type_name, func_name) {           \
+        dst->data = (type *) calloc(src->capacity, sizeof(type));   \
+        if (dst->data == NULL) {                                    \
+            return SCUFCONT_OUT_OF_MEMORY;                          \
+        }                                                           \
+        dst->len = src->len;                                        \
+        dst->capacity = src->capacity;                              \
+        dst->elem_destroy = src->elem_destroy;                      \
+        dst->elem_copy = src->elem_copy;                            \
+        memcpy(dst->data, src->data, src->len);                     \
+        if (dst->elem_copy != NULL) {                               \
+            for (size_t i = 0; i < src->len; i++) {                 \
+                type *dst_elem = SCDArray_##type_name##_at(dst, i); \
+                type *src_elem = SCDArray_##type_name##_at(src, i); \
+                dst->elem_copy(dst_elem, src_elem);                 \
+            }                                                       \
+        }                                                           \
+        return SCUFCONT_SUCCESS;                                    \
     }
 
 // SCDARRAY_ALLOC_COPY(char *, charp, SCDArray_charp_alloc_copy)
@@ -141,7 +158,7 @@
         return darr->data == NULL;                     \
     }
 
-#define SCDARRAY_PUSH_BACK_SIG(type, type_name, func_name) ScufContResult func_name(SCDArray_##type_name *const darr, const type *const new_elem)
+#define SCDARRAY_PUSH_BACK_SIG(type, type_name, func_name) ScufContResult func_name(SCDArray_##type_name *const darr, type *new_elem)
 
 /**
   @fn SCDArray_push_back
@@ -160,10 +177,14 @@
         }                                                                                  \
         memcpy(darr->data + darr->len, new_elem, sizeof(*new_elem));                       \
         darr->len++;                                                                       \
+        if (darr->elem_copy != NULL) {                                                     \
+            type *dst = SCDArray_##type_name##_at(darr, darr->len - 1);                    \
+            darr->elem_copy(dst, new_elem);                                                \
+        }                                                                                  \
         return SCUFCONT_SUCCESS;                                                           \
     }
 
-#define SCDARRAY_PUSH_BACK_MULTIPLE_SIG(type, type_name, func_name) ScufContResult func_name(SCDArray_##type_name *const darr, const type *const new_elems, const size_t new_elems_n)
+#define SCDARRAY_PUSH_BACK_MULTIPLE_SIG(type, type_name, func_name) ScufContResult func_name(SCDArray_##type_name *const darr, type *new_elems, const size_t new_elems_n)
 
 /**
   @fn SCDArray_push_back_multiple
@@ -181,6 +202,13 @@
             darr->capacity += new_elems_n;                                                            \
         }                                                                                             \
         memcpy(darr->data + darr->len, new_elems, sizeof(type) * new_elems_n);                        \
+        if (darr->elem_copy != NULL) {                                                                \
+            for (size_t i = 0; i < new_elems_n; i++) {                                                \
+                type *dst = SCDArray_##type_name##_at(darr, darr->len + i);                           \
+                type *src = new_elems + i;                                                            \
+                darr->elem_copy(dst, src);                                                            \
+            }                                                                                         \
+        }                                                                                             \
         darr->len += new_elems_n;                                                                     \
         return SCUFCONT_SUCCESS;                                                                      \
     }
@@ -230,7 +258,7 @@
 #define SCDARRAY_REMOVE_NO_RESIZE(type, type_name, func_name)                                  \
     SCDARRAY_REMOVE_SIG(type, type_name, func_name) {                                          \
         if (darr->elem_destroy != NULL) {                                                      \
-            type *const elem = SCDArray_##type_name##_at(darr, i);                             \
+            type *elem = SCDArray_##type_name##_at(darr, i);                                   \
             darr->elem_destroy(elem);                                                          \
         }                                                                                      \
         if (i != darr->len - 1) {                                                              \

+ 162 - 66
tests/cmocka/DArray_tests.c

@@ -1,23 +1,40 @@
 #include <cmocka.h>
 #include <stdlib.h>
 #include <time.h>
+#include <stdio.h>
 
 #include "scufcont/DArray.h"
 #include "scufcont/scufcont.h"
 
-SCDARRAY_HEADER(int, int);
-SCDARRAY_IMPL(int, int)
+#define SCDARRAY_CHARP_ALLOC(arrptr) SCDArray_charp_alloc((arrptr), darray_charp_destroyer, darray_charp_copyer)
+
+SCDARRAY_HEADER(char *, charp);
+SCDARRAY_IMPL(char *, charp)
+
+void darray_charp_destroyer(char **elem) {
+    if (elem != NULL && *elem != NULL) {
+        free(*elem);
+        *elem = NULL;
+    }
+}
+
+void darray_charp_copyer(char **dst, char **src) {
+    if (dst != NULL && src != NULL && *src != NULL) {
+        *dst = (char *) malloc(sizeof(char) * (strlen(*src) + 1));
+        strncpy(*dst, *src, strlen(*src) + 1);
+    }
+}
 
 void test_DArray_alloc(void **state) {
     (void) state;
 
-    SCDArray_int arr;
-    assert_true(SCDArray_int_alloc(&arr, NULL) == SCUFCONT_SUCCESS);
+    SCDArray_charp arr;
+    assert_true(SCDARRAY_CHARP_ALLOC(&arr) == SCUFCONT_SUCCESS);
     assert_non_null(arr.data);
     assert_uint_equal(arr.len, 0u);
     assert_uint_equal(arr.capacity, 1u);
 
-    SCDArray_int_free(&arr);
+    SCDArray_charp_free(&arr);
 }
 
 void test_DArray_alloc_copy(void **state) {
@@ -25,151 +42,230 @@ void test_DArray_alloc_copy(void **state) {
 
     srand(time(NULL));
 
-    SCDArray_int arr;
-    assert_true(SCDArray_int_alloc(&arr, NULL) == SCUFCONT_SUCCESS);
+    SCDArray_charp arr;
+    assert_true(SCDARRAY_CHARP_ALLOC(&arr) == SCUFCONT_SUCCESS);
 
     const size_t elems_n = rand() % 100;
     for (size_t i = 0; i < elems_n; i++) {
-        const int elem = rand();
-        assert_true(SCDArray_int_push_back(&arr, &elem) == SCUFCONT_SUCCESS);
+        const int n = rand();
+        char buf[100];
+        snprintf(buf, 100, "%d", n);
+        char *bufptr = buf;
+        assert_true(SCDArray_charp_push_back(&arr, &bufptr) == SCUFCONT_SUCCESS);
     }
 
-    SCDArray_int copied;
-    assert_true(SCDArray_int_alloc_copy(&copied, &arr) == SCUFCONT_SUCCESS);
+    SCDArray_charp copied;
+    assert_true(SCDArray_charp_alloc_copy(&copied, &arr) == SCUFCONT_SUCCESS);
     assert_non_null(copied.data);
     assert_uint_equal(copied.capacity, arr.capacity);
     assert_uint_equal(copied.len, arr.len);
-    assert_memory_equal(copied.data, arr.data, copied.len);
+    for (size_t i = 0; i < arr.len; i++) {
+        char **orig_elem = SCDArray_charp_at(&arr, i);
+        assert_non_null(orig_elem);
+        assert_non_null(*orig_elem);
+        char **copied_elem = SCDArray_charp_at(&copied, i);
+        assert_non_null(copied_elem);
+        assert_non_null(*copied_elem);
+        assert_string_equal(*orig_elem, *copied_elem);
+    }
 
-    SCDArray_int_free(&arr);
-    SCDArray_int_free(&copied);
+    const size_t arr_data_len = arr.len;
+    char **arr_data = (char **) malloc(sizeof(char *) * arr.len);
+    assert_non_null(arr_data);
+    for (size_t i = 0; i < arr.len; i++) {
+        char **src = SCDArray_charp_at(&arr, i);
+        assert_non_null(src);
+        assert_non_null(*src);
+        arr.elem_copy(arr_data + i, src);
+    }
+
+    SCDArray_charp_free(&arr);
+
+    // Freeing the first array shouldn't affect the data in the copied array.
+    assert_uint_equal(arr_data_len, copied.len);
+    for (size_t i = 0; i < arr_data_len; i++) {
+        assert_string_equal(arr_data[i], *SCDArray_charp_at(&copied, i));
+    }
+
+    SCDArray_charp_free(&copied);
+
+    for (size_t i = 0; i < arr_data_len; i++) {
+        free(arr_data[i]);
+    }
+    free(arr_data);
 }
 
 void test_DArray_push_back(void **state) {
     (void) state;
 
-    SCDArray_int arr;
-    SCDArray_int_alloc(&arr, NULL);
+    SCDArray_charp arr;
+    SCDARRAY_CHARP_ALLOC(&arr);
+
+    const char *str1_lit = "Hello";
+    const size_t str1_len = strlen(str1_lit);
+    char *str1 = malloc(sizeof(char) * (str1_len + 1));
+    strncpy(str1, str1_lit, str1_len + 1);
 
-    assert_true(SCDArray_int_push_back(&arr, &(int) { 7 }) == SCUFCONT_SUCCESS);
-    assert_true(arr.data[0] == 7);
+    char *str2 = "World";
 
-    assert_true(SCDArray_int_push_back(&arr, &(int) { 10 }) == SCUFCONT_SUCCESS);
-    assert_true(arr.data[0] == 7);
-    assert_true(arr.data[1] == 10);
+    assert_true(SCDArray_charp_push_back(&arr, &(char *) { str1 }) == SCUFCONT_SUCCESS);
+    assert_string_equal(*SCDArray_charp_at(&arr, 0), str1);
 
-    SCDArray_int_free(&arr);
+    assert_true(SCDArray_charp_push_back(&arr, &(char *) { str2 }) == SCUFCONT_SUCCESS);
+    assert_string_equal(*SCDArray_charp_at(&arr, 0), str1);
+    assert_string_equal(*SCDArray_charp_at(&arr, 1), str2);
+
+    // Freeing the original memory should not affect the array element.
+    free(str1);
+    assert_string_equal(*SCDArray_charp_at(&arr, 0), str1_lit);
+
+    SCDArray_charp_free(&arr);
 }
 
 void test_DArray_at(void **state) {
     (void) state;
 
-    SCDArray_int arr;
-    SCDArray_int_alloc(&arr, NULL);
+    SCDArray_charp arr;
+    SCDARRAY_CHARP_ALLOC(&arr);
 
-    assert_true(SCDArray_int_push_back(&arr, &(int) { 7 }) == SCUFCONT_SUCCESS);
-    assert_int_equal(*SCDArray_int_at(&arr, 0), 7);
+    assert_true(SCDArray_charp_push_back(&arr, &(char *) { "Hello" }) == SCUFCONT_SUCCESS);
+    assert_string_equal(*SCDArray_charp_at(&arr, 0), "Hello");
 
-    assert_true(SCDArray_int_push_back(&arr, &(int) { 10 }) == SCUFCONT_SUCCESS);
-    assert_int_equal(*SCDArray_int_at(&arr, 0), 7);
-    assert_int_equal(*SCDArray_int_at(&arr, 1), 10);
+    assert_true(SCDArray_charp_push_back(&arr, &(char *) { "World" }) == SCUFCONT_SUCCESS);
+    assert_string_equal(*SCDArray_charp_at(&arr, 0), "Hello");
+    assert_string_equal(*SCDArray_charp_at(&arr, 1), "World");
 
-    SCDArray_int_free(&arr);
+    SCDArray_charp_free(&arr);
 }
 
 void test_DArray_push_back_multiple(void **state) {
     (void) state;
 
-    SCDArray_int arr;
-    SCDArray_int_alloc(&arr, NULL);
+    SCDArray_charp arr;
+    SCDARRAY_CHARP_ALLOC(&arr);
 
-    int nums[] = { 1, 2, 3, 4, 5 };
+    char *nums[] = { "1", "2", "3", "4", "5" };
     const size_t nums_len = sizeof(nums) / sizeof(nums[0]);
-    assert_true(SCDArray_int_push_back_multiple(&arr, nums, sizeof(nums) / sizeof(nums[0])) == SCUFCONT_SUCCESS);
+    assert_true(SCDArray_charp_push_back_multiple(&arr, nums, nums_len) == SCUFCONT_SUCCESS);
     for (size_t i = 0; i < arr.len; i++) {
-        assert_int_equal(*SCDArray_int_at(&arr, i), nums[i]);
+        assert_string_equal(*SCDArray_charp_at(&arr, i), nums[i]);
     }
-    int nums2[] = { 6, 7, 8 };
-    const size_t nums2_len = sizeof(nums2) / sizeof(nums2[0]);
-    assert_true(SCDArray_int_push_back_multiple(&arr, nums2, sizeof(nums2) / sizeof(nums2[0])) == SCUFCONT_SUCCESS);
+    const size_t nums2_len = 3;
+    char **nums2 = (char **) malloc(sizeof(char *) * nums2_len);
+    nums2[0] = "6";
+    nums2[1] = "7";
+    nums2[2] = "8";
+    assert_true(SCDArray_charp_push_back_multiple(&arr, nums2, nums2_len) == SCUFCONT_SUCCESS);
     for (size_t i = 0; i < nums_len; i++) {
-        assert_int_equal(*SCDArray_int_at(&arr, i), nums[i]);
+        assert_string_equal(*SCDArray_charp_at(&arr, i), nums[i]);
     }
     for (size_t i = 0; i < nums2_len; i++) {
-        assert_int_equal(*SCDArray_int_at(&arr, i + nums_len), nums2[i]);
+        assert_string_equal(*SCDArray_charp_at(&arr, i + nums_len), nums2[i]);
     }
 
-    SCDArray_int_free(&arr);
+    SCDArray_charp_free(&arr);
+
+    // Freeing the array should not affect the original memory.
+    assert_string_equal(nums2[0], "6");
+    assert_string_equal(nums2[1], "7");
+    assert_string_equal(nums2[2], "8");
+
+    free(nums2);
 }
 
 void test_DArray_remove(void **state) {
     (void) state;
 
-    SCDArray_int arr;
-    SCDArray_int_alloc(&arr, NULL);
-
-    int nums[] = { 1, 2, 3, 4, 5 };
-    SCDArray_int_push_back_multiple(&arr, nums, sizeof(nums) / sizeof(nums[0]));
+    SCDArray_charp arr;
+    SCDARRAY_CHARP_ALLOC(&arr);
+
+    char *nums[5];
+    nums[0] = (char *) malloc(2);
+    strncpy(nums[0], "1", 2);
+    nums[1] = (char *) malloc(2);
+    strncpy(nums[1], "2", 2);
+    nums[2] = (char *) malloc(2);
+    strncpy(nums[2], "3", 2);
+    nums[3] = (char *) malloc(2);
+    strncpy(nums[3], "4", 2);
+    nums[4] = (char *) malloc(2);
+    strncpy(nums[4], "5", 2);
+    const size_t nums_len = 5;
+    SCDArray_charp_push_back_multiple(&arr, nums, nums_len);
     for (size_t i = 0; i < arr.len; i++) {
-        assert_int_equal(*SCDArray_int_at(&arr, i), nums[i]);
+        assert_string_equal(*SCDArray_charp_at(&arr, i), nums[i]);
     }
 
     // Remove last
     {
         const size_t old_len = arr.len;
-        SCDArray_int_remove(&arr, arr.len - 1);
+        SCDArray_charp_remove(&arr, arr.len - 1);
         assert_uint_equal(arr.len, old_len - 1);
-        int new_nums[] = { 1, 2, 3, 4 };
+        char *new_nums[] = { "1", "2", "3", "4" };
         assert_uint_equal(arr.len, sizeof(new_nums) / sizeof(new_nums[0]));
         for (size_t i = 0; i < arr.len; i++) {
-            assert_int_equal(*SCDArray_int_at(&arr, i), new_nums[i]);
+            char **elem = SCDArray_charp_at(&arr, i);
+            assert_string_equal(*elem, new_nums[i]);
         }
+        assert_non_null(nums[nums_len - 1]);
+        assert_string_equal(nums[nums_len - 1], "5");
     }
 
     // Remove from middle
     {
         const size_t old_len = arr.len;
-        SCDArray_int_remove(&arr, 1);
+        SCDArray_charp_remove(&arr, 1);
         assert_uint_equal(arr.len, old_len - 1);
-        int new_nums[] = { 1, 3, 4 };
+        char *new_nums[] = { "1", "3", "4" };
         assert_uint_equal(arr.len, sizeof(new_nums) / sizeof(new_nums[0]));
         for (size_t i = 0; i < arr.len; i++) {
-            assert_int_equal(*SCDArray_int_at(&arr, i), new_nums[i]);
+            assert_string_equal(*SCDArray_charp_at(&arr, i), new_nums[i]);
         }
+        assert_non_null(nums[1]);
+        assert_string_equal(nums[1], "2");
     }
 
     // Remove first
     {
         const size_t old_len = arr.len;
-        SCDArray_int_remove(&arr, 0);
+        SCDArray_charp_remove(&arr, 0);
         assert_uint_equal(arr.len, old_len - 1);
-        int new_nums[] = { 3, 4 };
+        char *new_nums[] = { "3", "4" };
         assert_uint_equal(arr.len, sizeof(new_nums) / sizeof(new_nums[0]));
         for (size_t i = 0; i < arr.len; i++) {
-            assert_int_equal(*SCDArray_int_at(&arr, i), new_nums[i]);
+            assert_string_equal(*SCDArray_charp_at(&arr, i), new_nums[i]);
         }
+        assert_non_null(nums[0]);
+        assert_string_equal(nums[0], "1");
     }
 
-    SCDArray_int_free(&arr);
+    SCDArray_charp_free(&arr);
+
+    for (size_t i = 0; i < nums_len; i++) {
+        free(nums[i]);
+    }
 }
 
 void test_DArray_clear(void **state) {
     (void) state;
 
-    SCDArray_int arr;
-    SCDArray_int_alloc(&arr, NULL);
+    SCDArray_charp arr;
+    SCDARRAY_CHARP_ALLOC(&arr);
+
+    char *words[] = { "Hello", "World", "!", "Foo", "bar" };
+    const size_t words_len = sizeof(words) / sizeof(words[0]);
 
-    for (int i = 1; i <= 10; i++) {
-        SCDArray_int_push_back(&arr, &i);
+    for (size_t i = 0; i < words_len; i++) {
+        SCDArray_charp_push_back(&arr, words + i);
     }
 
-    assert_uint_equal(arr.len, 10);
-    for (int i = 0; i < 10; i++) {
-        const int *const n = SCDArray_int_at(&arr, i);
-        assert_int_equal(*n, i + 1);
+    assert_uint_equal(arr.len, words_len);
+    for (size_t i = 0; i < words_len; i++) {
+        char **w = SCDArray_charp_at(&arr, i);
+        assert_string_equal(*w, words[i]);
     }
 
-    SCDArray_int_clear(&arr);
+    SCDArray_charp_clear(&arr);
     assert_uint_equal(arr.len, 0);
 }