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

universe@816 1 /*
universe@816 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@816 3 *
universe@816 4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved.
universe@816 5 *
universe@816 6 * Redistribution and use in source and binary forms, with or without
universe@816 7 * modification, are permitted provided that the following conditions are met:
universe@816 8 *
universe@816 9 * 1. Redistributions of source code must retain the above copyright
universe@816 10 * notice, this list of conditions and the following disclaimer.
universe@816 11 *
universe@816 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@816 13 * notice, this list of conditions and the following disclaimer in the
universe@816 14 * documentation and/or other materials provided with the distribution.
universe@816 15 *
universe@816 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@816 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@816 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@816 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@816 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@816 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@816 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@816 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@816 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@816 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@816 26 * POSSIBILITY OF SUCH DAMAGE.
universe@816 27 */
universe@816 28
universe@816 29 #include "cx/tree.h"
universe@816 30
universe@826 31 #include "cx/array_list.h"
universe@826 32
universe@816 33 #include <assert.h>
universe@816 34
universe@816 35 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
universe@816 36 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
universe@816 37 #define tree_parent(node) CX_TREE_PTR(node, loc_parent)
universe@816 38 #define tree_children(node) CX_TREE_PTR(node, loc_children)
universe@816 39 #define tree_prev(node) CX_TREE_PTR(node, loc_prev)
universe@816 40 #define tree_next(node) CX_TREE_PTR(node, loc_next)
universe@816 41
universe@816 42 void cx_tree_link(
universe@816 43 void *restrict parent,
universe@816 44 void *restrict node,
universe@816 45 ptrdiff_t loc_parent,
universe@816 46 ptrdiff_t loc_children,
universe@816 47 ptrdiff_t loc_prev,
universe@816 48 ptrdiff_t loc_next
universe@816 49 ) {
universe@816 50 void *current_parent = tree_parent(node);
universe@816 51 if (current_parent == parent) return;
universe@816 52 if (current_parent != NULL) {
universe@816 53 cx_tree_unlink(node, loc_parent, loc_children,
universe@816 54 loc_prev, loc_next);
universe@816 55 }
universe@816 56
universe@816 57 if (tree_children(parent) == NULL) {
universe@816 58 tree_children(parent) = node;
universe@816 59 } else {
universe@816 60 void *children = tree_children(parent);
universe@816 61 tree_prev(children) = node;
universe@816 62 tree_next(node) = children;
universe@816 63 tree_children(parent) = node;
universe@816 64 }
universe@816 65 tree_parent(node) = parent;
universe@816 66 }
universe@816 67
universe@816 68 void cx_tree_unlink(
universe@816 69 void *node,
universe@816 70 ptrdiff_t loc_parent,
universe@816 71 ptrdiff_t loc_children,
universe@816 72 ptrdiff_t loc_prev,
universe@816 73 ptrdiff_t loc_next
universe@816 74 ) {
universe@816 75 if (tree_parent(node) == NULL) return;
universe@816 76
universe@816 77 void *left = tree_prev(node);
universe@816 78 void *right = tree_next(node);
universe@816 79 assert(left == NULL || tree_children(tree_parent(node)) != node);
universe@816 80 if (left == NULL) {
universe@816 81 tree_children(tree_parent(node)) = right;
universe@816 82 } else {
universe@816 83 tree_next(left) = right;
universe@816 84 }
universe@816 85 if (right != NULL) tree_prev(right) = left;
universe@816 86 tree_parent(node) = NULL;
universe@816 87 tree_prev(node) = NULL;
universe@816 88 tree_next(node) = NULL;
universe@816 89 }
universe@826 90
universe@826 91 int cx_tree_search(
universe@826 92 void const *root,
universe@826 93 void const *data,
universe@826 94 cx_tree_search_func sfunc,
universe@826 95 void **result,
universe@826 96 ptrdiff_t loc_children,
universe@826 97 ptrdiff_t loc_next
universe@826 98 ) {
universe@826 99 int ret;
universe@826 100 *result = NULL;
universe@826 101
universe@826 102 // shortcut: compare root before doing anything else
universe@826 103 ret = sfunc(root, data);
universe@826 104 if (ret < 0) {
universe@826 105 return ret;
universe@826 106 } else if (ret == 0 || tree_children(root) == NULL) {
universe@826 107 *result = (void*)root;
universe@826 108 return ret;
universe@826 109 }
universe@826 110
universe@826 111 // create a working stack
universe@826 112 size_t work_cap = 32;
universe@826 113 size_t work_size = 0;
universe@826 114 void const **work = malloc(sizeof(void*) * work_cap);
universe@826 115 #define work_add(node) cx_array_add(&work, &work_size, &work_cap, \
universe@826 116 sizeof(void*), &(node), cx_array_default_reallocator)
universe@826 117
universe@826 118 // add the children of root to the working stack
universe@826 119 {
universe@826 120 void *c = tree_children(root);
universe@826 121 while (c != NULL) {
universe@826 122 work_add(c);
universe@826 123 c = tree_next(c);
universe@826 124 }
universe@826 125 }
universe@826 126
universe@826 127 // remember a candidate for adding the data
universe@826 128 // also remember the exact return code from sfunc
universe@826 129 void *candidate = NULL;
universe@826 130 int ret_candidate = -1;
universe@826 131
universe@826 132 // process the working stack
universe@826 133 while (work_size > 0) {
universe@826 134 // pop element
universe@826 135 void const *node = work[--work_size];
universe@826 136
universe@826 137 // apply the search function
universe@826 138 ret = sfunc(node, data);
universe@826 139
universe@826 140 if (ret == 0) {
universe@826 141 // if found, exit the search
universe@826 142 *result = (void*) node;
universe@826 143 work_size = 0;
universe@826 144 break;
universe@826 145 } else if (ret > 0) {
universe@826 146 // if children might contain the data, add them to the stack
universe@826 147 void *c = tree_children(node);
universe@826 148 while (c != NULL) {
universe@826 149 work_add(c);
universe@826 150 c = tree_next(c);
universe@826 151 }
universe@826 152
universe@826 153 // remember this node in case no child is suitable
universe@826 154 if (ret_candidate < 0 || ret < ret_candidate) {
universe@826 155 candidate = (void *) node;
universe@826 156 ret_candidate = ret;
universe@826 157 }
universe@826 158 }
universe@826 159 }
universe@826 160
universe@826 161 // not found, but was there a candidate?
universe@826 162 if (ret != 0 && candidate != NULL) {
universe@826 163 ret = ret_candidate;
universe@826 164 *result = candidate;
universe@826 165 }
universe@826 166
universe@826 167 // free the working queue and return
universe@826 168 #undef workq_add
universe@826 169 free(work);
universe@826 170 return ret;
universe@826 171 }
universe@830 172
universe@830 173 static bool cx_tree_iter_valid(void const *it) {
universe@830 174 struct cx_tree_iterator_s const *iter = it;
universe@830 175 return iter->node != NULL;
universe@830 176 }
universe@830 177
universe@830 178 static void *cx_tree_iter_current(void const *it) {
universe@830 179 struct cx_tree_iterator_s const *iter = it;
universe@830 180 return iter->node;
universe@830 181 }
universe@830 182
universe@830 183 static void cx_tree_iter_stack_add(
universe@830 184 struct cx_tree_iterator_s *iter,
universe@830 185 void *node
universe@830 186 ) {
universe@830 187 cx_array_add(&iter->stack, &iter->depth, &iter->stack_capacity,
universe@830 188 sizeof(void*), &node, cx_array_default_reallocator);
universe@830 189 }
universe@830 190
universe@830 191 static void cx_tree_iter_next(void *it) {
universe@830 192 struct cx_tree_iterator_s *iter = it;
universe@830 193 // TODO: support mutating iterator
universe@830 194
universe@830 195 // TODO: implement
universe@830 196 }
universe@830 197
universe@830 198
universe@830 199 CxTreeIterator cx_tree_iterator(
universe@830 200 void *root,
universe@830 201 int passes,
universe@830 202 ptrdiff_t loc_children,
universe@830 203 ptrdiff_t loc_next
universe@830 204 ) {
universe@830 205 CxTreeIterator iter;
universe@830 206 iter.loc_children = loc_children;
universe@830 207 iter.loc_next = loc_next;
universe@830 208 iter.requested_passes = passes;
universe@830 209
universe@830 210 // invalidate iterator immediately when passes is invalid
universe@830 211 if ((passes & (CX_TREE_ITERATOR_ENTER |
universe@830 212 CX_TREE_ITERATOR_NEXT_CHILD |
universe@830 213 CX_TREE_ITERATOR_EXIT)) == 0) {
universe@830 214 iter.stack = NULL;
universe@830 215 iter.node = NULL;
universe@830 216 return iter;
universe@830 217 }
universe@830 218
universe@830 219 // allocate stack
universe@830 220 iter.stack_capacity = 16;
universe@830 221 iter.stack = malloc(sizeof(void *) * 16);
universe@830 222 iter.depth = 0;
universe@830 223
universe@830 224 // determine start
universe@830 225 if ((passes & CX_TREE_ITERATOR_ENTER) == 0) {
universe@830 226 // we have to skip the first "entering" passes
universe@830 227 void *s = NULL;
universe@830 228 void *n = root;
universe@830 229 iter.counter = 0;
universe@830 230 do {
universe@830 231 iter.counter++;
universe@830 232 iter.source = s;
universe@830 233 iter.node = n;
universe@830 234 cx_tree_iter_stack_add(&iter, n);
universe@830 235 s = n;
universe@830 236 n = tree_children(n);
universe@830 237 } while (n != NULL);
universe@830 238 // found a leaf node s (might be root itself if it has no children)
universe@830 239
universe@830 240 // check if there is a sibling
universe@830 241 n = tree_next(s);
universe@830 242
universe@830 243 if (n == NULL) {
universe@830 244 // no sibling found, exit back to parent node
universe@830 245 // TODO: implement
universe@830 246 } else {
universe@830 247 // there is a sibling
universe@830 248 if ((passes & CX_TREE_ITERATOR_EXIT) == 0) {
universe@830 249 // no exit requested, conclude that only next_child is requested
universe@830 250 iter.source = s;
universe@830 251 iter.node = n;
universe@830 252 iter.counter++;
universe@830 253 iter.current_pass = CX_TREE_ITERATOR_NEXT_CHILD;
universe@830 254 } else {
universe@830 255 // exit requested, so we have found our first pass
universe@830 256 // iter.node and iter.source are still correct
universe@830 257 iter.current_pass = CX_TREE_ITERATOR_EXIT;
universe@830 258 }
universe@830 259 }
universe@830 260 } else {
universe@830 261 // enter passes are requested, we can start by entering the root node
universe@830 262 iter.source = NULL;
universe@830 263 iter.node = root;
universe@830 264 iter.current_pass = CX_TREE_ITERATOR_ENTER;
universe@830 265 iter.counter = 1;
universe@830 266 iter.depth = 1;
universe@830 267 iter.stack[0] = root;
universe@830 268 }
universe@830 269
universe@830 270 // assign base iterator functions
universe@830 271 iter.base.mutating = false;
universe@830 272 iter.base.remove = false;
universe@830 273 iter.base.current_impl = NULL;
universe@830 274 iter.base.valid = cx_tree_iter_valid;
universe@830 275 iter.base.next = cx_tree_iter_next;
universe@830 276 iter.base.current = cx_tree_iter_current;
universe@830 277
universe@830 278 return iter;
universe@830 279 }

mercurial