src/tree.c

changeset 872
d607a184925a
parent 871
e29c1f96646d
child 890
54565fd74e74
equal deleted inserted replaced
862:387414a7afd8 872:d607a184925a
31 #include "cx/array_list.h" 31 #include "cx/array_list.h"
32 32
33 #include <assert.h> 33 #include <assert.h>
34 34
35 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) 35 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
36 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
37 #define tree_parent(node) CX_TREE_PTR(node, loc_parent) 36 #define tree_parent(node) CX_TREE_PTR(node, loc_parent)
38 #define tree_children(node) CX_TREE_PTR(node, loc_children) 37 #define tree_children(node) CX_TREE_PTR(node, loc_children)
39 #define tree_last_child(node) CX_TREE_PTR(node, loc_last_child) 38 #define tree_last_child(node) CX_TREE_PTR(node, loc_last_child)
40 #define tree_prev(node) CX_TREE_PTR(node, loc_prev) 39 #define tree_prev(node) CX_TREE_PTR(node, loc_prev)
41 #define tree_next(node) CX_TREE_PTR(node, loc_next) 40 #define tree_next(node) CX_TREE_PTR(node, loc_next)
42 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 }
61
43 void cx_tree_link( 62 void cx_tree_link(
44 void *restrict parent, 63 void *restrict parent,
45 void *restrict node, 64 void *restrict node,
46 ptrdiff_t loc_parent, 65 ptrdiff_t loc_parent,
47 ptrdiff_t loc_children, 66 ptrdiff_t loc_children,
50 ptrdiff_t loc_next 69 ptrdiff_t loc_next
51 ) { 70 ) {
52 void *current_parent = tree_parent(node); 71 void *current_parent = tree_parent(node);
53 if (current_parent == parent) return; 72 if (current_parent == parent) return;
54 if (current_parent != NULL) { 73 if (current_parent != NULL) {
55 cx_tree_unlink(node, loc_parent, loc_children, loc_last_child, 74 cx_tree_unlink(node, cx_tree_ptr_locations);
56 loc_prev, loc_next);
57 } 75 }
58 76
59 if (tree_children(parent) == NULL) { 77 if (tree_children(parent) == NULL) {
60 tree_children(parent) = node; 78 tree_children(parent) = node;
61 if (loc_last_child >= 0) { 79 if (loc_last_child >= 0) {
115 tree_next(node) = NULL; 133 tree_next(node) = NULL;
116 } 134 }
117 135
118 int cx_tree_search( 136 int cx_tree_search(
119 void const *root, 137 void const *root,
120 void const *data, 138 void const *node,
121 cx_tree_search_func sfunc, 139 cx_tree_search_func sfunc,
122 void **result, 140 void **result,
123 ptrdiff_t loc_children, 141 ptrdiff_t loc_children,
124 ptrdiff_t loc_next 142 ptrdiff_t loc_next
125 ) { 143 ) {
126 int ret; 144 int ret;
127 *result = NULL; 145 *result = NULL;
128 146
129 // shortcut: compare root before doing anything else 147 // shortcut: compare root before doing anything else
130 ret = sfunc(root, data); 148 ret = sfunc(root, node);
131 if (ret < 0) { 149 if (ret < 0) {
132 return ret; 150 return ret;
133 } else if (ret == 0 || tree_children(root) == NULL) { 151 } else if (ret == 0 || tree_children(root) == NULL) {
134 *result = (void*)root; 152 *result = (void*)root;
135 return ret; 153 return ret;
148 } 166 }
149 } 167 }
150 168
151 // remember a candidate for adding the data 169 // remember a candidate for adding the data
152 // also remember the exact return code from sfunc 170 // also remember the exact return code from sfunc
153 void *candidate = NULL; 171 void *candidate = (void *) root;
154 int ret_candidate = -1; 172 int ret_candidate = ret;
155 173
156 // process the working stack 174 // process the working stack
157 while (work_size > 0) { 175 while (work_size > 0) {
158 // pop element 176 // pop element
159 void const *node = work[--work_size]; 177 void const *elem = work[--work_size];
160 178
161 // apply the search function 179 // apply the search function
162 ret = sfunc(node, data); 180 ret = sfunc(elem, node);
163 181
164 if (ret == 0) { 182 if (ret == 0) {
165 // if found, exit the search 183 // if found, exit the search
166 *result = (void*) node; 184 *result = (void *) elem;
167 work_size = 0; 185 work_size = 0;
168 break; 186 break;
169 } else if (ret > 0) { 187 } else if (ret > 0) {
170 // if children might contain the data, add them to the stack 188 // if children might contain the data, add them to the stack
171 void *c = tree_children(node); 189 void *c = tree_children(elem);
172 while (c != NULL) { 190 while (c != NULL) {
173 cx_array_simple_add(work, c); 191 cx_array_simple_add(work, c);
174 c = tree_next(c); 192 c = tree_next(c);
175 } 193 }
176 194
177 // remember this node in case no child is suitable 195 // remember this node in case no child is suitable
178 if (ret_candidate < 0 || ret < ret_candidate) { 196 if (ret < ret_candidate) {
179 candidate = (void *) node; 197 candidate = (void *) elem;
180 ret_candidate = ret; 198 ret_candidate = ret;
181 } 199 }
182 } 200 }
183 } 201 }
184 202
189 } 207 }
190 208
191 // free the working queue and return 209 // free the working queue and return
192 free(work); 210 free(work);
193 return ret; 211 return ret;
212 }
213
214 int cx_tree_search_data(
215 void const *root,
216 void const *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);
194 } 228 }
195 229
196 static bool cx_tree_iter_valid(void const *it) { 230 static bool cx_tree_iter_valid(void const *it) {
197 struct cx_tree_iterator_s const *iter = it; 231 struct cx_tree_iterator_s const *iter = it;
198 return iter->node != NULL; 232 return iter->node != NULL;
424 iter.base.current = cx_tree_visitor_current; 458 iter.base.current = cx_tree_visitor_current;
425 459
426 return iter; 460 return iter;
427 } 461 }
428 462
463 static void cx_tree_add_link_duplicate(
464 void *original, void *duplicate,
465 ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
466 ptrdiff_t loc_prev, ptrdiff_t loc_next
467 ) {
468 void *shared_parent = tree_parent(original);
469 if (shared_parent == NULL) {
470 cx_tree_link(original, duplicate, cx_tree_ptr_locations);
471 } else {
472 cx_tree_link(shared_parent, duplicate, cx_tree_ptr_locations);
473 }
474 }
475
476 static void cx_tree_add_link_new(
477 void *parent, void *node, cx_tree_search_func sfunc,
478 ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
479 ptrdiff_t loc_prev, ptrdiff_t loc_next
480 ) {
481 // check the current children one by one,
482 // if they could be children of the new node
483 void *child = tree_children(parent);
484 while (child != NULL) {
485 void *next = tree_next(child);
486
487 if (sfunc(node, child) > 0) {
488 // the sibling could be a child -> re-link
489 cx_tree_link(node, child, cx_tree_ptr_locations);
490 }
491
492 child = next;
493 }
494
495 // add new node as new child
496 cx_tree_link(parent, node, cx_tree_ptr_locations);
497 }
498
499 int cx_tree_add(
500 void const *src,
501 cx_tree_search_func sfunc,
502 cx_tree_node_create_func cfunc,
503 void *cdata,
504 void **cnode,
505 void *root,
506 ptrdiff_t loc_parent,
507 ptrdiff_t loc_children,
508 ptrdiff_t loc_last_child,
509 ptrdiff_t loc_prev,
510 ptrdiff_t loc_next
511 ) {
512 *cnode = cfunc(src, cdata);
513 if (*cnode == NULL) return 1;
514 cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations);
515
516 void *match = NULL;
517 int result = cx_tree_search(
518 root,
519 *cnode,
520 sfunc,
521 &match,
522 loc_children,
523 loc_next
524 );
525
526 if (result < 0) {
527 // node does not fit into the tree - return non-zero value
528 return 1;
529 } else if (result == 0) {
530 // data already found in the tree, link duplicate
531 cx_tree_add_link_duplicate(match, *cnode, cx_tree_ptr_locations);
532 } else {
533 // closest match found, add new node
534 cx_tree_add_link_new(match, *cnode, sfunc, cx_tree_ptr_locations);
535 }
536
537 return 0;
538 }
539
540 unsigned int cx_tree_add_look_around_depth = 3;
541
542 size_t cx_tree_add_iter(
543 struct cx_iterator_base_s *iter,
544 cx_tree_search_func sfunc,
545 cx_tree_node_create_func cfunc,
546 void *cdata,
547 void **failed,
548 void *root,
549 ptrdiff_t loc_parent,
550 ptrdiff_t loc_children,
551 ptrdiff_t loc_last_child,
552 ptrdiff_t loc_prev,
553 ptrdiff_t loc_next
554 ) {
555 // erase the failed pointer
556 *failed = NULL;
557
558 // iter not valid? cancel...
559 if (!iter->valid(iter)) return 0;
560
561 size_t processed = 0;
562 void *current_node = root;
563 void const *elem;
564
565 for (void **eptr;
566 iter->valid(iter) && (eptr = iter->current(iter)) != NULL;
567 iter->next(iter)) {
568 elem = *eptr;
569
570 // create the new node
571 void *new_node = cfunc(elem, cdata);
572 if (new_node == NULL) return processed;
573 cx_tree_zero_pointers(new_node, cx_tree_ptr_locations);
574
575 // start searching from current node
576 void *match;
577 int result;
578 unsigned int look_around_retries = cx_tree_add_look_around_depth;
579 cx_tree_add_look_around_retry:
580 result = cx_tree_search(
581 current_node,
582 new_node,
583 sfunc,
584 &match,
585 loc_children,
586 loc_next
587 );
588
589 if (result < 0) {
590 // traverse upwards and try to find better parents
591 void *parent = tree_parent(current_node);
592 if (parent != NULL) {
593 if (look_around_retries > 0) {
594 look_around_retries--;
595 current_node = parent;
596 } else {
597 // look around retries exhausted, start from the root
598 current_node = root;
599 }
600 goto cx_tree_add_look_around_retry;
601 } else {
602 // no parents. so we failed
603 *failed = new_node;
604 return processed;
605 }
606 } else if (result == 0) {
607 // data already found in the tree, link duplicate
608 cx_tree_add_link_duplicate(match, new_node, cx_tree_ptr_locations);
609 // but stick with the original match, in case we needed a new root
610 current_node = match;
611 } else {
612 // closest match found, add new node as child
613 cx_tree_add_link_new(match, new_node, sfunc,
614 cx_tree_ptr_locations);
615 current_node = match;
616 }
617
618 processed++;
619 }
620 return processed;
621 }
622
623 size_t cx_tree_add_array(
624 void const *src,
625 size_t num,
626 size_t elem_size,
627 cx_tree_search_func sfunc,
628 cx_tree_node_create_func cfunc,
629 void *cdata,
630 void **failed,
631 void *root,
632 ptrdiff_t loc_parent,
633 ptrdiff_t loc_children,
634 ptrdiff_t loc_last_child,
635 ptrdiff_t loc_prev,
636 ptrdiff_t loc_next
637 ) {
638 // erase failed pointer
639 *failed = NULL;
640
641 // super special case: zero elements
642 if (num == 0) {
643 return 0;
644 }
645
646 // special case: one element does not need an iterator
647 if (num == 1) {
648 void *node;
649 if (0 == cx_tree_add(
650 src, sfunc, cfunc, cdata, &node, root,
651 loc_parent, loc_children, loc_last_child,
652 loc_prev, loc_next)) {
653 return 1;
654 } else {
655 *failed = node;
656 return 0;
657 }
658 }
659
660 // otherwise, create iterator and hand over to other function
661 CxIterator iter = cxIterator(src, elem_size, num);
662 return cx_tree_add_iter(cxIteratorRef(iter), sfunc,
663 cfunc, cdata, failed, root,
664 loc_parent, loc_children, loc_last_child,
665 loc_prev, loc_next);
666 }
667

mercurial