From 73e1cf53c225b24e6403c28823fb945831eaba8b Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Thu, 25 Nov 2010 17:36:36 -0500
Subject: [PATCH] [REFARRAY] Adding ref_array copy method

* Added a method to copy ref array
* Added unit test to check copy functionality
* Took advantage of the new trace macros
* Reworked the debug function to be able to
  trace arrays with different types of data.
---
 refarray/ref_array.c    |  135 ++++++++++++++++++++++++------
 refarray/ref_array.h    |   50 +++++++++++
 refarray/ref_array_ut.c |  210 ++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 355 insertions(+), 40 deletions(-)

diff --git a/refarray/ref_array.c b/refarray/ref_array.c
index e9482de0a423f8b8b90ca434f6839e0c8b000603..4b23d42492e75b9c53fc6d0a7f140a349b915454 100644
--- a/refarray/ref_array.c
+++ b/refarray/ref_array.c
@@ -48,7 +48,7 @@ static int ref_array_grow(struct ref_array *ra)
     int error = EOK;
     void *newbuf = NULL;
 
-    TRACE_FLOW_STRING("ref_array_grow", "Entry");
+    TRACE_FLOW_ENTRY();
 
     TRACE_INFO_NUMBER("Current length: ", ra->len);
     TRACE_INFO_NUMBER("Current size: ", ra->size);
