mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Add tommytree with bound searches, inlined helpers, and tail-recursion elimination
This commit is contained in:
parent
3bddc127f0
commit
d7e06271d9
2 changed files with 680 additions and 212 deletions
|
|
@ -3,262 +3,359 @@
|
|||
|
||||
#include "tommytree.h"
|
||||
|
||||
#include <assert.h> /* for assert */
|
||||
|
||||
/******************************************************************************/
|
||||
/* tree */
|
||||
|
||||
TOMMY_API void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp)
|
||||
/**
|
||||
* The parent pointer and the AVL balance factor share the index field.
|
||||
* tommy_tree_node is pointer aligned, leaving its two least significant bits
|
||||
* available for the balance factor.
|
||||
*/
|
||||
#define TOMMY_TREE_BALANCE_MASK ((tommy_size_t)3)
|
||||
#define TOMMY_TREE_BALANCE_LEFT ((tommy_size_t)1)
|
||||
#define TOMMY_TREE_BALANCE_RIGHT ((tommy_size_t)2)
|
||||
|
||||
tommy_inline int tommy_tree_balance_get(tommy_tree_node* node)
|
||||
{
|
||||
tree->root = 0;
|
||||
tree->count = 0;
|
||||
tree->cmp = cmp;
|
||||
tommy_size_t balance = node->index & TOMMY_TREE_BALANCE_MASK;
|
||||
|
||||
if (balance == TOMMY_TREE_BALANCE_LEFT)
|
||||
return -1;
|
||||
if (balance == TOMMY_TREE_BALANCE_RIGHT)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static tommy_ssize_t tommy_tree_delta(tommy_tree_node* root)
|
||||
tommy_inline void tommy_tree_balance_set(tommy_tree_node* node, int balance)
|
||||
{
|
||||
tommy_ssize_t left_height = root->prev ? root->prev->index : 0;
|
||||
tommy_ssize_t right_height = root->next ? root->next->index : 0;
|
||||
tommy_size_t value = 0;
|
||||
|
||||
return left_height - right_height;
|
||||
if (balance < 0)
|
||||
value = TOMMY_TREE_BALANCE_LEFT;
|
||||
else if (balance > 0)
|
||||
value = TOMMY_TREE_BALANCE_RIGHT;
|
||||
|
||||
node->index = (node->index & ~TOMMY_TREE_BALANCE_MASK) | value;
|
||||
}
|
||||
|
||||
/* AVL tree operations */
|
||||
static tommy_tree_node* tommy_tree_balance(tommy_tree_node*);
|
||||
tommy_inline void tommy_tree_parent_set(tommy_tree_node* node, tommy_tree_node* parent)
|
||||
{
|
||||
node->index = (tommy_size_t)(tommy_uintptr_t)parent | (node->index & TOMMY_TREE_BALANCE_MASK);
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_rotate_left(tommy_tree_node* root)
|
||||
tommy_inline void tommy_tree_replace(tommy_tree* tree, tommy_tree_node* root, tommy_tree_node* node)
|
||||
{
|
||||
tommy_tree_node* parent = tommy_tree_parent(root);
|
||||
|
||||
if (!parent)
|
||||
tree->root = node;
|
||||
else if (parent->prev == root)
|
||||
parent->prev = node;
|
||||
else {
|
||||
parent->next = node;
|
||||
}
|
||||
|
||||
if (node)
|
||||
tommy_tree_parent_set(node, parent);
|
||||
}
|
||||
|
||||
tommy_inline tommy_tree_node* tommy_tree_rotate_left(tommy_tree* tree, tommy_tree_node* root)
|
||||
{
|
||||
tommy_tree_node* next = root->next;
|
||||
|
||||
tommy_tree_replace(tree, root, next);
|
||||
root->next = next->prev;
|
||||
if (root->next)
|
||||
tommy_tree_parent_set(root->next, root);
|
||||
|
||||
next->prev = tommy_tree_balance(root);
|
||||
next->prev = root;
|
||||
tommy_tree_parent_set(root, next);
|
||||
|
||||
return tommy_tree_balance(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_rotate_right(tommy_tree_node* root)
|
||||
tommy_inline tommy_tree_node* tommy_tree_rotate_right(tommy_tree* tree, tommy_tree_node* root)
|
||||
{
|
||||
tommy_tree_node* prev = root->prev;
|
||||
|
||||
tommy_tree_replace(tree, root, prev);
|
||||
root->prev = prev->next;
|
||||
if (root->prev)
|
||||
tommy_tree_parent_set(root->prev, root);
|
||||
|
||||
prev->next = tommy_tree_balance(root);
|
||||
prev->next = root;
|
||||
tommy_tree_parent_set(root, prev);
|
||||
|
||||
return tommy_tree_balance(prev);
|
||||
return prev;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_move_right(tommy_tree_node* root, tommy_tree_node* node)
|
||||
tommy_inline void tommy_tree_insert_balance(tommy_tree* tree, tommy_tree_node* node)
|
||||
{
|
||||
if (!root)
|
||||
return node;
|
||||
tommy_tree_node* parent = tommy_tree_parent(node);
|
||||
|
||||
root->next = tommy_tree_move_right(root->next, node);
|
||||
while (parent) {
|
||||
int balance = tommy_tree_balance_get(parent);
|
||||
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
if (node == parent->prev)
|
||||
--balance;
|
||||
else {
|
||||
++balance;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_balance(tommy_tree_node* root)
|
||||
{
|
||||
tommy_ssize_t delta = tommy_tree_delta(root);
|
||||
if (balance == 0) {
|
||||
tommy_tree_balance_set(parent, balance);
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta < -1) {
|
||||
if (tommy_tree_delta(root->next) > 0)
|
||||
root->next = tommy_tree_rotate_right(root->next);
|
||||
return tommy_tree_rotate_left(root);
|
||||
if (balance >= -1 && balance <= 1) {
|
||||
tommy_tree_balance_set(parent, balance);
|
||||
node = parent;
|
||||
parent = tommy_tree_parent(parent);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (balance == -2) {
|
||||
tommy_tree_node* left = parent->prev;
|
||||
int left_balance = tommy_tree_balance_get(left);
|
||||
|
||||
if (left_balance < 0) {
|
||||
tommy_tree_rotate_right(tree, parent);
|
||||
tommy_tree_balance_set(parent, 0);
|
||||
tommy_tree_balance_set(left, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = left->next;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_left(tree, left);
|
||||
tommy_tree_rotate_right(tree, parent);
|
||||
tommy_tree_balance_set(parent, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(left, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
}
|
||||
} else {
|
||||
tommy_tree_node* right = parent->next;
|
||||
int right_balance = tommy_tree_balance_get(right);
|
||||
|
||||
if (right_balance > 0) {
|
||||
tommy_tree_rotate_left(tree, parent);
|
||||
tommy_tree_balance_set(parent, 0);
|
||||
tommy_tree_balance_set(right, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = right->prev;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_right(tree, right);
|
||||
tommy_tree_rotate_left(tree, parent);
|
||||
tommy_tree_balance_set(parent, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(right, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta > 1) {
|
||||
if (tommy_tree_delta(root->prev) < 0)
|
||||
root->prev = tommy_tree_rotate_left(root->prev);
|
||||
return tommy_tree_rotate_right(root);
|
||||
}
|
||||
|
||||
/* recompute key */
|
||||
root->index = 0;
|
||||
|
||||
if (root->prev && root->prev->index > root->index)
|
||||
root->index = root->prev->index;
|
||||
|
||||
if (root->next && root->next->index > root->index)
|
||||
root->index = root->next->index;
|
||||
|
||||
/* count itself */
|
||||
root->index += 1;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_insert_node(tommy_compare_func* cmp, tommy_tree_node* root, tommy_tree_node** let)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!root)
|
||||
return *let;
|
||||
|
||||
c = cmp((*let)->data, root->data);
|
||||
|
||||
if (c < 0) {
|
||||
root->prev = tommy_tree_insert_node(cmp, root->prev, let);
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
|
||||
if (c > 0) {
|
||||
root->next = tommy_tree_insert_node(cmp, root->next, let);
|
||||
return tommy_tree_balance(root);
|
||||
}
|
||||
|
||||
/* already present, set the return pointer */
|
||||
*let = root;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
TOMMY_API void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data)
|
||||
{
|
||||
tommy_tree_node* insert = node;
|
||||
tommy_tree_node* parent = 0;
|
||||
tommy_tree_node** link = &tree->root;
|
||||
|
||||
insert->data = data;
|
||||
insert->prev = 0;
|
||||
insert->next = 0;
|
||||
insert->index = 0;
|
||||
node->data = data;
|
||||
node->prev = 0;
|
||||
node->next = 0;
|
||||
node->index = 0;
|
||||
|
||||
tree->root = tommy_tree_insert_node(tree->cmp, tree->root, &insert);
|
||||
while (*link) {
|
||||
int cmp = tree->cmp(data, (*link)->data);
|
||||
|
||||
if (insert == node)
|
||||
++tree->count;
|
||||
|
||||
return insert->data;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_remove_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data, tommy_tree_node** let)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!root)
|
||||
return 0;
|
||||
|
||||
c = cmp(data, root->data);
|
||||
|
||||
if (c < 0) {
|
||||
root->prev = tommy_tree_remove_node(cmp, root->prev, data, let);
|
||||
return tommy_tree_balance(root);
|
||||
parent = *link;
|
||||
if (cmp < 0)
|
||||
link = &parent->prev;
|
||||
else if (cmp > 0)
|
||||
link = &parent->next;
|
||||
else
|
||||
return parent->data;
|
||||
}
|
||||
|
||||
if (c > 0) {
|
||||
root->next = tommy_tree_remove_node(cmp, root->next, data, let);
|
||||
return tommy_tree_balance(root);
|
||||
node->index = (tommy_size_t)(tommy_uintptr_t)parent;
|
||||
*link = node;
|
||||
++tree->count;
|
||||
|
||||
tommy_tree_insert_balance(tree, node);
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
tommy_inline void tommy_tree_remove_balance(tommy_tree* tree, tommy_tree_node* node, tommy_bool_t left_shrunk)
|
||||
{
|
||||
while (node) {
|
||||
int balance = tommy_tree_balance_get(node);
|
||||
|
||||
if (left_shrunk)
|
||||
++balance;
|
||||
else
|
||||
--balance;
|
||||
|
||||
if (balance == -1 || balance == 1) {
|
||||
tommy_tree_balance_set(node, balance);
|
||||
return;
|
||||
}
|
||||
|
||||
if (balance == 0) {
|
||||
tommy_tree_node* parent;
|
||||
|
||||
tommy_tree_balance_set(node, balance);
|
||||
parent = tommy_tree_parent(node);
|
||||
if (!parent)
|
||||
return;
|
||||
left_shrunk = node == parent->prev;
|
||||
node = parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (balance == -2) {
|
||||
tommy_tree_node* left = node->prev;
|
||||
int left_balance = tommy_tree_balance_get(left);
|
||||
tommy_tree_node* root;
|
||||
|
||||
if (left_balance <= 0) {
|
||||
root = tommy_tree_rotate_right(tree, node);
|
||||
if (left_balance == 0) {
|
||||
tommy_tree_balance_set(node, -1);
|
||||
tommy_tree_balance_set(left, 1);
|
||||
return;
|
||||
}
|
||||
tommy_tree_balance_set(node, 0);
|
||||
tommy_tree_balance_set(left, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = left->next;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_left(tree, left);
|
||||
root = tommy_tree_rotate_right(tree, node);
|
||||
tommy_tree_balance_set(node, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(left, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
}
|
||||
|
||||
node = tommy_tree_parent(root);
|
||||
if (!node)
|
||||
return;
|
||||
left_shrunk = root == node->prev;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
tommy_tree_node* right = node->next;
|
||||
int right_balance = tommy_tree_balance_get(right);
|
||||
tommy_tree_node* root;
|
||||
|
||||
if (right_balance >= 0) {
|
||||
root = tommy_tree_rotate_left(tree, node);
|
||||
if (right_balance == 0) {
|
||||
tommy_tree_balance_set(node, 1);
|
||||
tommy_tree_balance_set(right, -1);
|
||||
return;
|
||||
}
|
||||
tommy_tree_balance_set(node, 0);
|
||||
tommy_tree_balance_set(right, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = right->prev;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_right(tree, right);
|
||||
root = tommy_tree_rotate_left(tree, node);
|
||||
tommy_tree_balance_set(node, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(right, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
}
|
||||
|
||||
node = tommy_tree_parent(root);
|
||||
if (!node)
|
||||
return;
|
||||
left_shrunk = root == node->prev;
|
||||
}
|
||||
}
|
||||
|
||||
/* found */
|
||||
*let = root;
|
||||
|
||||
return tommy_tree_move_right(root->prev, root->next);
|
||||
}
|
||||
|
||||
TOMMY_API void* tommy_tree_remove(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = 0;
|
||||
|
||||
tree->root = tommy_tree_remove_node(tree->cmp, tree->root, data, &node);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
--tree->count;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
static tommy_tree_node* tommy_tree_search_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!root)
|
||||
return 0;
|
||||
|
||||
c = cmp(data, root->data);
|
||||
|
||||
if (c < 0)
|
||||
return tommy_tree_search_node(cmp, root->prev, data);
|
||||
|
||||
if (c > 0)
|
||||
return tommy_tree_search_node(cmp, root->next, data);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
TOMMY_API void* tommy_tree_search(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
TOMMY_API void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(cmp, tree->root, cmp_arg);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node)
|
||||
{
|
||||
void* data = tommy_tree_remove(tree, node->data);
|
||||
tommy_tree_node* parent;
|
||||
tommy_bool_t left_shrunk;
|
||||
void* data = node->data;
|
||||
|
||||
assert(data != 0);
|
||||
if (!node->prev || !node->next) {
|
||||
tommy_tree_node* child = node->prev ? node->prev : node->next;
|
||||
|
||||
parent = tommy_tree_parent(node);
|
||||
left_shrunk = parent && node == parent->prev;
|
||||
tommy_tree_replace(tree, node, child);
|
||||
} else {
|
||||
tommy_tree_node* next = node->next;
|
||||
|
||||
while (next->prev)
|
||||
next = next->prev;
|
||||
|
||||
parent = tommy_tree_parent(next);
|
||||
if (parent == node) {
|
||||
tommy_tree_replace(tree, node, next);
|
||||
next->prev = node->prev;
|
||||
tommy_tree_parent_set(next->prev, next);
|
||||
tommy_tree_balance_set(next, tommy_tree_balance_get(node));
|
||||
parent = next;
|
||||
left_shrunk = 0;
|
||||
} else {
|
||||
parent->prev = next->next;
|
||||
if (parent->prev)
|
||||
tommy_tree_parent_set(parent->prev, parent);
|
||||
|
||||
tommy_tree_replace(tree, node, next);
|
||||
next->prev = node->prev;
|
||||
next->next = node->next;
|
||||
tommy_tree_parent_set(next->prev, next);
|
||||
tommy_tree_parent_set(next->next, next);
|
||||
tommy_tree_balance_set(next, tommy_tree_balance_get(node));
|
||||
left_shrunk = 1;
|
||||
}
|
||||
}
|
||||
|
||||
--tree->count;
|
||||
if (parent)
|
||||
tommy_tree_remove_balance(tree, parent, left_shrunk);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func)
|
||||
TOMMY_API void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func)
|
||||
{
|
||||
tommy_tree_node* next;
|
||||
while (root) {
|
||||
tommy_tree_node* next;
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
tommy_tree_foreach_node(root->prev, func);
|
||||
|
||||
tommy_tree_foreach_node(root->prev, func);
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
func(root->data);
|
||||
|
||||
func(root->data);
|
||||
|
||||
tommy_tree_foreach_node(next, func);
|
||||
root = next;
|
||||
}
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func)
|
||||
TOMMY_API void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
tommy_tree_foreach_node(tree->root, func);
|
||||
}
|
||||
while (root) {
|
||||
tommy_tree_node* next;
|
||||
|
||||
static void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
tommy_tree_node* next;
|
||||
tommy_tree_foreach_arg_node(root->prev, func, arg);
|
||||
|
||||
if (!root)
|
||||
return;
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
|
||||
tommy_tree_foreach_arg_node(root->prev, func, arg);
|
||||
func(arg, root->data);
|
||||
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
|
||||
func(arg, root->data);
|
||||
|
||||
tommy_tree_foreach_arg_node(next, func, arg);
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
tommy_tree_foreach_arg_node(tree->root, func, arg);
|
||||
root = next;
|
||||
}
|
||||
}
|
||||
|
||||
TOMMY_API tommy_size_t tommy_tree_memory_usage(tommy_tree* tree)
|
||||
|
|
|
|||
|
|
@ -52,6 +52,21 @@
|
|||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To iterate over all the elements in the tree you can call
|
||||
* tommy_tree_head() to get the head of the tree and follow the
|
||||
* next elements with tommy_tree_next().
|
||||
*
|
||||
* \code
|
||||
* tommy_tree_node* i = tommy_tree_head(&tree);
|
||||
* while (i) {
|
||||
* struct object* obj = i->data; // gets the object pointer
|
||||
*
|
||||
* printf("%d\n", obj->value); // process the object
|
||||
*
|
||||
* i = tommy_tree_next(i); // go to the next element
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To remove an element from the tree you have to call tommy_tree_remove()
|
||||
* providing the key to search and remove.
|
||||
*
|
||||
|
|
@ -63,14 +78,33 @@
|
|||
* }
|
||||
* \endcode
|
||||
*
|
||||
* You can also remove an element from the tree if you already have the node
|
||||
* address using tommy_tree_remove_existing().
|
||||
*
|
||||
* \code
|
||||
* struct object* obj = ...;
|
||||
* tommy_tree_remove_existing(&tree, &obj->node);
|
||||
* free(obj);
|
||||
* \endcode
|
||||
*
|
||||
* To remove the smallest or greatest element you can use tommy_tree_remove_head()
|
||||
* or tommy_tree_remove_tail().
|
||||
*
|
||||
* \code
|
||||
* struct object* obj = tommy_tree_remove_head(&tree);
|
||||
* if (obj) {
|
||||
* free(obj);
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To destroy the tree you have to remove or destroy all the contained elements.
|
||||
* The tree itself doesn't have or need a deallocation function.
|
||||
* This can be done with the tommy_tree_foreach() function.
|
||||
*
|
||||
* If you need to iterate over all the elements in the tree, you can use
|
||||
* tommy_tree_foreach() or tommy_tree_foreach_arg().
|
||||
* If you need a more precise control with a real iteration, you have to insert
|
||||
* all the elements also in a ::tommy_list, and use the list to iterate.
|
||||
* See the \ref multiindex example for more detail.
|
||||
* \code
|
||||
* // deallocates all the objects iterating the tree
|
||||
* tommy_tree_foreach(&tree, free);
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYTREE_H
|
||||
|
|
@ -84,6 +118,8 @@
|
|||
/**
|
||||
* Tree node.
|
||||
* This is the node that you have to include inside your objects.
|
||||
* The node must preserve its natural alignment because the tree stores the
|
||||
* balance factor in the two least significant bits of the parent pointer.
|
||||
*/
|
||||
typedef tommy_node tommy_tree_node;
|
||||
|
||||
|
|
@ -101,7 +137,12 @@ typedef struct tommy_tree_struct {
|
|||
* Initializes the tree.
|
||||
* \param cmp The comparison function that defines the order in the tree.
|
||||
*/
|
||||
TOMMY_API void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp);
|
||||
tommy_inline void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp)
|
||||
{
|
||||
tree->root = 0;
|
||||
tree->count = 0;
|
||||
tree->cmp = cmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an element in the tree.
|
||||
|
|
@ -116,12 +157,175 @@ TOMMY_API void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp);
|
|||
TOMMY_API void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data);
|
||||
|
||||
/**
|
||||
* Searches and removes an element.
|
||||
* If the element is not found, 0 is returned.
|
||||
* \param data Element used for comparison.
|
||||
* \return The removed element, or 0 if not found.
|
||||
* Removes an element from the tree.
|
||||
* You must already have the address of the element to remove.
|
||||
* \return The tommy_node::data field of the node removed.
|
||||
*/
|
||||
TOMMY_API void* tommy_tree_remove(tommy_tree* tree, void* data);
|
||||
TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node);
|
||||
|
||||
|
||||
/** \internal
|
||||
* Calls the specified function for each element in the tree.
|
||||
* \param root Root node of the tree.
|
||||
* \param func Function to call.
|
||||
*/
|
||||
TOMMY_API void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func);
|
||||
|
||||
/** \internal
|
||||
* Calls the specified function with an argument for each element in the tree.
|
||||
* \param root Root node of the tree.
|
||||
* \param func Function to call.
|
||||
* \param arg Custom argument for the function.
|
||||
*/
|
||||
TOMMY_API void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg);
|
||||
|
||||
/**
|
||||
* Gets the parent of the specified node.
|
||||
* \param node Node contained in the tree.
|
||||
* \return The parent node. For the root node 0 is returned.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_parent(tommy_tree_node* node)
|
||||
{
|
||||
return (tommy_tree_node*)(tommy_uintptr_t)(node->index & ~(tommy_size_t)3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node following the specified one in tree order.
|
||||
* \param node Node contained in the tree.
|
||||
* \return The following node. For the tail node 0 is returned.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_next(tommy_tree_node* node)
|
||||
{
|
||||
tommy_tree_node* parent;
|
||||
|
||||
if (node->next) {
|
||||
node = node->next;
|
||||
while (node->prev)
|
||||
node = node->prev;
|
||||
return node;
|
||||
}
|
||||
|
||||
parent = tommy_tree_parent(node);
|
||||
while (parent && node == parent->next) {
|
||||
node = parent;
|
||||
parent = tommy_tree_parent(parent);
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node preceding the specified one in tree order.
|
||||
* \param node Node contained in the tree.
|
||||
* \return The preceding node. For the head node 0 is returned.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_prev(tommy_tree_node* node)
|
||||
{
|
||||
tommy_tree_node* parent;
|
||||
|
||||
if (node->prev) {
|
||||
node = node->prev;
|
||||
while (node->next)
|
||||
node = node->next;
|
||||
return node;
|
||||
}
|
||||
|
||||
parent = tommy_tree_parent(node);
|
||||
while (parent && node == parent->prev) {
|
||||
node = parent;
|
||||
parent = tommy_tree_parent(parent);
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the head (smallest) element in the tree.
|
||||
* \return The head node. For empty trees 0 is returned.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_head(tommy_tree* tree)
|
||||
{
|
||||
tommy_tree_node* node = tree->root;
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
while (node->prev)
|
||||
node = node->prev;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tail (greatest) element in the tree.
|
||||
* \return The tail node. For empty trees 0 is returned.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_tail(tommy_tree* tree)
|
||||
{
|
||||
tommy_tree_node* node = tree->root;
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
while (node->next)
|
||||
node = node->next;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the head (smallest) element.
|
||||
* If the tree is empty, 0 is returned.
|
||||
* \return The removed element, or 0 if empty.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_remove_head(tommy_tree* tree)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_head(tree);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return tommy_tree_remove_existing(tree, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the tail (greatest) element.
|
||||
* If the tree is empty, 0 is returned.
|
||||
* \return The removed element, or 0 if empty.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_remove_tail(tommy_tree* tree)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_tail(tree);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return tommy_tree_remove_existing(tree, node);
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* Searches a node in the tree.
|
||||
* If the element is not found, 0 is returned.
|
||||
* \param cmp Comparison function.
|
||||
* \param node Node to start the search from.
|
||||
* \param data Element used for comparison.
|
||||
* \return The first node found, or 0 if none.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_search_node(tommy_compare_func* cmp, tommy_tree_node* node, void* data)
|
||||
{
|
||||
while (node) {
|
||||
int c = cmp(data, node->data);
|
||||
|
||||
if (c < 0)
|
||||
node = node->prev;
|
||||
else if (c > 0)
|
||||
node = node->next;
|
||||
else
|
||||
return node;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an element in the tree.
|
||||
|
|
@ -129,7 +333,15 @@ TOMMY_API void* tommy_tree_remove(tommy_tree* tree, void* data);
|
|||
* \param data Element used for comparison.
|
||||
* \return The first element found, or 0 if none.
|
||||
*/
|
||||
TOMMY_API void* tommy_tree_search(tommy_tree* tree, void* data);
|
||||
tommy_inline void* tommy_tree_search(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an element in the tree with a specific comparison function.
|
||||
|
|
@ -140,14 +352,157 @@ TOMMY_API void* tommy_tree_search(tommy_tree* tree, void* data);
|
|||
* The ::data argument will be the first argument of the comparison function,
|
||||
* and it can be of a different type than the objects in the tree.
|
||||
*/
|
||||
TOMMY_API void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg);
|
||||
tommy_inline void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(cmp, tree->root, cmp_arg);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* Searches a node in the tree with key greater or equal than the specified one.
|
||||
* If no such element exists, 0 is returned.
|
||||
* \param cmp Comparison function.
|
||||
* \param node Node to start the search from.
|
||||
* \param data Element used for comparison.
|
||||
* \return The first node found, or 0 if none.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_search_greater_equal_node(tommy_compare_func* cmp, tommy_tree_node* node, void* data)
|
||||
{
|
||||
tommy_tree_node* candidate = 0;
|
||||
|
||||
while (node) {
|
||||
int c = cmp(data, node->data);
|
||||
|
||||
if (c < 0) {
|
||||
candidate = node;
|
||||
node = node->prev;
|
||||
} else if (c > 0) {
|
||||
node = node->next;
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element from the tree.
|
||||
* You must already have the address of the element to remove.
|
||||
* \return The tommy_node::data field of the node removed.
|
||||
* Searches an element in the tree with key greater or equal than the specified one.
|
||||
* If no such element exists, 0 is returned.
|
||||
* \param data Element used for comparison.
|
||||
* \return The first element found, or 0 if none.
|
||||
*/
|
||||
TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node);
|
||||
tommy_inline void* tommy_tree_search_greater_equal(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_greater_equal_node(tree->cmp, tree->root, data);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an element in the tree with key greater or equal than the specified one with a specific comparison function.
|
||||
*
|
||||
* Like tommy_tree_search_greater_equal() but you can specify a different comparison function.
|
||||
* Note that this function must define a suborder of the original one.
|
||||
*
|
||||
* The ::data argument will be the first argument of the comparison function,
|
||||
* and it can be of a different type than the objects in the tree.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_search_greater_equal_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_greater_equal_node(cmp, tree->root, cmp_arg);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* Searches a node in the tree with key less or equal than the specified one.
|
||||
* If no such element exists, 0 is returned.
|
||||
* \param cmp Comparison function.
|
||||
* \param node Node to start the search from.
|
||||
* \param data Element used for comparison.
|
||||
* \return The first node found, or 0 if none.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_search_less_equal_node(tommy_compare_func* cmp, tommy_tree_node* node, void* data)
|
||||
{
|
||||
tommy_tree_node* candidate = 0;
|
||||
|
||||
while (node) {
|
||||
int c = cmp(data, node->data);
|
||||
|
||||
if (c < 0) {
|
||||
node = node->prev;
|
||||
} else if (c > 0) {
|
||||
candidate = node;
|
||||
node = node->next;
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an element in the tree with key less or equal than the specified one.
|
||||
* If no such element exists, 0 is returned.
|
||||
* \param data Element used for comparison.
|
||||
* \return The first element found, or 0 if none.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_search_less_equal(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_less_equal_node(tree->cmp, tree->root, data);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an element in the tree with key less or equal than the specified one with a specific comparison function.
|
||||
*
|
||||
* Like tommy_tree_search_less_equal() but you can specify a different comparison function.
|
||||
* Note that this function must define a suborder of the original one.
|
||||
*
|
||||
* The ::data argument will be the first argument of the comparison function,
|
||||
* and it can be of a different type than the objects in the tree.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_search_less_equal_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_less_equal_node(cmp, tree->root, cmp_arg);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches and removes an element.
|
||||
* If the element is not found, 0 is returned.
|
||||
* \param data Element used for comparison.
|
||||
* \return The removed element, or 0 if not found.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_remove(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return tommy_tree_remove_existing(tree, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the specified function for each element in the tree.
|
||||
|
|
@ -179,12 +534,27 @@ TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* no
|
|||
* tommy_tree_foreach(&tree, free);
|
||||
* \endcode
|
||||
*/
|
||||
TOMMY_API void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func);
|
||||
tommy_inline void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func)
|
||||
{
|
||||
tommy_tree_foreach_node(tree->root, func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the specified function with an argument for each element in the tree.
|
||||
*/
|
||||
TOMMY_API void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg);
|
||||
tommy_inline void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
tommy_tree_foreach_arg_node(tree->root, func, arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if empty.
|
||||
* \return If the tree is empty.
|
||||
*/
|
||||
tommy_inline tommy_bool_t tommy_tree_empty(tommy_tree* tree)
|
||||
{
|
||||
return tree->root == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of elements.
|
||||
|
|
@ -201,3 +571,4 @@ tommy_inline tommy_size_t tommy_tree_count(tommy_tree* tree)
|
|||
TOMMY_API tommy_size_t tommy_tree_memory_usage(tommy_tree* tree);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue