add new pointer list wrapper - resolves #234

Thu, 26 Jan 2023 20:59:36 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 26 Jan 2023 20:59:36 +0100
changeset 641
d402fead3386
parent 640
55cc3b373c5e
child 642
98c90759f69e

add new pointer list wrapper - resolves #234

since we need a thread local variable, this drops C99 support

CMakeLists.txt file | annotate | diff | comparison | revisions
src/array_list.c file | annotate | diff | comparison | revisions
src/cx/iterator.h file | annotate | diff | comparison | revisions
src/cx/list.h file | annotate | diff | comparison | revisions
src/linked_list.c file | annotate | diff | comparison | revisions
src/list.c file | annotate | diff | comparison | revisions
test/test_list.cpp file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Wed Jan 25 19:19:29 2023 +0100
+++ b/CMakeLists.txt	Thu Jan 26 20:59:36 2023 +0100
@@ -3,7 +3,7 @@
 
 # Configuration
 set(CMAKE_C_STANDARD 11)
-set(CMAKE_C_STANDARD_REQUIRED  99)
+set(CMAKE_C_STANDARD_REQUIRED 11)
 
 option(GCC_MORE_WARNINGS "Enable -Wall -Wextra -pedantic when using gcc." OFF)
 if (GCC_MORE_WARNINGS AND CMAKE_COMPILER_IS_GNUCC)
--- a/src/array_list.c	Wed Jan 25 19:19:29 2023 +0100
+++ b/src/array_list.c	Thu Jan 26 20:59:36 2023 +0100
@@ -224,6 +224,14 @@
     }
 }
 
