Add tommytree with bound searches, inlined helpers, and tail-recursion elimination

This commit is contained in:
Andrea Mazzoleni 2026-09-14 09:20:29 +02:00
parent 3bddc127f0
commit d7e06271d9
2 changed files with 680 additions and 212 deletions

View file

@ -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)

View file

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