src/tree.c

Sun, 18 Feb 2024 12:24:04 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 18 Feb 2024 12:24:04 +0100
changeset 830
c4dae6fe6d5b
parent 826
21840975d541
child 833
5c926801f052
permissions
-rw-r--r--

commit complicated stuff before simplifying it

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     size_t work_cap = 32;
   113     size_t work_size = 0;
   114     void const **work = malloc(sizeof(void*) * work_cap);
   115     #define work_add(node) cx_array_add(&work, &work_size, &work_cap, \
   116         sizeof(void*), &(node), cx_array_default_reallocator)
   118     // add the children of root to the working stack
   119     {
   120         void *c = tree_children(root);
   121         while (c != NULL) {
   122             work_add(c);
   123             c = tree_next(c);
   124         }
   125     }
   127     // remember a candidate for adding the data
   128     // also remember the exact return code from sfunc
   129     void *candidate = NULL;
   130     int ret_candidate = -1;
   132     // process the working stack
   133     while (work_size > 0) {
   134         // pop element
   135         void const *node = work[--work_size];
   137         // apply the search function
   138         ret = sfunc(node, data);
   140         if (ret == 0) {
   141             // if found, exit the search
   142             *result = (void*) node;
   143             work_size = 0;
   144             break;
   145         } else if (ret > 0) {
   146             // if children might contain the data, add them to the stack
   147             void *c = tree_children(node);
   148             while (c != NULL) {
   149                 work_add(c);
   150                 c = tree_next(c);
   151             }
   153             // remember this node in case no child is suitable
   154             if (ret_candidate < 0 || ret < ret_candidate) {
   155                 candidate = (void *) node;
   156                 ret_candidate = ret;
   157             }
   158         }
   159     }
   161     // not found, but was there a candidate?
   162     if (ret != 0 && candidate != NULL) {
   163         ret = ret_candidate;
   164         *result = candidate;
   165     }
   167     // free the working queue and return
   168     #undef workq_add
   169     free(work);
   170     return ret;
   171 }
   173 static bool cx_tree_iter_valid(void const *it) {
   174     struct cx_tree_iterator_s const *iter = it;
   175     return iter->node != NULL;
   176 }
   178 static void *cx_tree_iter_current(void const *it) {
   179     struct cx_tree_iterator_s const *iter = it;
   180     return iter->node;
   181 }
   183 static void cx_tree_iter_stack_add(
   184         struct cx_tree_iterator_s *iter,
   185         void *node
   186 ) {
   187     cx_array_add(&iter->stack, &iter->depth, &iter->stack_capacity,
   188         sizeof(void*), &node, cx_array_default_reallocator);
   189 }
   191 static void cx_tree_iter_next(void *it) {
   192     struct cx_tree_iterator_s *iter = it;
   193     // TODO: support mutating iterator
   195     // TODO: implement
   196 }
   199 CxTreeIterator cx_tree_iterator(
   200         void *root,
   201         int passes,
   202         ptrdiff_t loc_children,
   203         ptrdiff_t loc_next
   204 ) {
   205     CxTreeIterator iter;
   206     iter.loc_children = loc_children;
   207     iter.loc_next = loc_next;
   208     iter.requested_passes = passes;
   210     // invalidate iterator immediately when passes is invalid
   211     if ((passes & (CX_TREE_ITERATOR_ENTER |
   212                    CX_TREE_ITERATOR_NEXT_CHILD |
   213                    CX_TREE_ITERATOR_EXIT)) == 0) {
   214         iter.stack = NULL;
   215         iter.node = NULL;
   216         return iter;
   217     }
   219     // allocate stack
   220     iter.stack_capacity = 16;
   221     iter.stack = malloc(sizeof(void *) * 16);
   222     iter.depth = 0;
   224     // determine start
   225     if ((passes & CX_TREE_ITERATOR_ENTER) == 0) {
   226         // we have to skip the first "entering" passes
   227         void *s = NULL;
   228         void *n = root;
   229         iter.counter = 0;
   230         do {
   231             iter.counter++;
   232             iter.source = s;
   233             iter.node = n;
   234             cx_tree_iter_stack_add(&iter, n);
   235             s = n;
   236             n = tree_children(n);
   237         } while (n != NULL);
   238         // found a leaf node s (might be root itself if it has no children)
   240         // check if there is a sibling
   241         n = tree_next(s);
   243         if (n == NULL) {
   244             // no sibling found, exit back to parent node
   245             // TODO: implement
   246         } else {
   247             // there is a sibling
   248             if ((passes & CX_TREE_ITERATOR_EXIT) == 0) {
   249                 // no exit requested, conclude that only next_child is requested
   250                 iter.source = s;
   251                 iter.node = n;
   252                 iter.counter++;
   253                 iter.current_pass = CX_TREE_ITERATOR_NEXT_CHILD;
   254             } else {
   255                 // exit requested, so we have found our first pass
   256                 // iter.node and iter.source are still correct
   257                 iter.current_pass = CX_TREE_ITERATOR_EXIT;
   258             }
   259         }
   260     } else {
   261         // enter passes are requested, we can start by entering the root node
   262         iter.source = NULL;
   263         iter.node = root;
   264         iter.current_pass = CX_TREE_ITERATOR_ENTER;
   265         iter.counter = 1;
   266         iter.depth = 1;
   267         iter.stack[0] = root;
   268     }
   270     // assign base iterator functions
   271     iter.base.mutating = false;
   272     iter.base.remove = false;
   273     iter.base.current_impl = NULL;
   274     iter.base.valid = cx_tree_iter_valid;
   275     iter.base.next = cx_tree_iter_next;
   276     iter.base.current = cx_tree_iter_current;
   278     return iter;
   279 }

mercurial