| Line | Branch | Exec | Source |
|---|---|---|---|
| 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 | */ | ||
| 28 | |||
| 29 | #include "cx/tree.h" | ||
| 30 | |||
| 31 | #include "cx/array_list.h" | ||
| 32 | |||
| 33 | #include <assert.h> | ||
| 34 | |||
| 35 | #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) | ||
| 36 | #define tree_parent(node) CX_TREE_PTR(node, loc_parent) | ||
| 37 | #define tree_children(node) CX_TREE_PTR(node, loc_children) | ||
| 38 | #define tree_last_child(node) CX_TREE_PTR(node, loc_last_child) | ||
| 39 | #define tree_prev(node) CX_TREE_PTR(node, loc_prev) | ||
| 40 | #define tree_next(node) CX_TREE_PTR(node, loc_next) | ||
| 41 | |||
| 42 | #define cx_tree_ptr_locations \ | ||
| 43 | loc_parent, loc_children, loc_last_child, loc_prev, loc_next | ||
| 44 | |||
| 45 | #define cx_tree_node_layout(tree) \ | ||
| 46 | (tree)->loc_parent,\ | ||
| 47 | (tree)->loc_children,\ | ||
| 48 | (tree)->loc_last_child,\ | ||
| 49 | (tree)->loc_prev, \ | ||
| 50 | (tree)->loc_next | ||
| 51 | |||
| 52 | 72 | static void cx_tree_zero_pointers( | |
| 53 | void *node, | ||
| 54 | ptrdiff_t loc_parent, | ||
| 55 | ptrdiff_t loc_children, | ||
| 56 | ptrdiff_t loc_last_child, | ||
| 57 | ptrdiff_t loc_prev, | ||
| 58 | ptrdiff_t loc_next | ||
| 59 | ) { | ||
| 60 | 72 | tree_parent(node) = NULL; | |
| 61 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | if (loc_prev >= 0) { |
| 62 | 72 | tree_prev(node) = NULL; | |
| 63 | } | ||
| 64 | 72 | tree_next(node) = NULL; | |
| 65 | 72 | tree_children(node) = NULL; | |
| 66 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | if (loc_last_child >= 0) { |
| 67 | 72 | tree_last_child(node) = NULL; | |
| 68 | } | ||
| 69 | 72 | } | |
| 70 | |||
| 71 | 224 | void cx_tree_link( | |
| 72 | void *parent, | ||
| 73 | void *node, | ||
| 74 | ptrdiff_t loc_parent, | ||
| 75 | ptrdiff_t loc_children, | ||
| 76 | ptrdiff_t loc_last_child, | ||
| 77 | ptrdiff_t loc_prev, | ||
| 78 | ptrdiff_t loc_next | ||
| 79 | ) { | ||
| 80 | assert(loc_parent >= 0); | ||
| 81 | assert(loc_children >= 0); | ||
| 82 | assert(loc_next >= 0); | ||
| 83 | |||
| 84 | 224 | void *current_parent = tree_parent(node); | |
| 85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
|
224 | if (current_parent == parent) return; |
| 86 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 218 times.
|
224 | if (current_parent != NULL) { |
| 87 | 6 | cx_tree_unlink(node, cx_tree_ptr_locations); | |
| 88 | } | ||
| 89 | |||
| 90 |
2/2✓ Branch 0 taken 131 times.
✓ Branch 1 taken 93 times.
|
224 | if (tree_children(parent) == NULL) { |
| 91 | 131 | tree_children(parent) = node; | |
| 92 |
2/2✓ Branch 0 taken 65 times.
✓ Branch 1 taken 66 times.
|
131 | if (loc_last_child >= 0) { |
| 93 | 65 | tree_last_child(parent) = node; | |
| 94 | } | ||
| 95 | } else { | ||
| 96 | void *child; | ||
| 97 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 60 times.
|
93 | if (loc_last_child >= 0) { |
| 98 | 33 | child = tree_last_child(parent); | |
| 99 | 33 | tree_last_child(parent) = node; | |
| 100 | } else { | ||
| 101 | 60 | child = tree_children(parent); | |
| 102 | void *next; | ||
| 103 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 60 times.
|
84 | while ((next = tree_next(child)) != NULL) { |
| 104 | 24 | child = next; | |
| 105 | } | ||
| 106 | } | ||
| 107 |
2/2✓ Branch 0 taken 91 times.
✓ Branch 1 taken 2 times.
|
93 | if (loc_prev >= 0) { |
| 108 | 91 | tree_prev(node) = child; | |
| 109 | } | ||
| 110 | 93 | tree_next(child) = node; | |
| 111 | } | ||
| 112 | 224 | tree_parent(node) = parent; | |
| 113 | } | ||
| 114 | |||
| 115 | 2 | static void *cx_tree_node_prev( | |
| 116 | ptrdiff_t loc_parent, | ||
| 117 | ptrdiff_t loc_children, | ||
| 118 | ptrdiff_t loc_next, | ||
| 119 | const void *node | ||
| 120 | ) { | ||
| 121 | 2 | void *parent = tree_parent(node); | |
| 122 | 2 | void *begin = tree_children(parent); | |
| 123 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (begin == node) return NULL; |
| 124 | 1 | const void *cur = begin; | |
| 125 | const void *next; | ||
| 126 | while (1) { | ||
| 127 | 2 | next = tree_next(cur); | |
| 128 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (next == node) return (void *) cur; |
| 129 | 1 | cur = next; | |
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | 27 | void cx_tree_unlink( | |
| 134 | void *node, | ||
| 135 | ptrdiff_t loc_parent, | ||
| 136 | ptrdiff_t loc_children, | ||
| 137 | ptrdiff_t loc_last_child, | ||
| 138 | ptrdiff_t loc_prev, | ||
| 139 | ptrdiff_t loc_next | ||
| 140 | ) { | ||
| 141 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 17 times.
|
27 | if (tree_parent(node) == NULL) return; |
| 142 | |||
| 143 | assert(loc_children >= 0); | ||
| 144 | assert(loc_next >= 0); | ||
| 145 | assert(loc_parent >= 0); | ||
| 146 | void *left; | ||
| 147 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2 times.
|
17 | if (loc_prev >= 0) { |
| 148 | 15 | left = tree_prev(node); | |
| 149 | } else { | ||
| 150 | 2 | left = cx_tree_node_prev(loc_parent, loc_children, loc_next, node); | |
| 151 | } | ||
| 152 | 17 | void *right = tree_next(node); | |
| 153 | 17 | void *parent = tree_parent(node); | |
| 154 | assert(left == NULL || tree_children(parent) != node); | ||
| 155 | assert(right == NULL || loc_last_child < 0 || | ||
| 156 | tree_last_child(parent) != node); | ||
| 157 | |||
| 158 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6 times.
|
17 | if (left == NULL) { |
| 159 | 11 | tree_children(parent) = right; | |
| 160 | } else { | ||
| 161 | 6 | tree_next(left) = right; | |
| 162 | } | ||
| 163 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 10 times.
|
17 | if (right == NULL) { |
| 164 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
|
7 | if (loc_last_child >= 0) { |
| 165 | 5 | tree_last_child(parent) = left; | |
| 166 | } | ||
| 167 | } else { | ||
| 168 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
|
10 | if (loc_prev >= 0) { |
| 169 | 9 | tree_prev(right) = left; | |
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | 17 | tree_parent(node) = NULL; | |
| 174 | 17 | tree_next(node) = NULL; | |
| 175 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2 times.
|
17 | if (loc_prev >= 0) { |
| 176 | 15 | tree_prev(node) = NULL; | |
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | 148 | int cx_tree_search( | |
| 181 | const void *root, | ||
| 182 | size_t depth, | ||
| 183 | const void *node, | ||
| 184 | cx_tree_search_func sfunc, | ||
| 185 | void **result, | ||
| 186 | ptrdiff_t loc_children, | ||
| 187 | ptrdiff_t loc_next | ||
| 188 | ) { | ||
| 189 | // help avoiding bugs due to uninitialized memory | ||
| 190 | assert(result != NULL); | ||
| 191 | 148 | *result = NULL; | |
| 192 | |||
| 193 | // remember return value for best match | ||
| 194 | 148 | int ret = sfunc(root, node); | |
| 195 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 114 times.
|
148 | if (ret < 0) { |
| 196 | // not contained, exit | ||
| 197 | 34 | return -1; | |
| 198 | } | ||
| 199 | 114 | *result = (void*) root; | |
| 200 | // if root is already exact match, exit | ||
| 201 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 111 times.
|
114 | if (ret == 0) { |
| 202 | 3 | return 0; | |
| 203 | } | ||
| 204 | |||
| 205 | // when depth is one, we are already done | ||
| 206 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 109 times.
|
111 | if (depth == 1) { |
| 207 | 2 | return ret; | |
| 208 | } | ||
| 209 | |||
| 210 | // special case: indefinite depth | ||
| 211 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 9 times.
|
109 | if (depth == 0) { |
| 212 | 100 | depth = SIZE_MAX; | |
| 213 | } | ||
| 214 | |||
| 215 | // create an iterator | ||
| 216 | 109 | CxTreeIterator iter = cx_tree_iterator( | |
| 217 | (void*) root, false, loc_children, loc_next | ||
| 218 | ); | ||
| 219 | |||
| 220 | // skip root, we already handled it | ||
| 221 | 109 | cxIteratorNext(iter); | |
| 222 | |||
| 223 | // loop through the remaining tree | ||
| 224 |
3/4✓ Branch 2 taken 282 times.
✓ Branch 3 taken 75 times.
✓ Branch 5 taken 282 times.
✗ Branch 6 not taken.
|
357 | cx_foreach(void *, elem, iter) { |
| 225 | // investigate the current node | ||
| 226 | 282 | int ret_elem = sfunc(elem, node); | |
| 227 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 248 times.
|
282 | if (ret_elem == 0) { |
| 228 | // if found, exit the search | ||
| 229 | 34 | *result = (void *) elem; | |
| 230 | 34 | ret = 0; | |
| 231 | 34 | break; | |
| 232 |
3/4✓ Branch 0 taken 141 times.
✓ Branch 1 taken 107 times.
✓ Branch 2 taken 141 times.
✗ Branch 3 not taken.
|
248 | } else if (ret_elem > 0 && ret_elem < ret) { |
| 233 | // new distance is better | ||
| 234 | 141 | *result = elem; | |
| 235 | 141 | ret = ret_elem; | |
| 236 | } else { | ||
| 237 | // not contained or distance is worse, skip entire subtree | ||
| 238 | 107 | cxTreeIteratorContinue(iter); | |
| 239 | } | ||
| 240 | |||
| 241 | // when we reached the max depth, skip the subtree | ||
| 242 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 138 times.
|
141 | if (iter.depth == depth) { |
| 243 | 3 | cxTreeIteratorContinue(iter); | |
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | // dispose the iterator as we might have exited the loop early | ||
| 248 | 109 | cxTreeIteratorDispose(&iter); | |
| 249 | |||
| 250 | assert(ret < 0 || *result != NULL); | ||
| 251 | 109 | return ret; | |
| 252 | } | ||
| 253 | |||
| 254 | 58 | int cx_tree_search_data( | |
| 255 | const void *root, | ||
| 256 | size_t depth, | ||
| 257 | const void *data, | ||
| 258 | cx_tree_search_data_func sfunc, | ||
| 259 | void **result, | ||
| 260 | ptrdiff_t loc_children, | ||
| 261 | ptrdiff_t loc_next | ||
| 262 | ) { | ||
| 263 | // it is basically the same implementation | ||
| 264 | 58 | return cx_tree_search( | |
| 265 | root, depth, data, | ||
| 266 | (cx_tree_search_func) sfunc, | ||
| 267 | result, | ||
| 268 | loc_children, loc_next); | ||
| 269 | } | ||
| 270 | |||
| 271 | 1235 | static bool cx_tree_iter_valid(const void *it) { | |
| 272 | 1235 | const struct cx_tree_iterator_s *iter = it; | |
| 273 | 1235 | return iter->node != NULL; | |
| 274 | } | ||
| 275 | |||
| 276 | 532 | static void *cx_tree_iter_current(const void *it) { | |
| 277 | 532 | const struct cx_tree_iterator_s *iter = it; | |
| 278 | 532 | return iter->node; | |
| 279 | } | ||
| 280 | |||
| 281 | 607 | static void cx_tree_iter_next(void *it) { | |
| 282 | 607 | struct cx_tree_iterator_s *iter = it; | |
| 283 | 607 | ptrdiff_t const loc_next = iter->loc_next; | |
| 284 | 607 | ptrdiff_t const loc_children = iter->loc_children; | |
| 285 | // protect us from misuse | ||
| 286 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 607 times.
|
607 | if (!iter->base.valid(iter)) return; |
| 287 | |||
| 288 | void *children; | ||
| 289 | |||
| 290 | // check if we are currently exiting or entering nodes | ||
| 291 |
2/2✓ Branch 0 taken 116 times.
✓ Branch 1 taken 491 times.
|
607 | if (iter->exiting) { |
| 292 | 116 | children = NULL; | |
| 293 | // skipping on exit is pointless, just clear the flag | ||
| 294 | 116 | iter->skip = false; | |
| 295 | } else { | ||
| 296 |
2/2✓ Branch 0 taken 112 times.
✓ Branch 1 taken 379 times.
|
491 | if (iter->skip) { |
| 297 | // skip flag is set, pretend that there are no children | ||
| 298 | 112 | iter->skip = false; | |
| 299 | 112 | children = NULL; | |
| 300 | } else { | ||
| 301 | // try to enter the children (if any) | ||
| 302 | 379 | children = tree_children(iter->node); | |
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 |
2/2✓ Branch 0 taken 374 times.
✓ Branch 1 taken 233 times.
|
607 | if (children == NULL) { |
| 307 | // search for the next node | ||
| 308 | void *next; | ||
| 309 | 374 | cx_tree_iter_search_next: | |
| 310 | // check if there is a sibling | ||
| 311 |
2/2✓ Branch 0 taken 116 times.
✓ Branch 1 taken 437 times.
|
553 | if (iter->exiting) { |
| 312 | 116 | next = iter->node_next; | |
| 313 | } else { | ||
| 314 | 437 | next = tree_next(iter->node); | |
| 315 | 437 | iter->node_next = next; | |
| 316 | } | ||
| 317 |
2/2✓ Branch 0 taken 353 times.
✓ Branch 1 taken 200 times.
|
553 | if (next == NULL) { |
| 318 | // no sibling, we are done with this node and exit | ||
| 319 |
4/4✓ Branch 0 taken 158 times.
✓ Branch 1 taken 195 times.
✓ Branch 2 taken 79 times.
✓ Branch 3 taken 79 times.
|
353 | if (iter->visit_on_exit && !iter->exiting) { |
| 320 | // iter is supposed to visit the node again | ||
| 321 | 79 | iter->exiting = true; | |
| 322 | } else { | ||
| 323 | 274 | iter->exiting = false; | |
| 324 |
2/2✓ Branch 0 taken 95 times.
✓ Branch 1 taken 179 times.
|
274 | if (iter->depth == 1) { |
| 325 | // there is no parent - we have iterated the entire tree | ||
| 326 | // invalidate the iterator and free the node stack | ||
| 327 | 95 | iter->node = iter->node_next = NULL; | |
| 328 | 95 | iter->stack_capacity = iter->depth = 0; | |
| 329 | 95 | free(iter->stack); | |
| 330 | 95 | iter->stack = NULL; | |
| 331 | } else { | ||
| 332 | // the parent node can be obtained from the top of stack | ||
| 333 | // this way we can avoid the loc_parent in the iterator | ||
| 334 | 179 | iter->depth--; | |
| 335 | 179 | iter->node = iter->stack[iter->depth - 1]; | |
| 336 | // retry with the parent node to find a sibling | ||
| 337 | 179 | goto cx_tree_iter_search_next; | |
| 338 | } | ||
| 339 | } | ||
| 340 | } else { | ||
| 341 |
4/4✓ Branch 0 taken 74 times.
✓ Branch 1 taken 126 times.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 37 times.
|
200 | if (iter->visit_on_exit && !iter->exiting) { |
| 342 | // iter is supposed to visit the node again | ||
| 343 | 37 | iter->exiting = true; | |
| 344 | } else { | ||
| 345 | 163 | iter->exiting = false; | |
| 346 | // move to the sibling | ||
| 347 | 163 | iter->counter++; | |
| 348 | 163 | iter->node = next; | |
| 349 | // new top of stack is the sibling | ||
| 350 | 163 | iter->stack[iter->depth - 1] = next; | |
| 351 | } | ||
| 352 | } | ||
| 353 | } else { | ||
| 354 | // node has children, push the first child onto the stack and enter it | ||
| 355 | 233 | cx_array_simple_add(iter->stack, children); | |
| 356 | 233 | iter->node = children; | |
| 357 | 233 | iter->counter++; | |
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | 131 | CxTreeIterator cx_tree_iterator( | |
| 362 | void *root, | ||
| 363 | bool visit_on_exit, | ||
| 364 | ptrdiff_t loc_children, | ||
| 365 | ptrdiff_t loc_next | ||
| 366 | ) { | ||
| 367 | CxTreeIterator iter; | ||
| 368 | 131 | iter.loc_children = loc_children; | |
| 369 | 131 | iter.loc_next = loc_next; | |
| 370 | 131 | iter.visit_on_exit = visit_on_exit; | |
| 371 | |||
| 372 | // initialize members | ||
| 373 | 131 | iter.node_next = NULL; | |
| 374 | 131 | iter.exiting = false; | |
| 375 | 131 | iter.skip = false; | |
| 376 | |||
| 377 | // assign base iterator functions | ||
| 378 | 131 | iter.base.mutating = false; | |
| 379 | 131 | iter.base.remove = false; | |
| 380 | 131 | iter.base.current_impl = NULL; | |
| 381 | 131 | iter.base.valid = cx_tree_iter_valid; | |
| 382 | 131 | iter.base.next = cx_tree_iter_next; | |
| 383 | 131 | iter.base.current = cx_tree_iter_current; | |
| 384 | |||
| 385 | // visit the root node | ||
| 386 | 131 | iter.node = root; | |
| 387 |
2/2✓ Branch 0 taken 130 times.
✓ Branch 1 taken 1 times.
|
131 | if (root != NULL) { |
| 388 | 130 | iter.stack_capacity = 16; | |
| 389 | 130 | iter.stack = malloc(sizeof(void *) * 16); | |
| 390 | 130 | iter.stack[0] = root; | |
| 391 | 130 | iter.counter = 1; | |
| 392 | 130 | iter.depth = 1; | |
| 393 | } else { | ||
| 394 | 1 | iter.stack_capacity = 0; | |
| 395 | 1 | iter.stack = NULL; | |
| 396 | 1 | iter.counter = 0; | |
| 397 | 1 | iter.depth = 0; | |
| 398 | } | ||
| 399 | |||
| 400 | 131 | return iter; | |
| 401 | } | ||
| 402 | |||
| 403 | 184 | static bool cx_tree_visitor_valid(const void *it) { | |
| 404 | 184 | const struct cx_tree_visitor_s *iter = it; | |
| 405 | 184 | return iter->node != NULL; | |
| 406 | } | ||
| 407 | |||
| 408 | 23 | static void *cx_tree_visitor_current(const void *it) { | |
| 409 | 23 | const struct cx_tree_visitor_s *iter = it; | |
| 410 | 23 | return iter->node; | |
| 411 | } | ||
| 412 | |||
| 413 | cx_attr_nonnull | ||
| 414 | 36 | static void cx_tree_visitor_enqueue_siblings( | |
| 415 | struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) { | ||
| 416 | 36 | node = tree_next(node); | |
| 417 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 36 times.
|
53 | while (node != NULL) { |
| 418 | struct cx_tree_visitor_queue_s *q; | ||
| 419 | 17 | q = malloc(sizeof(struct cx_tree_visitor_queue_s)); | |
| 420 | 17 | q->depth = iter->queue_last->depth; | |
| 421 | 17 | q->node = node; | |
| 422 | 17 | iter->queue_last->next = q; | |
| 423 | 17 | iter->queue_last = q; | |
| 424 | 17 | node = tree_next(node); | |
| 425 | } | ||
| 426 | 36 | iter->queue_last->next = NULL; | |
| 427 | 36 | } | |
| 428 | |||
| 429 | 78 | static void cx_tree_visitor_next(void *it) { | |
| 430 | 78 | struct cx_tree_visitor_s *iter = it; | |
| 431 | // protect us from misuse | ||
| 432 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
|
78 | if (!iter->base.valid(iter)) return; |
| 433 | |||
| 434 | 78 | ptrdiff_t const loc_next = iter->loc_next; | |
| 435 | 78 | ptrdiff_t const loc_children = iter->loc_children; | |
| 436 | |||
| 437 | // add the children of the current node to the queue | ||
| 438 | // unless the skip flag is set | ||
| 439 | void *children; | ||
| 440 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 77 times.
|
78 | if (iter->skip) { |
| 441 | 1 | iter->skip = false; | |
| 442 | 1 | children = NULL; | |
| 443 | } else { | ||
| 444 | 77 | children = tree_children(iter->node); | |
| 445 | } | ||
| 446 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 42 times.
|
78 | if (children != NULL) { |
| 447 | struct cx_tree_visitor_queue_s *q; | ||
| 448 | 36 | q = malloc(sizeof(struct cx_tree_visitor_queue_s)); | |
| 449 | 36 | q->depth = iter->depth + 1; | |
| 450 | 36 | q->node = children; | |
| 451 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 15 times.
|
36 | if (iter->queue_last == NULL) { |
| 452 | assert(iter->queue_next == NULL); | ||
| 453 | 21 | iter->queue_next = q; | |
| 454 | } else { | ||
| 455 | 15 | iter->queue_last->next = q; | |
| 456 | } | ||
| 457 | 36 | iter->queue_last = q; | |
| 458 | 36 | cx_tree_visitor_enqueue_siblings(iter, children, loc_next); | |
| 459 | } | ||
| 460 | |||
| 461 | // check if there is a next node | ||
| 462 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 53 times.
|
78 | if (iter->queue_next == NULL) { |
| 463 | 25 | iter->node = NULL; | |
| 464 | 25 | return; | |
| 465 | } | ||
| 466 | |||
| 467 | // dequeue the next node | ||
| 468 | 53 | iter->node = iter->queue_next->node; | |
| 469 | 53 | iter->depth = iter->queue_next->depth; | |
| 470 | { | ||
| 471 | 53 | struct cx_tree_visitor_queue_s *q = iter->queue_next; | |
| 472 | 53 | iter->queue_next = q->next; | |
| 473 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 32 times.
|
53 | if (iter->queue_next == NULL) { |
| 474 | assert(iter->queue_last == q); | ||
| 475 | 21 | iter->queue_last = NULL; | |
| 476 | } | ||
| 477 | 53 | free(q); | |
| 478 | } | ||
| 479 | |||
| 480 | // increment the node counter | ||
| 481 | 53 | iter->counter++; | |
| 482 | } | ||
| 483 | |||
| 484 | 28 | CxTreeVisitor cx_tree_visitor( | |
| 485 | void *root, | ||
| 486 | ptrdiff_t loc_children, | ||
| 487 | ptrdiff_t loc_next | ||
| 488 | ) { | ||
| 489 | CxTreeVisitor iter; | ||
| 490 | 28 | iter.loc_children = loc_children; | |
| 491 | 28 | iter.loc_next = loc_next; | |
| 492 | |||
| 493 | // initialize members | ||
| 494 | 28 | iter.skip = false; | |
| 495 | 28 | iter.queue_next = NULL; | |
| 496 | 28 | iter.queue_last = NULL; | |
| 497 | |||
| 498 | // assign base iterator functions | ||
| 499 | 28 | iter.base.mutating = false; | |
| 500 | 28 | iter.base.remove = false; | |
| 501 | 28 | iter.base.current_impl = NULL; | |
| 502 | 28 | iter.base.valid = cx_tree_visitor_valid; | |
| 503 | 28 | iter.base.next = cx_tree_visitor_next; | |
| 504 | 28 | iter.base.current = cx_tree_visitor_current; | |
| 505 | |||
| 506 | // visit the root node | ||
| 507 | 28 | iter.node = root; | |
| 508 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 3 times.
|
28 | if (root != NULL) { |
| 509 | 25 | iter.counter = 1; | |
| 510 | 25 | iter.depth = 1; | |
| 511 | } else { | ||
| 512 | 3 | iter.counter = 0; | |
| 513 | 3 | iter.depth = 0; | |
| 514 | } | ||
| 515 | |||
| 516 | 28 | return iter; | |
| 517 | } | ||
| 518 | |||
| 519 | 3 | static void cx_tree_add_link_duplicate( | |
| 520 | void *original, void *duplicate, | ||
| 521 | ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, | ||
| 522 | ptrdiff_t loc_prev, ptrdiff_t loc_next | ||
| 523 | ) { | ||
| 524 | 3 | void *shared_parent = tree_parent(original); | |
| 525 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (shared_parent == NULL) { |
| 526 | 1 | cx_tree_link(original, duplicate, cx_tree_ptr_locations); | |
| 527 | } else { | ||
| 528 | 2 | cx_tree_link(shared_parent, duplicate, cx_tree_ptr_locations); | |
| 529 | } | ||
| 530 | 3 | } | |
| 531 | |||
| 532 | 56 | static void cx_tree_add_link_new( | |
| 533 | void *parent, void *node, cx_tree_search_func sfunc, | ||
| 534 | ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, | ||
| 535 | ptrdiff_t loc_prev, ptrdiff_t loc_next | ||
| 536 | ) { | ||
| 537 | // check the current children one by one, | ||
| 538 | // if they could be children of the new node | ||
| 539 | 56 | void *child = tree_children(parent); | |
| 540 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 56 times.
|
75 | while (child != NULL) { |
| 541 | 19 | void *next = tree_next(child); | |
| 542 | |||
| 543 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 16 times.
|
19 | if (sfunc(node, child) > 0) { |
| 544 | // the sibling could be a child -> re-link | ||
| 545 | 3 | cx_tree_link(node, child, cx_tree_ptr_locations); | |
| 546 | } | ||
| 547 | |||
| 548 | 19 | child = next; | |
| 549 | } | ||
| 550 | |||
| 551 | // add new node as new child | ||
| 552 | 56 | cx_tree_link(parent, node, cx_tree_ptr_locations); | |
| 553 | 56 | } | |
| 554 | |||
| 555 | 13 | int cx_tree_add( | |
| 556 | const void *src, | ||
| 557 | cx_tree_search_func sfunc, | ||
| 558 | cx_tree_node_create_func cfunc, | ||
| 559 | void *cdata, | ||
| 560 | void **cnode, | ||
| 561 | void *root, | ||
| 562 | ptrdiff_t loc_parent, | ||
| 563 | ptrdiff_t loc_children, | ||
| 564 | ptrdiff_t loc_last_child, | ||
| 565 | ptrdiff_t loc_prev, | ||
| 566 | ptrdiff_t loc_next | ||
| 567 | ) { | ||
| 568 | 13 | *cnode = cfunc(src, cdata); | |
| 569 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if (*cnode == NULL) return 1; |
| 570 | 13 | cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations); | |
| 571 | |||
| 572 | 13 | void *match = NULL; | |
| 573 | 13 | int result = cx_tree_search( | |
| 574 | root, | ||
| 575 | 0, | ||
| 576 | *cnode, | ||
| 577 | sfunc, | ||
| 578 | &match, | ||
| 579 | loc_children, | ||
| 580 | loc_next | ||
| 581 | ); | ||
| 582 | |||
| 583 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9 times.
|
13 | if (result < 0) { |
| 584 | // node does not fit into the tree - return non-zero value | ||
| 585 | 4 | return 1; | |
| 586 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
|
9 | } else if (result == 0) { |
| 587 | // data already found in the tree, link duplicate | ||
| 588 | 2 | cx_tree_add_link_duplicate(match, *cnode, cx_tree_ptr_locations); | |
| 589 | } else { | ||
| 590 | // closest match found, add new node | ||
| 591 | 7 | cx_tree_add_link_new(match, *cnode, sfunc, cx_tree_ptr_locations); | |
| 592 | } | ||
| 593 | |||
| 594 | 9 | return 0; | |
| 595 | } | ||
| 596 | |||
| 597 | unsigned int cx_tree_add_look_around_depth = 3; | ||
| 598 | |||
| 599 | 10 | size_t cx_tree_add_iter( | |
| 600 | struct cx_iterator_base_s *iter, | ||
| 601 | size_t num, | ||
| 602 | cx_tree_search_func sfunc, | ||
| 603 | cx_tree_node_create_func cfunc, | ||
| 604 | void *cdata, | ||
| 605 | void **failed, | ||
| 606 | void *root, | ||
| 607 | ptrdiff_t loc_parent, | ||
| 608 | ptrdiff_t loc_children, | ||
| 609 | ptrdiff_t loc_last_child, | ||
| 610 | ptrdiff_t loc_prev, | ||
| 611 | ptrdiff_t loc_next | ||
| 612 | ) { | ||
| 613 | // erase the failed pointer | ||
| 614 | 10 | *failed = NULL; | |
| 615 | |||
| 616 | // iter not valid? cancel... | ||
| 617 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
|
10 | if (!iter->valid(iter)) return 0; |
| 618 | |||
| 619 | 9 | size_t processed = 0; | |
| 620 | 9 | void *current_node = root; | |
| 621 | const void *elem; | ||
| 622 | |||
| 623 |
2/2✓ Branch 0 taken 52 times.
✓ Branch 1 taken 3 times.
|
64 | for (void **eptr; processed < num && |
| 624 |
3/4✓ Branch 0 taken 55 times.
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
|
111 | iter->valid(iter) && (eptr = iter->current(iter)) != NULL; |
| 625 | 50 | iter->next(iter)) { | |
| 626 | 52 | elem = *eptr; | |
| 627 | |||
| 628 | // create the new node | ||
| 629 | 52 | void *new_node = cfunc(elem, cdata); | |
| 630 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
54 | if (new_node == NULL) return processed; |
| 631 | 52 | cx_tree_zero_pointers(new_node, cx_tree_ptr_locations); | |
| 632 | |||
| 633 | // start searching from current node | ||
| 634 | void *match; | ||
| 635 | int result; | ||
| 636 | 52 | unsigned int look_around_retries = cx_tree_add_look_around_depth; | |
| 637 | 77 | cx_tree_add_look_around_retry: | |
| 638 | 77 | result = cx_tree_search( | |
| 639 | current_node, | ||
| 640 | 0, | ||
| 641 | new_node, | ||
| 642 | sfunc, | ||
| 643 | &match, | ||
| 644 | loc_children, | ||
| 645 | loc_next | ||
| 646 | ); | ||
| 647 | |||
| 648 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 50 times.
|
77 | if (result < 0) { |
| 649 | // traverse upwards and try to find better parents | ||
| 650 | 27 | void *parent = tree_parent(current_node); | |
| 651 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 2 times.
|
27 | if (parent != NULL) { |
| 652 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2 times.
|
25 | if (look_around_retries > 0) { |
| 653 | 23 | look_around_retries--; | |
| 654 | 23 | current_node = parent; | |
| 655 | } else { | ||
| 656 | // look around retries exhausted, start from the root | ||
| 657 | 2 | current_node = root; | |
| 658 | } | ||
| 659 | 25 | goto cx_tree_add_look_around_retry; | |
| 660 | } else { | ||
| 661 | // no parents. so we failed | ||
| 662 | 2 | *failed = new_node; | |
| 663 | 2 | return processed; | |
| 664 | } | ||
| 665 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 49 times.
|
50 | } else if (result == 0) { |
| 666 | // data already found in the tree, link duplicate | ||
| 667 | 1 | cx_tree_add_link_duplicate(match, new_node, cx_tree_ptr_locations); | |
| 668 | // but stick with the original match, in case we needed a new root | ||
| 669 | 1 | current_node = match; | |
| 670 | } else { | ||
| 671 | // closest match found, add new node as child | ||
| 672 | 49 | cx_tree_add_link_new(match, new_node, sfunc, | |
| 673 | cx_tree_ptr_locations); | ||
| 674 | 49 | current_node = match; | |
| 675 | } | ||
| 676 | |||
| 677 | 50 | processed++; | |
| 678 | } | ||
| 679 | 7 | return processed; | |
| 680 | } | ||
| 681 | |||
| 682 | 7 | size_t cx_tree_add_array( | |
| 683 | const void *src, | ||
| 684 | size_t num, | ||
| 685 | size_t elem_size, | ||
| 686 | cx_tree_search_func sfunc, | ||
| 687 | cx_tree_node_create_func cfunc, | ||
| 688 | void *cdata, | ||
| 689 | void **failed, | ||
| 690 | void *root, | ||
| 691 | ptrdiff_t loc_parent, | ||
| 692 | ptrdiff_t loc_children, | ||
| 693 | ptrdiff_t loc_last_child, | ||
| 694 | ptrdiff_t loc_prev, | ||
| 695 | ptrdiff_t loc_next | ||
| 696 | ) { | ||
| 697 | // erase failed pointer | ||
| 698 | 7 | *failed = NULL; | |
| 699 | |||
| 700 | // super special case: zero elements | ||
| 701 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
|
7 | if (num == 0) { |
| 702 | 1 | return 0; | |
| 703 | } | ||
| 704 | |||
| 705 | // special case: one element does not need an iterator | ||
| 706 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (num == 1) { |
| 707 | void *node; | ||
| 708 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
|
2 | if (0 == cx_tree_add( |
| 709 | src, sfunc, cfunc, cdata, &node, root, | ||
| 710 | loc_parent, loc_children, loc_last_child, | ||
| 711 | loc_prev, loc_next)) { | ||
| 712 | 1 | return 1; | |
| 713 | } else { | ||
| 714 | 1 | *failed = node; | |
| 715 | 1 | return 0; | |
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 719 | // otherwise, create iterator and hand over to other function | ||
| 720 | 4 | CxIterator iter = cxIterator(src, elem_size, num); | |
| 721 | 4 | return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc, | |
| 722 | cfunc, cdata, failed, root, | ||
| 723 | loc_parent, loc_children, loc_last_child, | ||
| 724 | loc_prev, loc_next); | ||
| 725 | } | ||
| 726 | |||
| 727 | 7 | static int cx_tree_default_insert_element( | |
| 728 | CxTree *tree, | ||
| 729 | const void *data | ||
| 730 | ) { | ||
| 731 | void *node; | ||
| 732 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
|
7 | if (tree->root == NULL) { |
| 733 | 1 | node = tree->node_create(data, tree); | |
| 734 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (node == NULL) return 1; |
| 735 | 1 | cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); | |
| 736 | 1 | tree->root = node; | |
| 737 | 1 | tree->size = 1; | |
| 738 | 1 | return 0; | |
| 739 | } | ||
| 740 | 6 | int result = cx_tree_add(data, tree->search, tree->node_create, | |
| 741 | tree, &node, tree->root, cx_tree_node_layout(tree)); | ||
| 742 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
|
6 | if (0 == result) { |
| 743 | 5 | tree->size++; | |
| 744 | } else { | ||
| 745 | 1 | cxFree(tree->allocator, node); | |
| 746 | } | ||
| 747 | 6 | return result; | |
| 748 | } | ||
| 749 | |||
| 750 | 4 | static size_t cx_tree_default_insert_many( | |
| 751 | CxTree *tree, | ||
| 752 | CxIteratorBase *iter, | ||
| 753 | size_t n | ||
| 754 | ) { | ||
| 755 | 4 | size_t ins = 0; | |
| 756 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (!iter->valid(iter)) return 0; |
| 757 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (tree->root == NULL) { |
| 758 | // use the first element from the iter to create the root node | ||
| 759 | 4 | void **eptr = iter->current(iter); | |
| 760 | 4 | void *node = tree->node_create(*eptr, tree); | |
| 761 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (node == NULL) return 0; |
| 762 | 4 | cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); | |
| 763 | 4 | tree->root = node; | |
| 764 | 4 | ins = 1; | |
| 765 | 4 | iter->next(iter); | |
| 766 | } | ||
| 767 | void *failed; | ||
| 768 | 4 | ins += cx_tree_add_iter(iter, n, tree->search, tree->node_create, | |
| 769 | tree, &failed, tree->root, cx_tree_node_layout(tree)); | ||
| 770 | 4 | tree->size += ins; | |
| 771 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (ins < n) { |
| 772 | 1 | cxFree(tree->allocator, failed); | |
| 773 | } | ||
| 774 | 4 | return ins; | |
| 775 | } | ||
| 776 | |||
| 777 | 30 | static void *cx_tree_default_find( | |
| 778 | CxTree *tree, | ||
| 779 | const void *subtree, | ||
| 780 | const void *data, | ||
| 781 | size_t depth | ||
| 782 | ) { | ||
| 783 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | if (tree->root == NULL) return NULL; |
| 784 | |||
| 785 | void *found; | ||
| 786 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 14 times.
|
30 | if (0 == cx_tree_search_data( |
| 787 | subtree, | ||
| 788 | depth, | ||
| 789 | data, | ||
| 790 | tree->search_data, | ||
| 791 | &found, | ||
| 792 | tree->loc_children, | ||
| 793 | tree->loc_next | ||
| 794 | )) { | ||
| 795 | 16 | return found; | |
| 796 | } else { | ||
| 797 | 14 | return NULL; | |
| 798 | } | ||
| 799 | } | ||
| 800 | |||
| 801 | static cx_tree_class cx_tree_default_class = { | ||
| 802 | cx_tree_default_insert_element, | ||
| 803 | cx_tree_default_insert_many, | ||
| 804 | cx_tree_default_find | ||
| 805 | }; | ||
| 806 | |||
| 807 | 9 | CxTree *cxTreeCreate( | |
| 808 | const CxAllocator *allocator, | ||
| 809 | cx_tree_node_create_func create_func, | ||
| 810 | cx_tree_search_func search_func, | ||
| 811 | cx_tree_search_data_func search_data_func, | ||
| 812 | ptrdiff_t loc_parent, | ||
| 813 | ptrdiff_t loc_children, | ||
| 814 | ptrdiff_t loc_last_child, | ||
| 815 | ptrdiff_t loc_prev, | ||
| 816 | ptrdiff_t loc_next | ||
| 817 | ) { | ||
| 818 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (allocator == NULL) { |
| 819 | ✗ | allocator = cxDefaultAllocator; | |
| 820 | } | ||
| 821 | assert(create_func != NULL); | ||
| 822 | assert(search_func != NULL); | ||
| 823 | assert(search_data_func != NULL); | ||
| 824 | |||
| 825 | 9 | CxTree *tree = cxMalloc(allocator, sizeof(CxTree)); | |
| 826 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (tree == NULL) return NULL; |
| 827 | |||
| 828 | 9 | tree->cl = &cx_tree_default_class; | |
| 829 | 9 | tree->allocator = allocator; | |
| 830 | 9 | tree->node_create = create_func; | |
| 831 | 9 | tree->search = search_func; | |
| 832 | 9 | tree->search_data = search_data_func; | |
| 833 | 9 | tree->simple_destructor = NULL; | |
| 834 | 9 | tree->advanced_destructor = (cx_destructor_func2) cxFree; | |
| 835 | 9 | tree->destructor_data = (void *) allocator; | |
| 836 | 9 | tree->loc_parent = loc_parent; | |
| 837 | 9 | tree->loc_children = loc_children; | |
| 838 | 9 | tree->loc_last_child = loc_last_child; | |
| 839 | 9 | tree->loc_prev = loc_prev; | |
| 840 | 9 | tree->loc_next = loc_next; | |
| 841 | 9 | tree->root = NULL; | |
| 842 | 9 | tree->size = 0; | |
| 843 | |||
| 844 | 9 | return tree; | |
| 845 | } | ||
| 846 | |||
| 847 | 15 | void cxTreeFree(CxTree *tree) { | |
| 848 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if (tree == NULL) return; |
| 849 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
|
15 | if (tree->root != NULL) { |
| 850 | 9 | cxTreeClear(tree); | |
| 851 | } | ||
| 852 | 15 | cxFree(tree->allocator, tree); | |
| 853 | } | ||
| 854 | |||
| 855 | 6 | CxTree *cxTreeCreateWrapped( | |
| 856 | const CxAllocator *allocator, | ||
| 857 | void *root, | ||
| 858 | ptrdiff_t loc_parent, | ||
| 859 | ptrdiff_t loc_children, | ||
| 860 | ptrdiff_t loc_last_child, | ||
| 861 | ptrdiff_t loc_prev, | ||
| 862 | ptrdiff_t loc_next | ||
| 863 | ) { | ||
| 864 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (allocator == NULL) { |
| 865 | ✗ | allocator = cxDefaultAllocator; | |
| 866 | } | ||
| 867 | assert(root != NULL); | ||
| 868 | |||
| 869 | 6 | CxTree *tree = cxMalloc(allocator, sizeof(CxTree)); | |
| 870 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (tree == NULL) return NULL; |
| 871 | |||
| 872 | 6 | tree->cl = &cx_tree_default_class; | |
| 873 | // set the allocator anyway, just in case... | ||
| 874 | 6 | tree->allocator = allocator; | |
| 875 | 6 | tree->node_create = NULL; | |
| 876 | 6 | tree->search = NULL; | |
| 877 | 6 | tree->search_data = NULL; | |
| 878 | 6 | tree->simple_destructor = NULL; | |
| 879 | 6 | tree->advanced_destructor = NULL; | |
| 880 | 6 | tree->destructor_data = NULL; | |
| 881 | 6 | tree->loc_parent = loc_parent; | |
| 882 | 6 | tree->loc_children = loc_children; | |
| 883 | 6 | tree->loc_last_child = loc_last_child; | |
| 884 | 6 | tree->loc_prev = loc_prev; | |
| 885 | 6 | tree->loc_next = loc_next; | |
| 886 | 6 | tree->root = root; | |
| 887 | 6 | tree->size = cxTreeSubtreeSize(tree, root); | |
| 888 | 6 | return tree; | |
| 889 | } | ||
| 890 | |||
| 891 | 4 | void cxTreeSetParent( | |
| 892 | CxTree *tree, | ||
| 893 | void *parent, | ||
| 894 | void *child | ||
| 895 | ) { | ||
| 896 | 4 | size_t loc_parent = tree->loc_parent; | |
| 897 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | if (tree_parent(child) == NULL) { |
| 898 | 3 | tree->size++; | |
| 899 | } | ||
| 900 | 4 | cx_tree_link(parent, child, cx_tree_node_layout(tree)); | |
| 901 | 4 | } | |
| 902 | |||
| 903 | 2 | void cxTreeAddChildNode( | |
| 904 | CxTree *tree, | ||
| 905 | void *parent, | ||
| 906 | void *child | ||
| 907 | ) { | ||
| 908 | 2 | cx_tree_link(parent, child, cx_tree_node_layout(tree)); | |
| 909 | 2 | tree->size++; | |
| 910 | 2 | } | |
| 911 | |||
| 912 | 2 | int cxTreeAddChild( | |
| 913 | CxTree *tree, | ||
| 914 | void *parent, | ||
| 915 | const void *data) { | ||
| 916 | 2 | void *node = tree->node_create(data, tree); | |
| 917 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (node == NULL) return 1; |
| 918 | 2 | cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); | |
| 919 | 2 | cx_tree_link(parent, node, cx_tree_node_layout(tree)); | |
| 920 | 2 | tree->size++; | |
| 921 | 2 | return 0; | |
| 922 | } | ||
| 923 | |||
| 924 | 7 | size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) { | |
| 925 | 7 | CxTreeVisitor visitor = cx_tree_visitor( | |
| 926 | subtree_root, | ||
| 927 | tree->loc_children, | ||
| 928 | tree->loc_next | ||
| 929 | ); | ||
| 930 |
2/2✓ Branch 1 taken 23 times.
✓ Branch 2 taken 7 times.
|
30 | while (cxIteratorValid(visitor)) { |
| 931 | 23 | cxIteratorNext(visitor); | |
| 932 | } | ||
| 933 | 7 | return visitor.counter; | |
| 934 | } | ||
| 935 | |||
| 936 | 12 | size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root) { | |
| 937 | 12 | CxTreeVisitor visitor = cx_tree_visitor( | |
| 938 | subtree_root, | ||
| 939 | tree->loc_children, | ||
| 940 | tree->loc_next | ||
| 941 | ); | ||
| 942 |
2/2✓ Branch 1 taken 24 times.
✓ Branch 2 taken 12 times.
|
36 | while (cxIteratorValid(visitor)) { |
| 943 | 24 | cxIteratorNext(visitor); | |
| 944 | } | ||
| 945 | 12 | return visitor.depth; | |
| 946 | } | ||
| 947 | |||
| 948 | 5 | size_t cxTreeDepth(CxTree *tree) { | |
| 949 | 5 | CxTreeVisitor visitor = cx_tree_visitor( | |
| 950 | tree->root, tree->loc_children, tree->loc_next | ||
| 951 | ); | ||
| 952 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 5 times.
|
13 | while (cxIteratorValid(visitor)) { |
| 953 | 8 | cxIteratorNext(visitor); | |
| 954 | } | ||
| 955 | 5 | return visitor.depth; | |
| 956 | } | ||
| 957 | |||
| 958 | 5 | int cxTreeRemoveNode( | |
| 959 | CxTree *tree, | ||
| 960 | void *node, | ||
| 961 | cx_tree_relink_func relink_func | ||
| 962 | ) { | ||
| 963 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
|
5 | if (node == tree->root) return 1; |
| 964 | |||
| 965 | // determine the new parent | ||
| 966 | 3 | ptrdiff_t loc_parent = tree->loc_parent; | |
| 967 | 3 | void *new_parent = tree_parent(node); | |
| 968 | |||
| 969 | // first, unlink from the parent | ||
| 970 | 3 | cx_tree_unlink(node, cx_tree_node_layout(tree)); | |
| 971 | |||
| 972 | // then relink each child | ||
| 973 | 3 | ptrdiff_t loc_children = tree->loc_children; | |
| 974 | 3 | ptrdiff_t loc_next = tree->loc_next; | |
| 975 | 3 | void *child = tree_children(node); | |
| 976 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
|
8 | while (child != NULL) { |
| 977 | // forcibly set the parent to NULL - we do not use the unlink function | ||
| 978 | // because that would unnecessarily modify the children linked list | ||
| 979 | 5 | tree_parent(child) = NULL; | |
| 980 | |||
| 981 | // update contents, if required | ||
| 982 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (relink_func != NULL) { |
| 983 | 4 | relink_func(child, node, new_parent); | |
| 984 | } | ||
| 985 | |||
| 986 | // link to new parent | ||
| 987 | 5 | cx_tree_link(new_parent, child, cx_tree_node_layout(tree)); | |
| 988 | |||
| 989 | // proceed to next child | ||
| 990 | 5 | child = tree_next(child); | |
| 991 | } | ||
| 992 | |||
| 993 | // clear the linked list of the removed node | ||
| 994 | 3 | tree_children(node) = NULL; | |
| 995 | 3 | ptrdiff_t loc_last_child = tree->loc_last_child; | |
| 996 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (loc_last_child >= 0) tree_last_child(node) = NULL; |
| 997 | |||
| 998 | // the tree now has one member less | ||
| 999 | 3 | tree->size--; | |
| 1000 | |||
| 1001 | 3 | return 0; | |
| 1002 | } | ||
| 1003 | |||
| 1004 | 2 | void cxTreeRemoveSubtree(CxTree *tree, void *node) { | |
| 1005 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (node == tree->root) { |
| 1006 | 1 | tree->root = NULL; | |
| 1007 | 1 | tree->size = 0; | |
| 1008 | 1 | return; | |
| 1009 | } | ||
| 1010 | 1 | size_t subtree_size = cxTreeSubtreeSize(tree, node); | |
| 1011 | 1 | cx_tree_unlink(node, cx_tree_node_layout(tree)); | |
| 1012 | 1 | tree->size -= subtree_size; | |
| 1013 | } | ||
| 1014 | |||
| 1015 | 3 | int cxTreeDestroyNode( | |
| 1016 | CxTree *tree, | ||
| 1017 | void *node, | ||
| 1018 | cx_tree_relink_func relink_func | ||
| 1019 | ) { | ||
| 1020 | 3 | int result = cxTreeRemoveNode(tree, node, relink_func); | |
| 1021 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (result == 0) { |
| 1022 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (tree->simple_destructor) { |
| 1023 | 1 | tree->simple_destructor(node); | |
| 1024 | } | ||
| 1025 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (tree->advanced_destructor) { |
| 1026 | 1 | tree->advanced_destructor(tree->destructor_data, node); | |
| 1027 | } | ||
| 1028 | 2 | return 0; | |
| 1029 | } else { | ||
| 1030 | 1 | return result; | |
| 1031 | } | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | 11 | void cxTreeDestroySubtree(CxTree *tree, void *node) { | |
| 1035 | 11 | cx_tree_unlink(node, cx_tree_node_layout(tree)); | |
| 1036 | 11 | CxTreeIterator iter = cx_tree_iterator( | |
| 1037 | node, true, | ||
| 1038 | tree->loc_children, tree->loc_next | ||
| 1039 | ); | ||
| 1040 |
3/4✓ Branch 2 taken 94 times.
✓ Branch 3 taken 11 times.
✓ Branch 5 taken 94 times.
✗ Branch 6 not taken.
|
105 | cx_foreach(void *, child, iter) { |
| 1041 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 47 times.
|
94 | if (iter.exiting) { |
| 1042 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 44 times.
|
47 | if (tree->simple_destructor) { |
| 1043 | 3 | tree->simple_destructor(child); | |
| 1044 | } | ||
| 1045 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 15 times.
|
47 | if (tree->advanced_destructor) { |
| 1046 | 32 | tree->advanced_destructor(tree->destructor_data, child); | |
| 1047 | } | ||
| 1048 | } | ||
| 1049 | } | ||
| 1050 | 11 | tree->size -= iter.counter; | |
| 1051 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
|
11 | if (node == tree->root) { |
| 1052 | 10 | tree->root = NULL; | |
| 1053 | } | ||
| 1054 | 11 | } | |
| 1055 |