378 lines
10 KiB
C
378 lines
10 KiB
C
/// MentOS, The Mentoring Operating system project
|
|
/// @file ndtree.c
|
|
/// @brief Red/Black tree.
|
|
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
|
/// See LICENSE.md for details.
|
|
|
|
#include "debug.h"
|
|
#include "ndtree.h"
|
|
#include "assert.h"
|
|
#include "list_head.h"
|
|
#include "slab.h"
|
|
|
|
// ============================================================================
|
|
// Tree types.
|
|
|
|
/// @brief Stores data about an NDTree node.
|
|
struct ndtree_node_t {
|
|
/// User provided, used indirectly via ndtree_tree_cmp_f.
|
|
void *value;
|
|
/// Pointer to the parent.
|
|
ndtree_node_t *parent;
|
|
/// List of siblings.
|
|
list_head siblings;
|
|
/// List of children.
|
|
list_head children;
|
|
};
|
|
|
|
/// @brief Stores data about an NDTree.
|
|
struct ndtree_t {
|
|
/// Comparison function.
|
|
ndtree_tree_cmp_f cmp;
|
|
/// Size of the tree.
|
|
size_t size;
|
|
/// Pointer to the root node.
|
|
ndtree_node_t *root;
|
|
/// List of orphans.
|
|
list_head orphans;
|
|
};
|
|
|
|
/// @brief Stores data about an NDTree iterator.
|
|
struct ndtree_iter_t {
|
|
/// Pointer to the head of the list.
|
|
list_head *head;
|
|
/// Pointer to the current element of the list.
|
|
list_head *current;
|
|
};
|
|
|
|
// ============================================================================
|
|
// Default Comparison functions.
|
|
static inline int __ndtree_tree_node_cmp_ptr_cb(ndtree_t *self, void *a, void *b)
|
|
{
|
|
return (a > b) - (a < b);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Node management functions.
|
|
|
|
ndtree_node_t *ndtree_node_alloc()
|
|
{
|
|
return kmalloc(sizeof(ndtree_node_t));
|
|
}
|
|
|
|
ndtree_node_t *ndtree_node_create(void *value)
|
|
{
|
|
ndtree_node_t *node = ndtree_node_alloc();
|
|
node = ndtree_node_init(node, value);
|
|
return node;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value)
|
|
{
|
|
if (node) {
|
|
node->value = value;
|
|
node->parent = NULL;
|
|
list_head_init(&node->siblings);
|
|
list_head_init(&node->children);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
void ndtree_node_set_value(ndtree_node_t *node, void *value)
|
|
{
|
|
if (node && value) {
|
|
node->value = value;
|
|
}
|
|
}
|
|
|
|
void *ndtree_node_get_value(ndtree_node_t *node)
|
|
{
|
|
if (node)
|
|
return node->value;
|
|
return NULL;
|
|
}
|
|
|
|
void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node)
|
|
{
|
|
tree->root = node;
|
|
++tree->size;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value)
|
|
{
|
|
ndtree_node_t *node = ndtree_node_create(value);
|
|
ndtree_set_root(tree, node);
|
|
return node;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_get_root(ndtree_t *tree)
|
|
{
|
|
return tree->root;
|
|
}
|
|
|
|
void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child)
|
|
{
|
|
child->parent = parent;
|
|
list_head_add(&child->siblings, &parent->children);
|
|
++tree->size;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value)
|
|
{
|
|
ndtree_node_t *child = ndtree_node_create(value);
|
|
ndtree_add_child_to_node(tree, parent, child);
|
|
return child;
|
|
}
|
|
|
|
unsigned int ndtree_node_count_children(ndtree_node_t *node)
|
|
{
|
|
unsigned int children = 0;
|
|
if (node) {
|
|
list_for_each_decl(it, &node->children)
|
|
{
|
|
++children;
|
|
}
|
|
}
|
|
return children;
|
|
}
|
|
|
|
void ndtree_node_dealloc(ndtree_node_t *node)
|
|
{
|
|
if (node)
|
|
kfree(node);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Tree management functions.
|
|
ndtree_t *ndtree_tree_alloc()
|
|
{
|
|
return kmalloc(sizeof(ndtree_t));
|
|
}
|
|
|
|
ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp)
|
|
{
|
|
return ndtree_tree_init(ndtree_tree_alloc(), cmp);
|
|
}
|
|
|
|
ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f node_cmp_cb)
|
|
{
|
|
if (tree) {
|
|
tree->size = 0;
|
|
tree->cmp = node_cmp_cb ? node_cmp_cb : __ndtree_tree_node_cmp_ptr_cb;
|
|
tree->root = NULL;
|
|
}
|
|
return tree;
|
|
}
|
|
|
|
static void __ndtree_tree_dealloc_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb)
|
|
{
|
|
if (node && node_cb) {
|
|
if (!list_head_empty(&node->children)) {
|
|
list_head *it_save;
|
|
list_for_each_decl(it, &node->children)
|
|
{
|
|
ndtree_node_t *entry = list_entry(it, ndtree_node_t, siblings);
|
|
it_save = it->prev;
|
|
list_head_del(it);
|
|
it = it_save;
|
|
__ndtree_tree_dealloc_rec(tree, entry, node_cb);
|
|
}
|
|
}
|
|
node_cb(tree, node);
|
|
kfree(node);
|
|
}
|
|
}
|
|
|
|
void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb)
|
|
{
|
|
if (tree && tree->root && node_cb)
|
|
__ndtree_tree_dealloc_rec(tree, tree->root, node_cb);
|
|
kfree(tree);
|
|
}
|
|
|
|
static ndtree_node_t *__ndtree_tree_find_rec(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value, ndtree_node_t *node)
|
|
{
|
|
ndtree_node_t *result = NULL;
|
|
if (tree && cmp && node && value) {
|
|
if (cmp(tree, node->value, value) == 0) {
|
|
result = node;
|
|
} else if (!list_head_empty(&node->children)) {
|
|
list_for_each_decl(it, &node->children)
|
|
{
|
|
ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings);
|
|
if ((result = __ndtree_tree_find_rec(tree, cmp, value, child)) != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value)
|
|
{
|
|
if (tree && tree->root && value)
|
|
return __ndtree_tree_find_rec(tree, cmp, value, tree->root);
|
|
return NULL;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value)
|
|
{
|
|
if (tree && node && value) {
|
|
// Check only if the node has children.
|
|
if (!list_head_empty(&node->children)) {
|
|
// Check which compare function we need to use.
|
|
ndtree_tree_cmp_f cmp_fun = cmp ? cmp : tree->cmp;
|
|
// If neither the tree nor the function argument are valid, rollback to the
|
|
// default comparison function.
|
|
if (cmp_fun == NULL)
|
|
cmp_fun = __ndtree_tree_node_cmp_ptr_cb;
|
|
// Iterate throught the children.
|
|
list_for_each_decl(it, &node->children)
|
|
{
|
|
ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings);
|
|
if (cmp_fun(tree, child->value, value) == 0)
|
|
return child;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int ndtree_tree_size(ndtree_t *tree)
|
|
{
|
|
if (tree)
|
|
return tree->size;
|
|
return 0;
|
|
}
|
|
|
|
int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb)
|
|
{
|
|
if (tree && node) {
|
|
// Remove the node from the parent list.
|
|
list_head_del(&node->siblings);
|
|
// If the node has children, we need to migrate them.
|
|
if (!list_head_empty(&node->children)) {
|
|
// The new parent, by default it is NULL.
|
|
ndtree_node_t *new_parent = NULL;
|
|
// The new list, by default it is the list of orphans of the tree.
|
|
list_head *new_list = &tree->orphans;
|
|
// If the found node has a parent, we need to set the variables
|
|
// so that we can migrate the children.
|
|
if (node->parent) {
|
|
new_parent = node->parent;
|
|
new_list = &node->parent->children;
|
|
}
|
|
// Migrate the children.
|
|
list_for_each_decl(it, &node->children)
|
|
{
|
|
ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings);
|
|
child->parent = new_parent;
|
|
}
|
|
// Merge the lists.
|
|
list_head_merge(new_list, &node->children);
|
|
}
|
|
if (node_cb)
|
|
node_cb(tree, node);
|
|
else
|
|
ndtree_node_dealloc(node);
|
|
--tree->size;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb)
|
|
{
|
|
if (tree && value) {
|
|
ndtree_node_t *node = ndtree_tree_find(tree, tree->cmp, value);
|
|
return ndtree_tree_remove_node_with_cb(tree, node, node_cb);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Iterators.
|
|
ndtree_iter_t *ndtree_iter_alloc()
|
|
{
|
|
ndtree_iter_t *iter = kmalloc(sizeof(ndtree_iter_t));
|
|
iter->head = NULL;
|
|
iter->current = NULL;
|
|
return iter;
|
|
}
|
|
|
|
void ndtree_iter_dealloc(ndtree_iter_t *iter)
|
|
{
|
|
if (iter)
|
|
kfree(iter);
|
|
}
|
|
|
|
ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter)
|
|
{
|
|
if (node && iter) {
|
|
if (!list_head_empty(&node->children)) {
|
|
iter->head = &node->children;
|
|
iter->current = iter->head->next;
|
|
return list_entry(iter->current, ndtree_node_t, siblings);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter)
|
|
{
|
|
if (node && iter) {
|
|
if (!list_head_empty(&node->children)) {
|
|
iter->head = &node->children;
|
|
iter->current = iter->head->prev;
|
|
return list_entry(iter->current, ndtree_node_t, siblings);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter)
|
|
{
|
|
if (iter) {
|
|
if (iter->current->next != iter->head) {
|
|
iter->current = iter->current->next;
|
|
return list_entry(iter->current, ndtree_node_t, siblings);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter)
|
|
{
|
|
if (iter) {
|
|
if (iter->current->next != iter->head) {
|
|
iter->current = iter->current->prev;
|
|
return list_entry(iter->current, ndtree_node_t, siblings);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Tree debugging functions.
|
|
static void __ndtree_tree_visitor_iter(ndtree_t *tree,
|
|
ndtree_node_t *node,
|
|
ndtree_tree_node_f enter_fun,
|
|
ndtree_tree_node_f exit_fun)
|
|
{
|
|
assert(tree);
|
|
assert(node);
|
|
if (enter_fun)
|
|
enter_fun(tree, node);
|
|
if (!list_head_empty(&node->children))
|
|
list_for_each_decl(it, &node->children)
|
|
__ndtree_tree_visitor_iter(tree, list_entry(it, ndtree_node_t, siblings), enter_fun, exit_fun);
|
|
if (exit_fun)
|
|
exit_fun(tree, node);
|
|
}
|
|
|
|
void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun)
|
|
{
|
|
if (tree && tree->root)
|
|
__ndtree_tree_visitor_iter(tree, tree->root, enter_fun, exit_fun);
|
|
} |