Mon, 20 Mar 2023 19:09:08 +0100
add unit test for cxListClear + fix destructor functions not always invoked with the correct pointer
src/array_list.c | 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 | |
tests/test_list.cpp | file | annotate | diff | comparison | revisions |
1.1 --- a/src/array_list.c Mon Mar 20 18:05:12 2023 +0100 1.2 +++ b/src/array_list.c Mon Mar 20 19:09:08 2023 +0100 1.3 @@ -310,15 +310,14 @@ 1.4 switch (list->content_destructor_type) { 1.5 case CX_DESTRUCTOR_SIMPLE: { 1.6 for (size_t i = 0; i < list->size; i++) { 1.7 - list->simple_destructor(ptr); 1.8 + cx_list_invoke_simple_destructor(list, ptr); 1.9 ptr += list->itemsize; 1.10 } 1.11 break; 1.12 } 1.13 case CX_DESTRUCTOR_ADVANCED: { 1.14 for (size_t i = 0; i < list->size; i++) { 1.15 - list->advanced_destructor.func(list->advanced_destructor.data, 1.16 - ptr); 1.17 + cx_list_invoke_advanced_destructor(list, ptr); 1.18 ptr += list->itemsize; 1.19 } 1.20 break; 1.21 @@ -326,6 +325,9 @@ 1.22 case CX_DESTRUCTOR_NONE: 1.23 break; // nothing 1.24 } 1.25 + 1.26 + memset(arl->data, 0, list->size * list->itemsize); 1.27 + list->size = 0; 1.28 } 1.29 1.30 static int cx_arl_swap(
2.1 --- a/src/cx/list.h Mon Mar 20 18:05:12 2023 +0100 2.2 +++ b/src/cx/list.h Mon Mar 20 19:09:08 2023 +0100 2.3 @@ -227,7 +227,7 @@ 2.4 typedef struct cx_list_s CxList; 2.5 2.6 /** 2.7 - * Invokes the destructor function for a specific element. 2.8 + * Invokes the configured destructor function for a specific element. 2.9 * 2.10 * Usually only used by list implementations. There should be no need 2.11 * to invoke this function manually. 2.12 @@ -242,6 +242,36 @@ 2.13 ); 2.14 2.15 /** 2.16 + * Invokes the simple destructor function for a specific element. 2.17 + * 2.18 + * Usually only used by list implementations. There should be no need 2.19 + * to invoke this function manually. 2.20 + * 2.21 + * @param list the list 2.22 + * @param elem the element 2.23 + */ 2.24 +__attribute__((__nonnull__)) 2.25 +void cx_list_invoke_simple_destructor( 2.26 + struct cx_list_s const *list, 2.27 + void *elem 2.28 +); 2.29 + 2.30 +/** 2.31 + * Invokes the advanced destructor function for a specific element. 2.32 + * 2.33 + * Usually only used by list implementations. There should be no need 2.34 + * to invoke this function manually. 2.35 + * 2.36 + * @param list the list 2.37 + * @param elem the element 2.38 + */ 2.39 +__attribute__((__nonnull__)) 2.40 +void cx_list_invoke_advanced_destructor( 2.41 + struct cx_list_s const *list, 2.42 + void *elem 2.43 +); 2.44 + 2.45 +/** 2.46 * Advises the list to store copies of the objects (default mode of operation). 2.47 * 2.48 * Retrieving objects from this list will yield pointers to the copies stored 2.49 @@ -276,7 +306,7 @@ 2.50 * @see cxListStorePointers() 2.51 */ 2.52 __attribute__((__nonnull__)) 2.53 -bool cxListIsStoringPointers(CxList *list); 2.54 +bool cxListIsStoringPointers(CxList const *list); 2.55 2.56 /** 2.57 * Adds an item to the end of the list.
3.1 --- a/src/linked_list.c Mon Mar 20 18:05:12 2023 +0100 3.2 +++ b/src/linked_list.c Mon Mar 20 19:09:08 2023 +0100 3.3 @@ -598,7 +598,7 @@ 3.4 switch (list->content_destructor_type) { 3.5 case CX_DESTRUCTOR_SIMPLE: { 3.6 while (node != NULL) { 3.7 - list->simple_destructor(node->payload); 3.8 + cx_list_invoke_simple_destructor(list, node->payload); 3.9 cx_linked_list_node *next = node->next; 3.10 cxFree(list->allocator, node); 3.11 node = next; 3.12 @@ -607,8 +607,7 @@ 3.13 } 3.14 case CX_DESTRUCTOR_ADVANCED: { 3.15 while (node != NULL) { 3.16 - list->advanced_destructor.func(list->advanced_destructor.data, 3.17 - node->payload); 3.18 + cx_list_invoke_advanced_destructor(list, node->payload); 3.19 cx_linked_list_node *next = node->next; 3.20 cxFree(list->allocator, node); 3.21 node = next;
4.1 --- a/src/list.c Mon Mar 20 18:05:12 2023 +0100 4.2 +++ b/src/list.c Mon Mar 20 19:09:08 2023 +0100 4.3 @@ -191,7 +191,7 @@ 4.4 list->cl = &cx_pointer_list_class; 4.5 } 4.6 4.7 -bool cxListIsStoringPointers(CxList *list) { 4.8 +bool cxListIsStoringPointers(CxList const *list) { 4.9 return list->climpl != NULL; 4.10 } 4.11 4.12 @@ -203,11 +203,11 @@ 4.13 ) { 4.14 switch (list->content_destructor_type) { 4.15 case CX_DESTRUCTOR_SIMPLE: { 4.16 - list->simple_destructor(elem); 4.17 + cx_list_invoke_simple_destructor(list, elem); 4.18 break; 4.19 } 4.20 case CX_DESTRUCTOR_ADVANCED: { 4.21 - list->advanced_destructor.func(list->advanced_destructor.data, elem); 4.22 + cx_list_invoke_advanced_destructor(list, elem); 4.23 break; 4.24 } 4.25 case CX_DESTRUCTOR_NONE: 4.26 @@ -215,11 +215,32 @@ 4.27 } 4.28 } 4.29 4.30 +void cx_list_invoke_simple_destructor( 4.31 + CxList const *list, 4.32 + void *elem 4.33 +) { 4.34 + if (cxListIsStoringPointers(list)) { 4.35 + elem = *((void **) elem); 4.36 + } 4.37 + list->simple_destructor(elem); 4.38 +} 4.39 + 4.40 +void cx_list_invoke_advanced_destructor( 4.41 + CxList const *list, 4.42 + void *elem 4.43 +) { 4.44 + if (cxListIsStoringPointers(list)) { 4.45 + elem = *((void **) elem); 4.46 + } 4.47 + list->advanced_destructor.func(list->advanced_destructor.data, elem); 4.48 +} 4.49 + 4.50 void cxListDestroy(CxList *list) { 4.51 switch (list->content_destructor_type) { 4.52 case CX_DESTRUCTOR_SIMPLE: { 4.53 CxIterator iter = cxListIterator(list); 4.54 cx_foreach(void*, elem, iter) { 4.55 + // already correctly resolved pointer - immediately invoke dtor 4.56 list->simple_destructor(elem); 4.57 } 4.58 break; 4.59 @@ -227,6 +248,7 @@ 4.60 case CX_DESTRUCTOR_ADVANCED: { 4.61 CxIterator iter = cxListIterator(list); 4.62 cx_foreach(void*, elem, iter) { 4.63 + // already correctly resolved pointer - immediately invoke dtor 4.64 list->advanced_destructor.func(list->advanced_destructor.data, elem); 4.65 } 4.66 break;
5.1 --- a/tests/test_list.cpp Mon Mar 20 18:05:12 2023 +0100 5.2 +++ b/tests/test_list.cpp Mon Mar 20 19:09:08 2023 +0100 5.3 @@ -38,6 +38,19 @@ 5.4 #include <unordered_set> 5.5 #include <algorithm> 5.6 5.7 +struct testdatastruct { 5.8 + int x; 5.9 + void *ptr; 5.10 +}; 5.11 + 5.12 +static void free_testdatastruct( 5.13 + void *a, 5.14 + void *s 5.15 +) { 5.16 + auto al = reinterpret_cast<CxTestingAllocator *>(a); 5.17 + cxFree(al, reinterpret_cast<testdatastruct *>(s)->ptr); 5.18 +} 5.19 + 5.20 struct node { 5.21 node *next = nullptr; 5.22 node *prev = nullptr; 5.23 @@ -697,6 +710,21 @@ 5.24 EXPECT_NE(cxListRemove(list, testdata_len), 0); 5.25 } 5.26 5.27 + void verifyClear(CxList *list) { 5.28 + // use the testing allocator for testing the destructor function 5.29 + list->content_destructor_type = CX_DESTRUCTOR_ADVANCED; 5.30 + list->advanced_destructor.func = free_testdatastruct; 5.31 + list->advanced_destructor.data = &testingAllocator; 5.32 + 5.33 + testdatastruct s[10]; 5.34 + for (auto &t: s) { 5.35 + t.ptr = cxMalloc(&testingAllocator, 16); 5.36 + cxListAdd(list, &t); 5.37 + } 5.38 + 5.39 + cxListClear(list); 5.40 + } 5.41 + 5.42 static void verifySwap(CxList *list) { 5.43 ASSERT_EQ(list->size, 0); 5.44 5.45 @@ -997,6 +1025,20 @@ 5.46 verifyRemove(arrayListFromTestData()); 5.47 } 5.48 5.49 +TEST_F(LinkedList, cxListClear) { 5.50 + verifyClear(autofree(cxLinkedListCreateSimple(sizeof(testdatastruct)))); 5.51 +} 5.52 + 5.53 +TEST_F(PointerLinkedList, cxListClear) { 5.54 + auto l = cxLinkedListCreateSimple(sizeof(testdatastruct)); 5.55 + cxListStorePointers(l); 5.56 + verifyClear(autofree(l)); 5.57 +} 5.58 + 5.59 +TEST_F(ArrayList, cxListClear) { 5.60 + verifyClear(autofree(cxArrayListCreateSimple(sizeof(testdatastruct), 8))); 5.61 +} 5.62 + 5.63 TEST_F(LinkedList, cxListSwap) { 5.64 verifySwap(autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)))); 5.65 }