implement cxTreeCreate family of functions

Thu, 03 Oct 2024 15:38:05 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 03 Oct 2024 15:38:05 +0200
changeset 902
5ed7f634f046
parent 901
109567325fe7
child 903
a018f5916d3b

implement cxTreeCreate family of functions

relates to #166

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
tests/ucxtest.c file | annotate | diff | comparison | revisions
--- a/src/cx/tree.h	Wed Oct 02 19:11:40 2024 +0200
+++ b/src/cx/tree.h	Thu Oct 03 15:38:05 2024 +0200
@@ -701,14 +701,14 @@
     /**
      * Allocator to allocate new nodes.
      */
-    CxAllocator *allocator;
+    const CxAllocator *allocator;
 
     /**
      * A pointer to the root node.
      *
      * Will be \c NULL when \c size is 0.
      */
-    struct cx_tree_node_base_s *root;
+    void *root;
 
     /**
      * A function to create new nodes.
@@ -872,7 +872,7 @@
  * The specified \p allocator will be used for creating the tree struct
  * and SHALL be used by \p create_func to allocate memory for the nodes.
  *
- * \note This function will also register a simple destructor which
+ * \note This function will also register an advanced destructor which
  * will free the nodes with the allocator's free() method.
  *
  * @param allocator the allocator that shall be used
@@ -907,8 +907,8 @@
  * member (or at least respect the default offsets specified in the tree
  * struct) and they MUST be allocated with the default stdlib allocator.
  *
- * \note This function will also register a simple destructor which
- * will free the nodes with the default stdlib allocator.
+ * \note This function will also register an advanced destructor which
+ * will free the nodes with the #cxDefaultAllocator.
  *
  * @param create_func a function that creates new nodes
  * @param search_func a function that compares two nodes
@@ -1081,6 +1081,16 @@
 }
 
 /**
+ * Determines the size of the specified subtree.
+ *
+ * @param tree the tree
+ * @param subtree_root the root node of the subtree
+ * @return the number of nodes in the specified subtree
+ */
+__attribute__((__nonnull__))
+size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
+
+/**
  * Creates a depth-first iterator for the specified tree.
  *
  * @param tree the tree to iterate
--- a/src/tree.c	Wed Oct 02 19:11:40 2024 +0200
+++ b/src/tree.c	Thu Oct 03 15:38:05 2024 +0200
@@ -666,6 +666,91 @@
                             loc_prev, loc_next);
 }
 
+static void cx_tree_default_destructor(CxTree *tree) {
+    // TODO: destroy the nodes
+    cxFree(tree->allocator, tree);
+}
+
+static CxTreeIterator cx_tree_default_iterator(
+        CxTree *tree,
+        bool visit_on_exit
+) {
+    return cx_tree_iterator(
+            tree->root, visit_on_exit,
+            tree->loc_children, tree->loc_next
+    );
+}
+
+static CxTreeVisitor cx_tree_default_visitor(CxTree *tree) {
+    return cx_tree_visitor(tree->root, tree->loc_children, tree->loc_next);
+}
+
+static cx_tree_class cx_tree_default_class = {
+        cx_tree_default_destructor,
+        NULL,
+        NULL,
+        NULL,
+        cx_tree_default_iterator,
+        cx_tree_default_visitor
+};
+
+CxTree *cxTreeCreate(
+        const CxAllocator *allocator,
+        cx_tree_node_create_func create_func,
+        cx_tree_search_func search_func,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+    if (tree == NULL) return NULL;
+
+    tree->cl = &cx_tree_default_class;
+    tree->allocator = allocator;
+    tree->node_create = create_func;
+    tree->search = search_func;
+    tree->advanced_destructor = (cx_destructor_func2) cxFree;
+    tree->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;
+}
+
+CxTree *cxTreeCreateWrapped(
+        void *root,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    CxTree *tree = malloc(sizeof(CxTree));
+    if (tree == NULL) return NULL;
+
+    tree->cl = &cx_tree_default_class;
+    // set the allocator anyway, just in case...
+    tree->allocator = cxDefaultAllocator;
+    tree->node_create = NULL;
+    tree->search = NULL;
+    tree->advanced_destructor = NULL;
+    tree->destructor_data = NULL;
+    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);
+    return tree;
+}
 
 int cxTreeAddChild(
         CxTree *tree,
@@ -678,3 +763,15 @@
     tree->size++;
     return 0;
 }
+
+size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) {
+    CxTreeVisitor visitor = cx_tree_visitor(
+            subtree_root,
+            tree->loc_children,
+            tree->loc_next
+    );
+    while (cxIteratorValid(visitor)) {
+        cxIteratorNext(visitor);
+    }
+    return visitor.counter;
+}
--- a/tests/test_tree.c	Wed Oct 02 19:11:40 2024 +0200
+++ b/tests/test_tree.c	Thu Oct 03 15:38:05 2024 +0200
@@ -54,7 +54,7 @@
     const char *path;
 } tree_node_file;
 
-static void *create_tree_node_file(
+static void *tree_node_file_create(
         const void *dptr,
         void *allocator) {
     if (allocator == NULL) allocator = cxDefaultAllocator;
@@ -1057,7 +1057,7 @@
         result = cx_tree_add(
                 "/home/foo/",
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 (void **) &foo, &root,
                 tree_node_file_layout
         );
@@ -1068,7 +1068,7 @@
         size_t added = cx_tree_add_array(
                 bar_path, 1, sizeof(const char *),
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 &failed, &root,
                 tree_node_file_layout
         );
@@ -1084,7 +1084,7 @@
         result = cx_tree_add(
                 "newroot",
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 (void **) &new_node, &root,
                 tree_node_file_layout
         );
@@ -1126,7 +1126,7 @@
         int result = cx_tree_add(
                 "/usr/lib/",
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 (void **) &node, &root,
                 tree_node_file_layout
         );
@@ -1151,7 +1151,7 @@
         int result = cx_tree_add(
                 "/usr/lib/",
                 tree_node_file_search,
-                create_tree_node_file, NULL,
+                tree_node_file_create, NULL,
                 (void **) &node, &root,
                 tree_node_file_layout
         );
@@ -1164,7 +1164,7 @@
         size_t added = cx_tree_add_array(
                 "/", 1, sizeof(const char *),
                 tree_node_file_search,
-                create_tree_node_file, NULL,
+                tree_node_file_create, NULL,
                 (void **) &node, &root,
                 tree_node_file_layout
         );
@@ -1184,7 +1184,7 @@
         int result = cx_tree_add(
                 "/",
                 tree_node_file_search,
-                create_tree_node_file, NULL,
+                tree_node_file_create, NULL,
                 (void **) &node, &root,
                 tree_node_file_layout
         );
@@ -1207,7 +1207,7 @@
         size_t processed = cx_tree_add_array(
                 (void *) 0xc0ffee, 0, sizeof(void *),
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 &failed, &root, tree_node_file_layout
         );
         CX_TEST_ASSERT(failed == NULL);
@@ -1219,7 +1219,7 @@
                 cxIteratorRef(iter),
                 10, // deliberately specify more than the iter has
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 &failed, &root, tree_node_file_layout
         );
         CX_TEST_ASSERT(processed == 0);
@@ -1263,7 +1263,7 @@
         size_t processed = cx_tree_add_array(
                 paths, 4, sizeof(const char *),
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 &failed, &root, tree_node_file_layout
         );
 
@@ -1328,7 +1328,7 @@
         size_t processed = cx_tree_add_iter(
                 cxIteratorRef(iter), 3,
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 &failed, &root, tree_node_file_layout
         );
 
@@ -1384,7 +1384,7 @@
         size_t processed = cx_tree_add_array(
                 paths, 5, sizeof(const char *),
                 tree_node_file_search,
-                create_tree_node_file, alloc,
+                tree_node_file_create, alloc,
                 (void **) &failed, &root, tree_node_file_layout
         );
 
@@ -1428,7 +1428,7 @@
     size_t processed = cx_tree_add_array(
             paths, 10, sizeof(const char *),
             tree_node_file_search,
-            create_tree_node_file, alloc,
+            tree_node_file_create, alloc,
             &failed, &root, tree_node_file_layout
     );
 
@@ -1526,6 +1526,88 @@
     cx_testing_allocator_destroy(&talloc);
 }
 
+CX_TEST(test_tree_high_create) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CX_TEST_DO {
+        CxTree *tree = cxTreeCreate(
+                &talloc.base,
+                tree_node_file_create,
+                tree_node_file_search,
+                tree_node_file_layout
+        );
+        CX_TEST_ASSERT(tree->cl != NULL);
+        CX_TEST_ASSERT(tree->allocator == &talloc.base);
+        CX_TEST_ASSERT(tree->node_create == tree_node_file_create);
+        CX_TEST_ASSERT(tree->search == tree_node_file_search);
+        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->root == NULL);
+        CX_TEST_ASSERT(tree->loc_parent == offsetof(tree_node_file, parent));
+        CX_TEST_ASSERT(tree->loc_children == offsetof(tree_node_file, children));
+        CX_TEST_ASSERT(tree->loc_last_child == offsetof(tree_node_file, last_child));
+        CX_TEST_ASSERT(tree->loc_prev == offsetof(tree_node_file, prev));
+        CX_TEST_ASSERT(tree->loc_next == offsetof(tree_node_file, next));
+
+        CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
+        CX_TEST_ASSERT(talloc.alloc_total == 1);
+
+        cxTreeDestroy(tree);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
+
+CX_TEST(test_tree_high_create_simple) {
+    CX_TEST_DO {
+        CxTree *tree = cxTreeCreateSimple(
+                tree_node_file_create,
+                tree_node_file_search
+        );
+        CX_TEST_ASSERT(tree->cl != NULL);
+        CX_TEST_ASSERT(tree->allocator == cxDefaultAllocator);
+        CX_TEST_ASSERT(tree->node_create == tree_node_file_create);
+        CX_TEST_ASSERT(tree->search == tree_node_file_search);
+        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->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));
+        CX_TEST_ASSERT(tree->loc_last_child == offsetof(struct cx_tree_node_base_s, last_child));
+        CX_TEST_ASSERT(tree->loc_prev == offsetof(struct cx_tree_node_base_s, prev));
+        CX_TEST_ASSERT(tree->loc_next == offsetof(struct cx_tree_node_base_s, next));
+        cxTreeDestroy(tree);
+    }
+}
+
+CX_TEST(test_tree_high_create_wrapped) {
+    tree_node root = {0}, child1 = {0}, child2 = {0}, child3 = {0};
+    cx_tree_link(&root, &child1, tree_node_layout);
+    cx_tree_link(&root, &child2, tree_node_layout);
+    cx_tree_link(&child1, &child3, tree_node_layout);
+    CX_TEST_DO {
+        CxTree *tree = cxTreeCreateWrapped(&root, tree_node_layout);
+        CX_TEST_ASSERT(tree->cl != NULL);
+        CX_TEST_ASSERT(tree->allocator == cxDefaultAllocator);
+        CX_TEST_ASSERT(tree->node_create == NULL);
+        CX_TEST_ASSERT(tree->search == 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->loc_parent == offsetof(tree_node, parent));
+        CX_TEST_ASSERT(tree->loc_children == offsetof(tree_node, children));
+        CX_TEST_ASSERT(tree->loc_last_child == -1);
+        CX_TEST_ASSERT(tree->loc_prev == offsetof(tree_node, prev));
+        CX_TEST_ASSERT(tree->loc_next == offsetof(tree_node, next));
+        cxTreeDestroy(tree);
+    }
+}
 
 CxTestSuite *cx_test_suite_tree_low_level(void) {
     CxTestSuite *suite = cx_test_suite_new("tree (low level)");
@@ -1562,3 +1644,13 @@
 
     return suite;
 }
+
+CxTestSuite *cx_test_suite_tree_high_level(void) {
+    CxTestSuite *suite = cx_test_suite_new("tree (high level)");
+
+    cx_test_register(suite, test_tree_high_create);
+    cx_test_register(suite, test_tree_high_create_simple);
+    cx_test_register(suite, test_tree_high_create_wrapped);
+
+    return suite;
+}
--- a/tests/ucxtest.c	Wed Oct 02 19:11:40 2024 +0200
+++ b/tests/ucxtest.c	Thu Oct 03 15:38:05 2024 +0200
@@ -40,11 +40,10 @@
 CxTestSuite *cx_test_suite_empty_list(void);
 CxTestSuite *cx_test_suite_array_list(void);
 CxTestSuite *cx_test_suite_linked_list(void);
-
 CxTestSuite *cx_test_suite_array_list_defaulted_funcs(void);
-
 CxTestSuite *cx_test_suite_linked_list_defaulted_funcs(void);
 CxTestSuite *cx_test_suite_tree_low_level(void);
+CxTestSuite *cx_test_suite_tree_high_level(void);
 CxTestSuite *cx_test_suite_mempool(void);
 CxTestSuite *cx_test_suite_hash_map(void);
 
@@ -72,6 +71,7 @@
             cx_test_suite_array_list_defaulted_funcs(),
             cx_test_suite_linked_list_defaulted_funcs(),
             cx_test_suite_tree_low_level(),
+            cx_test_suite_tree_high_level(),
             cx_test_suite_mempool(),
             cx_test_suite_hash_map()
     );

mercurial