changes CxTree structure so that it now inherits CX_COLLECTION_BASE - resolves #629

Sat, 06 Dec 2025 18:01:52 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 06 Dec 2025 18:01:52 +0100
changeset 1549
72ad8a78378a
parent 1548
12315ee158ad
child 1550
19874e606235

changes CxTree structure so that it now inherits CX_COLLECTION_BASE - resolves #629

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
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
--- a/CHANGELOG	Sat Dec 06 17:51:08 2025 +0100
+++ b/CHANGELOG	Sat Dec 06 18:01:52 2025 +0100
@@ -5,6 +5,7 @@
  * changes cxBufferReserve() to allow reducing the capacity
  * changes the members of CxJson and CxJsonValue
  * changes the return value of cxJsonObjIter() to CxMapIterator
+ * changes CxTree structure so that it now inherits CX_COLLECTION_BASE
  * removes the sort_members feature from CxJsonWriter
  * fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped
  * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors
--- a/docs/Writerside/topics/about.md	Sat Dec 06 17:51:08 2025 +0100
+++ b/docs/Writerside/topics/about.md	Sat Dec 06 18:01:52 2025 +0100
@@ -32,6 +32,7 @@
 * changes cxBufferReserve() to allow reducing the capacity
 * changes the members of CxJson and CxJsonValue
 * changes the return value of cxJsonObjIter() to CxMapIterator
+* changes CxTree structure so that it now inherits CX_COLLECTION_BASE
 * removes the sort_members feature from CxJsonWriter
 * fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped
 * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors
--- a/src/cx/tree.h	Sat Dec 06 17:51:08 2025 +0100
+++ b/src/cx/tree.h	Sat Dec 06 18:01:52 2025 +0100
@@ -643,17 +643,13 @@
  * Structure for holding the base data of a tree.
  */
 struct cx_tree_s {
+    CX_COLLECTION_BASE;
     /**
      * The tree class definition.
      */
     const cx_tree_class *cl;
 
     /**
-     * Allocator to allocate new nodes.
-     */
-    const CxAllocator *allocator;
-
-    /**
      * A pointer to the root node.
      *
      * Will be @c NULL when @c size is 0.
@@ -671,21 +667,6 @@
     cx_tree_node_create_func node_create;
 
     /**
-     * An optional simple destructor for the tree nodes.
-     */
-    cx_destructor_func simple_destructor;
-
-    /**
-     * An optional advanced destructor for the tree nodes.
-     */
-    cx_destructor_func2 advanced_destructor;
-
-    /**
-     * The pointer to additional data that is passed to the advanced destructor.
-     */
-    void *destructor_data;
-
-    /**
      * A function to compare two nodes.
      */
     cx_tree_search_func search;
@@ -696,11 +677,6 @@
     cx_tree_search_data_func search_data;
 
     /**
-     * The number of currently stored elements.
-     */
-    size_t size;
-
-    /**
      * Offset in the node struct for the parent pointer.
      */
     ptrdiff_t loc_parent;
--- a/src/tree.c	Sat Dec 06 17:51:08 2025 +0100
+++ b/src/tree.c	Sat Dec 06 18:01:52 2025 +0100
@@ -734,15 +734,15 @@
         if (node == NULL) return 1;  // LCOV_EXCL_LINE
         cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
         tree->root = node;
-        tree->size = 1;
+        tree->collection.size = 1;
         return 0;
     }
     int result = cx_tree_add(data, tree->search, tree->node_create,
                 tree, &node, tree->root, cx_tree_node_layout(tree));
     if (0 == result) {
-        tree->size++;
+        tree->collection.size++;
     } else {
-        cxFree(tree->allocator, node);
+        cxFree(tree->collection.allocator, node);
     }
     return result;
 }
@@ -767,9 +767,9 @@
     void *failed;
     ins += cx_tree_add_iter(iter, n, tree->search, tree->node_create,
                                   tree, &failed, tree->root, cx_tree_node_layout(tree));
