ucx/tree.c

changeset 852
83fdf679df99
parent 816
839fefbdedc7
--- a/ucx/tree.c	Thu Nov 28 17:53:13 2024 +0100
+++ b/ucx/tree.c	Mon Jan 06 21:18:36 2025 +0100
@@ -33,146 +33,248 @@
 #include <assert.h>
 
 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
-#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
 #define tree_parent(node) CX_TREE_PTR(node, loc_parent)
 #define tree_children(node) CX_TREE_PTR(node, loc_children)
+#define tree_last_child(node) CX_TREE_PTR(node, loc_last_child)
 #define tree_prev(node) CX_TREE_PTR(node, loc_prev)
 #define tree_next(node) CX_TREE_PTR(node, loc_next)
 
-void cx_tree_link(
-        void *restrict parent,
-        void *restrict node,
+#define cx_tree_ptr_locations \
+    loc_parent, loc_children, loc_last_child, loc_prev, loc_next
+
+#define cx_tree_node_layout(tree) \
+    (tree)->loc_parent,\
+    (tree)->loc_children,\
+    (tree)->loc_last_child,\
+    (tree)->loc_prev,  \
+    (tree)->loc_next
+
+static void cx_tree_zero_pointers(
+        void *node,
         ptrdiff_t loc_parent,
         ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev,
         ptrdiff_t loc_next
 ) {
+    tree_parent(node) = NULL;
+    if (loc_prev >= 0) {
+        tree_prev(node) = NULL;
+    }
+    tree_next(node) = NULL;
+    tree_children(node) = NULL;
+    if (loc_last_child >= 0) {
+        tree_last_child(node) = NULL;
+    }
+}
+
+void cx_tree_link(
+        void *parent,
+        void *node,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    assert(loc_parent >= 0);
+    assert(loc_children >= 0);
+    assert(loc_next >= 0);
+
     void *current_parent = tree_parent(node);
     if (current_parent == parent) return;
     if (current_parent != NULL) {
-        cx_tree_unlink(node, loc_parent, loc_children,
-                       loc_prev, loc_next);
+        cx_tree_unlink(node, cx_tree_ptr_locations);
     }
 
     if (tree_children(parent) == NULL) {
         tree_children(parent) = node;
+        if (loc_last_child >= 0) {
+            tree_last_child(parent) = node;
+        }
     } else {
-        void *children = tree_children(parent);
-        tree_prev(children) = node;
-        tree_next(node) = children;
-        tree_children(parent) = node;
+        void *child;
+        if (loc_last_child >= 0) {
+            child = tree_last_child(parent);
+            tree_last_child(parent) = node;
+        } else {
+            child = tree_children(parent);
+            void *next;
+            while ((next = tree_next(child)) != NULL) {
+                child = next;
+            }
+        }
+        if (loc_prev >= 0) {
+            tree_prev(node) = child;
+        }
+        tree_next(child) = node;
     }
     tree_parent(node) = parent;
 }
 
+static void *cx_tree_node_prev(
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_next,
+        const void *node
+) {
+    void *parent = tree_parent(node);
+    void *begin = tree_children(parent);
+    if (begin == node) return NULL;
+    const void *cur = begin;
+    const void *next;
+    while (1) {
+        next = tree_next(cur);
+        if (next == node) return (void *) cur;
+        cur = next;
+    }
+}
+
 void cx_tree_unlink(
         void *node,
         ptrdiff_t loc_parent,
         ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev,
         ptrdiff_t loc_next
 ) {
     if (tree_parent(node) == NULL) return;
 
-    void *left = tree_prev(node);
+    assert(loc_children >= 0);
+    assert(loc_next >= 0);
+    assert(loc_parent >= 0);
+    void *left;
+    if (loc_prev >= 0) {
+        left = tree_prev(node);
+    } else {
+        left = cx_tree_node_prev(loc_parent, loc_children, loc_next, node);
+    }
     void *right = tree_next(node);
-    assert(left == NULL || tree_children(tree_parent(node)) != node);
+    void *parent = tree_parent(node);
+    assert(left == NULL || tree_children(parent) != node);
+    assert(right == NULL || loc_last_child < 0 ||
+           tree_last_child(parent) != node);
+
     if (left == NULL) {
-        tree_children(tree_parent(node)) = right;
+        tree_children(parent) = right;
     } else {
         tree_next(left) = right;
     }
-    if (right != NULL) tree_prev(right) = left;
+    if (right == NULL) {
+        if (loc_last_child >= 0) {
+            tree_last_child(parent) = left;
+        }
+    } else {
+        if (loc_prev >= 0) {
+            tree_prev(right) = left;
+        }
+    }
+
     tree_parent(node) = NULL;
-    tree_prev(node) = NULL;
     tree_next(node) = NULL;
+    if (loc_prev >= 0) {
+        tree_prev(node) = NULL;
+    }
 }
 
 int cx_tree_search(
-        void const *root,
-        void const *data,
+        const void *root,
+        size_t depth,
+        const void *node,
         cx_tree_search_func sfunc,
         void **result,
         ptrdiff_t loc_children,
         ptrdiff_t loc_next
 ) {
-    int ret;
+    // help avoiding bugs due to uninitialized memory
+    assert(result != NULL);
     *result = NULL;
 
-    // shortcut: compare root before doing anything else
-    ret = sfunc(root, data);
+    // remember return value for best match
+    int ret = sfunc(root, node);
     if (ret < 0) {
-        return ret;
-    } else if (ret == 0 || tree_children(root) == NULL) {
-        *result = (void*)root;
+        // not contained, exit
+        return -1;
+    }
+    *result = (void*) root;
+    // if root is already exact match, exit
+    if (ret == 0) {
+        return 0;
+    }
+
+    // when depth is one, we are already done
+    if (depth == 1) {
         return ret;
     }
 
-    // create a working stack
-    CX_ARRAY_DECLARE(void const*, work);
-    cx_array_initialize(work, 32);
+    // special case: indefinite depth
+    if (depth == 0) {
+        depth = SIZE_MAX;
+    }
+
+    // create an iterator
+    CxTreeIterator iter = cx_tree_iterator(
+            (void*) root, false, loc_children, loc_next
+    );
+
+    // skip root, we already handled it
+    cxIteratorNext(iter);
 
-    // add the children of root to the working stack
-    {
-        void *c = tree_children(root);
-        while (c != NULL) {
-            cx_array_simple_add(work, c);
-            c = tree_next(c);
+    // loop through the remaining tree
+    cx_foreach(void *, elem, iter) {
+        // investigate the current node
+        int ret_elem = sfunc(elem, node);
+        if (ret_elem == 0) {
+            // if found, exit the search
+            *result = (void *) elem;
+            ret = 0;
+            break;
+        } else if (ret_elem > 0 && ret_elem < ret) {
+            // new distance is better
+            *result = elem;
+            ret = ret_elem;
+        } else {
+            // not contained or distance is worse, skip entire subtree
+            cxTreeIteratorContinue(iter);
+        }
+
+        // when we reached the max depth, skip the subtree
+        if (iter.depth == depth) {
+            cxTreeIteratorContinue(iter);
         }
     }
 
-    // remember a candidate for adding the data
-    // also remember the exact return code from sfunc
-    void *candidate = NULL;
-    int ret_candidate = -1;
-
-    // process the working stack
-    while (work_size > 0) {
-        // pop element
-        void const *node = work[--work_size];
-
-        // apply the search function
-        ret = sfunc(node, data);
+    // dispose the iterator as we might have exited the loop early
+    cxTreeIteratorDispose(&iter);
 
-        if (ret == 0) {
-            // if found, exit the search
-            *result = (void*) node;
-            work_size = 0;
-            break;
-        } else if (ret > 0) {
-            // if children might contain the data, add them to the stack
-            void *c = tree_children(node);
-            while (c != NULL) {
-                cx_array_simple_add(work, c);
-                c = tree_next(c);
-            }
-
-            // remember this node in case no child is suitable
-            if (ret_candidate < 0 || ret < ret_candidate) {
-                candidate = (void *) node;
-                ret_candidate = ret;
-            }
-        }
-    }
-
-    // not found, but was there a candidate?
-    if (ret != 0 && candidate != NULL) {
-        ret = ret_candidate;
-        *result = candidate;
-    }
-
-    // free the working queue and return
-    free(work);
+    assert(ret < 0 || *result != NULL);
     return ret;
 }
 
-static bool cx_tree_iter_valid(void const *it) {
-    struct cx_tree_iterator_s const *iter = it;
+int cx_tree_search_data(
+        const void *root,
+        size_t depth,
+        const void *data,
+        cx_tree_search_data_func sfunc,
+        void **result,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_next
+) {
+    // it is basically the same implementation
+    return cx_tree_search(
+            root, depth, data,
+            (cx_tree_search_func) sfunc,
+            result,
+            loc_children, loc_next);
+}
+
+static bool cx_tree_iter_valid(const void *it) {
+    const struct cx_tree_iterator_s *iter = it;
     return iter->node != NULL;
 }
 
-static void *cx_tree_iter_current(void const *it) {
-    struct cx_tree_iterator_s const *iter = it;
+static void *cx_tree_iter_current(const void *it) {
+    const struct cx_tree_iterator_s *iter = it;
     return iter->node;
 }
 
@@ -180,6 +282,8 @@
     struct cx_tree_iterator_s *iter = it;
     ptrdiff_t const loc_next = iter->loc_next;
     ptrdiff_t const loc_children = iter->loc_children;
+    // protect us from misuse
+    if (!iter->base.valid(iter)) return;
 
     void *children;
 
@@ -265,17 +369,8 @@
     iter.loc_next = loc_next;
     iter.visit_on_exit = visit_on_exit;
 
-    // allocate stack
-    iter.stack_capacity = 16;
-    iter.stack = malloc(sizeof(void *) * 16);
-    iter.depth = 0;
-
-    // visit the root node
-    iter.node = root;
+    // initialize members
     iter.node_next = NULL;
-    iter.counter = 1;
-    iter.depth = 1;
-    iter.stack[0] = root;
     iter.exiting = false;
     iter.skip = false;
 
@@ -287,20 +382,35 @@
     iter.base.next = cx_tree_iter_next;
     iter.base.current = cx_tree_iter_current;
 
+    // visit the root node
+    iter.node = root;
+    if (root != NULL) {
+        iter.stack_capacity = 16;
+        iter.stack = malloc(sizeof(void *) * 16);
+        iter.stack[0] = root;
+        iter.counter = 1;
+        iter.depth = 1;
+    } else {
+        iter.stack_capacity = 0;
+        iter.stack = NULL;
+        iter.counter = 0;
+        iter.depth = 0;
+    }
+
     return iter;
 }
 
-static bool cx_tree_visitor_valid(void const *it) {
-    struct cx_tree_visitor_s const *iter = it;
+static bool cx_tree_visitor_valid(const void *it) {
+    const struct cx_tree_visitor_s *iter = it;
     return iter->node != NULL;
 }
 
-static void *cx_tree_visitor_current(void const *it) {
-    struct cx_tree_visitor_s const *iter = it;
+static void *cx_tree_visitor_current(const void *it) {
+    const struct cx_tree_visitor_s *iter = it;
     return iter->node;
 }
 
-__attribute__((__nonnull__))
+cx_attr_nonnull
 static void cx_tree_visitor_enqueue_siblings(
         struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) {
     node = tree_next(node);
@@ -318,6 +428,9 @@
 
 static void cx_tree_visitor_next(void *it) {
     struct cx_tree_visitor_s *iter = it;
+    // protect us from misuse
+    if (!iter->base.valid(iter)) return;
+
     ptrdiff_t const loc_next = iter->loc_next;
     ptrdiff_t const loc_children = iter->loc_children;
 
@@ -377,13 +490,7 @@
     iter.loc_children = loc_children;
     iter.loc_next = loc_next;
 
-    // allocate stack
-    iter.depth = 0;
-
-    // visit the root node
-    iter.node = root;
-    iter.counter = 1;
-    iter.depth = 1;
+    // initialize members
     iter.skip = false;
     iter.queue_next = NULL;
     iter.queue_last = NULL;
@@ -396,6 +503,552 @@
     iter.base.next = cx_tree_visitor_next;
     iter.base.current = cx_tree_visitor_current;
 
+    // visit the root node
+    iter.node = root;
+    if (root != NULL) {
+        iter.counter = 1;
+        iter.depth = 1;
+    } else {
+        iter.counter = 0;
+        iter.depth = 0;
+    }
+
     return iter;
 }
 
+static void cx_tree_add_link_duplicate(
+        void *original, void *duplicate,
+        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev, ptrdiff_t loc_next
+) {
+    void *shared_parent = tree_parent(original);
+    if (shared_parent == NULL) {
+        cx_tree_link(original, duplicate, cx_tree_ptr_locations);
+    } else {
+        cx_tree_link(shared_parent, duplicate, cx_tree_ptr_locations);
+    }
+}
+
+static void cx_tree_add_link_new(
+        void *parent, void *node, cx_tree_search_func sfunc,
+        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev, ptrdiff_t loc_next
+) {
+    // check the current children one by one,
+    // if they could be children of the new node
+    void *child = tree_children(parent);
+    while (child != NULL) {
+        void *next = tree_next(child);
+
+        if (sfunc(node, child) > 0) {
+            // the sibling could be a child -> re-link
+            cx_tree_link(node, child, cx_tree_ptr_locations);
+        }
+
+        child = next;
+    }
+
+    // add new node as new child
+    cx_tree_link(parent, node, cx_tree_ptr_locations);
+}
+
+int cx_tree_add(
+        const void *src,
+        cx_tree_search_func sfunc,
+        cx_tree_node_create_func cfunc,
+        void *cdata,
+        void **cnode,
+        void *root,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    *cnode = cfunc(src, cdata);
+    if (*cnode == NULL) return 1;
+    cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations);
+
+    void *match = NULL;
+    int result = cx_tree_search(
+            root,
+            0,
+            *cnode,
+            sfunc,
+            &match,
+            loc_children,
+            loc_next
+    );
+
+    if (result < 0) {
+        // node does not fit into the tree - return non-zero value
+        return 1;
+    } else if (result == 0) {
+        // data already found in the tree, link duplicate
+        cx_tree_add_link_duplicate(match, *cnode, cx_tree_ptr_locations);
+    } else {
+        // closest match found, add new node
+        cx_tree_add_link_new(match, *cnode, sfunc, cx_tree_ptr_locations);
+    }
+
+    return 0;
+}
+
+unsigned int cx_tree_add_look_around_depth = 3;
+
+size_t cx_tree_add_iter(
+        struct cx_iterator_base_s *iter,
+        size_t num,
+        cx_tree_search_func sfunc,
+        cx_tree_node_create_func cfunc,
+        void *cdata,
+        void **failed,
+        void *root,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    // erase the failed pointer
+    *failed = NULL;
+
+    // iter not valid? cancel...
+    if (!iter->valid(iter)) return 0;
+
+    size_t processed = 0;
+    void *current_node = root;
+    const void *elem;
+
+    for (void **eptr; processed < num &&
+         iter->valid(iter) && (eptr = iter->current(iter)) != NULL;
+         iter->next(iter)) {
+        elem = *eptr;
+
+        // create the new node
+        void *new_node = cfunc(elem, cdata);
+        if (new_node == NULL) return processed;
+        cx_tree_zero_pointers(new_node, cx_tree_ptr_locations);
+
+        // start searching from current node
+        void *match;
+        int result;
+        unsigned int look_around_retries = cx_tree_add_look_around_depth;
+        cx_tree_add_look_around_retry:
+        result = cx_tree_search(
+                current_node,
+                0,
+                new_node,
+                sfunc,
+                &match,
+                loc_children,
+                loc_next
+        );
+
+        if (result < 0) {
+            // traverse upwards and try to find better parents
+            void *parent = tree_parent(current_node);
+            if (parent != NULL) {
+                if (look_around_retries > 0) {
+                    look_around_retries--;
+                    current_node = parent;
+                } else {
+                    // look around retries exhausted, start from the root
+                    current_node = root;
+                }
+                goto cx_tree_add_look_around_retry;
+            } else {
+                // no parents. so we failed
+                *failed = new_node;
+                return processed;
+            }
+        } else if (result == 0) {
+            // data already found in the tree, link duplicate
+            cx_tree_add_link_duplicate(match, new_node, cx_tree_ptr_locations);
+            // but stick with the original match, in case we needed a new root
+            current_node = match;
+        } else {
+            // closest match found, add new node as child
+            cx_tree_add_link_new(match, new_node, sfunc,
+                                 cx_tree_ptr_locations);
+            current_node = match;
+        }
+
+        processed++;
+    }
+    return processed;
+}
+
+size_t cx_tree_add_array(
+        const void *src,
+        size_t num,
+        size_t elem_size,
+        cx_tree_search_func sfunc,
+        cx_tree_node_create_func cfunc,
+        void *cdata,
+        void **failed,
+        void *root,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    // erase failed pointer
+    *failed = NULL;
+
+    // super special case: zero elements
+    if (num == 0) {
+        return 0;
+    }
+
+    // special case: one element does not need an iterator
+    if (num == 1) {
+        void *node;
+        if (0 == cx_tree_add(
+                src, sfunc, cfunc, cdata, &node, root,
+                loc_parent, loc_children, loc_last_child,
+                loc_prev, loc_next)) {
+            return 1;
+        } else {
+            *failed = node;
+            return 0;
+        }
+    }
+
+    // otherwise, create iterator and hand over to other function
+    CxIterator iter = cxIterator(src, elem_size, num);
+    return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc,
+                            cfunc, cdata, failed, root,
+                            loc_parent, loc_children, loc_last_child,
+                            loc_prev, loc_next);
+}
+
+static int cx_tree_default_insert_element(
+        CxTree *tree,
+        const void *data
+) {
+    void *node;
+    if (tree->root == NULL) {
+        node = tree->node_create(data, tree);
+        if (node == NULL) return 1;
+        cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
+        tree->root = node;
+        tree->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++;
+    } else {
+        cxFree(tree->allocator, node);
+    }
+    return result;
+}
+
+static size_t cx_tree_default_insert_many(
+        CxTree *tree,
+        struct cx_iterator_base_s *iter,
+        size_t n
+) {
+    size_t ins = 0;
+    if (!iter->valid(iter)) return 0;
+    if (tree->root == NULL) {
+        // use the first element from the iter to create the root node
+        void **eptr = iter->current(iter);
+        void *node = tree->node_create(*eptr, tree);
+        if (node == NULL) return 0;
+        cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
+        tree->root = node;
+        ins = 1;
+        iter->next(iter);
+    }
+    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;
+    if (ins < n) {
+        cxFree(tree->allocator, failed);
+    }
+    return ins;
+}
+
+static void *cx_tree_default_find(
+        CxTree *tree,
+        const void *subtree,
+        const void *data,
+        size_t depth
+) {
+    if (tree->root == NULL) return NULL;
+
+    void *found;
+    if (0 == cx_tree_search_data(
+            subtree,
+            depth,
+            data,
+            tree->search_data,
+            &found,
+            tree->loc_children,
+            tree->loc_next
+    )) {
+        return found;
+    } else {
+        return NULL;
+    }
+}
+
+static cx_tree_class cx_tree_default_class = {
+        cx_tree_default_insert_element,
+        cx_tree_default_insert_many,
+        cx_tree_default_find
+};
+
+CxTree *cxTreeCreate(
+        const CxAllocator *allocator,
+        cx_tree_node_create_func create_func,
+        cx_tree_search_func search_func,
+        cx_tree_search_data_func search_data_func,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    if (allocator == NULL) {
+        allocator = cxDefaultAllocator;
+    }
+    assert(create_func != NULL);
+    assert(search_func != NULL);
+    assert(search_data_func != NULL);
+
+    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->search_data = search_data_func;
+    tree->simple_destructor = NULL;
+    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;
+}
+
+void cxTreeFree(CxTree *tree) {
+    if (tree == NULL) return;
+    if (tree->root != NULL) {
+        cxTreeClear(tree);
+    }
+    cxFree(tree->allocator, tree);
+}
+
+CxTree *cxTreeCreateWrapped(
+        const CxAllocator *allocator,
+        void *root,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_last_child,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    if (allocator == NULL) {
+        allocator = cxDefaultAllocator;
+    }
+    assert(root != NULL);
+
+    CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+    if (tree == NULL) return NULL;
+
+    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->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;
+}
+
+void cxTreeSetParent(
+        CxTree *tree,
+        void *parent,
+        void *child
+) {
+    size_t loc_parent = tree->loc_parent;
+    if (tree_parent(child) == NULL) {
+        tree->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++;
+}
+
+int cxTreeAddChild(
+        CxTree *tree,
+        void *parent,
+        const void *data) {
+    void *node = tree->node_create(data, tree);
+    if (node == NULL) return 1;
+    cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
+    cx_tree_link(parent, node, cx_tree_node_layout(tree));
+    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;
+}
+
+size_t cxTreeSubtreeDepth(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.depth;
+}
+
+size_t cxTreeDepth(CxTree *tree) {
+    CxTreeVisitor visitor = cx_tree_visitor(
+            tree->root, tree->loc_children, tree->loc_next
+    );
+    while (cxIteratorValid(visitor)) {
+        cxIteratorNext(visitor);
+    }
+    return visitor.depth;
+}
+
+int cxTreeRemoveNode(
+        CxTree *tree,
+        void *node,
+        cx_tree_relink_func relink_func
+) {
+    if (node == tree->root) return 1;
+
+    // determine the new parent
+    ptrdiff_t loc_parent = tree->loc_parent;
+    void *new_parent = tree_parent(node);
+
+    // first, unlink from the parent
+    cx_tree_unlink(node, cx_tree_node_layout(tree));
+
+    // then relink each child
+    ptrdiff_t loc_children = tree->loc_children;
+    ptrdiff_t loc_next = tree->loc_next;
+    void *child = tree_children(node);
+    while (child != NULL) {
+        // forcibly set the parent to NULL - we do not use the unlink function
+        // because that would unnecessarily modify the children linked list
+        tree_parent(child) = NULL;
+
+        // update contents, if required
+        if (relink_func != NULL) {
+            relink_func(child, node, new_parent);
+        }
+
+        // link to new parent
+        cx_tree_link(new_parent, child, cx_tree_node_layout(tree));
+
+        // proceed to next child
+        child = tree_next(child);
+    }
+
+    // clear the linked list of the removed node
+    tree_children(node) = NULL;
+    ptrdiff_t loc_last_child = tree->loc_last_child;
+    if (loc_last_child >= 0) tree_last_child(node) = NULL;
+
+    // the tree now has one member less
+    tree->size--;
+
+    return 0;
+}
+
+void cxTreeRemoveSubtree(CxTree *tree, void *node) {
+    if (node == tree->root) {
+        tree->root = NULL;
+        tree->size = 0;
+        return;
+    }
+    size_t subtree_size = cxTreeSubtreeSize(tree, node);
+    cx_tree_unlink(node, cx_tree_node_layout(tree));
+    tree->size -= subtree_size;
+}
+
+int cxTreeDestroyNode(
+        CxTree *tree,
+        void *node,
+        cx_tree_relink_func relink_func
+) {
+    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);
+        }
+        return 0;
+    } else {
+        return result;
+    }
+}
+
+void cxTreeDestroySubtree(CxTree *tree, void *node) {
+    cx_tree_unlink(node, cx_tree_node_layout(tree));
+    CxTreeIterator iter = cx_tree_iterator(
+            node, true,
+            tree->loc_children, tree->loc_next
+    );
+    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);
+            }
+        }
+    }
+    tree->size -= iter.counter;
+    if (node == tree->root) {
+        tree->root = NULL;
+    }
+}

mercurial