add empty list implementation - fixes #258

Sun, 21 May 2023 14:03:21 +0200

author
Mike Becker <universe@uap-core.de>
date
Sun, 21 May 2023 14:03:21 +0200
changeset 704
35f06c5eeb0e
parent 703
425d4279856f
child 705
0d5447230044

add empty list implementation - fixes #258

src/cx/iterator.h file | annotate | diff | comparison | revisions
src/cx/list.h file | annotate | diff | comparison | revisions
src/list.c file | annotate | diff | comparison | revisions
tests/test_list.cpp file | annotate | diff | comparison | revisions
--- a/src/cx/iterator.h	Sun May 21 11:52:58 2023 +0200
+++ b/src/cx/iterator.h	Sun May 21 14:03:21 2023 +0200
@@ -51,6 +51,8 @@
 
     /**
      * Returns a pointer to the current element.
+     *
+     * When valid returns false, the behavior of this function is undefined.
      */
     __attribute__ ((__nonnull__))
     void *(*current)(void const *);
@@ -63,12 +65,16 @@
 
     /**
      * Advances the iterator.
+     *
+     * When valid returns false, the behavior of this function is undefined.
      */
     __attribute__ ((__nonnull__))
     void (*next)(void *);
 
     /**
      * Flag current element for removal, if possible.
+     *
+     * When valid returns false, the behavior of this function is undefined.
      */
     __attribute__ ((__nonnull__))
     bool (*flag_removal)(void *);
--- a/src/cx/list.h	Sun May 21 11:52:58 2023 +0200
+++ b/src/cx/list.h	Sun May 21 14:03:21 2023 +0200
@@ -632,6 +632,14 @@
 __attribute__((__nonnull__))
 void cxListDestroy(CxList *list);
 
+/**
+ * A shared instance of an empty list.
+ *
+ * Writing to that list is undefined.
+ */
+extern CxList * const cxEmptyList;
+
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
--- a/src/list.c	Sun May 21 11:52:58 2023 +0200
+++ b/src/list.c	Sun May 21 14:03:21 2023 +0200
@@ -195,6 +195,83 @@
 
 // </editor-fold>
 
+// <editor-fold desc="empty list implementation">
+
+static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) {
+    // this is a noop, but MUST be implemented
+}
+
+static void *cx_emptyl_at(
+        __attribute__((__unused__)) struct cx_list_s const *list,
+        __attribute__((__unused__)) size_t index
+) {
+    return NULL;
+}
+
+static ssize_t cx_emptyl_find(
+        __attribute__((__unused__)) struct cx_list_s const *list,
+        __attribute__((__unused__)) void const *elem
+) {
+    return -1;
+}
+
+static int cx_emptyl_compare(
+        __attribute__((__unused__)) struct cx_list_s const *list,
+        struct cx_list_s const *other
+) {
+    if (other->size == 0) return 0;
+    return -1;
+}
+
+static bool cx_emptyl_iter_valid(__attribute__((__unused__)) void const *iter) {
+    return false;
+}
+
+static CxIterator cx_emptyl_iterator(
+        struct cx_list_s const *list,
+        size_t index,
+        __attribute__((__unused__)) bool backwards
+) {
+    CxIterator iter = {0};
+    iter.src_handle = list;
+    iter.index = index;
+    iter.base.valid = cx_emptyl_iter_valid;
+    return iter;
+}
+
+static cx_list_class cx_empty_list_class = {
+        cx_emptyl_noop,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        cx_emptyl_noop,
+        NULL,
+        cx_emptyl_at,
+        cx_emptyl_find,
+        cx_emptyl_noop,
+        cx_emptyl_compare,
+        cx_emptyl_noop,
+        cx_emptyl_iterator,
+};
+
+CxList cx_empty_list = {
+        NULL,
+        NULL,
+        0,
+        0,
+        NULL,
+        NULL,
+        NULL,
+        false,
+        &cx_empty_list_class,
+        NULL
+};
+
+CxList *const cxEmptyList = &cx_empty_list;
+
+// </editor-fold>
+
 void cxListDestroy(CxList *list) {
     if (list->simple_destructor) {
         CxIterator iter = cxListIterator(list);
@@ -212,7 +289,9 @@
     }
 
     list->cl->destructor(list);
-    cxFree(list->allocator, list);
+    if (list->allocator) {
+        cxFree(list->allocator, list);
+    }
 }
 
 int cxListCompare(
--- a/tests/test_list.cpp	Sun May 21 11:52:58 2023 +0200
+++ b/tests/test_list.cpp	Sun May 21 14:03:21 2023 +0200
@@ -1477,3 +1477,80 @@
     EXPECT_TRUE(testingAllocator.verify());
 }
 
