# HG changeset patch # User Mike Becker # Date 1765040512 -3600 # Node ID 72ad8a78378a9801929a0f77e334a0be4ff5f9a9 # Parent 12315ee158ad9dbbfa401bb419d2217ab51f9a5b changes CxTree structure so that it now inherits CX_COLLECTION_BASE - resolves #629 diff -r 12315ee158ad -r 72ad8a78378a CHANGELOG --- 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 diff -r 12315ee158ad -r 72ad8a78378a docs/Writerside/topics/about.md --- 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 diff -r 12315ee158ad -r 72ad8a78378a src/cx/tree.h --- 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; diff -r 12315ee158ad -r 72ad8a78378a src/tree.c --- 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; } diff -r 12315ee158ad -r 72ad8a78378a tests/test_tree.c --- 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);