diff --git a/tommyds/tommytree.c b/tommyds/tommytree.c index 74342067..7f3fcd19 100644 --- a/tommyds/tommytree.c +++ b/tommyds/tommytree.c @@ -3,262 +3,359 @@ #include "tommytree.h" -#include /* 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) diff --git a/tommyds/tommytree.h b/tommyds/tommytree.h index ed602b1f..9bae8611 100644 --- a/tommyds/tommytree.h +++ b/tommyds/tommytree.h @@ -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 +