@@ -83,7 +83,7 @@ int ref_array_create(struct ref_array **ra,
 {
     struct ref_array *new_ra = NULL;
 
-    TRACE_FLOW_STRING("ref_array_create", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if (!ra) {
         TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL);
@@ -113,14 +113,14 @@ int ref_array_create(struct ref_array **ra,
 
     *ra = new_ra;
 
-    TRACE_FLOW_STRING("ref_array_create", "Exit");
+    TRACE_FLOW_EXIT();
     return EOK;
 }
 
 /* Get new reference to an array */
 struct ref_array *ref_array_getref(struct ref_array *ra)
 {
-    TRACE_FLOW_STRING("ref_array_getref", "Entry");
+    TRACE_FLOW_ENTRY();
 
     /* Check if array is not NULL */
     if (ra) {
@@ -134,7 +134,7 @@ struct ref_array *ref_array_getref(struct ref_array *ra)
         TRACE_ERROR_STRING("Uninitialized array.", "Returning NULL");
     }
 
-    TRACE_FLOW_STRING("ref_array_getref", "Exit");
+    TRACE_FLOW_EXIT();
     return ra;
 }
 
@@ -143,7 +143,7 @@ void ref_array_destroy(struct ref_array *ra)
 {
     int idx;
 
-    TRACE_FLOW_STRING("ref_array_destroy", "Entry");
+    TRACE_FLOW_ENTRY();
 
     /* Check if array is not NULL */
     if (!ra) {
@@ -175,7 +175,7 @@ void ref_array_destroy(struct ref_array *ra)
         TRACE_ERROR_STRING("Reference count is 0.", "Coding error???");
     }
 
-    TRACE_FLOW_STRING("ref_array_destroy", "Exit");
+    TRACE_FLOW_EXIT();
 }
 
 /* Add new element to the array */
@@ -183,7 +183,8 @@ int ref_array_append(struct ref_array *ra, void *element)
 {
     int error = EOK;
 
-    TRACE_FLOW_STRING("ref_array_append", "Entry");
+    TRACE_FLOW_ENTRY();
+
     if ((!ra) || (!element)) {
         TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL);
         return EINVAL;
@@ -205,14 +206,14 @@ int ref_array_append(struct ref_array *ra, void *element)
 
     ra->len++;
 
-    TRACE_FLOW_STRING("ref_array_append", "Exit");
+    TRACE_FLOW_EXIT();
     return error;
 }
 
 /* Get element */
 void *ref_array_get(struct ref_array *ra, uint32_t idx, void *acptr)
 {
-    TRACE_FLOW_STRING("ref_array_get", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if (!ra) {
         TRACE_ERROR_STRING("Uninitialized argument.", "");
@@ -235,7 +236,7 @@ void *ref_array_get(struct ref_array *ra, uint32_t idx, void *acptr)
 
     }
 
-    TRACE_FLOW_STRING("ref_array_get returning internal storage", "Exit");
+    TRACE_FLOW_EXIT();
     return (unsigned char *)(ra->storage) + idx * ra->elsize;
 }
 
@@ -243,7 +244,7 @@ void *ref_array_get(struct ref_array *ra, uint32_t idx, void *acptr)
 /* Get length */
 int ref_array_getlen(struct ref_array *ra, uint32_t *len)
 {
-    TRACE_FLOW_STRING("ref_array_getlen", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if ((!ra) || (!len)) {
         TRACE_ERROR_STRING("Uninitialized argument.", "");
@@ -252,14 +253,14 @@ int ref_array_getlen(struct ref_array *ra, uint32_t *len)
 
     *len = ra->len;
 
-    TRACE_FLOW_STRING("ref_array_getlen", "Exit");
+    TRACE_FLOW_EXIT();
     return EOK;
 }
 
 /* Alternative function to get length */
 uint32_t ref_array_len(struct ref_array *ra)
 {
-    TRACE_FLOW_STRING("ref_array_len", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if (!ra) {
         TRACE_ERROR_STRING("Uninitialized argument.", "");
@@ -267,7 +268,7 @@ uint32_t ref_array_len(struct ref_array *ra)
         return 0;
     }
 
-    TRACE_FLOW_STRING("ref_array_len", "Exit");
+    TRACE_FLOW_EXIT();
     return ra->len;
 }
 
@@ -280,7 +281,7 @@ int ref_array_insert(struct ref_array *ra,
     int error = EOK;
     uint32_t i;
 
-    TRACE_FLOW_STRING("ref_array_insert", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if ((!ra) || (!element)) {
         TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL);
@@ -315,7 +316,7 @@ int ref_array_insert(struct ref_array *ra,
 
     ra->len++;
 
-    TRACE_FLOW_STRING("ref_array_insert", "Exit");
+    TRACE_FLOW_EXIT();
     return error;
 
 }
@@ -328,7 +329,7 @@ int ref_array_replace(struct ref_array *ra,
 {
     int error = EOK;
 
-    TRACE_FLOW_STRING("ref_array_replace", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if ((!ra) || (!element)) {
         TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL);
@@ -351,7 +352,7 @@ int ref_array_replace(struct ref_array *ra,
            ra->elsize);
 
 
-    TRACE_FLOW_STRING("ref_array_replace", "Exit");
+    TRACE_FLOW_EXIT();
     return error;
 }
 
@@ -363,7 +364,7 @@ int ref_array_remove(struct ref_array *ra,
     int error = EOK;
     uint32_t i;
 
-    TRACE_FLOW_STRING("ref_array_remove", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if (!ra) {
         TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL);
@@ -389,7 +390,7 @@ int ref_array_remove(struct ref_array *ra,
 
     ra->len--;
 
-    TRACE_FLOW_STRING("ref_array_remove", "Exit");
+    TRACE_FLOW_EXIT();
     return error;
 }
 
@@ -398,7 +399,7 @@ void ref_array_reset(struct ref_array *ra)
 {
     int idx;
 
-    TRACE_FLOW_STRING("ref_array_reset", "Entry");
+    TRACE_FLOW_ENTRY();
 
     /* Check if array is not NULL */
     if (!ra) {
@@ -418,7 +419,7 @@ void ref_array_reset(struct ref_array *ra)
     ra->size = 0;
     ra->len = 0;
 
-    TRACE_FLOW_STRING("ref_array_reset", "Exit");
+    TRACE_FLOW_EXIT();
 }
 
 /* Swap two elements in the array */
@@ -429,7 +430,7 @@ int ref_array_swap(struct ref_array *ra,
     int error = EOK;
     void *temp = NULL;
 
-    TRACE_FLOW_STRING("ref_array_swap", "Entry");
+    TRACE_FLOW_ENTRY();
 
     if (!ra) {
         TRACE_ERROR_NUMBER("Uninitialized argument.", EINVAL);
@@ -465,12 +466,84 @@ int ref_array_swap(struct ref_array *ra,
 
     free(temp);
 
-    TRACE_FLOW_STRING("ref_array_swap", "Exit");
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+/* Copy array */
+int ref_array_copy(struct ref_array *ra,
+                   ref_array_copy_cb copy_cb,
+                   ref_array_fn cb,
+                   void *data,
+                   struct ref_array **copy_ra)
+{
+    int error = EOK;
+    int idx;
+    struct ref_array *new_ra = NULL;
+    void *src;
+    void *dst;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check if array is not NULL */
+    if ((!ra) || (!copy_ra)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        return EINVAL;
+    }
+
+    new_ra = (struct ref_array *)malloc(sizeof(struct ref_array));
+    if (!new_ra) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        return ENOMEM;
+    }
+
+    new_ra->storage = calloc(ra->size, ra->elsize);
+    if (!(new_ra->storage)) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        free(new_ra);
+        return ENOMEM;
+    }
+
+    new_ra->elsize = ra->elsize;
+    new_ra->size = ra->size;
+    new_ra->grow_by = ra->grow_by;
+    new_ra->len = 0;
+    new_ra->refcount = 1;
+    new_ra->cb = cb;
+    new_ra->cb_data = data;
+
+    for (idx = 0; idx < ra->len; idx++) {
+        if (copy_cb) {
+            src = (void *)((unsigned char *)(ra->storage) + idx * ra->elsize);
+            dst = (void *)((unsigned char *)(new_ra->storage) +
+                                             idx * new_ra->elsize);
+
+            error = copy_cb(src, (void *)(dst));
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to copy data.", error);
+                ref_array_destroy(new_ra);
+                return error;
+            }
+        }
+        else {
+            memcpy((unsigned char *)(new_ra->storage) + idx * new_ra->elsize,
+                   (unsigned char *)(ra->storage) + idx * ra->elsize,
+                   new_ra->elsize);
+        }
+        (new_ra->len)++;
+    }
+
+
+    *copy_ra = new_ra;
+
+    TRACE_FLOW_EXIT();
     return error;
 }
 
+
+
 /* Debug function */
-void ref_array_debug(struct ref_array *ra)
+void ref_array_debug(struct ref_array *ra, int num)
 {
     int i,j;
 
@@ -485,7 +558,15 @@ void ref_array_debug(struct ref_array *ra)
         for (j = 0; j < ra->elsize; j++) {
             printf("%x", *((unsigned char *)(ra->storage) + i * ra->elsize + j));
         }
-        printf("\n%s\n", *((char **)((unsigned char *)(ra->storage) + i * ra->elsize)));
+        if (num == 0) {
+            printf("\n%s\n", *((char **)((unsigned char *)(ra->storage) + i * ra->elsize)));
+        }
+        else if (num > 0) {
+            printf("\n%d\n", *((uint32_t *)((unsigned char *)(ra->storage) + i * ra->elsize)));
+        }
+        else {
+            printf("\nIt is an object.\n");
+        }
     }
     printf("\nARRAY DUMP END\n\n");
 }
diff --git a/refarray/ref_array.h b/refarray/ref_array.h
index 0040fe43f4e70eda9a1860129fc45d3e892a20d1..d271d38b48b458ebf469f1387ef8e0679c8fa757 100644
--- a/refarray/ref_array.h
+++ b/refarray/ref_array.h
@@ -83,11 +83,34 @@ typedef enum
  *
  * Callback that can be provided by a caller
  * to free data when the storage is actually destroyed.
+ *
+ * @param[in]  elem             Pointer to the array element.
+ * @param[in]  type             Type of the operation performed.
+ * @param[in]  data             Application data that can be used
+ *                              inside the callback.
+ * No return value.
  */
 typedef void (*ref_array_fn)(void *elem,
                              ref_array_del_enum type,
                              void *data);
 
+/**
+ * @brief Copy callback
+ *
+ * Callback that can be provided by a caller
+ * to copy elements of the array.
+ *
+ * @param[in]  elem             Pointer to the array element.
+ * @param[out] new_elem         Pointer to pointer to the new element.
+ *
+ * @return 0 - Success.
+ * @return ENOMEM - No memory.
+ * @return EINVAL - Invalid argument.
+ *
+ * Callback can return other errors and the implementor's discretion.
+ */
+typedef int (*ref_array_copy_cb)(void *elem,
+                                 void *new_elem);
 
 /**
  * @brief Create referenced array
@@ -313,6 +336,33 @@ int ref_array_swap(struct ref_array *ra,
  */
 void ref_array_reset(struct ref_array *ra);
 
+
+/**
+ * @brief Copy array
+ *
+ * Function copies all contents calling a provided
+ * callback for every entry of the array.
+ *
+ *
+ * @param[in]  ra        Existing array object to copy.
+ * @param[in]  copy_cb   Copy callback.
+ * @param[in]  cb        Cleanup callback, will be used
+ *                       to clean data in the array copy.
+ * @param[in]  data      Caller supplied data
+ *                       passed to cleanup callback.
+ * @param[out] copy_ra   Newly allocated copy.
+ *
+ * @return 0 - Success.
+ * @return ENOMEM - No memory.
+ * @return EINVAL - Invalid argument.
+ */
+int ref_array_copy(struct ref_array *ra,
+                   ref_array_copy_cb copy_cb,
+                   ref_array_fn cb,
+                   void *data,
+                   struct ref_array **copy_ra);
+
+
 /**
  * @}
  */
diff --git a/refarray/ref_array_ut.c b/refarray/ref_array_ut.c
index 6e6009a0a6b4acccd296059c9dc4015730d14c02..5347634ffc6899f0fe86aae2d39469b346930fd4 100644
--- a/refarray/ref_array_ut.c
+++ b/refarray/ref_array_ut.c
@@ -36,7 +36,7 @@ int verbose = 0;
         if (verbose) foo; \
     } while(0)
 
-extern void ref_array_debug(struct ref_array *ra);
+extern void ref_array_debug(struct ref_array *ra, int num);
 
 typedef int (*test_fn)(void);
 
@@ -65,7 +65,7 @@ int ref_array_basic_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     error = ref_array_append(ra, &line1);
     if (error) {
@@ -74,7 +74,7 @@ int ref_array_basic_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     error = ref_array_append(ra, &line2);
     if (error) {
@@ -83,7 +83,7 @@ int ref_array_basic_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     error = ref_array_append(ra, &line3);
     if (error) {
@@ -92,7 +92,7 @@ int ref_array_basic_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     error = ref_array_append(ra, &line4);
     if (error) {
@@ -101,7 +101,7 @@ int ref_array_basic_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     error = ref_array_append(ra, &line5);
     if (error) {
@@ -110,7 +110,7 @@ int ref_array_basic_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     error = ref_array_append(ra, &line6);
     if (error) {
@@ -119,7 +119,7 @@ int ref_array_basic_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     RAOUT(printf("\n\nTest 1 - Printing lines.\n\n"));
 
@@ -247,7 +247,7 @@ int ref_array_free_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     str = strdup(line1);
 
@@ -258,7 +258,7 @@ int ref_array_free_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     str = strdup(line2);
 
@@ -269,7 +269,7 @@ int ref_array_free_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     str = strdup(line3);
 
@@ -280,7 +280,7 @@ int ref_array_free_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     str = strdup(line4);
 
@@ -291,7 +291,7 @@ int ref_array_free_test(void)
         return error;
     }
 
-    RAOUT(ref_array_debug(ra));
+    RAOUT(ref_array_debug(ra, 0));
 
     i = 0;
     for (;;) {
@@ -560,7 +560,189 @@ int ref_array_adv_test(void)
     return EOK;
 }
 
+int copy_cb(void *elem,
+            void *new_elem)
+{
+    char *ne = NULL;
+
+    ne = strdup(*((char **)elem));
+    *((char **)new_elem) = ne;
+
+    RAOUT(printf("Source: %s\nCopy:%s\n", *((char **)elem), ne));
+
+
+    return EOK;
+}
+
+int ref_array_copy_test(void)
+{
+    const char *line1 = "line1";
+    const char *line2 = "line2";
+    const char *line3 = "line3";
+    const char *line4 = "line4";
+    const char *line5 = "line5";
+    const char *line6 = "line6";
+    uint32_t i;
+    struct ref_array *ra;
+    struct ref_array *ra2;
+    int error = EOK;
+    uint32_t len = 0;
+    char text[] = "Deleting: ";
+
+    error = ref_array_create(&ra, sizeof(char *), 1, NULL, NULL);
+    if (error) {
+        printf("Failed to create array %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 0));
+
+    error = ref_array_append(ra, &line1);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to append to array line 1 %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 0));
+
+    error = ref_array_append(ra, &line2);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to append to array line 2 %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 0));
+
+    error = ref_array_append(ra, &line3);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to append to array line 3 %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 0));
+
+    error = ref_array_append(ra, &line4);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to append to array line 4 %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 0));
+
+    error = ref_array_append(ra, &line5);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to append to array line 5 %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 0));
+
+    error = ref_array_append(ra, &line6);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to append to array line 6 %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 0));
+
+    RAOUT(printf("\n\nCopy lines.\n\n"));
+
+    error = ref_array_copy(ra, copy_cb, array_cleanup, (char *)text, &ra2);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to copy array %d\n", error);
+        return error;
+    }
+
+    for (i = 0; i < len; i++) {
+        if (strcmp(ref_array_get(ra, i, NULL),
+                   ref_array_get(ra2, i, NULL)) != 0) {
+            printf("\nRetrieved strings were expected to be same,\n");
+            printf("but they are not:\n");
+            printf("First:[%s]\nSecond:[%s]\n",
+                    (char *)ref_array_get(ra, i, NULL),
+                    (char *)ref_array_get(ra2, i, NULL));
+            ref_array_destroy(ra);
+            ref_array_destroy(ra2);
+            return EFAULT;
+        }
+    }
+
+    RAOUT(printf("\n\nSource array.\n\n"));
+    RAOUT(ref_array_debug(ra, 0));
+    ref_array_destroy(ra);
+
+    RAOUT(printf("\n\nAbout to destroy a copy.\n\n"));
+    RAOUT(ref_array_debug(ra2, 0));
+    ref_array_destroy(ra2);
+
+    RAOUT(printf("\n\nDone!!!\n\n"));
+    return EOK;
+
+}
+
+int ref_array_copy_num_test(void)
+{
+    uint32_t i,j,k;
+    struct ref_array *ra;
+    struct ref_array *ra2;
+    int error = EOK;
+    uint32_t len = 0;
+
+    error = ref_array_create(&ra, sizeof(uint32_t), 1, NULL, NULL);
+    if (error) {
+        printf("Failed to create array %d\n", error);
+        return error;
+    }
+
+    RAOUT(ref_array_debug(ra, 1));
+
+    for (i=0; i<5; i++) {
+        error = ref_array_append(ra, &i);
+        if (error) {
+            ref_array_destroy(ra);
+            printf("Failed to append number to array %d\n", error);
+            return error;
+        }
+
+        RAOUT(ref_array_debug(ra, 1));
+    }
 
+    RAOUT(printf("\n\nCopy num test.\n\n"));
+
+    error = ref_array_copy(ra, NULL, NULL, NULL, &ra2);
+    if (error) {
+        ref_array_destroy(ra);
+        printf("Failed to copy array %d\n", error);
+        return error;
+    }
+
+    for (i = 0; i < len; i++) {
+        j = *((uint32_t *)(ref_array_get(ra, i, NULL)));
+        k = *((uint32_t *)(ref_array_get(ra2, i, NULL)));
+        if (j != k) {
+            printf("\nRetrieved values were expected to be same,\n");
+            printf("but they are not:\n");
+            printf("First:[%d]\nSecond:[%d]\n", j, k);
+            ref_array_destroy(ra);
+            ref_array_destroy(ra2);
+            return EFAULT;
+        }
+    }
+
+    ref_array_destroy(ra);
+    ref_array_destroy(ra2);
+
+    RAOUT(printf("\n\nDone!!!\n\n"));
+    return EOK;
+
+}
 
 /* Main function of the unit test */
 int main(int argc, char *argv[])
@@ -569,6 +751,8 @@ int main(int argc, char *argv[])
     test_fn tests[] = { ref_array_basic_test,
                         ref_array_free_test,
                         ref_array_adv_test,
+                        ref_array_copy_test,
+                        ref_array_copy_num_test,
                         NULL };
     test_fn t;
     int i = 0;
-- 
1.5.5.6

