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, |
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 |
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 |