+TEST(EmptyList, Size) {
+    auto list = cxEmptyList;
+
+    EXPECT_EQ(list->size, 0);
+    EXPECT_EQ(cxListSize(list), 0);
+}
+
+TEST(EmptyList, Iterator) {
+    auto list = cxEmptyList;
+
+    auto it1 = cxListIterator(list);
+    auto it2 = cxListBackwardsIterator(list);
+    auto it3 = cxListMutIterator(list);
+    auto it4 = cxListMutBackwardsIterator(list);
+
+    EXPECT_FALSE(cxIteratorValid(it1));
+    EXPECT_FALSE(cxIteratorValid(it2));
+    EXPECT_FALSE(cxIteratorValid(it3));
+    EXPECT_FALSE(cxIteratorValid(it4));
+
+    int c = 0;
+    cx_foreach(void*, data, it1) c++;
+    cx_foreach(void*, data, it2) c++;
+    cx_foreach(void*, data, it3) c++;
+    cx_foreach(void*, data, it4) c++;
+    EXPECT_EQ(c, 0);
+}
+
+TEST(EmptyList, NoOps) {
+    auto list = cxEmptyList;
+
+    ASSERT_NO_FATAL_FAILURE(cxListSort(list));
+    ASSERT_NO_FATAL_FAILURE(cxListClear(list));
+    ASSERT_NO_FATAL_FAILURE(cxListDestroy(list));
+}
+
+TEST(EmptyList, At) {
+    auto list = cxEmptyList;
+
+    EXPECT_EQ(cxListAt(list, 0), nullptr);
+    EXPECT_EQ(cxListAt(list, 1), nullptr);
+}
+
+TEST(EmptyList, Find) {
+    auto list = cxEmptyList;
+
+    int x = 42, y = 1337;
+
+    EXPECT_LT(cxListFind(list, &x), 0);
+    EXPECT_LT(cxListFind(list, &y), 0);
+}
+
+TEST(EmptyList, Compare) {
+    auto empty = cxEmptyList;
+
+    auto ll = cxLinkedListCreateSimple(sizeof(int));
+    auto al = cxArrayListCreateSimple(sizeof(int), 8);
+
+    int x = 5;
+
+    EXPECT_EQ(cxListCompare(empty, cxEmptyList), 0);
+    EXPECT_EQ(cxListCompare(ll, cxEmptyList), 0);
+    EXPECT_EQ(cxListCompare(al, cxEmptyList), 0);
+    EXPECT_EQ(cxListCompare(cxEmptyList, ll), 0);
+    EXPECT_EQ(cxListCompare(cxEmptyList, al), 0);
+
+    cxListAdd(ll, &x);
+    cxListAdd(al, &x);
+
+    EXPECT_GT(cxListCompare(ll, cxEmptyList), 0);
+    EXPECT_GT(cxListCompare(al, cxEmptyList), 0);
+    EXPECT_LT(cxListCompare(cxEmptyList, ll), 0);
+    EXPECT_LT(cxListCompare(cxEmptyList, al), 0);
+
+    cxListDestroy(ll);
+    cxListDestroy(al);
+}

mercurial