add cxListClear and fix missing destructor invocations - #241 #246

Tue, 14 Mar 2023 20:25:24 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 14 Mar 2023 20:25:24 +0100
changeset 664
af5bf4603a5d
parent 663
d50b5dc1e058
child 665
c4041b07165e

add cxListClear and fix missing destructor invocations - #241 #246

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
--- a/src/array_list.c	Sun Mar 05 10:55:32 2023 +0100
+++ b/src/array_list.c	Tue Mar 14 20:25:24 2023 +0100
@@ -263,11 +263,20 @@
         struct cx_list_s *list,
         size_t index
 ) {
+    cx_array_list *arl = (cx_array_list *) list;
+
     // out-of-bounds check
     if (index >= list->size) {
         return 1;
     }
 
+    // content destruction
+    if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+        char *ptr = arl->data;
+        ptr += index * list->itemsize;
+        cx_list_invoke_destructor(list, ptr);
+    }
+
     // short-circuit removal of last element
     if (index == list->size - 1) {
         list->size--;
@@ -275,7 +284,6 @@
     }
 
     // just move the elements starting at index to the left
-    cx_array_list *arl = (cx_array_list *) list;
     int result = cx_array_copy(
             &arl->data,
             &list->size,
@@ -293,6 +301,33 @@
     return result;
 }
 