-    tree->size += ins;
+    tree->collection.size += ins;
     if (ins < n) {
-        cxFree(tree->allocator, failed);
+        cxFree(tree->collection.allocator, failed);
     }
     return ins;
 }
@@ -818,24 +818,21 @@
     assert(search_func != NULL);
     assert(search_data_func != NULL);
 
-    CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+    CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
     if (tree == NULL) return NULL;  // LCOV_EXCL_LINE
 
     tree->cl = &cx_tree_default_class;
-    tree->allocator = allocator;
+    tree->collection.allocator = allocator;
     tree->node_create = create_func;
     tree->search = search_func;
     tree->search_data = search_data_func;
-    tree->simple_destructor = NULL;
-    tree->advanced_destructor = (cx_destructor_func2) cxFree;
-    tree->destructor_data = (void *) allocator;
+    tree->collection.advanced_destructor = (cx_destructor_func2) cxFree;
+    tree->collection.destructor_data = (void *) allocator;
     tree->loc_parent = loc_parent;
     tree->loc_children = loc_children;
     tree->loc_last_child = loc_last_child;
     tree->loc_prev = loc_prev;
     tree->loc_next = loc_next;
-    tree->root = NULL;
-    tree->size = 0;
 
     return tree;
 }
@@ -845,7 +842,7 @@
     if (tree->root != NULL) {
         cxTreeClear(tree);
     }
-    cxFree(tree->allocator, tree);
+    cxFree(tree->collection.allocator, tree);
 }
 
 CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
@@ -856,39 +853,33 @@
     }
     assert(root != NULL);
 
-    CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+    CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
     if (tree == NULL) return NULL;  // LCOV_EXCL_LINE
 
     tree->cl = &cx_tree_default_class;
     // set the allocator anyway, just in case...
-    tree->allocator = allocator;
-    tree->node_create = NULL;
-    tree->search = NULL;
-    tree->search_data = NULL;
-    tree->simple_destructor = NULL;
-    tree->advanced_destructor = NULL;
-    tree->destructor_data = NULL;
+    tree->collection.allocator = allocator;
     tree->loc_parent = loc_parent;
     tree->loc_children = loc_children;
     tree->loc_last_child = loc_last_child;
     tree->loc_prev = loc_prev;
     tree->loc_next = loc_next;
     tree->root = root;
-    tree->size = cxTreeSubtreeSize(tree, root);
+    tree->collection.size = cxTreeSubtreeSize(tree, root);
     return tree;
 }
 
 void cxTreeSetParent(CxTree *tree, void *parent, void *child) {
     size_t loc_parent = tree->loc_parent;
     if (tree_parent(child) == NULL) {
-        tree->size++;
+        tree->collection.size++;
     }
     cx_tree_link(parent, child, cx_tree_node_layout(tree));
 }
 
 void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) {
     cx_tree_link(parent, child, cx_tree_node_layout(tree));
-    tree->size++;
+    tree->collection.size++;
 }
 
 int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
@@ -896,7 +887,7 @@
     if (node == NULL) return 1; // LCOV_EXCL_LINE
     cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
     cx_tree_link(parent, node, cx_tree_node_layout(tree));
-    tree->size++;
+    tree->collection.size++;
     return 0;
 }
 
@@ -948,7 +939,7 @@
 }
 
 size_t cxTreeSize(CxTree *tree) {
-    return tree->size;
+    return tree->collection.size;
 }
 
 size_t cxTreeDepth(CxTree *tree) {
@@ -1002,7 +993,7 @@
     if (loc_last_child >= 0) tree_last_child(node) = NULL;
 
     // the tree now has one member less
-    tree->size--;
+    tree->collection.size--;
 
     return 0;
 }
