src/tree.c

Wed, 20 Mar 2024 23:31:41 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 20 Mar 2024 23:31:41 +0100
changeset 845
2615317311b7
parent 840
4f02995ce44e
child 848
6456036bbb37
permissions
-rw-r--r--

add cx_tree_visitor()

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  */
    29 #include "cx/tree.h"
    31 #include "cx/array_list.h"
    33 #include <assert.h>
    35 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
    36 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
    37 #define tree_parent(node) CX_TREE_PTR(node, loc_parent)
    38 #define tree_children(node) CX_TREE_PTR(node, loc_children)
    39 #define tree_prev(node) CX_TREE_PTR(node, loc_prev)
    40 #define tree_next(node) CX_TREE_PTR(node, loc_next)
    42 void cx_tree_link(
    43         void *restrict parent,
    44         void *restrict node,
    45         ptrdiff_t loc_parent,
    46         ptrdiff_t loc_children,
    47         ptrdiff_t loc_prev,
    48         ptrdiff_t loc_next
    49 ) {
    50     void *current_parent = tree_parent(node);
    51     if (current_parent == parent) return;
    52     if (current_parent != NULL) {
    53         cx_tree_unlink(node, loc_parent, loc_children,
    54                        loc_prev, loc_next);
    55     }
    57     if (tree_children(parent) == NULL) {
    58         tree_children(parent) = node;
    59     } else {
    60         void *children = tree_children(parent);
    61         tree_prev(children) = node;
    62         tree_next(node) = children;
    63         tree_children(parent) = node;
    64     }
    65     tree_parent(node) = parent;
    66 }
    68 void cx_tree_unlink(
    69         void *node,
    70         ptrdiff_t loc_parent,
    71         ptrdiff_t loc_children,
    72         ptrdiff_t loc_prev,
    73         ptrdiff_t loc_next
    74 ) {
    75     if (tree_parent(node) == NULL) return;
    77     void *left = tree_prev(node);
    78     void *right = tree_next(node);
    79     assert(left == NULL || tree_children(tree_parent(node)) != node);
    80     if (left == NULL) {
    81         tree_children(tree_parent(node)) = right;
    82     } else {
    83         tree_next(left) = right;
    84     }
    85     if (right != NULL) tree_prev(right) = left;
    86     tree_parent(node) = NULL;
    87     tree_prev(node) = NULL;
    88     tree_next(node) = NULL;
    89 }
    91 int cx_tree_search(
    92         void const *root,
    93         void const *data,
    94         cx_tree_search_func sfunc,
    95         void **result,
    96         ptrdiff_t loc_children,
    97         ptrdiff_t loc_next
    98 ) {
    99     int ret;
   100     *result = NULL;
   102     // shortcut: compare root before doing anything else
   103     ret = sfunc(root, data);
   104     if (ret < 0) {
   105         return ret;
   106     } else if (ret == 0 || tree_children(root) == NULL) {
   107         *result = (void*)root;
   108         return ret;
   109     }
   111     // create a working stack
   112     CX_ARRAY_DECLARE(void const*, work);
   113     cx_array_initialize(work, 32);
   115     // add the children of root to the working stack
   116     {
   117         void *c = tree_children(root);
   118         while (c != NULL) {
   119             cx_array_simple_add(work, c);
   120             c = tree_next(c);
   121         }
   122     }
   124     // remember a candidate for adding the data
   125     // also remember the exact return code from sfunc
   126     void *candidate = NULL;
   127     int ret_candidate = -1;
   129     // process the working stack
   130     while (work_size > 0) {
   131         // pop element
   132         void const *node = work[--work_size];
   134         // apply the search function
   135         ret = sfunc(node, data);
   137         if (ret == 0) {
   138             // if found, exit the search
   139             *result = (void*) node;
   140             work_size = 0;
   141             break;
   142         } else if (ret > 0) {
   143             // if children might contain the data, add them to the stack
   144             void *c = tree_children(node);
   145             while (c != NULL) {
   146                 cx_array_simple_add(work, c);
   147                 c = tree_next(c);
   148             }
   150             // remember this node in case no child is suitable
   151             if (ret_candidate < 0 || ret < ret_candidate) {
   152                 candidate = (void *) node;
   153                 ret_candidate = ret;
   154             }
   155         }
   156     }
   158     // not found, but was there a candidate?
   159     if (ret != 0 && candidate != NULL) {
   160         ret = ret_candidate;
   161         *result = candidate;
   162     }
   164     // free the working queue and return
   165     free(work);
   166     return ret;
   167 }
   169 static bool cx_tree_iter_valid(void const *it) {
   170     struct cx_tree_iterator_s const *iter = it;
   171     return iter->node != NULL;
   172 }
   174 static void *cx_tree_iter_current(void const *it) {
   175     struct cx_tree_iterator_s const *iter = it;
   176     return iter->node;
   177 }
   179 static void cx_tree_iter_next(void *it) {
   180     struct cx_tree_iterator_s *iter = it;
   181     ptrdiff_t const loc_next = iter->loc_next;
   182     ptrdiff_t const loc_children = iter->loc_children;
   184     void *children;
   186     // check if we are currently exiting or entering nodes
   187     if (iter->exiting) {
   188         children = NULL;
   189     } else {
   190         children = tree_children(iter->node);
   191     }
   193     if (children == NULL) {
   194         // search for the next node
   195         void *next;
   196         cx_tree_iter_search_next:
   197         // check if there is a sibling
   198         if (iter->exiting) {
   199             next = iter->next;
   200         } else {
   201             next = tree_next(iter->node);
   202             iter->next = next;
   203         }
   204         if (next == NULL) {
   205             // no sibling, we are done with this node and exit
   206             if (iter->visit_on_exit && !iter->exiting) {
   207                 // iter is supposed to visit the node again
   208                 iter->exiting = true;
   209             } else {
   210                 iter->exiting = false;
   211                 if (iter->depth == 1) {
   212                     // there is no parent - we have iterated the entire tree
   213                     // invalidate the iterator and free the node stack
   214                     iter->node = iter->next = NULL;
   215                     iter->stack_capacity = iter->depth = 0;
   216                     free(iter->stack);
   217                     iter->stack = NULL;
   218                 } else {
   219                     // the parent node can be obtained from the top of stack
   220                     // this way we can avoid the loc_parent in the iterator
   221                     iter->depth--;
   222                     iter->node = iter->stack[iter->depth - 1];
   223                     // retry with the parent node to find a sibling
   224                     goto cx_tree_iter_search_next;
   225                 }
   226             }
   227         } else {
   228             if (iter->visit_on_exit && !iter->exiting) {
   229                 // iter is supposed to visit the node again
   230                 iter->exiting = true;
   231             } else {
   232                 iter->exiting = false;
   233                 // move to the sibling
   234                 iter->counter++;
   235                 iter->node = next;
   236                 // new top of stack is the sibling
   237                 iter->stack[iter->depth - 1] = next;
   238             }
   239         }
   240     } else {
   241         // node has children, push the first child onto the stack and enter it
   242         cx_array_simple_add(iter->stack, children);
   243         iter->node = children;
   244         iter->counter++;
   245     }
   246 }
   248 CxTreeIterator cx_tree_iterator(
   249         void *root,
   250         bool visit_on_exit,
   251         ptrdiff_t loc_children,
   252         ptrdiff_t loc_next
   253 ) {
   254     CxTreeIterator iter;
   255     iter.loc_children = loc_children;
   256     iter.loc_next = loc_next;
   257     iter.visit_on_exit = visit_on_exit;
   259     // allocate stack
   260     iter.stack_capacity = 16;
   261     iter.stack = malloc(sizeof(void *) * 16);
   262     iter.depth = 0;
   264     // visit the root node
   265     iter.node = root;
   266     iter.counter = 1;
   267     iter.depth = 1;
   268     iter.stack[0] = root;
   269     iter.exiting = false;
   271     // assign base iterator functions
   272     iter.base.mutating = false;
   273     iter.base.remove = false;
   274     iter.base.current_impl = NULL;
   275     iter.base.valid = cx_tree_iter_valid;
   276     iter.base.next = cx_tree_iter_next;
   277     iter.base.current = cx_tree_iter_current;
   279     return iter;
   280 }
   282 static bool cx_tree_visitor_valid(void const *it) {
   283     struct cx_tree_visitor_s const *iter = it;
   284     return iter->node != NULL;
   285 }
   287 static void *cx_tree_visitor_current(void const *it) {
   288     struct cx_tree_visitor_s const *iter = it;
   289     return iter->node;
   290 }
   292 __attribute__((__nonnull__))
   293 static void cx_tree_visitor_enqueue_siblings(
   294         struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) {
   295     node = tree_next(node);
   296     while (node != NULL) {
   297         struct cx_tree_visitor_queue_s *q;
   298         q = malloc(sizeof(struct cx_tree_visitor_queue_s));
   299         q->depth = iter->queue_last->depth;
   300         q->node = node;
   301         iter->queue_last->next = q;
   302         iter->queue_last = q;
   303         node = tree_next(node);
   304     }
   305     iter->queue_last->next = NULL;
   306 }
   308 static void cx_tree_visitor_next(void *it) {
   309     struct cx_tree_visitor_s *iter = it;
   310     ptrdiff_t const loc_next = iter->loc_next;
   311     ptrdiff_t const loc_children = iter->loc_children;
   313     // check if there is a next node
   314     if (iter->queue_next == NULL) {
   315         iter->node = NULL;
   316         return;
   317     }
   319     // dequeue the next node
   320     iter->node = iter->queue_next->node;
   321     iter->depth = iter->queue_next->depth;
   322     {
   323         struct cx_tree_visitor_queue_s *q = iter->queue_next;
   324         iter->queue_next = q->next;
   325         if (iter->queue_next == NULL) {
   326             assert(iter->queue_last == q);
   327             iter->queue_last = NULL;
   328         }
   329         free(q);
   330     }
   332     // increment the node counter
   333     iter->counter++;
   335     // add the children of the new node to the queue
   336     void *children = tree_children(iter->node);
   337     if (children != NULL) {
   338         struct cx_tree_visitor_queue_s *q;
   339         q = malloc(sizeof(struct cx_tree_visitor_queue_s));
   340         q->depth = iter->depth + 1;
   341         q->node = children;
   342         if (iter->queue_last == NULL) {
   343             assert(iter->queue_next == NULL);
   344             iter->queue_next = q;
   345         } else {
   346             iter->queue_last->next = q;
   347         }
   348         iter->queue_last = q;
   349         cx_tree_visitor_enqueue_siblings(iter, children, loc_next);
   350     }
   351 }
   353 CxTreeVisitor cx_tree_visitor(
   354         void *root,
   355         ptrdiff_t loc_children,
   356         ptrdiff_t loc_next
   357 ) {
   358     CxTreeVisitor iter;
   359     iter.loc_children = loc_children;
   360     iter.loc_next = loc_next;
   362     // allocate stack
   363     iter.depth = 0;
   365     // visit the root node
   366     iter.node = root;
   367     iter.counter = 1;
   368     iter.depth = 1;
   370     // put all children of root into the queue
   371     void *children = tree_children(root);
   372     if (children == NULL) {
   373         iter.queue_next = NULL;
   374         iter.queue_last = NULL;
   375     } else {
   376         iter.queue_next = malloc(sizeof(struct cx_tree_visitor_queue_s));
   377         iter.queue_next->depth = 2;
   378         iter.queue_next->node = children;
   379         iter.queue_last = iter.queue_next;
   380         cx_tree_visitor_enqueue_siblings(&iter, children, loc_next);
   381     }
   383     // assign base iterator functions
   384     iter.base.mutating = false;
   385     iter.base.remove = false;
   386     iter.base.current_impl = NULL;
   387     iter.base.valid = cx_tree_visitor_valid;
   388     iter.base.next = cx_tree_visitor_next;
   389     iter.base.current = cx_tree_visitor_current;
   391     return iter;
   392 }

mercurial