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