@@ -1010,12 +1001,12 @@
 void cxTreeRemoveSubtree(CxTree *tree, void *node) {
     if (node == tree->root) {
         tree->root = NULL;
-        tree->size = 0;
+        tree->collection.size = 0;
         return;
     }
     size_t subtree_size = cxTreeSubtreeSize(tree, node);
     cx_tree_unlink(node, cx_tree_node_layout(tree));
-    tree->size -= subtree_size;
+    tree->collection.size -= subtree_size;
 }
 
 int cxTreeDestroyNode(
@@ -1025,12 +1016,7 @@
 ) {
     int result = cxTreeRemoveNode(tree, node, relink_func);
     if (result == 0) {
-        if (tree->simple_destructor) {
-            tree->simple_destructor(node);
-        }
-        if (tree->advanced_destructor) {
-            tree->advanced_destructor(tree->destructor_data, node);
-        }
+        cx_invoke_destructor(tree, node);
         return 0;
     } else {
         return result;
@@ -1045,15 +1031,10 @@
     );
     cx_foreach(void *, child, iter) {
         if (iter.exiting) {
-            if (tree->simple_destructor) {
-                tree->simple_destructor(child);
-            }
-            if (tree->advanced_destructor) {
-                tree->advanced_destructor(tree->destructor_data, child);
-            }
+            cx_invoke_destructor(tree, child);
         }
     }
-    tree->size -= iter.counter;
+    tree->collection.size -= iter.counter;
     if (node == tree->root) {
         tree->root = NULL;
     }
--- a/tests/test_tree.c	Sat Dec 06 17:51:08 2025 +0100
+++ b/tests/test_tree.c	Sat Dec 06 18:01:52 2025 +0100
@@ -75,7 +75,7 @@
 static void *tree_node_file_create_hl(
         const void *dptr,
         void *tree) {
-    return tree_node_file_create(dptr, (void*)((CxTree*)tree)->allocator);
+    return tree_node_file_create(dptr, (void*)((CxTree*)tree)->collection.allocator);
 }
 
 static int tree_node_file_search(const void *l, const void *r) {
@@ -1888,14 +1888,16 @@
                 tree_node_file_layout
         );
         CX_TEST_ASSERT(tree->cl != NULL);
-        CX_TEST_ASSERT(tree->allocator == &talloc.base);
+        CX_TEST_ASSERT(tree->collection.allocator == &talloc.base);
         CX_TEST_ASSERT(tree->node_create == tree_node_file_create_hl);
         CX_TEST_ASSERT(tree->search == tree_node_file_search);
         CX_TEST_ASSERT(tree->search_data == tree_node_file_search_data);
-        CX_TEST_ASSERT(tree->size == 0);
-        CX_TEST_ASSERT(tree->simple_destructor == NULL);
-        CX_TEST_ASSERT(tree->advanced_destructor == (cx_destructor_func2) cxFree);
-        CX_TEST_ASSERT(tree->destructor_data == &talloc.base);
+        CX_TEST_ASSERT(tree->collection.size == 0);
+        CX_TEST_ASSERT(tree->collection.simple_destructor == NULL);
+        CX_TEST_ASSERT(tree->collection.advanced_destructor == (cx_destructor_func2) cxFree);
+        CX_TEST_ASSERT(tree->collection.destructor_data == &talloc.base);
+        CX_TEST_ASSERT(tree->collection.store_pointer == false);
+        CX_TEST_ASSERT(tree->collection.sorted == false);
         CX_TEST_ASSERT(tree->root == NULL);
         CX_TEST_ASSERT(tree->loc_parent == offsetof(tree_node_file, parent));
         CX_TEST_ASSERT(tree->loc_children == offsetof(tree_node_file, children));
@@ -1921,14 +1923,14 @@
                 tree_node_file_search_data
         );
         CX_TEST_ASSERT(tree->cl != NULL);
-        CX_TEST_ASSERT(tree->allocator == cxDefaultAllocator);
+        CX_TEST_ASSERT(tree->collection.allocator == cxDefaultAllocator);
         CX_TEST_ASSERT(tree->node_create == tree_node_file_create_hl);
         CX_TEST_ASSERT(tree->search == tree_node_file_search);
         CX_TEST_ASSERT(tree->search_data == tree_node_file_search_data);
