Tue, 15 Feb 2022 19:41:48 +0100
add new destructor API and apply it to CxList
src/CMakeLists.txt | file | annotate | diff | comparison | revisions | |
src/cx/allocator.h | file | annotate | diff | comparison | revisions | |
src/cx/common.h | file | annotate | diff | comparison | revisions | |
src/cx/linked_list.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.c | file | annotate | diff | comparison | revisions |
--- a/src/CMakeLists.txt Tue Feb 15 19:07:14 2022 +0100 +++ b/src/CMakeLists.txt Tue Feb 15 19:41:48 2022 +0100 @@ -1,6 +1,7 @@ set(sources utils.c allocator.c + list.c linked_list.c tree.c buffer.c
--- a/src/cx/allocator.h Tue Feb 15 19:07:14 2022 +0100 +++ b/src/cx/allocator.h Tue Feb 15 19:41:48 2022 +0100 @@ -46,23 +46,37 @@ /** * The allocator's malloc() implementation. */ - void *(*malloc)(void *data, size_t n); + void *(*malloc)( + void *data, + size_t n + ); /** * The allocator's realloc() implementation. */ - void *(*realloc)(void *data, void *mem, size_t n) + void *(*realloc)( + void *data, + void *mem, + size_t n + ) __attribute__((__warn_unused_result__)); /** * The allocator's calloc() implementation. */ - void *(*calloc)(void *data, size_t nelem, size_t n); + void *(*calloc)( + void *data, + size_t nelem, + size_t n + ); /** * The allocator's free() implementation. */ - void (*free)(void *data, void *mem) + void (*free)( + void *data, + void *mem + ) __attribute__((__nonnull__)); } cx_allocator_class; @@ -91,6 +105,18 @@ extern CxAllocator *cxDefaultAllocator; /** + * Function pointer type for destructor functions. + * + * A destructor function deallocates possible contents and MAY free the memory + * pointed to by \p memory. + * + * @param memory a pointer to the object to destruct + * @return \p memory if it has NOT been free'd by this destructor, otherwise \c NULL + */ +typedef void *(*cx_destructor_func)(void *memory) + __attribute__((__nonnull__, __warn_unused_result__)); + +/** * Allocate \p n bytes of memory. * * @param allocator the allocator
--- a/src/cx/common.h Tue Feb 15 19:07:14 2022 +0100 +++ b/src/cx/common.h Tue Feb 15 19:41:48 2022 +0100 @@ -114,6 +114,9 @@ #endif /* _WIN32 */ #ifndef __GNUC__ +/** + * Removes GNU C attributes where they are not supported. + */ #define __attribute__(x) #endif
--- a/src/cx/linked_list.h Tue Feb 15 19:07:14 2022 +0100 +++ b/src/cx/linked_list.h Tue Feb 15 19:41:48 2022 +0100 @@ -94,15 +94,6 @@ ) __attribute__((__nonnull__)); /** - * Deallocates the memory of the entire list. - * - * \attention If this is a pointer list, the memory the pointers are referring to is \em not freed. - * - * @param list the list - */ -void cxLinkedListDestroy(CxList *list) __attribute__((__nonnull__)); - -/** * Finds the node at a certain index. * * This function can be used to start at an arbitrary position within the list.
--- a/src/cx/list.h Tue Feb 15 19:07:14 2022 +0100 +++ b/src/cx/list.h Tue Feb 15 19:41:48 2022 +0100 @@ -71,6 +71,14 @@ */ CxAllocator *allocator; /** + * A mandatory destructor for the list structure. + */ + cx_destructor_func list_destructor; + /** + * An optional destructor for the list contents. + */ + cx_destructor_func content_destructor; + /** * The comparator function for the elements. */ CxListComparator cmpfunc; @@ -86,6 +94,16 @@ * The capacity of the list (maximum number of elements). */ size_t capacity; + /** + * Flag indicating whether cxListDestroy() shall free the list structure, + * even if cx_list_s.list_destructor did not do that. + */ + bool autofree; + /** + * Flag indicating whether cxListDestroy() shall free each list element, + * even if cx_list_s.content_destructor did not do that. + */ + bool autofree_contents; }; /** @@ -364,6 +382,21 @@ return list->cl->compare(list, other); } +/** + * Calls the list's destructor function for every element. + * If CxList.autofree_content is \c true, the elements are automatically free'd + * unless the content destructor function did not already do that. + * Similarly, if CxList.autofree is \c true, the list structure is free'd, unless + * the list destructor function did not already do that. + * + * This function itself is a destructor function for the CxList. + * + * @param list the list which contents shall be destroyed + * @return \p list if the list structure has been free'd during the process + */ +__attribute__((__nonnull__)) +CxList *cxListDestroy(CxList *list); + #ifdef __cplusplus } /* extern "C" */ #endif
--- a/src/linked_list.c Tue Feb 15 19:07:14 2022 +0100 +++ b/src/linked_list.c Tue Feb 15 19:41:48 2022 +0100 @@ -757,24 +757,35 @@ cx_pll_iterator, }; +static CxList *cx_ll_default_destructor(CxList *list) { + cx_linked_list *ll = (cx_linked_list *) list; + + cx_linked_list_node *node = ll->begin; + while (node) { + void *next = node->next; + cxFree(list->allocator, node); + node = next; + } + + cxFree(list->allocator, list); + return NULL; +} + CxList *cxLinkedListCreate( CxAllocator *allocator, CxListComparator comparator, size_t item_size ) { - cx_linked_list *list = cxMalloc(allocator, sizeof(cx_linked_list)); + cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); if (list == NULL) return NULL; list->base.cl = &cx_linked_list_class; list->base.allocator = allocator; + list->base.list_destructor = (cx_destructor_func) cx_ll_default_destructor; list->base.cmpfunc = comparator; list->base.itemsize = item_size; list->base.capacity = SIZE_MAX; - list->base.size = 0; - - list->begin = NULL; - list->end = NULL; return (CxList *) list; } @@ -783,19 +794,16 @@ CxAllocator *allocator, CxListComparator comparator ) { - cx_linked_list *list = cxMalloc(allocator, sizeof(cx_linked_list)); + cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); if (list == NULL) return NULL; list->base.cl = &cx_pointer_linked_list_class; list->base.allocator = allocator; + list->base.list_destructor = (cx_destructor_func) cx_ll_default_destructor; list->base.cmpfunc = comparator; list->base.itemsize = sizeof(void *); list->base.capacity = SIZE_MAX; - list->base.size = 0; - - list->begin = NULL; - list->end = NULL; return (CxList *) list; } @@ -811,22 +819,8 @@ if (list == NULL) return NULL; for (size_t i = 0; i < num_items; i++) { if (0 != cxListAdd(list, ((const unsigned char *) array) + i * item_size)) { - cxLinkedListDestroy(list); - return NULL; + return cx_ll_default_destructor(list); } } return list; } - -void cxLinkedListDestroy(CxList *list) { - cx_linked_list *ll = (cx_linked_list *) list; - - cx_linked_list_node *node = ll->begin; - while (node) { - void *next = node->next; - cxFree(list->allocator, node); - node = next; - } - - cxFree(list->allocator, list); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/list.c Tue Feb 15 19:41:48 2022 +0100 @@ -0,0 +1,59 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/list.h" + +CxList *cxListDestroy(CxList *list) { + if (list->content_destructor == NULL) { + if (list->autofree_contents) { + CxIterator iter = cxListBegin(list); + cx_foreach(void*, elem, iter) { + cxFree(list->allocator, elem); + } + } + } else { + // avoid checking this condition every loop iteration + if (list->autofree_contents) { + CxIterator iter = cxListBegin(list); + cx_foreach(void*, elem, iter) { + cxFree(list->allocator, list->content_destructor(elem)); + } + } else { + CxIterator iter = cxListBegin(list); + cx_foreach(void*, elem, iter) { + elem = list->content_destructor(elem); + } + } + } + if (list->autofree) { + cxFree(list->allocator, list->list_destructor(list)); + return NULL; + } else { + return list->list_destructor(list); + } +}
--- a/test/test_list.c Tue Feb 15 19:07:14 2022 +0100 +++ b/test/test_list.c Tue Feb 15 19:41:48 2022 +0100 @@ -568,7 +568,7 @@ CU_ASSERT_EQUAL(list->itemsize, sizeof(int)) CU_ASSERT_PTR_EQUAL(list->cmpfunc, cmp_int) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -583,7 +583,7 @@ CU_ASSERT_EQUAL(list->itemsize, sizeof(void *)) CU_ASSERT_PTR_EQUAL(list->cmpfunc, cmp_int) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -599,8 +599,8 @@ CU_ASSERT_TRUE(0 == cxListCompare(list, expected)) - cxLinkedListDestroy(list); - cxLinkedListDestroy(expected); + cxListDestroy(list); + cxListDestroy(expected); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -624,8 +624,8 @@ CxList *expected = cxLinkedListFromArray(cxTestingAllocator, cmp_int, sizeof(int), 3, exp); CU_ASSERT_TRUE(0 == cxListCompare(list, expected)) - cxLinkedListDestroy(list); - cxLinkedListDestroy(expected); + cxListDestroy(list); + cxListDestroy(expected); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -655,7 +655,7 @@ CU_ASSERT_EQUAL(*(int *) cxListAt(list, 1), 10) CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 11) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -686,8 +686,8 @@ CxList *expected = cxLinkedListFromArray(cxTestingAllocator, cmp_int, sizeof(int), 4, exp); CU_ASSERT_TRUE(0 == cxListCompare(list, expected)) - cxLinkedListDestroy(list); - cxLinkedListDestroy(expected); + cxListDestroy(list); + cxListDestroy(expected); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -716,7 +716,7 @@ CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 5) CU_ASSERT_EQUAL(*(int *) cxListAt(list, 3), 42) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -756,7 +756,7 @@ CU_ASSERT_NOT_EQUAL(cxListRemove(list, 0), 0) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -800,7 +800,7 @@ CU_ASSERT_NOT_EQUAL(cxListRemove(list, 0), 0) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -816,7 +816,7 @@ CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 13) CU_ASSERT_PTR_NULL(cxListAt(list, 3)) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -835,7 +835,7 @@ CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 13) CU_ASSERT_PTR_NULL(cxListAt(list, 3)) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -861,7 +861,7 @@ criteria = -5; CU_ASSERT_EQUAL(cxListFind(list, &criteria), 3) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -891,7 +891,7 @@ b = -5; CU_ASSERT_EQUAL(cxListFind(list, &criteria), 1) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -921,8 +921,8 @@ cxListSort(list); CU_ASSERT_TRUE(0 == cxListCompare(list, exp)) - cxLinkedListDestroy(list); - cxLinkedListDestroy(exp); + cxListDestroy(list); + cxListDestroy(exp); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -958,7 +958,7 @@ CU_ASSERT_EQUAL(*(int *) cxListAt(list, i), expected[i]) } - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -978,7 +978,7 @@ CU_ASSERT_EQUAL(*(int *) cxListAt(list, 2), 4) CU_ASSERT_EQUAL(*(int *) cxListAt(list, 3), 6) CU_ASSERT_EQUAL(*(int *) cxListAt(list, 4), 8) - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -1041,8 +1041,8 @@ cmp_int, sizeof(int), 10, expdata); CU_ASSERT_EQUAL(0, cxListCompare(list, expected)) - cxLinkedListDestroy(list); - cxLinkedListDestroy(expected); + cxListDestroy(list); + cxListDestroy(expected); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) } @@ -1083,7 +1083,7 @@ CU_ASSERT_EQUAL(*(int *) cxListAt(list, i), expdata[i]) } - cxLinkedListDestroy(list); + cxListDestroy(list); CU_ASSERT_TRUE(cxTestingAllocatorVerify()) }