# HG changeset patch # User Mike Becker # Date 1760176546 -7200 # Node ID 8bfccb342895bd8b6c302912ee0d82c314dbdbe8 # Parent 809eb30cd62130fd9ceb7560ae71664e767cb829 changes the compare function wrapper for pointer lists so that it no longer invokes the actual compare function for NULL pointers diff -r 809eb30cd621 -r 8bfccb342895 CHANGELOG --- a/CHANGELOG Fri Oct 10 19:40:24 2025 +0200 +++ b/CHANGELOG Sat Oct 11 11:55:46 2025 +0200 @@ -28,6 +28,7 @@ * changes the implementation of cx_strreplacen() for improved efficiency * changes all cxListIterator() and cxMapIterator() family of functions to also accept NULL as argument * changes insert_element member function of CxList to accept NULL source and return a pointer to the inserted element + * changes the compare function wrapper for pointer lists so that it no longer invokes the actual compare function for NULL pointers * fixes critical memory overflow in the stack-based array reallocator (this unfortunately breaks the function signature) * fixes critical bug in cx_array_insert_sorted() that caused an infinite loop when inserting duplicates * fixes mempool implementation not supporting NULL as argument for realloc diff -r 809eb30cd621 -r 8bfccb342895 docs/Writerside/topics/about.md --- a/docs/Writerside/topics/about.md Fri Oct 10 19:40:24 2025 +0200 +++ b/docs/Writerside/topics/about.md Sat Oct 11 11:55:46 2025 +0200 @@ -55,6 +55,7 @@ * changes the implementation of cx_strreplacen() for improved efficiency * changes all cxListIterator() and cxMapIterator() family of functions to also accept NULL as argument * changes insert_element member function of CxList to accept NULL source and return a pointer to the inserted element +* changes the compare function wrapper for pointer lists so that it no longer invokes the actual compare function for NULL pointers * fixes critical memory overflow in the stack-based array reallocator (this unfortunately breaks the function signature) * fixes critical bug in cx_array_insert_sorted() that caused an infinite loop when inserting duplicates * fixes mempool implementation not supporting NULL as argument for realloc diff -r 809eb30cd621 -r 8bfccb342895 src/list.c --- a/src/list.c Fri Oct 10 19:40:24 2025 +0200 +++ b/src/list.c Sat Oct 11 11:55:46 2025 +0200 @@ -38,10 +38,18 @@ const void *l, const void *r ) { + // l and r are guaranteed to be non-NULL pointing to the list's memory void *const *lptr = l; void *const *rptr = r; - const void *left = lptr == NULL ? NULL : *lptr; - const void *right = rptr == NULL ? NULL : *rptr; + const void *left = *lptr; + const void *right = *rptr; + if (left == NULL) { + // NULL is smaller than any value except NULL + return right == NULL ? 0 : -1; + } else if (right == NULL) { + // any value is larger than NULL + return 1; + } return cx_pl_cmpfunc_impl(left, right); } @@ -297,7 +305,10 @@ for (; i < n; i++) { if (NULL == invoke_list_func( insert_element, list, index + i, - src + (i * elem_size))) return i; + src + i * elem_size) + ) { + return i; // LCOV_EXCL_LINE + } } return i; } @@ -336,7 +347,9 @@ si++; inserted++; } - if (inserted == n) return inserted; + if (inserted == n) { + return inserted; + } } continue; } @@ -357,17 +370,22 @@ // insert the elements at location si if (ins == 1) { - if (NULL == invoke_list_func( - insert_element, list, di, src)) return inserted; + if (NULL == invoke_list_func(insert_element, list, di, src)) { + return inserted; // LCOV_EXCL_LINE + } } else { size_t r = invoke_list_func(insert_array, list, di, src, ins); - if (r < ins) return inserted + r; + if (r < ins) { + return inserted + r; // LCOV_EXCL_LINE + } } inserted += ins; di += ins; // everything inserted? - if (inserted == n) return inserted; + if (inserted == n) { + return inserted; + } src = next; } @@ -399,7 +417,7 @@ size_t elem_size = list->collection.elem_size; size_t list_size = list->collection.size; void *tmp = cxMallocDefault(elem_size * list_size); - if (tmp == NULL) abort(); + if (tmp == NULL) abort(); // LCOV_EXCL_LINE // copy elements from source array char *loc = tmp; @@ -432,7 +450,7 @@ size_t elem_size = list->collection.elem_size; void *tmp = cxMallocDefault(elem_size); - if (tmp == NULL) return 1; + if (tmp == NULL) return 1; // LCOV_EXCL_LINE void *ip = invoke_list_func(at, list, i); void *jp = invoke_list_func(at, list, j); diff -r 809eb30cd621 -r 8bfccb342895 tests/test_list.c --- a/tests/test_list.c Fri Oct 10 19:40:24 2025 +0200 +++ b/tests/test_list.c Sat Oct 11 11:55:46 2025 +0200 @@ -1143,18 +1143,24 @@ CxIterator it2 = cxListBackwardsIterator(list); CxIterator it3 = cxListMutIterator(list); CxIterator it4 = cxListMutBackwardsIterator(list); + CxIterator it5 = cxListMutIteratorAt(list, 0); + CxIterator it6 = cxListMutBackwardsIteratorAt(list, 0); CX_TEST_DO { CX_TEST_ASSERT(!cxIteratorValid(it1)); CX_TEST_ASSERT(!cxIteratorValid(it2)); CX_TEST_ASSERT(!cxIteratorValid(it3)); CX_TEST_ASSERT(!cxIteratorValid(it4)); + CX_TEST_ASSERT(!cxIteratorValid(it5)); + CX_TEST_ASSERT(!cxIteratorValid(it6)); 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++; + cx_foreach(void*, data, it5) c++; + cx_foreach(void*, data, it6) c++; CX_TEST_ASSERT(c == 0); } } @@ -1220,6 +1226,13 @@ cxListFree(al); } +CX_TEST(test_null_list_free) { + CX_TEST_DO { + // cannot really verify, but asan or valgrind would complain + cxListFree(NULL); + } +} + CX_TEST(test_list_ll_create) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); @@ -2415,6 +2428,34 @@ free(testdata); }) +CX_TEST(test_list_pointer_list_supports_null) { + CxList *list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS); + int x = 47; + int y = 11; + int z = 1337; + int *nptr = NULL; + cxListAdd(list, &x); + cxListAdd(list, &y); + cxListAdd(list, nptr); + cxListAdd(list, &z); + CX_TEST_DO { + CX_TEST_ASSERT(cxListSize(list) == 4); + CX_TEST_ASSERT(*(int *) cxListAt(list, 0) == 47); + CX_TEST_ASSERT(*(int *) cxListAt(list, 1) == 11); + CX_TEST_ASSERT((int *) cxListAt(list, 2) == NULL); + CX_TEST_ASSERT(*(int *) cxListAt(list, 3) == 1337); + CX_TEST_ASSERT(cxListFind(list, nptr) == 2); + + // when we sort the list, NULL is supposed to be smaller than any value + cxListSort(list); + CX_TEST_ASSERT((int *) cxListAt(list, 0) == NULL); + CX_TEST_ASSERT(*(int *) cxListAt(list, 1) == 11); + CX_TEST_ASSERT(*(int *) cxListAt(list, 2) == 47); + CX_TEST_ASSERT(*(int *) cxListAt(list, 3) == 1337); + } + cxListFree(list); +} + CxTestSuite *cx_test_suite_array_list(void) { CxTestSuite *suite = cx_test_suite_new("array_list"); @@ -2697,6 +2738,15 @@ cx_test_register(suite, test_empty_list_at); cx_test_register(suite, test_empty_list_find); cx_test_register(suite, test_empty_list_compare); + cx_test_register(suite, test_null_list_free); return suite; } + +CxTestSuite *cx_test_suite_list_corner_cases(void) { + CxTestSuite *suite = cx_test_suite_new("list corner cases"); + + cx_test_register(suite, test_list_pointer_list_supports_null); + + return suite; +} diff -r 809eb30cd621 -r 8bfccb342895 tests/ucxtest.c --- a/tests/ucxtest.c Fri Oct 10 19:40:24 2025 +0200 +++ b/tests/ucxtest.c Sat Oct 11 11:55:46 2025 +0200 @@ -48,6 +48,7 @@ CxTestSuite *cx_test_suite_linked_list_defaulted_funcs(void); CxTestSuite *cx_test_suite_kv_list(void); CxTestSuite *cx_test_suite_kv_list_specifics(void); +CxTestSuite *cx_test_suite_list_corner_cases(void); CxTestSuite *cx_test_suite_tree_low_level(void); CxTestSuite *cx_test_suite_tree_high_level(void); CxTestSuite *cx_test_suite_properties(void); @@ -101,6 +102,7 @@ cx_test_suite_linked_list_defaulted_funcs(), cx_test_suite_kv_list(), cx_test_suite_kv_list_specifics(), + cx_test_suite_list_corner_cases(), cx_test_suite_tree_low_level(), cx_test_suite_tree_high_level(), cx_test_suite_properties(),