-        CX_TEST_ASSERT(tree->size == 0);
-        CX_TEST_ASSERT(tree->simple_destructor == NULL);
-        CX_TEST_ASSERT(tree->advanced_destructor == (cx_destructor_func2) cxFree);
-        CX_TEST_ASSERT(tree->destructor_data == cxDefaultAllocator);
+        CX_TEST_ASSERT(tree->collection.size == 0);
+        CX_TEST_ASSERT(tree->collection.simple_destructor == NULL);
+        CX_TEST_ASSERT(tree->collection.advanced_destructor == (cx_destructor_func2) cxFree);
+        CX_TEST_ASSERT(tree->collection.destructor_data == cxDefaultAllocator);
         CX_TEST_ASSERT(tree->root == NULL);
         CX_TEST_ASSERT(tree->loc_parent == offsetof(struct cx_tree_node_base_s, parent));
         CX_TEST_ASSERT(tree->loc_children == offsetof(struct cx_tree_node_base_s, children));
@@ -1947,15 +1949,15 @@
     CX_TEST_DO {
         CxTree *tree = cxTreeCreateWrapped(NULL, &root, tree_node_layout);
         CX_TEST_ASSERT(tree->cl != NULL);
-        CX_TEST_ASSERT(tree->allocator == cxDefaultAllocator);
+        CX_TEST_ASSERT(tree->collection.allocator == cxDefaultAllocator);
         CX_TEST_ASSERT(tree->node_create == NULL);
         CX_TEST_ASSERT(tree->search == NULL);
         CX_TEST_ASSERT(tree->search_data == NULL);
         CX_TEST_ASSERT(tree->root == &root);
-        CX_TEST_ASSERT(tree->size == 4);
-        CX_TEST_ASSERT(tree->simple_destructor == NULL);
-        CX_TEST_ASSERT(tree->advanced_destructor == NULL);
-        CX_TEST_ASSERT(tree->destructor_data == NULL);
+        CX_TEST_ASSERT(tree->collection.size == 4);
+        CX_TEST_ASSERT(tree->collection.simple_destructor == NULL);
+        CX_TEST_ASSERT(tree->collection.advanced_destructor == NULL);
+        CX_TEST_ASSERT(tree->collection.destructor_data == NULL);
         CX_TEST_ASSERT(tree->loc_parent == offsetof(tree_node, parent));
         CX_TEST_ASSERT(tree->loc_children == offsetof(tree_node, children));
         CX_TEST_ASSERT(tree->loc_last_child == -1);
@@ -2185,9 +2187,8 @@
         cxFree(alloc, usr);
         // for the subtree, we use a little trick and wrap it in a new tree
         CxTree *foo_tree = cxTreeCreateWrapped(alloc, foo, tree_node_file_layout);
-        foo_tree->allocator = alloc;
-        foo_tree->advanced_destructor = (cx_destructor_func2 ) cxFree;
-        foo_tree->destructor_data = alloc;
+        foo_tree->collection.allocator = alloc;
+        cxDefineAdvancedDestructor(foo_tree, cxFree, alloc);
         cxTreeFree(foo_tree);
         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
     }
@@ -2305,8 +2306,7 @@
         cxTreeFree(tree);
         CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
         CxTree *w = cxTreeCreateWrapped(alloc, root, tree_node_file_layout);
-        w->advanced_destructor = (cx_destructor_func2) cxFree;
-        w->destructor_data = alloc;
+        cxDefineAdvancedDestructor(w, cxFree, alloc);
         cxTreeDestroySubtree(w, w->root);
         CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
         cxTreeFree(w);
@@ -2326,7 +2326,7 @@
     cx_tree_link(&child1, &child3, tree_node_layout);
     CX_TEST_DO {
         CxTree *tree = cxTreeCreateWrapped(cxDefaultAllocator, &root, tree_node_layout);
-        tree->simple_destructor = test_tree_high_simple_destructor_func;
+        cxDefineDestructor(tree,test_tree_high_simple_destructor_func);
         cxTreeDestroyNode(tree, &child1, NULL);
         cxTreeFree(tree);
         CX_TEST_ASSERT(root.data == 1);

mercurial