add new destructor API and apply it to CxList

Tue, 15 Feb 2022 19:41:48 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 15 Feb 2022 19:41:48 +0100
changeset 503
a89857072ace
parent 502
33e7b6ebf403
child 504
aaf89ce0cbf6

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())
 }
 

mercurial