ucx/tree.c

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

mercurial