+static void cx_arl_clear(struct cx_list_s *list) {
+    if (list->size == 0) return;
+
+    cx_array_list *arl = (cx_array_list *) list;
+    char *ptr = arl->data;
+
+    switch (list->content_destructor_type) {
+        case CX_DESTRUCTOR_SIMPLE: {
+            for (size_t i = 0; i < list->size; i++) {
+                list->simple_destructor(ptr);
+                ptr += list->itemsize;
+            }
+            break;
+        }
+        case CX_DESTRUCTOR_ADVANCED: {
+            for (size_t i = 0; i < list->size; i++) {
+                list->advanced_destructor.func(list->advanced_destructor.data,
+                                               ptr);
+                ptr += list->itemsize;
+            }
+            break;
+        }
+        case CX_DESTRUCTOR_NONE:
+            break; // nothing
+    }
+}
+
 static int cx_arl_swap(
         struct cx_list_s *list,
         size_t i,
@@ -451,6 +486,7 @@
         cx_arl_insert_array,
         cx_arl_insert_iter,
         cx_arl_remove,
+        cx_arl_clear,
         cx_arl_swap,
         cx_arl_at,
         cx_arl_find,
--- a/src/cx/list.h	Sun Mar 05 10:55:32 2023 +0100
+++ b/src/cx/list.h	Tue Mar 14 20:25:24 2023 +0100
@@ -164,6 +164,11 @@
     );
 
     /**
+     * Member function for removing all elements.
+     */
+    void (*clear)(struct cx_list_s *list);
+
+    /**
      * Member function for swapping two elements.
      */
     int (*swap)(
@@ -222,6 +227,21 @@
 typedef struct cx_list_s CxList;
 
 /**
+ * Invokes the destructor function for a specific element.
+ *
+ * Usually only used by list implementations. There should be no need
+ * to invoke this function manually.
+ *
+ * @param list the list
+ * @param elem the element
+ */
+__attribute__((__nonnull__))
+void cx_list_invoke_destructor(
+        struct cx_list_s const *list,
+        void *elem
+);
+
+/**
  * Advises the list to store copies of the objects (default mode of operation).
  *
  * Retrieving objects from this list will yield pointers to the copies stored
@@ -398,6 +418,10 @@
 
 /**
  * Removes the element at the specified index.
+ *
+ * If an element destructor function is specified, it is called before
+ * removing the element.
+ *
  * @param list the list
  * @param index the index of the element
  * @return zero on success, non-zero if the index is out of bounds
@@ -411,6 +435,19 @@
 }
 
 /**
+ * Removes all elements from this list.
+ *
+ * If an element destructor function is specified, it is called for each
+ * element before removing them.
+ *
+ * @param list the list
+ */
+__attribute__((__nonnull__))
+static inline void cxListClear(CxList *list) {
+    list->cl->clear(list);
+}
+
+/**
  * Swaps two items in the list.
  *
  * Implementations should only allocate temporary memory for the swap, if
--- a/src/linked_list.c	Sun Mar 05 10:55:32 2023 +0100
+++ b/src/linked_list.c	Tue Mar 14 20:25:24 2023 +0100
@@ -569,6 +569,11 @@
     // out-of-bounds check
     if (node == NULL) return 1;
 
+    // element destruction
+    if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+        cx_list_invoke_destructor(list, node->payload);
+    }
+
     // remove
     cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
                           CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
@@ -582,6 +587,48 @@
     return 0;
 }
 
+static void cx_ll_clear(struct cx_list_s *list) {
+    if (list->size == 0) return;
+
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_linked_list_node *node = ll->begin;
+
+    // looks super redundant, but avoids repeatedly checking
+    // the destructor type for each element
+    switch (list->content_destructor_type) {
+        case CX_DESTRUCTOR_SIMPLE: {
+            while (node != NULL) {
+                list->simple_destructor(node->payload);
+                cx_linked_list_node *next = node->next;
+                cxFree(list->allocator, node);
+                node = next;
+            }
+            break;
+        }
+        case CX_DESTRUCTOR_ADVANCED: {
+            while (node != NULL) {
+                list->advanced_destructor.func(list->advanced_destructor.data,
+                                               node->payload);
+                cx_linked_list_node *next = node->next;
+                cxFree(list->allocator, node);
+                node = next;
+            }
+            break;
+        }
+        case CX_DESTRUCTOR_NONE: {
+            while (node != NULL) {
+                cx_linked_list_node *next = node->next;
+                cxFree(list->allocator, node);
+                node = next;
+            }
+            break;
+        }
+    }
+
+    ll->begin = ll->end = NULL;
+    list->size = 0;
+}
+
 #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
 #define CX_LINKED_LIST_SWAP_SBO_SIZE 16
 #endif
@@ -753,13 +800,17 @@
     if (itbase->remove) {
         itbase->remove = false;
         struct cx_mut_iterator_s *iter = it;
+        struct cx_list_s *list = iter->src_handle;
         cx_linked_list *ll = iter->src_handle;
         cx_linked_list_node *node = iter->elem_handle;
         iter->elem_handle = node->next;
+        if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+            cx_list_invoke_destructor(list, node->payload);
+        }
         cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
                               CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
-        ll->base.size--;
-        cxFree(ll->base.allocator, node);
+        list->size--;
+        cxFree(list->allocator, node);
     } else {
         struct cx_iterator_s *iter = it;
         iter->index++;
@@ -773,14 +824,18 @@
     if (itbase->remove) {
         itbase->remove = false;
         struct cx_mut_iterator_s *iter = it;
+        struct cx_list_s *list = iter->src_handle;
         cx_linked_list *ll = iter->src_handle;
         cx_linked_list_node *node = iter->elem_handle;
         iter->elem_handle = node->prev;
         iter->index--;
+        if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+            cx_list_invoke_destructor(list, node->payload);
+        }
         cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
                               CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
-        ll->base.size--;
-        cxFree(ll->base.allocator, node);
+        list->size--;
+        cxFree(list->allocator, node);
     } else {
         struct cx_iterator_s *iter = it;
         iter->index--;
@@ -861,6 +916,7 @@
         cx_ll_insert_array,
         cx_ll_insert_iter,
         cx_ll_remove,
+        cx_ll_clear,
         cx_ll_swap,
         cx_ll_at,
         cx_ll_find,
--- a/src/list.c	Sun Mar 05 10:55:32 2023 +0100
+++ b/src/list.c	Tue Mar 14 20:25:24 2023 +0100
@@ -95,6 +95,10 @@
     return list->climpl->remove(list, index);
 }
 
+static void cx_pl_clear(struct cx_list_s *list) {
+    list->climpl->clear(list);
+}
+
 static int cx_pl_swap(
         struct cx_list_s *list,
         size_t i,
@@ -164,6 +168,7 @@
         cx_pl_insert_array,
         cx_pl_insert_iter,
         cx_pl_remove,
+        cx_pl_clear,
         cx_pl_swap,
         cx_pl_at,
         cx_pl_find,
@@ -192,6 +197,24 @@
 
 // </editor-fold>
 
+void cx_list_invoke_destructor(
+        CxList const *list,
+        void *elem
+) {
+    switch (list->content_destructor_type) {
+        case CX_DESTRUCTOR_SIMPLE: {
+            list->simple_destructor(elem);
+            break;
+        }
+        case CX_DESTRUCTOR_ADVANCED: {
+            list->advanced_destructor.func(list->advanced_destructor.data, elem);
+            break;
+        }
+        case CX_DESTRUCTOR_NONE:
+            break; // nothing
+    }
+}
+
 void cxListDestroy(CxList *list) {
     switch (list->content_destructor_type) {
         case CX_DESTRUCTOR_SIMPLE: {

mercurial