+static int cx_arl_insert_element(
+        struct cx_list_s *list,
+        size_t index,
+        void const *element
+) {
+    return 1 != cx_arl_insert_array(list, index, element, 1);
+}
+
 static int cx_arl_insert_iter(
         struct cx_mut_iterator_s *iter,
         void const *elem,
@@ -231,11 +239,10 @@
 ) {
     struct cx_list_s *list = iter->src_handle;
     if (iter->index < list->size) {
-        int result = 1 != cx_arl_insert_array(
+        int result = cx_arl_insert_element(
                 list,
                 iter->index + 1 - prepend,
-                elem,
-                1
+                elem
         );
         if (result == 0 && prepend != 0) {
             iter->index++;
@@ -243,7 +250,7 @@
         }
         return result;
     } else {
-        int result = 1 != cx_arl_insert_array(list, list->size, elem, 1);
+        int result = cx_arl_insert_element(list, list->size, elem);
         iter->index = list->size;
         return result;
     }
@@ -407,6 +414,7 @@
 
 static cx_list_class cx_array_list_class = {
         cx_arl_destructor,
+        cx_arl_insert_element,
         cx_arl_insert_array,
         cx_arl_insert_iter,
         cx_arl_remove,
--- a/src/cx/iterator.h	Wed Jan 25 19:19:29 2023 +0100
+++ b/src/cx/iterator.h	Thu Jan 26 20:59:36 2023 +0100
@@ -56,6 +56,12 @@
     void *(*current)(void const *);
 
     /**
+     * Original implementation in case the function needs to be wrapped.
+     */
+    __attribute__ ((__nonnull__))
+    void *(*current_impl)(void const *);
+
+    /**
      * Advances the iterator.
      */
     __attribute__ ((__nonnull__))
--- a/src/cx/list.h	Wed Jan 25 19:19:29 2023 +0100
+++ b/src/cx/list.h	Thu Jan 26 20:59:36 2023 +0100
@@ -65,7 +65,11 @@
     /**
      * The list class definition.
      */
-    cx_list_class *cl;
+    cx_list_class const *cl;
+    /**
+     * The actual implementation in case the list class is delegating.
+     */
+    cx_list_class const *climpl;
     /**
      * The allocator to use.
      */
@@ -122,6 +126,16 @@
     void (*destructor)(struct cx_list_s *list);
 
     /**
+     * Member function for inserting a single elements.
+     * Implementors SHOULD see to performant implementations for corner cases.
+     */
+    int (*insert_element)(
+            struct cx_list_s *list,
+            size_t index,
+            void const *data
+    );
+
+    /**
      * Member function for inserting multiple elements.
      * Implementors SHOULD see to performant implementations for corner cases.
      */
@@ -198,6 +212,43 @@
 typedef struct cx_list_s CxList;
 
 /**
+ * Advises the list to store copies of the objects (default mode of operation).
+ *
+ * Retrieving objects from this list will yield pointers to the copies stored
+ * within this list.
+ *
+ * @param list the list
+ * @see cxListStorePointers()
+ */
+__attribute__((__nonnull__))
+void cxListStoreObjects(CxList *list);
+
+/**
+ * Advises the list to only store pointers to the objects.
+ *
+ * Retrieving objects from this list will yield the original pointers stored.
+ *
+ * @note This function forcibly sets the element size to the size of a pointer.
+ * Invoking this function on a non-empty list that already stores copies of
+ * objects is undefined.
+ *
+ * @param list the list
+ * @see cxListStoreObjects()
+ */
+__attribute__((__nonnull__))
+void cxListStorePointers(CxList *list);
+
+/**
+ * Returns true, if this list is storing pointers instead of the actual data.
+ *
+ * @param list
+ * @return
+ * @see cxListStorePointers()
+ */
+__attribute__((__nonnull__))
+bool cxListIsStoringPointers(CxList *list);
+
+/**
  * Adds an item to the end of the list.
  *
  * @param list the list
@@ -210,7 +261,7 @@
         CxList *list,
         void const *elem
 ) {
-    return list->cl->insert_array(list, list->size, elem, 1) != 1;
+    return list->cl->insert_element(list, list->size, elem);
 }
 
 /**
@@ -221,6 +272,9 @@
  * If there is not enough memory to add all elements, the returned value is
  * less than \p n.
  *
+ * If this list is storing pointers instead of objects \p array is expected to
+ * be an array of pointers.
+ *
  * @param list the list
  * @param array a pointer to the elements to add
  * @param n the number of elements to add
@@ -254,7 +308,7 @@
         size_t index,
         void const *elem
 ) {
-    return list->cl->insert_array(list, index, elem, 1) != 1;
+    return list->cl->insert_element(list, index, elem);
 }
 
 /**
@@ -267,6 +321,9 @@
  * If there is not enough memory to add all elements, the returned value is
  * less than \p n.
  *
+ * If this list is storing pointers instead of objects \p array is expected to
+ * be an array of pointers.
+ *
  * @param list the list
  * @param index the index where to add the elements
  * @param array a pointer to the elements to add
--- a/src/linked_list.c	Wed Jan 25 19:19:29 2023 +0100
+++ b/src/linked_list.c	Thu Jan 26 20:59:36 2023 +0100
@@ -546,6 +546,14 @@
     return n;
 }
 
+static int cx_ll_insert_element(
+        struct cx_list_s *list,
+        size_t index,
+        void const *element
+) {
+    return 1 != cx_ll_insert_array(list, index, element, 1);
+}
+
 static int cx_ll_remove(
         struct cx_list_s *list,
         size_t index
@@ -682,7 +690,7 @@
         iter->index += prepend * (0 == result);
         return result;
     } else {
-        int result = cx_ll_insert_array(list, list->size, elem, 1) != 1;
+        int result = cx_ll_insert_element(list, list->size, elem);
         iter->index = list->size;
         return result;
     }
@@ -702,6 +710,7 @@
 
 static cx_list_class cx_linked_list_class = {
         cx_ll_destructor,
+        cx_ll_insert_element,
         cx_ll_insert_array,
         cx_ll_insert_iter,
         cx_ll_remove,
--- a/src/list.c	Wed Jan 25 19:19:29 2023 +0100
+++ b/src/list.c	Thu Jan 26 20:59:36 2023 +0100
@@ -30,6 +30,158 @@
 
 #include <string.h>
 
+// <editor-fold desc="Store Pointers Functionality">
+
+static _Thread_local CxListComparator cx_pl_cmpfunc_impl;
+
+static int cx_pl_cmpfunc(
+        void const *l,
+        void const *r
+) {
+    void *const *lptr = l;
+    void *const *rptr = r;
+    void const *left = lptr == NULL ? NULL : *lptr;
+    void const *right = rptr == NULL ? NULL : *rptr;
+    return cx_pl_cmpfunc_impl(left, right);
+}
+
+static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) {
+    // cast away const - this is the hacky thing
+    struct cx_list_s *l = (struct cx_list_s *) list;
+    cx_pl_cmpfunc_impl = l->cmpfunc;
+    l->cmpfunc = cx_pl_cmpfunc;
+}
+
+static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) {
+    // cast away const - this is the hacky thing
+    struct cx_list_s *l = (struct cx_list_s *) list;
+    l->cmpfunc = cx_pl_cmpfunc_impl;
+}
+
+static void cx_pl_destructor(struct cx_list_s *list) {
+    list->climpl->destructor(list);
+}
+
+static int cx_pl_insert_element(
+        struct cx_list_s *list,
+        size_t index,
+        void const *element
+) {
+    return list->climpl->insert_element(list, index, &element);
+}
+
+static size_t cx_pl_insert_array(
+        struct cx_list_s *list,
+        size_t index,
+        void const *array,
+        size_t n
+) {
+    return list->climpl->insert_array(list, index, array, n);
+}
+
+static int cx_pl_insert_iter(
+        struct cx_mut_iterator_s *iter,
+        void const *elem,
+        int prepend
+) {
+    struct cx_list_s *list = iter->src_handle;
+    return list->climpl->insert_iter(iter, &elem, prepend);
+}
+
+static int cx_pl_remove(
+        struct cx_list_s *list,
+        size_t index
+) {
+    return list->climpl->remove(list, index);
+}
+
+static void *cx_pl_at(
+        struct cx_list_s const *list,
+        size_t index
+) {
+    void **ptr = list->climpl->at(list, index);
+    return ptr == NULL ? NULL : *ptr;
+}
+
+static size_t cx_pl_find(
+        struct cx_list_s const *list,
+        void const *elem
+) {
+    cx_pl_hack_cmpfunc(list);
+    size_t ret = list->climpl->find(list, &elem);
+    cx_pl_unhack_cmpfunc(list);
+    return ret;
+}
+
+static void cx_pl_sort(struct cx_list_s *list) {
+    cx_pl_hack_cmpfunc(list);
+    list->climpl->sort(list);
+    cx_pl_unhack_cmpfunc(list);
+}
+
+static int cx_pl_compare(
+        struct cx_list_s const *list,
+        struct cx_list_s const *other
+) {
+    cx_pl_hack_cmpfunc(list);
+    int ret = list->climpl->compare(list, other);
+    cx_pl_unhack_cmpfunc(list);
+    return ret;
+}
+
+static void cx_pl_reverse(struct cx_list_s *list) {
+    list->climpl->reverse(list);
+}
+
+static void *cx_pl_iter_current(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    void **ptr = iter->base.current_impl(it);
+    return ptr == NULL ? NULL : *ptr;
+}
+
+static struct cx_iterator_s cx_pl_iterator(
+        struct cx_list_s const *list,
+        size_t index
+) {
+    struct cx_iterator_s iter = list->climpl->iterator(list, index);
+    iter.base.current_impl = iter.base.current;
+    iter.base.current = cx_pl_iter_current;
+    return iter;
+}
+
+static cx_list_class cx_pointer_list_class = {
+        cx_pl_destructor,
+        cx_pl_insert_element,
+        cx_pl_insert_array,
+        cx_pl_insert_iter,
+        cx_pl_remove,
+        cx_pl_at,
+        cx_pl_find,
+        cx_pl_sort,
+        cx_pl_compare,
+        cx_pl_reverse,
+        cx_pl_iterator,
+};
+
+void cxListStoreObjects(CxList *list) {
+    if (list->climpl != NULL) {
+        list->cl = list->climpl;
+        list->climpl = NULL;
+    }
+}
+
+void cxListStorePointers(CxList *list) {
+    list->itemsize = sizeof(void *);
+    list->climpl = list->cl;
+    list->cl = &cx_pointer_list_class;
+}
+
+bool cxListIsStoringPointers(CxList *list) {
+    return list->climpl != NULL;
+}
+
+// </editor-fold>
+
 void cxListDestroy(CxList *list) {
     switch (list->content_destructor_type) {
         case CX_DESTRUCTOR_SIMPLE: {
--- a/test/test_list.cpp	Wed Jan 25 19:19:29 2023 +0100
+++ b/test/test_list.cpp	Thu Jan 26 20:59:36 2023 +0100
@@ -573,6 +573,14 @@
         return list;
     }
 
+    auto pointerLinkedListFromTestData() const -> CxList * {
+        auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *)));
+        cxListStorePointers(list);
+        // note: cannot use cxListAddArray() because we don't have a list of pointers
+        cx_for_n(i, testdata_len) cxListAdd(list, &testdata.data[i]);
+        return list;
+    }
+
     auto arrayListFromTestData() const -> CxList * {
         auto list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), testdata_len));
         cxListAddArray(list, testdata.data.data(), testdata_len);
@@ -625,21 +633,37 @@
         EXPECT_EQ(*(int *) cxListAt(list, 3), 42);
     }
 
-    static void verifyInsertArray(CxList *list) {
+    static void verifyInsertArray(
+            CxList *list,
+            bool pointers = false
+    ) {
         int a[5] = {5, 47, 11, 13, 42};
         int b[5] = {9, 18, 72, 50, 7};
+        int *aptr[5];
+        int *bptr[5];
+        cx_for_n(i, 5) {
+            aptr[i] = &a[i];
+            bptr[i] = &b[i];
+        }
 
         size_t inserted;
 
-        inserted = cxListInsertArray(list, 0, a, 5);
+        if (pointers) {
+            inserted = cxListInsertArray(list, 0, aptr, 5);
+        } else {
+            inserted = cxListInsertArray(list, 0, a, 5);
+        }
         EXPECT_EQ(inserted, 5);
         EXPECT_EQ(*(int *) cxListAt(list, 0), 5);
         EXPECT_EQ(*(int *) cxListAt(list, 1), 47);
         EXPECT_EQ(*(int *) cxListAt(list, 2), 11);
         EXPECT_EQ(*(int *) cxListAt(list, 3), 13);
         EXPECT_EQ(*(int *) cxListAt(list, 4), 42);
-
-        inserted = cxListInsertArray(list, 3, b, 5);
+        if (pointers) {
+            inserted = cxListInsertArray(list, 3, bptr, 5);
+        } else {
+            inserted = cxListInsertArray(list, 3, b, 5);
+        }
         EXPECT_EQ(inserted, 5);
         EXPECT_EQ(*(int *) cxListAt(list, 0), 5);
         EXPECT_EQ(*(int *) cxListAt(list, 1), 47);
@@ -787,9 +811,26 @@
 class LinkedList : public HighLevelTest {
 };
 
+class PointerLinkedList : public HighLevelTest {
+};
+
 class ArrayList : public HighLevelTest {
 };
 
+TEST_F(PointerLinkedList, cxListStorePointers) {
+    auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, 47));
+    EXPECT_FALSE(cxListIsStoringPointers(list));
+    cxListStorePointers(list);
+    EXPECT_EQ(list->itemsize, sizeof(void *));
+    EXPECT_NE(list->cl, nullptr);
+    EXPECT_NE(list->climpl, nullptr);
+    EXPECT_TRUE(cxListIsStoringPointers(list));
+    cxListStoreObjects(list);
+    EXPECT_NE(list->cl, nullptr);
+    EXPECT_EQ(list->climpl, nullptr);
+    EXPECT_FALSE(cxListIsStoringPointers(list));
+}
+
 TEST_F(LinkedList, cxLinkedListCreate) {
     CxList *list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)));
     ASSERT_NE(list, nullptr);
@@ -807,12 +848,18 @@
 }
 
 TEST_F(LinkedList, cxListAdd) {
-    CxList *list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)));
+    auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)));
     verifyAdd(list, false);
 }
 
+TEST_F(PointerLinkedList, cxListAdd) {
+    auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *)));
+    cxListStorePointers(list);
+    verifyAdd(list, true);
+}
+
 TEST_F(ArrayList, cxListAdd) {
-    CxList *list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 8));
+    auto list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 8));
     verifyAdd(list, false);
 }
 
@@ -820,6 +867,12 @@
     verifyInsert(autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))));
 }
 
+TEST_F(PointerLinkedList, cxListInsert) {
+    auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *)));
+    cxListStorePointers(list);
+    verifyInsert(list);
+}
+
 TEST_F(ArrayList, cxListInsert) {
     verifyInsert(autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 2)));
 }
@@ -828,6 +881,12 @@
     verifyInsertArray(autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))));
 }
 
+TEST_F(PointerLinkedList, cxListInsertArray) {
+    auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *)));
+    cxListStorePointers(list);
+    verifyInsertArray(list, true);
+}
+
 TEST_F(ArrayList, cxListInsertArray) {
     verifyInsertArray(autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 4)));
 }
@@ -836,6 +895,10 @@
     verifyRemove(linkedListFromTestData());
 }
 
+TEST_F(PointerLinkedList, cxListRemove) {
+    verifyRemove(pointerLinkedListFromTestData());
+}
+
 TEST_F(ArrayList, cxListRemove) {
     verifyRemove(arrayListFromTestData());
 }
@@ -844,6 +907,10 @@
     verifyAt(linkedListFromTestData());
 }
 
+TEST_F(PointerLinkedList, cxListAt) {
+    verifyAt(pointerLinkedListFromTestData());
+}
+
 TEST_F(ArrayList, cxListAt) {
     verifyAt(arrayListFromTestData());
 }
@@ -852,6 +919,10 @@
     verifyFind(linkedListFromTestData());
 }
 
+TEST_F(PointerLinkedList, cxListFind) {
+    verifyFind(pointerLinkedListFromTestData());
+}
+
 TEST_F(ArrayList, cxListFind) {
     verifyFind(arrayListFromTestData());
 }
@@ -860,6 +931,10 @@
     verifySort(linkedListFromTestData());
 }
 
+TEST_F(PointerLinkedList, cxListSort) {
+    verifySort(pointerLinkedListFromTestData());
+}
+
 TEST_F(ArrayList, cxListSort) {
     verifySort(arrayListFromTestData());
 }
@@ -868,6 +943,10 @@
     verifyIterator(linkedListFromTestData());
 }
 
+TEST_F(PointerLinkedList, Iterator) {
+    verifyIterator(pointerLinkedListFromTestData());
+}
+
 TEST_F(ArrayList, Iterator) {
     verifyIterator(arrayListFromTestData());
 }
@@ -879,6 +958,15 @@
     verifyInsertViaIterator(list);
 }
 
+TEST_F(PointerLinkedList, InsertViaIterator) {
+    int fivenums[] = {0, 1, 2, 3, 4, 5};
+    auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *)));
+    cxListStorePointers(list);
+    // note: cannot use cxListAddArray() because we don't have a list of pointers
+    cx_for_n(i, 5) cxListAdd(list, &fivenums[i]);
+    verifyInsertViaIterator(list);
+}
+
 TEST_F(ArrayList, InsertViaIterator) {
     int fivenums[] = {0, 1, 2, 3, 4, 5};
     CxList *list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 4));
@@ -890,6 +978,10 @@
     verifyReverse(linkedListFromTestData());
 }
 
+TEST_F(PointerLinkedList, cxListReverse) {
+    verifyReverse(pointerLinkedListFromTestData());
+}
+
 TEST_F(ArrayList, cxListReverse) {
     verifyReverse(arrayListFromTestData());
 }
@@ -900,20 +992,86 @@
     verifyCompare(left, right);
 }
 
+TEST_F(LinkedList, cxListCompareWithPtrList) {
+    auto left = linkedListFromTestData();
+    auto right = pointerLinkedListFromTestData();
+    verifyCompare(left, right);
+}
+
 TEST_F(LinkedList, cxListCompareWithArrayList) {
     auto left = linkedListFromTestData();
     auto right = arrayListFromTestData();
     verifyCompare(left, right);
 }
 
+TEST_F(PointerLinkedList, cxListCompare) {
+    auto left = pointerLinkedListFromTestData();
+    auto right = pointerLinkedListFromTestData();
+    verifyCompare(left, right);
+}
+
+TEST_F(PointerLinkedList, cxListCompareWithNormalList) {
+    auto left = pointerLinkedListFromTestData();
+    auto right = linkedListFromTestData();
+    verifyCompare(left, right);
+}
+
+TEST_F(PointerLinkedList, cxListCompareWithArrayList) {
+    auto left = pointerLinkedListFromTestData();
+    auto right = arrayListFromTestData();
+    verifyCompare(left, right);
+}
+
 TEST_F(ArrayList, cxListCompare) {
     auto left = arrayListFromTestData();
     auto right = arrayListFromTestData();
     verifyCompare(left, right);
 }
 
-TEST_F(ArrayList, cxListCompareWithLinkedList) {
+TEST_F(ArrayList, cxListCompareWithPtrList) {
+    auto left = arrayListFromTestData();
+    auto right = pointerLinkedListFromTestData();
+    verifyCompare(left, right);
+}
+
+TEST_F(ArrayList, cxListCompareWithNormalList) {
     auto left = arrayListFromTestData();
     auto right = linkedListFromTestData();
     verifyCompare(left, right);
 }
+
+TEST_F(PointerLinkedList, NoDestructor) {
+    void *item = cxMalloc(&testingAllocator, sizeof(int));
+    auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *));
+    cxListStorePointers(list);
+    cxListAdd(list, item);
+    ASSERT_FALSE(testingAllocator.verify());
+    cxListDestroy(list);
+    EXPECT_FALSE(testingAllocator.verify());
+    cxFree(&testingAllocator, item);
+    EXPECT_TRUE(testingAllocator.verify());
+}
+
+TEST_F(PointerLinkedList, SimpleDestructor) {
+    int item = 0;
+    auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *));
+    cxListStorePointers(list);
+    list->content_destructor_type = CX_DESTRUCTOR_SIMPLE;
+    list->simple_destructor = [](void *elem) { *(int *) elem = 42; };
+    cxListAdd(list, &item);
+    cxListDestroy(list);
+    EXPECT_EQ(item, 42);
+}
+
+TEST_F(PointerLinkedList, AdvancedDestructor) {
+    void *item = cxMalloc(&testingAllocator, sizeof(int));
+    auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *));
+    cxListStorePointers(list);
+    list->content_destructor_type = CX_DESTRUCTOR_ADVANCED;
+    list->advanced_destructor.data = &testingAllocator;
+    list->advanced_destructor.func = (cx_destructor_func2) cxFree;
+    cxListAdd(list, item);
+    ASSERT_FALSE(testingAllocator.verify());
+    cxListDestroy(list);
+    EXPECT_TRUE(testingAllocator.verify());
+}

mercurial