src/tree.c

Fri, 12 Apr 2024 21:48:12 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 12 Apr 2024 21:48:12 +0200
changeset 849
edb9f875b7f9
parent 848
6456036bbb37
permissions
-rw-r--r--

improves interface of cx_sprintf() variants

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@834 112 CX_ARRAY_DECLARE(void const*, work);
universe@833 113 cx_array_initialize(work, 32);
universe@826 114
universe@826 115 // add the children of root to the working stack
universe@826 116 {
universe@826 117 void *c = tree_children(root);
universe@826 118 while (c != NULL) {
universe@833 119 cx_array_simple_add(work, c);
universe@826 120 c = tree_next(c);
universe@826 121 }
universe@826 122 }
universe@826 123
universe@826 124 // remember a candidate for adding the data
universe@826 125 // also remember the exact return code from sfunc
universe@826 126 void *candidate = NULL;
universe@826 127 int ret_candidate = -1;
universe@826 128
universe@826 129 // process the working stack
universe@826 130 while (work_size > 0) {
universe@826 131 // pop element
universe@826 132 void const *node = work[--work_size];
universe@826 133
universe@826 134 // apply the search function
universe@826 135 ret = sfunc(node, data);
universe@826 136
universe@826 137 if (ret == 0) {
universe@826 138 // if found, exit the search
universe@826 139 *result = (void*) node;
universe@826 140 work_size = 0;
universe@826 141 break;
universe@826 142 } else if (ret > 0) {
universe@826 143 // if children might contain the data, add them to the stack
universe@826 144 void *c = tree_children(node);
universe@826 145 while (c != NULL) {
universe@833 146 cx_array_simple_add(work, c);
universe@826 147 c = tree_next(c);
universe@826 148 }
universe@826 149
universe@826 150 // remember this node in case no child is suitable
universe@826 151 if (ret_candidate < 0 || ret < ret_candidate) {
universe@826 152 candidate = (void *) node;
universe@826 153 ret_candidate = ret;
universe@826 154 }
universe@826 155 }
universe@826 156 }
universe@826 157
universe@826 158 // not found, but was there a candidate?
universe@826 159 if (ret != 0 && candidate != NULL) {
universe@826 160 ret = ret_candidate;
universe@826 161 *result = candidate;
universe@826 162 }
universe@826 163
universe@826 164 // free the working queue and return
universe@826 165 free(work);
universe@826 166 return ret;
universe@826 167 }
universe@830 168
universe@830 169 static bool cx_tree_iter_valid(void const *it) {
universe@830 170 struct cx_tree_iterator_s const *iter = it;
universe@830 171 return iter->node != NULL;
universe@830 172 }
universe@830 173
universe@830 174 static void *cx_tree_iter_current(void const *it) {
universe@830 175 struct cx_tree_iterator_s const *iter = it;
universe@830 176 return iter->node;
universe@830 177 }
universe@830 178
universe@830 179 static void cx_tree_iter_next(void *it) {
universe@830 180 struct cx_tree_iterator_s *iter = it;
universe@845 181 ptrdiff_t const loc_next = iter->loc_next;
universe@845 182 ptrdiff_t const loc_children = iter->loc_children;
universe@830 183
universe@838 184 void *children;
universe@838 185
universe@838 186 // check if we are currently exiting or entering nodes
universe@838 187 if (iter->exiting) {
universe@838 188 children = NULL;
universe@848 189 // skipping on exit is pointless, just clear the flag
universe@848 190 iter->skip = false;
universe@838 191 } else {
universe@848 192 if (iter->skip) {
universe@848 193 // skip flag is set, pretend that there are no children
universe@848 194 iter->skip = false;
universe@848 195 children = NULL;
universe@848 196 } else {
universe@848 197 // try to enter the children (if any)
universe@848 198 children = tree_children(iter->node);
universe@848 199 }
universe@838 200 }
universe@838 201
universe@836 202 if (children == NULL) {
universe@836 203 // search for the next node
universe@838 204 void *next;
universe@838 205 cx_tree_iter_search_next:
universe@838 206 // check if there is a sibling
universe@840 207 if (iter->exiting) {
universe@840 208 next = iter->next;
universe@840 209 } else {
universe@840 210 next = tree_next(iter->node);
universe@840 211 iter->next = next;
universe@840 212 }
universe@838 213 if (next == NULL) {
universe@838 214 // no sibling, we are done with this node and exit
universe@838 215 if (iter->visit_on_exit && !iter->exiting) {
universe@838 216 // iter is supposed to visit the node again
universe@838 217 iter->exiting = true;
universe@838 218 } else {
universe@838 219 iter->exiting = false;
universe@838 220 if (iter->depth == 1) {
universe@836 221 // there is no parent - we have iterated the entire tree
universe@836 222 // invalidate the iterator and free the node stack
universe@840 223 iter->node = iter->next = NULL;
universe@838 224 iter->stack_capacity = iter->depth = 0;
universe@836 225 free(iter->stack);
universe@836 226 iter->stack = NULL;
universe@836 227 } else {
universe@836 228 // the parent node can be obtained from the top of stack
universe@836 229 // this way we can avoid the loc_parent in the iterator
universe@838 230 iter->depth--;
universe@838 231 iter->node = iter->stack[iter->depth - 1];
universe@838 232 // retry with the parent node to find a sibling
universe@838 233 goto cx_tree_iter_search_next;
universe@836 234 }
universe@838 235 }
universe@838 236 } else {
universe@838 237 if (iter->visit_on_exit && !iter->exiting) {
universe@838 238 // iter is supposed to visit the node again
universe@838 239 iter->exiting = true;
universe@836 240 } else {
universe@838 241 iter->exiting = false;
universe@838 242 // move to the sibling
universe@836 243 iter->counter++;
universe@836 244 iter->node = next;
universe@836 245 // new top of stack is the sibling
universe@836 246 iter->stack[iter->depth - 1] = next;
universe@836 247 }
universe@838 248 }
universe@836 249 } else {
universe@836 250 // node has children, push the first child onto the stack and enter it
universe@836 251 cx_array_simple_add(iter->stack, children);
universe@836 252 iter->node = children;
universe@836 253 iter->counter++;
universe@836 254 }
universe@830 255 }
universe@830 256
universe@830 257 CxTreeIterator cx_tree_iterator(
universe@830 258 void *root,
universe@833 259 bool visit_on_exit,
universe@830 260 ptrdiff_t loc_children,
universe@830 261 ptrdiff_t loc_next
universe@830 262 ) {
universe@830 263 CxTreeIterator iter;
universe@830 264 iter.loc_children = loc_children;
universe@830 265 iter.loc_next = loc_next;
universe@833 266 iter.visit_on_exit = visit_on_exit;
universe@830 267
universe@830 268 // allocate stack
universe@830 269 iter.stack_capacity = 16;
universe@830 270 iter.stack = malloc(sizeof(void *) * 16);
universe@830 271 iter.depth = 0;
universe@830 272
universe@833 273 // visit the root node
universe@833 274 iter.node = root;
universe@848 275 iter.next = NULL;
universe@833 276 iter.counter = 1;
universe@833 277 iter.depth = 1;
universe@833 278 iter.stack[0] = root;
universe@833 279 iter.exiting = false;
universe@848 280 iter.skip = false;
universe@830 281
universe@830 282 // assign base iterator functions
universe@830 283 iter.base.mutating = false;
universe@830 284 iter.base.remove = false;
universe@830 285 iter.base.current_impl = NULL;
universe@830 286 iter.base.valid = cx_tree_iter_valid;
universe@830 287 iter.base.next = cx_tree_iter_next;
universe@830 288 iter.base.current = cx_tree_iter_current;
universe@830 289
universe@830 290 return iter;
universe@830 291 }
universe@845 292
universe@845 293 static bool cx_tree_visitor_valid(void const *it) {
universe@845 294 struct cx_tree_visitor_s const *iter = it;
universe@845 295 return iter->node != NULL;
universe@845 296 }
universe@845 297
universe@845 298 static void *cx_tree_visitor_current(void const *it) {
universe@845 299 struct cx_tree_visitor_s const *iter = it;
universe@845 300 return iter->node;
universe@845 301 }
universe@845 302
universe@845 303 __attribute__((__nonnull__))
universe@845 304 static void cx_tree_visitor_enqueue_siblings(
universe@845 305 struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) {
universe@845 306 node = tree_next(node);
universe@845 307 while (node != NULL) {
universe@845 308 struct cx_tree_visitor_queue_s *q;
universe@845 309 q = malloc(sizeof(struct cx_tree_visitor_queue_s));
universe@845 310 q->depth = iter->queue_last->depth;
universe@845 311 q->node = node;
universe@845 312 iter->queue_last->next = q;
universe@845 313 iter->queue_last = q;
universe@845 314 node = tree_next(node);
universe@845 315 }
universe@845 316 iter->queue_last->next = NULL;
universe@845 317 }
universe@845 318
universe@845 319 static void cx_tree_visitor_next(void *it) {
universe@845 320 struct cx_tree_visitor_s *iter = it;
universe@845 321 ptrdiff_t const loc_next = iter->loc_next;
universe@845 322 ptrdiff_t const loc_children = iter->loc_children;
universe@845 323
universe@848 324 // add the children of the current node to the queue
universe@848 325 // unless the skip flag is set
universe@848 326 void *children;
universe@848 327 if (iter->skip) {
universe@848 328 iter->skip = false;
universe@848 329 children = NULL;
universe@848 330 } else {
universe@848 331 children = tree_children(iter->node);
universe@848 332 }
universe@848 333 if (children != NULL) {
universe@848 334 struct cx_tree_visitor_queue_s *q;
universe@848 335 q = malloc(sizeof(struct cx_tree_visitor_queue_s));
universe@848 336 q->depth = iter->depth + 1;
universe@848 337 q->node = children;
universe@848 338 if (iter->queue_last == NULL) {
universe@848 339 assert(iter->queue_next == NULL);
universe@848 340 iter->queue_next = q;
universe@848 341 } else {
universe@848 342 iter->queue_last->next = q;
universe@848 343 }
universe@848 344 iter->queue_last = q;
universe@848 345 cx_tree_visitor_enqueue_siblings(iter, children, loc_next);
universe@848 346 }
universe@848 347
universe@845 348 // check if there is a next node
universe@845 349 if (iter->queue_next == NULL) {
universe@845 350 iter->node = NULL;
universe@845 351 return;
universe@845 352 }
universe@845 353
universe@845 354 // dequeue the next node
universe@845 355 iter->node = iter->queue_next->node;
universe@845 356 iter->depth = iter->queue_next->depth;
universe@845 357 {
universe@845 358 struct cx_tree_visitor_queue_s *q = iter->queue_next;
universe@845 359 iter->queue_next = q->next;
universe@845 360 if (iter->queue_next == NULL) {
universe@845 361 assert(iter->queue_last == q);
universe@845 362 iter->queue_last = NULL;
universe@845 363 }
universe@845 364 free(q);
universe@845 365 }
universe@845 366
universe@845 367 // increment the node counter
universe@845 368 iter->counter++;
universe@845 369 }
universe@845 370
universe@845 371 CxTreeVisitor cx_tree_visitor(
universe@845 372 void *root,
universe@845 373 ptrdiff_t loc_children,
universe@845 374 ptrdiff_t loc_next
universe@845 375 ) {
universe@845 376 CxTreeVisitor iter;
universe@845 377 iter.loc_children = loc_children;
universe@845 378 iter.loc_next = loc_next;
universe@845 379
universe@845 380 // allocate stack
universe@845 381 iter.depth = 0;
universe@845 382
universe@845 383 // visit the root node
universe@845 384 iter.node = root;
universe@845 385 iter.counter = 1;
universe@845 386 iter.depth = 1;
universe@848 387 iter.skip = false;
universe@848 388 iter.queue_next = NULL;
universe@848 389 iter.queue_last = NULL;
universe@845 390
universe@845 391 // assign base iterator functions
universe@845 392 iter.base.mutating = false;
universe@845 393 iter.base.remove = false;
universe@845 394 iter.base.current_impl = NULL;
universe@845 395 iter.base.valid = cx_tree_visitor_valid;
universe@845 396 iter.base.next = cx_tree_visitor_next;
universe@845 397 iter.base.current = cx_tree_visitor_current;
universe@845 398
universe@845 399 return iter;
universe@845 400 }
universe@845 401

mercurial