src/tree.c

Mon, 19 Feb 2024 22:09:16 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 19 Feb 2024 22:09:16 +0100
changeset 836
2672a2f79484
parent 834
04c53b3c8378
child 838
1ce90ab4fab9
permissions
-rw-r--r--

implement basic (enter only) tree iterator

relates to #371

     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     off_t const loc_next = iter->loc_next;
   182     off_t const loc_children = iter->loc_children;
   184     // TODO: support mutating iterator
   185     // TODO: support visit_on_exit
   187     void *children = tree_children(iter->node);
   188     if (children == NULL) {
   189         // search for the next node
   190         void *current = iter->node;
   191         do {
   192             // check if there is a sibling
   193             void *next = tree_next(current);
   194             if (next == NULL) {
   195                 // no sibling, check again for parent node
   196                 --iter->depth;
   197                 if (iter->depth == 0) {
   198                     // there is no parent - we have iterated the entire tree
   199                     // invalidate the iterator and free the node stack
   200                     iter->node = NULL;
   201                     current = NULL;
   202                     iter->stack_capacity = 0;
   203                     free(iter->stack);
   204                     iter->stack = NULL;
   205                 } else {
   206                     // the parent node can be obtained from the top of stack
   207                     // this way we can avoid the loc_parent in the iterator
   208                     current = iter->stack[iter->depth - 1];
   209                 }
   210             } else {
   211                 // move to the sibling and break the loop
   212                 current = NULL;
   213                 iter->counter++;
   214                 iter->node = next;
   215                 // new top of stack is the sibling
   216                 iter->stack[iter->depth - 1] = next;
   217             }
   218         } while (current != NULL);
   219     } else {
   220         // node has children, push the first child onto the stack and enter it
   221         cx_array_simple_add(iter->stack, children);
   222         iter->node = children;
   223         iter->counter++;
   224     }
   225 }
   227 CxTreeIterator cx_tree_iterator(
   228         void *root,
   229         bool visit_on_exit,
   230         ptrdiff_t loc_children,
   231         ptrdiff_t loc_next
   232 ) {
   233     CxTreeIterator iter;
   234     iter.loc_children = loc_children;
   235     iter.loc_next = loc_next;
   236     iter.visit_on_exit = visit_on_exit;
   238     // allocate stack
   239     iter.stack_capacity = 16;
   240     iter.stack = malloc(sizeof(void *) * 16);
   241     iter.depth = 0;
   243     // visit the root node
   244     iter.node = root;
   245     iter.counter = 1;
   246     iter.depth = 1;
   247     iter.stack[0] = root;
   248     iter.exiting = false;
   250     // assign base iterator functions
   251     iter.base.mutating = false;
   252     iter.base.remove = false;
   253     iter.base.current_impl = NULL;
   254     iter.base.valid = cx_tree_iter_valid;
   255     iter.base.next = cx_tree_iter_next;
   256     iter.base.current = cx_tree_iter_current;
   258     return iter;
   259 }

mercurial