#include "cx/tree.h"
#include "cx/array_list.h"
#include <assert.h>
#define CX_TREE_PTR(cur, off) (*(
void**)(((
char*)(cur))+(off)))
#define tree_parent(node)
CX_TREE_PTR(node, loc_parent)
#define tree_children(node)
CX_TREE_PTR(node, loc_children)
#define tree_last_child(node)
CX_TREE_PTR(node, loc_last_child)
#define tree_prev(node)
CX_TREE_PTR(node, loc_prev)
#define tree_next(node)
CX_TREE_PTR(node, loc_next)
#define cx_tree_ptr_locations \
loc_parent, loc_children, loc_last_child, loc_prev, loc_next
static void cx_tree_zero_pointers(
void *node,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
tree_parent(node) =
NULL;
tree_prev(node) =
NULL;
tree_next(node) =
NULL;
tree_children(node) =
NULL;
if (loc_last_child >=
0) {
tree_last_child(node) =
NULL;
}
}
void cx_tree_link(
void *restrict parent,
void *restrict node,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
void *current_parent = tree_parent(node);
if (current_parent == parent)
return;
if (current_parent !=
NULL) {
cx_tree_unlink(node, cx_tree_ptr_locations);
}
if (tree_children(parent) ==
NULL) {
tree_children(parent) = node;
if (loc_last_child >=
0) {
tree_last_child(parent) = node;
}
}
else {
if (loc_last_child >=
0) {
void *child = tree_last_child(parent);
tree_prev(node) = child;
tree_next(child) = node;
tree_last_child(parent) = node;
}
else {
void *child = tree_children(parent);
void *next;
while ((next = tree_next(child)) !=
NULL) {
child = next;
}
tree_prev(node) = child;
tree_next(child) = node;
}
}
tree_parent(node) = parent;
}
void cx_tree_unlink(
void *node,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
if (tree_parent(node) ==
NULL)
return;
void *left = tree_prev(node);
void *right = tree_next(node);
void *parent = tree_parent(node);
assert(left ==
NULL || tree_children(parent) != node);
assert(right ==
NULL || loc_last_child <
0 ||
tree_last_child(parent) != node);
if (left ==
NULL) {
tree_children(parent) = right;
}
else {
tree_next(left) = right;
}
if (right ==
NULL) {
if (loc_last_child >=
0) {
tree_last_child(parent) = left;
}
}
else {
tree_prev(right) = left;
}
tree_parent(node) =
NULL;
tree_prev(node) =
NULL;
tree_next(node) =
NULL;
}
int cx_tree_search(
const void *root,
const void *node,
cx_tree_search_func sfunc,
void **result,
ptrdiff_t loc_children,
ptrdiff_t loc_next
) {
int ret;
*result =
NULL;
ret = sfunc(root, node);
if (ret <
0) {
return ret;
}
else if (ret ==
0 || tree_children(root) ==
NULL) {
*result = (
void*)root;
return ret;
}
CX_ARRAY_DECLARE(
const void *, work);
cx_array_initialize(work,
32);
{
void *c = tree_children(root);
while (c !=
NULL) {
cx_array_simple_add(work, c);
c = tree_next(c);
}
}
void *candidate = (
void *) root;
int ret_candidate = ret;
while (work_size >
0) {
const void *elem = work[--work_size];
ret = sfunc(elem, node);
if (ret ==
0) {
*result = (
void *) elem;
work_size =
0;
break;
}
else if (ret >
0) {
void *c = tree_children(elem);
while (c !=
NULL) {
cx_array_simple_add(work, c);
c = tree_next(c);
}
if (ret < ret_candidate) {
candidate = (
void *) elem;
ret_candidate = ret;
}
}
}
if (ret !=
0 && candidate !=
NULL) {
ret = ret_candidate;
*result = candidate;
}
free(work);
return ret;
}
int cx_tree_search_data(
const void *root,
const void *data,
cx_tree_search_data_func sfunc,
void **result,
ptrdiff_t loc_children,
ptrdiff_t loc_next
) {
return cx_tree_search(
root, data,
(cx_tree_search_func) sfunc,
result,
loc_children, loc_next);
}
static bool cx_tree_iter_valid(
const void *it) {
const struct cx_tree_iterator_s *iter = it;
return iter->node !=
NULL;
}
static void *cx_tree_iter_current(
const void *it) {
const struct cx_tree_iterator_s *iter = it;
return iter->node;
}
static void cx_tree_iter_next(
void *it) {
struct cx_tree_iterator_s *iter = it;
ptrdiff_t const loc_next = iter->loc_next;
ptrdiff_t const loc_children = iter->loc_children;
if (!iter->base.valid(iter))
return;
void *children;
if (iter->exiting) {
children =
NULL;
iter->skip = false;
}
else {
if (iter->skip) {
iter->skip = false;
children =
NULL;
}
else {
children = tree_children(iter->node);
}
}
if (children ==
NULL) {
void *next;
cx_tree_iter_search_next:
if (iter->exiting) {
next = iter->node_next;
}
else {
next = tree_next(iter->node);
iter->node_next = next;
}
if (next ==
NULL) {
if (iter->visit_on_exit && !iter->exiting) {
iter->exiting = true;
}
else {
iter->exiting = false;
if (iter->depth ==
1) {
iter->node = iter->node_next =
NULL;
iter->stack_capacity = iter->depth =
0;
free(iter->stack);
iter->stack =
NULL;
}
else {
iter->depth--;
iter->node = iter->stack[iter->depth -
1];
goto cx_tree_iter_search_next;
}
}
}
else {
if (iter->visit_on_exit && !iter->exiting) {
iter->exiting = true;
}
else {
iter->exiting = false;
iter->counter++;
iter->node = next;
iter->stack[iter->depth -
1] = next;
}
}
}
else {
cx_array_simple_add(iter->stack, children);
iter->node = children;
iter->counter++;
}
}
CxTreeIterator cx_tree_iterator(
void *root,
bool visit_on_exit,
ptrdiff_t loc_children,
ptrdiff_t loc_next
) {
CxTreeIterator iter;
iter.loc_children = loc_children;
iter.loc_next = loc_next;
iter.visit_on_exit = visit_on_exit;
iter.node_next =
NULL;
iter.exiting = false;
iter.skip = false;
iter.base.mutating = false;
iter.base.remove = false;
iter.base.current_impl =
NULL;
iter.base.valid = cx_tree_iter_valid;
iter.base.next = cx_tree_iter_next;
iter.base.current = cx_tree_iter_current;
iter.node = root;
if (root !=
NULL) {
iter.stack_capacity =
16;
iter.stack = malloc(
sizeof(
void *) *
16);
iter.stack[
0] = root;
iter.counter =
1;
iter.depth =
1;
}
else {
iter.stack_capacity =
0;
iter.stack =
NULL;
iter.counter =
0;
iter.depth =
0;
}
return iter;
}
static bool cx_tree_visitor_valid(
const void *it) {
const struct cx_tree_visitor_s *iter = it;
return iter->node !=
NULL;
}
static void *cx_tree_visitor_current(
const void *it) {
const struct cx_tree_visitor_s *iter = it;
return iter->node;
}
__attribute__((__nonnull__))
static void cx_tree_visitor_enqueue_siblings(
struct cx_tree_visitor_s *iter,
void *node,
ptrdiff_t loc_next) {
node = tree_next(node);
while (node !=
NULL) {
struct cx_tree_visitor_queue_s *q;
q = malloc(
sizeof(
struct cx_tree_visitor_queue_s));
q->depth = iter->queue_last->depth;
q->node = node;
iter->queue_last->next = q;
iter->queue_last = q;
node = tree_next(node);
}
iter->queue_last->next =
NULL;
}
static void cx_tree_visitor_next(
void *it) {
struct cx_tree_visitor_s *iter = it;
if (!iter->base.valid(iter))
return;
ptrdiff_t const loc_next = iter->loc_next;
ptrdiff_t const loc_children = iter->loc_children;
void *children;
if (iter->skip) {
iter->skip = false;
children =
NULL;
}
else {
children = tree_children(iter->node);
}
if (children !=
NULL) {
struct cx_tree_visitor_queue_s *q;
q = malloc(
sizeof(
struct cx_tree_visitor_queue_s));
q->depth = iter->depth +
1;
q->node = children;
if (iter->queue_last ==
NULL) {
assert(iter->queue_next ==
NULL);
iter->queue_next = q;
}
else {
iter->queue_last->next = q;
}
iter->queue_last = q;
cx_tree_visitor_enqueue_siblings(iter, children, loc_next);
}
if (iter->queue_next ==
NULL) {
iter->node =
NULL;
return;
}
iter->node = iter->queue_next->node;
iter->depth = iter->queue_next->depth;
{
struct cx_tree_visitor_queue_s *q = iter->queue_next;
iter->queue_next = q->next;
if (iter->queue_next ==
NULL) {
assert(iter->queue_last == q);
iter->queue_last =
NULL;
}
free(q);
}
iter->counter++;
}
CxTreeVisitor cx_tree_visitor(
void *root,
ptrdiff_t loc_children,
ptrdiff_t loc_next
) {
CxTreeVisitor iter;
iter.loc_children = loc_children;
iter.loc_next = loc_next;
iter.skip = false;
iter.queue_next =
NULL;
iter.queue_last =
NULL;
iter.base.mutating = false;
iter.base.remove = false;
iter.base.current_impl =
NULL;
iter.base.valid = cx_tree_visitor_valid;
iter.base.next = cx_tree_visitor_next;
iter.base.current = cx_tree_visitor_current;
iter.node = root;
if (root !=
NULL) {
iter.counter =
1;
iter.depth =
1;
}
else {
iter.counter =
0;
iter.depth =
0;
}
return iter;
}
static void cx_tree_add_link_duplicate(
void *original,
void *duplicate,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
void *shared_parent = tree_parent(original);
if (shared_parent ==
NULL) {
cx_tree_link(original, duplicate, cx_tree_ptr_locations);
}
else {
cx_tree_link(shared_parent, duplicate, cx_tree_ptr_locations);
}
}
static void cx_tree_add_link_new(
void *parent,
void *node, cx_tree_search_func sfunc,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
void *child = tree_children(parent);
while (child !=
NULL) {
void *next = tree_next(child);
if (sfunc(node, child) >
0) {
cx_tree_link(node, child, cx_tree_ptr_locations);
}
child = next;
}
cx_tree_link(parent, node, cx_tree_ptr_locations);
}
int cx_tree_add(
const void *src,
cx_tree_search_func sfunc,
cx_tree_node_create_func cfunc,
void *cdata,
void **cnode,
void *root,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
*cnode = cfunc(src, cdata);
if (*cnode ==
NULL)
return 1;
cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations);
void *match =
NULL;
int result = cx_tree_search(
root,
*cnode,
sfunc,
&match,
loc_children,
loc_next
);
if (result <
0) {
return 1;
}
else if (result ==
0) {
cx_tree_add_link_duplicate(match, *cnode, cx_tree_ptr_locations);
}
else {
cx_tree_add_link_new(match, *cnode, sfunc, cx_tree_ptr_locations);
}
return 0;
}
unsigned int cx_tree_add_look_around_depth =
3;
size_t cx_tree_add_iter(
struct cx_iterator_base_s *iter,
size_t num,
cx_tree_search_func sfunc,
cx_tree_node_create_func cfunc,
void *cdata,
void **failed,
void *root,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
*failed =
NULL;
if (!iter->valid(iter))
return 0;
size_t processed =
0;
void *current_node = root;
const void *elem;
for (
void **eptr; processed < num &&
iter->valid(iter) && (eptr = iter->current(iter)) !=
NULL;
iter->next(iter)) {
elem = *eptr;
void *new_node = cfunc(elem, cdata);
if (new_node ==
NULL)
return processed;
cx_tree_zero_pointers(new_node, cx_tree_ptr_locations);
void *match;
int result;
unsigned int look_around_retries = cx_tree_add_look_around_depth;
cx_tree_add_look_around_retry:
result = cx_tree_search(
current_node,
new_node,
sfunc,
&match,
loc_children,
loc_next
);
if (result <
0) {
void *parent = tree_parent(current_node);
if (parent !=
NULL) {
if (look_around_retries >
0) {
look_around_retries--;
current_node = parent;
}
else {
current_node = root;
}
goto cx_tree_add_look_around_retry;
}
else {
*failed = new_node;
return processed;
}
}
else if (result ==
0) {
cx_tree_add_link_duplicate(match, new_node, cx_tree_ptr_locations);
current_node = match;
}
else {
cx_tree_add_link_new(match, new_node, sfunc,
cx_tree_ptr_locations);
current_node = match;
}
processed++;
}
return processed;
}
size_t cx_tree_add_array(
const void *src,
size_t num,
size_t elem_size,
cx_tree_search_func sfunc,
cx_tree_node_create_func cfunc,
void *cdata,
void **failed,
void *root,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
*failed =
NULL;
if (num ==
0) {
return 0;
}
if (num ==
1) {
void *node;
if (
0 == cx_tree_add(
src, sfunc, cfunc, cdata, &node, root,
loc_parent, loc_children, loc_last_child,
loc_prev, loc_next)) {
return 1;
}
else {
*failed = node;
return 0;
}
}
CxIterator iter = cxIterator(src, elem_size, num);
return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc,
cfunc, cdata, failed, root,
loc_parent, loc_children, loc_last_child,
loc_prev, loc_next);
}
static void cx_tree_default_destructor(CxTree *tree) {
if (tree->simple_destructor !=
NULL || tree->advanced_destructor !=
NULL) {
CxTreeIterator iter = tree->cl->iterator(tree, true);
cx_foreach(
void *, node, iter) {
if (iter.exiting) {
if (tree->simple_destructor) {
tree->simple_destructor(node);
}
if (tree->advanced_destructor) {
tree->advanced_destructor(tree->destructor_data, node);
}
}
}
}
cxFree(tree->allocator, tree);
}
static CxTreeIterator cx_tree_default_iterator(
CxTree *tree,
bool visit_on_exit
) {
return cx_tree_iterator(
tree->root, visit_on_exit,
tree->loc_children, tree->loc_next
);
}
static CxTreeVisitor cx_tree_default_visitor(CxTree *tree) {
return cx_tree_visitor(tree->root, tree->loc_children, tree->loc_next);
}
static int cx_tree_default_insert_element(
CxTree *tree,
const void *data
) {
void *node;
if (tree->root ==
NULL) {
node = tree->node_create(data, tree);
if (node ==
NULL)
return 1;
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
tree->root = node;
tree->size =
1;
return 0;
}
int result = cx_tree_add(data, tree->search, tree->node_create,
tree, &node, tree->root, cx_tree_node_layout(tree));
if (
0 == result) {
tree->size++;
}
else {
cxFree(tree->allocator, node);
}
return result;
}
static size_t cx_tree_default_insert_many(
CxTree *tree,
struct cx_iterator_base_s *iter,
size_t n
) {
size_t ins =
0;
if (!iter->valid(iter))
return 0;
if (tree->root ==
NULL) {
void **eptr = iter->current(iter);
void *node = tree->node_create(*eptr, tree);
if (node ==
NULL)
return 0;
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
tree->root = node;
ins =
1;
iter->next(iter);
}
void *failed;
ins += cx_tree_add_iter(iter, n, tree->search, tree->node_create,
tree, &failed, tree->root, cx_tree_node_layout(tree));
tree->size += ins;
if (ins < n) {
cxFree(tree->allocator, failed);
}
return ins;
}
static void *cx_tree_default_find(
CxTree *tree,
const void *subtree,
const void *data
) {
if (tree->root ==
NULL)
return NULL;
void *found;
if (
0 == cx_tree_search_data(
subtree,
data,
tree->search_data,
&found,
tree->loc_children,
tree->loc_next
)) {
return found;
}
else {
return NULL;
}
}
static cx_tree_class cx_tree_default_class = {
cx_tree_default_destructor,
cx_tree_default_insert_element,
cx_tree_default_insert_many,
cx_tree_default_find,
cx_tree_default_iterator,
cx_tree_default_visitor
};
CxTree *cxTreeCreate(
const CxAllocator *allocator,
cx_tree_node_create_func create_func,
cx_tree_search_func search_func,
cx_tree_search_data_func search_data_func,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
CxTree *tree = cxMalloc(allocator,
sizeof(CxTree));
if (tree ==
NULL)
return NULL;
tree->cl = &cx_tree_default_class;
tree->allocator = allocator;
tree->node_create = create_func;
tree->search = search_func;
tree->search_data = search_data_func;
tree->advanced_destructor = (cx_destructor_func2) cxFree;
tree->destructor_data = (
void *) allocator;
tree->loc_parent = loc_parent;
tree->loc_children = loc_children;
tree->loc_last_child = loc_last_child;
tree->loc_prev = loc_prev;
tree->loc_next = loc_next;
tree->root =
NULL;
tree->size =
0;
return tree;
}
CxTree *cxTreeCreateWrapped(
const CxAllocator *allocator,
void *root,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
ptrdiff_t loc_last_child,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
CxTree *tree = cxMalloc(allocator,
sizeof(CxTree));
if (tree ==
NULL)
return NULL;
tree->cl = &cx_tree_default_class;
tree->allocator = allocator;
tree->node_create =
NULL;
tree->search =
NULL;
tree->search_data =
NULL;
tree->simple_destructor =
NULL;
tree->advanced_destructor =
NULL;
tree->destructor_data =
NULL;
tree->loc_parent = loc_parent;
tree->loc_children = loc_children;
tree->loc_last_child = loc_last_child;
tree->loc_prev = loc_prev;
tree->loc_next = loc_next;
tree->root = root;
tree->size = cxTreeSubtreeSize(tree, root);
return tree;
}
int cxTreeAddChild(
CxTree *tree,
void *parent,
const void *data) {
void *node = tree->node_create(data, tree);
if (node ==
NULL)
return 1;
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
cx_tree_link(parent, node, cx_tree_node_layout(tree));
tree->size++;
return 0;
}
size_t cxTreeSubtreeSize(CxTree *tree,
void *subtree_root) {
CxTreeVisitor visitor = cx_tree_visitor(
subtree_root,
tree->loc_children,
tree->loc_next
);
while (cxIteratorValid(visitor)) {
cxIteratorNext(visitor);
}
return visitor.counter;
}
size_t cxTreeSubtreeDepth(CxTree *tree,
void *subtree_root) {
CxTreeVisitor visitor = cx_tree_visitor(
subtree_root,
tree->loc_children,
tree->loc_next
);
while (cxIteratorValid(visitor)) {
cxIteratorNext(visitor);
}
return visitor.depth;
}
size_t cxTreeDepth(CxTree *tree) {
CxTreeVisitor visitor = tree->cl->visitor(tree);
while (cxIteratorValid(visitor)) {
cxIteratorNext(visitor);
}
return visitor.depth;
}
int cxTreeRemove(
CxTree *tree,
void *node,
cx_tree_relink_func relink_func
) {
if (node == tree->root)
return 1;
ptrdiff_t loc_parent = tree->loc_parent;
void *new_parent = tree_parent(node);
cx_tree_unlink(node, cx_tree_node_layout(tree));
ptrdiff_t loc_children = tree->loc_children;
ptrdiff_t loc_next = tree->loc_next;
void *child = tree_children(node);
while (child !=
NULL) {
tree_parent(child) =
NULL;
if (relink_func !=
NULL) {
relink_func(child, node, new_parent);
}
cx_tree_link(new_parent, child, cx_tree_node_layout(tree));
child = tree_next(child);
}
tree_children(node) =
NULL;
ptrdiff_t loc_last_child = tree->loc_last_child;
if (loc_last_child >=
0) tree_last_child(node) =
NULL;
tree->size--;
return 0;
}
void cxTreeRemoveSubtree(CxTree *tree,
void *node) {
if (node == tree->root) {
tree->root =
NULL;
tree->size =
0;
return;
}
size_t subtree_size = cxTreeSubtreeSize(tree, node);
cx_tree_unlink(node, cx_tree_node_layout(tree));
tree->size -= subtree_size;
}