diff --git a/src/tree.c b/src/tree.c new file mode 100644 index 0000000..5ac2a6f --- /dev/null +++ b/src/tree.c @@ -0,0 +1,119 @@ +/* tree.c - N-tree implementation + * + * Copyright (C) 2011 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Generic implementation of a N-tree + */ + +#include "xalloc.h" +#include "tree.h" + +struct tree* tree_new() { + + return xmallocz(sizeof(struct tree)); +} + +struct tree* tree_link(struct tree *parent, struct tree *tree) { + + if (parent->child) { + struct tree *it = parent->child; + while(it->next) + it = it->next; + it->next = tree; + } else { + parent->child = tree; + } + tree->parent = parent; + return tree; +} + +void tree_unlink(struct tree *tree) { + + if (tree_is_root(tree)) + return; + + /* remove the node from the tree */ + if (tree->parent->child == tree) { + tree->parent->child = tree->next; + } else { + struct tree *prev = tree->parent->child; + while(prev->next && prev->next != tree) + prev = prev->next; + prev->next = tree->next; + } + + /* move the children to node's old parent */ + if (tree->child) { + struct tree *it = tree->parent->child; + if (tree->parent->child) { + while(it->next) + it = it->next; + it->next = tree->child; + } else { + tree->parent->child = tree->child; + } + + /* Update parent */ + for(it = tree->child; it; it = it->next) + it->parent = tree->parent; + } + + tree->parent = NULL; + tree->next = NULL; + tree->child = NULL; +} + +void tree_move(struct tree *dest, struct tree *src) { + + if (src->parent == dest) + /* node are already in place */ + return; + + /* detach and attach att the new node */ + tree_detach(src); + tree_link(dest, src); +} + +void tree_detach(struct tree *tree) { + + if (tree_is_root(tree)) + return; + + if (tree->parent->child == tree) { + tree->parent->child = tree->next; + } else if (tree->parent->child) { + struct tree *prev = tree->parent->child; + while(prev->next && prev->next != tree) + prev = prev->next; + prev->next = tree->next; + } + tree->parent = NULL; + tree->next = NULL; +} + +void tree_traverse(struct tree *tree, tree_traverse_fn fn, void *data) { + + while(tree) { + struct tree *next = tree->next; + fn(tree, data); + if (tree->child) + tree_traverse(tree->child, fn, data); + tree = next; + } +} + +size_t tree_parent_count(struct tree *tree) { + + size_t count = 0; + + if (tree) { + for(; tree->parent; count++) + tree = tree->parent; + } + return count; +} diff --git a/src/tree.h b/src/tree.h new file mode 100644 index 0000000..164257d --- /dev/null +++ b/src/tree.h @@ -0,0 +1,48 @@ +/* tree.c - N-tree implementation + * + * Copyright (C) 2011 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef __TREE_H +#define __TREE_H + +#include + +struct tree { + struct tree *next; + struct tree *parent; + struct tree *child; +}; + +typedef void (*tree_traverse_fn)(struct tree *tree, void *data); + +#define tree_is_root(n) \ + ((n) && \ + (n)->parent == NULL && \ + (n)->next == NULL) + +#define tree_is_leaf(n) \ + ((n) && (n)->child == NULL) + +#define TREE_INIT { NULL } + +struct tree* tree_new(); + +struct tree* tree_link(struct tree *parent, struct tree *tree); + +void tree_unlink(struct tree *tree); + +void tree_move(struct tree *dest, struct tree *src); + +void tree_detach(struct tree *tree); + +void tree_traverse(struct tree *tree, tree_traverse_fn fn, void *data); + +size_t tree_parent_count(struct tree *tree); + +#endif /* __TREE_H */ diff --git a/test/Makefile b/test/Makefile index 05fc3ab..1a44ecd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -21,6 +21,7 @@ fscrawl : $(ROOT)src/fscrawl.o $(addprefix $(ROOT),$(obj-fscrawl)) notify : $(addprefix $(ROOT),$(obj-notify)) inotify-map : $(addprefix $(ROOT),$(obj-inotify-map)) log : $(addprefix $(ROOT),$(obj-log)) +tree : $(addprefix $(ROOT),$(obj-xalloc)) $(ROOT)src/tree.o str-list : unit.c $(addprefix $(ROOT),$(obj-str-list)) clean : diff --git a/test/t_tree.c b/test/t_tree.c new file mode 100644 index 0000000..727d426 --- /dev/null +++ b/test/t_tree.c @@ -0,0 +1,228 @@ + +#include +#include "../src/tree.h" + +void test_link() { + + struct tree nodes[6] = { TREE_INIT }; + + tree_link(&nodes[0], &nodes[1]); + tree_link(&nodes[0], &nodes[2]); + tree_link(&nodes[0], &nodes[3]); + + tree_link(&nodes[1], &nodes[4]); + tree_link(&nodes[2], &nodes[5]); + + /* check the root node */ + assert(nodes[0].parent == NULL); + assert(nodes[0].next == NULL); + assert(nodes[0].child == &nodes[1]); + + /* move to depth 2 */ + assert(nodes[1].parent == &nodes[0]); + assert(nodes[2].parent == &nodes[0]); + assert(nodes[3].parent == &nodes[0]); + assert(nodes[1].next == &nodes[2]); + assert(nodes[2].next == &nodes[3]); + assert(nodes[3].next == NULL); + assert(nodes[1].child == &nodes[4]); + assert(nodes[2].child == &nodes[5]); + + /* move to depth 3 */ + assert(nodes[4].parent == &nodes[1]); + assert(nodes[5].parent == &nodes[2]); + assert(nodes[4].next == NULL); + assert(nodes[5].next == NULL); + assert(nodes[4].child == NULL); + assert(nodes[5].child == NULL); +} + +void test_unlink() { + + struct tree nodes[6] = { TREE_INIT }; + + tree_link(&nodes[0], &nodes[1]); + tree_link(&nodes[0], &nodes[2]); + + tree_link(&nodes[1], &nodes[3]); + tree_link(&nodes[1], &nodes[4]); + tree_link(&nodes[2], &nodes[5]); + + /* check the tree */ + assert(nodes[0].parent == NULL); + assert(nodes[0].next == NULL); + assert(nodes[0].child == &nodes[1]); + + /* move to depth 2 */ + assert(nodes[1].parent == &nodes[0]); + assert(nodes[2].parent == &nodes[0]); + assert(nodes[1].next == &nodes[2]); + assert(nodes[2].next == NULL); + assert(nodes[1].child == &nodes[3]); + assert(nodes[2].child == &nodes[5]); + + /* move to depth 3 */ + assert(nodes[3].parent == &nodes[1]); + assert(nodes[4].parent == &nodes[1]); + assert(nodes[5].parent == &nodes[2]); + assert(nodes[3].next == &nodes[4]); + assert(nodes[4].next == NULL); + assert(nodes[5].next == NULL); + assert(nodes[3].child == NULL); + assert(nodes[4].child == NULL); + assert(nodes[5].child == NULL); + + tree_unlink(&nodes[1]); + + /* check the new tree */ + assert(nodes[0].parent == NULL); + assert(nodes[0].next == NULL); + assert(nodes[0].child == &nodes[2]); + + /* move to depth 2 */ + assert(nodes[2].parent == &nodes[0]); + assert(nodes[3].parent == &nodes[0]); + assert(nodes[4].parent == &nodes[0]); + assert(nodes[2].next == &nodes[3]); + assert(nodes[3].next == &nodes[4]); + assert(nodes[4].next == NULL); + assert(nodes[2].child == &nodes[5]); + assert(nodes[3].child == NULL); + assert(nodes[4].child == NULL); + + /* move to depth 3 */ + assert(nodes[5].parent == &nodes[2]); + assert(nodes[5].next == NULL); + assert(nodes[5].child == NULL); +} + +void test_detach() { + + struct tree nodes[6] = { TREE_INIT }; + + tree_link(&nodes[0], &nodes[1]); + tree_link(&nodes[0], &nodes[2]); + + tree_link(&nodes[1], &nodes[3]); + tree_link(&nodes[1], &nodes[4]); + tree_link(&nodes[2], &nodes[5]); + + /* check the tree */ + assert(nodes[0].parent == NULL); + assert(nodes[0].next == NULL); + assert(nodes[0].child == &nodes[1]); + + /* move to depth 2 */ + assert(nodes[1].parent == &nodes[0]); + assert(nodes[2].parent == &nodes[0]); + assert(nodes[1].next == &nodes[2]); + assert(nodes[2].next == NULL); + assert(nodes[1].child == &nodes[3]); + assert(nodes[2].child == &nodes[5]); + + /* move to depth 3 */ + assert(nodes[3].parent == &nodes[1]); + assert(nodes[4].parent == &nodes[1]); + assert(nodes[5].parent == &nodes[2]); + assert(nodes[3].next == &nodes[4]); + assert(nodes[4].next == NULL); + assert(nodes[5].next == NULL); + assert(nodes[3].child == NULL); + assert(nodes[4].child == NULL); + assert(nodes[5].child == NULL); + + tree_detach(&nodes[1]); + + /* check the tree */ + assert(nodes[0].parent == NULL); + assert(nodes[0].next == NULL); + assert(nodes[0].child == &nodes[2]); + + /* move to depth 2 */ + assert(nodes[2].parent == &nodes[0]); + assert(nodes[2].next == NULL); + assert(nodes[2].child == &nodes[5]); + + /* move to depth 3 */ + assert(nodes[5].parent == &nodes[2]); + assert(nodes[5].next == NULL); + assert(nodes[5].child == NULL); + + /* detached tree */ + assert(nodes[1].parent == NULL); + assert(nodes[1].next == NULL); + assert(nodes[1].child == &nodes[3]); + + assert(nodes[3].parent == &nodes[1]); + assert(nodes[4].parent == &nodes[1]); + assert(nodes[3].next == &nodes[4]); + assert(nodes[4].next == NULL); + assert(nodes[3].child == NULL); + assert(nodes[4].child == NULL); +} + +void test_move() { + + struct tree nodes[7] = { TREE_INIT }; + + tree_link(&nodes[0], &nodes[1]); + tree_link(&nodes[0], &nodes[2]); + tree_link(&nodes[0], &nodes[3]); + + tree_link(&nodes[1], &nodes[4]); + tree_link(&nodes[2], &nodes[5]); + tree_link(&nodes[2], &nodes[6]); + + tree_move(&nodes[5], &nodes[1]); + + assert(nodes[0].parent == NULL); + assert(nodes[0].next == NULL); + assert(nodes[0].child == &nodes[2]); + + assert(nodes[3].parent == &nodes[0]); + assert(nodes[2].parent == &nodes[0]); + assert(nodes[2].next == &nodes[3]); + assert(nodes[3].next == NULL); + assert(nodes[2].child == &nodes[5]); + assert(nodes[3].child == NULL); + + assert(nodes[5].parent == &nodes[2]); + assert(nodes[6].parent == &nodes[2]); + assert(nodes[5].next == &nodes[6]); + assert(nodes[6].next == NULL); + assert(nodes[5].child == &nodes[1]); + assert(nodes[6].child == NULL); + + assert(nodes[1].parent == &nodes[5]); + assert(nodes[1].next == NULL); + assert(nodes[1].child == &nodes[4]); + + assert(nodes[4].parent == &nodes[1]); + assert(nodes[4].next == NULL); + assert(nodes[4].child == NULL); +} + +void test_parent_count() { + + struct tree nodes[6] = { TREE_INIT }; + + tree_link(&nodes[0], &nodes[1]); + tree_link(&nodes[0], &nodes[2]); + + tree_link(&nodes[1], &nodes[3]); + tree_link(&nodes[1], &nodes[4]); + tree_link(&nodes[2], &nodes[5]); + + assert(tree_parent_count(&nodes[5]) == 2); +} + +int main() { + + test_link(); + test_unlink(); + test_detach(); + test_move(); + test_parent_count(); + + return 0; +}