allow freeing tree nodes on exit - fixes #377

Mon, 26 Feb 2024 21:07:23 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 26 Feb 2024 21:07:23 +0100
changeset 840
4f02995ce44e
parent 839
62d3aecc5bb7
child 841
93851c0babe4

allow freeing tree nodes on exit - fixes #377

src/cx/tree.h file | annotate | diff | comparison | revisions
src/tree.c file | annotate | diff | comparison | revisions
tests/test_tree.c file | annotate | diff | comparison | revisions
     1.1 --- a/src/cx/tree.h	Wed Feb 21 18:53:55 2024 +0100
     1.2 +++ b/src/cx/tree.h	Mon Feb 26 21:07:23 2024 +0100
     1.3 @@ -90,6 +90,11 @@
     1.4       */
     1.5      void *node;
     1.6      /**
     1.7 +     * Stores a copy of the next pointer of the visited node.
     1.8 +     * Allows freeing a node on exit without corrupting the iteration.
     1.9 +     */
    1.10 +    void *next;
    1.11 +    /**
    1.12       * Internal stack.
    1.13       * Will be automatically freed once the iterator becomes invalid.
    1.14       *
     2.1 --- a/src/tree.c	Wed Feb 21 18:53:55 2024 +0100
     2.2 +++ b/src/tree.c	Mon Feb 26 21:07:23 2024 +0100
     2.3 @@ -197,7 +197,12 @@
     2.4          void *next;
     2.5          cx_tree_iter_search_next:
     2.6          // check if there is a sibling
     2.7 -        next = tree_next(iter->node);
     2.8 +        if (iter->exiting) {
     2.9 +            next = iter->next;
    2.10 +        } else {
    2.11 +            next = tree_next(iter->node);
    2.12 +            iter->next = next;
    2.13 +        }
    2.14          if (next == NULL) {
    2.15              // no sibling, we are done with this node and exit
    2.16              if (iter->visit_on_exit && !iter->exiting) {
    2.17 @@ -208,7 +213,7 @@
    2.18                  if (iter->depth == 1) {
    2.19                      // there is no parent - we have iterated the entire tree
    2.20                      // invalidate the iterator and free the node stack
    2.21 -                    iter->node = NULL;
    2.22 +                    iter->node = iter->next = NULL;
    2.23                      iter->stack_capacity = iter->depth = 0;
    2.24                      free(iter->stack);
    2.25                      iter->stack = NULL;
     3.1 --- a/tests/test_tree.c	Wed Feb 21 18:53:55 2024 +0100
     3.2 +++ b/tests/test_tree.c	Mon Feb 26 21:07:23 2024 +0100
     3.3 @@ -30,6 +30,8 @@
     3.4  
     3.5  #include "cx/test.h"
     3.6  
     3.7 +#include "util_allocator.h"
     3.8 +
     3.9  typedef struct tree_node {
    3.10      struct tree_node *parent;
    3.11      struct tree_node *next;
    3.12 @@ -465,6 +467,46 @@
    3.13      free(actual);
    3.14  }
    3.15  
    3.16 +CX_TEST(test_tree_iterator_free_nodes) {
    3.17 +    CxTestingAllocator talloc;
    3.18 +    cx_testing_allocator_init(&talloc);
    3.19 +    CxAllocator *alloc = &talloc.base;
    3.20 +    CX_TEST_DO {
    3.21 +        tree_node *root = cxCalloc(alloc, 1, sizeof(tree_node));
    3.22 +        tree_node *a = cxCalloc(alloc, 1, sizeof(tree_node));
    3.23 +        tree_node *b = cxCalloc(alloc, 1, sizeof(tree_node));
    3.24 +        tree_node *c = cxCalloc(alloc, 1, sizeof(tree_node));
    3.25 +        tree_node *aa = cxCalloc(alloc, 1, sizeof(tree_node));
    3.26 +        tree_node *ab = cxCalloc(alloc, 1, sizeof(tree_node));
    3.27 +        tree_node *ba = cxCalloc(alloc, 1, sizeof(tree_node));
    3.28 +        tree_node *ca = cxCalloc(alloc, 1, sizeof(tree_node));
    3.29 +        tree_node *cb = cxCalloc(alloc, 1, sizeof(tree_node));
    3.30 +        tree_node *cc = cxCalloc(alloc, 1, sizeof(tree_node));
    3.31 +        tree_node *cba = cxCalloc(alloc, 1, sizeof(tree_node));
    3.32 +
    3.33 +        cx_tree_link(root, a, tree_node_layout);
    3.34 +        cx_tree_link(root, b, tree_node_layout);
    3.35 +        cx_tree_link(root, c, tree_node_layout);
    3.36 +        cx_tree_link(a, aa, tree_node_layout);
    3.37 +        cx_tree_link(a, ab, tree_node_layout);
    3.38 +        cx_tree_link(b, ba, tree_node_layout);
    3.39 +        cx_tree_link(c, ca, tree_node_layout);
    3.40 +        cx_tree_link(c, cb, tree_node_layout);
    3.41 +        cx_tree_link(c, cc, tree_node_layout);
    3.42 +        cx_tree_link(cb, cba, tree_node_layout);
    3.43 +
    3.44 +        CxTreeIterator iter = cx_tree_iterator(root, true, tree_child_list);
    3.45 +        cx_foreach(tree_node *, node, iter) {
    3.46 +            if (iter.exiting) {
    3.47 +                cxFree(alloc,node);
    3.48 +            }
    3.49 +        }
    3.50 +
    3.51 +        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
    3.52 +    }
    3.53 +    cx_testing_allocator_destroy(&talloc);
    3.54 +}
    3.55 +
    3.56  CxTestSuite *cx_test_suite_tree_low_level(void) {
    3.57      CxTestSuite *suite = cx_test_suite_new("tree (low level)");
    3.58  
    3.59 @@ -477,6 +519,7 @@
    3.60      cx_test_register(suite, test_tree_iterator_basic_only_enter);
    3.61      cx_test_register(suite, test_tree_iterator_basic_enter_and_exit);
    3.62      cx_test_register(suite, test_tree_iterator_xml);
    3.63 +    cx_test_register(suite, test_tree_iterator_free_nodes);
    3.64  
    3.65      return suite;
    3.66  }

mercurial