Sun, 21 May 2023 14:03:21 +0200
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); +}