efl/legacy/ecore/src/lib/ecore/ecore_tree.c

844 lines
21 KiB
C

#include "ecore_private.h"
#include "Ecore.h"
#include "Ecore_Data.h"
/* A macro for determining the highest node at given branch */
#define MAX_HEIGHT(node) (node ? MAX(node->max_left, node->max_right) : 0)
/* Utility functions for searching the tree and returning a node, or its
* parent */
static Ecore_Tree_Node *tree_node_find(Ecore_Tree * tree, const void *key);
static Ecore_Tree_Node *tree_node_find_parent(Ecore_Tree * tree, const void *key);
/* Balancing functions, keep the tree balanced within a one node height
* difference */
static int tree_node_balance(Ecore_Tree * Tree, Ecore_Tree_Node * top_node);
static int tree_node_rotate_right(Ecore_Tree * tree, Ecore_Tree_Node * top_node);
static int tree_node_rotate_left(Ecore_Tree * tree, Ecore_Tree_Node * top_node);
/* Fucntions for executing a specified function on each node of a tree */
static int tree_for_each_node(Ecore_Tree_Node * node, Ecore_For_Each for_each_func,
void *user_data);
static int tree_for_each_node_value(Ecore_Tree_Node * node,
Ecore_For_Each for_each_func, void *user_data);
/**
* @brief Allocate a new tree structure.
* @param compare_func: function used to compare the two values
* @return Returns NULL if the operation fails, otherwise the new tree
*/
EAPI Ecore_Tree *
ecore_tree_new(Ecore_Compare_Cb compare_func)
{
Ecore_Tree *new_tree;
new_tree = ECORE_TREE(malloc(sizeof(Ecore_Tree)));
if (!new_tree)
return NULL;
if (!ecore_tree_init(new_tree, compare_func))
{
IF_FREE(new_tree);
return NULL;
}
return new_tree;
}
/**
* @brief Initialize a tree structure to some sane initial values
* @param new_tree: the new tree structure to be initialized
* @param compare_func: the function used to compare node keys
* @return Returns TRUE on successful initialization, FALSE on an error
*/
EAPI int
ecore_tree_init(Ecore_Tree *new_tree, Ecore_Compare_Cb compare_func)
{
CHECK_PARAM_POINTER_RETURN("new_tree", new_tree, FALSE);
memset(new_tree, 0, sizeof(Ecore_Tree));
if (!compare_func)
new_tree->compare_func = ecore_direct_compare;
else
new_tree->compare_func = compare_func;
return TRUE;
}
/*
* @brief Add a function to be called at node destroy time
* @param tree: the tree that will use this function when nodes are destroyed
* @param free_func: the function that will be passed the node being freed
* @return Returns TRUE on successful set, FALSE otherwise.
*/
EAPI int
ecore_tree_free_value_cb_set(Ecore_Tree *tree, Ecore_Free_Cb free_value)
{
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
tree->free_value = free_value;
return TRUE;
}
/*
* @brief Add a function to be called at node destroy time
* @param tree: the tree that will use this function when nodes are destroyed
* @param free_key: the function that will be passed the node being freed
* @return Returns TRUE on successful set, FALSE otherwise.
*/
EAPI int
ecore_tree_free_key_cb_set(Ecore_Tree *tree, Ecore_Free_Cb free_key)
{
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
tree->free_key = free_key;
return TRUE;
}
/*
* @brief Initialize a new tree node
* @return Returns FALSE if the operation fails, otherwise TRUE
*/
EAPI int
ecore_tree_node_init(Ecore_Tree_Node *new_node)
{
CHECK_PARAM_POINTER_RETURN("new_node", new_node, FALSE);
new_node->key = NULL;
new_node->value = NULL;
new_node->parent = NULL;
new_node->right_child = NULL;
new_node->left_child = NULL;
new_node->max_left = new_node->max_right = 0;
return TRUE;
}
/*
* @brief Allocate a new tree node
* @return Returns NULL if the operation fails, otherwise the new node.
*/
EAPI Ecore_Tree_Node *
ecore_tree_node_new()
{
Ecore_Tree_Node *new_node;
new_node = ECORE_TREE_NODE(malloc(sizeof(Ecore_Tree_Node)));
if (!new_node)
return NULL;
if (!ecore_tree_node_init(new_node))
{
IF_FREE(new_node);
return NULL;
}
return new_node;
}
/*
* @brief Free a tree node and it's children.
* @param node: tree node to be free()'d
* @param data_free: callback for destroying the data held in node
* @return Returns TRUE if the node is destroyed successfully, FALSE if not.
*
* If you don't want the children free'd then you need to remove the node first.
*/
EAPI int
ecore_tree_node_destroy(Ecore_Tree_Node *node, Ecore_Free_Cb value_free, Ecore_Free_Cb key_free)
{
CHECK_PARAM_POINTER_RETURN("node", node, FALSE);
if (key_free)
key_free(node->key);
if (value_free)
value_free(node->value);
FREE(node);
return TRUE;
}
/*
* @brief Set the value of the node to value
* @param node: the node to be set
* @param value: the value to set the node to.
* @return Returns TRUE if the node is set successfully, FALSE if not.
*/
EAPI int
ecore_tree_node_value_set(Ecore_Tree_Node *node, void *value)
{
CHECK_PARAM_POINTER_RETURN("node", node, FALSE);
node->value = value;
return TRUE;
}
/*
* @brief Get the value of the node
* @param node: the node that contains the desired value
* @return Returns NULL if an error, otherwise the value associated with node
*/
EAPI void *
ecore_tree_node_value_get(Ecore_Tree_Node *node)
{
void *ret;
CHECK_PARAM_POINTER_RETURN("node", node, NULL);
ret = node->value;
return ret;
}
/*
* @brief Set the value of the node's key to key
* @param node: the node to be set
* @param key: the value to set it's key to.
* @return Returns TRUE if the node is set successfully, FALSE if not.
*/
EAPI int
ecore_tree_node_key_set(Ecore_Tree_Node *node, void *key)
{
CHECK_PARAM_POINTER_RETURN("node", node, FALSE);
node->key = key;
return TRUE;
}
/*
* @brief Get the value of the node's key
* @param node: the node that contains the desired key
*
* @return Returns NULL if an error occurs, otherwise the key is returned
*/
EAPI void *
ecore_tree_node_key_get(Ecore_Tree_Node *node)
{
void *ret;
CHECK_PARAM_POINTER_RETURN("node", node, NULL);
ret = node->key;
return ret;
}
/**
* @brief Free the tree and it's stored data
* @param tree: the tree to destroy
*
* @return Returns TRUE if tree destroyed successfully, FALSE if not.
*/
EAPI int
ecore_tree_destroy(Ecore_Tree *tree)
{
Ecore_Tree_Node *node;
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
while ((node = tree->tree))
{
ecore_tree_node_remove(tree, node);
ecore_tree_node_destroy(node, tree->free_value, tree->free_key);
}
FREE(tree);
return TRUE;
}
/**
* @brief Return the node corresponding to key
* @param tree: the tree to search
* @param key: the key to search for in the tree
*
* @return Returns the node corresponding to the key if found, otherwise NULL.
*/
EAPI Ecore_Tree_Node *
ecore_tree_get_node(Ecore_Tree *tree, const void *key)
{
Ecore_Tree_Node *ret;
CHECK_PARAM_POINTER_RETURN("tree", tree, NULL);
ret = tree_node_find(tree, key);
return ret;
}
/**
* @brief Return the value corresponding to key
* @param tree: the tree to search
* @param key: the key to search for in @a tree
* @return Returns the value corresponding to the key if found, otherwise NULL.
*/
EAPI void *
ecore_tree_get(Ecore_Tree *tree, const void *key)
{
void *ret;
Ecore_Tree_Node *node;
CHECK_PARAM_POINTER_RETURN("tree", tree, NULL);
node = tree_node_find(tree, key);
ret = (node ? node->value : NULL);
return ret;
}
/**
* @brief Find the closest value greater than or equal to the key.
* @param tree The tree to search.
* @param key The key to search for in @a tree.
* @return NULL if no valid nodes, otherwise the node greater than or
* equal to the key
*/
EAPI void *
ecore_tree_closest_larger_get(Ecore_Tree *tree, const void *key)
{
Ecore_Tree_Node *node;
CHECK_PARAM_POINTER_RETURN("tree", tree, NULL);
node = tree_node_find(tree, key);
if (node)
return node;
node = tree_node_find_parent(tree, key);
if (!node)
return NULL;
if (tree->compare_func(node->key, key) < 0)
return NULL;
return node;
}
/**
* @brief Find the closest value <= key
* @param tree the tree to search
* @param key the key to search for in tree
* @return Returns NULL if no valid nodes, otherwise the node <= key
*/
EAPI void *
ecore_tree_closest_smaller_get(Ecore_Tree *tree, const void *key)
{
Ecore_Tree_Node *node;
CHECK_PARAM_POINTER_RETURN("tree", tree, NULL);
node = tree_node_find(tree, key);
if (node)
return node;
node = tree_node_find_parent(tree, key);
if (node)
node = node->right_child;
return node;
}
/**
* Set the value associated with key to @a value.
* @param tree The tree that contains the key/value pair.
* @param key The key to identify which node to set a value.
* @param value Value to set the found node.
* @return TRUE if successful, FALSE if not.
*/
EAPI int
ecore_tree_set(Ecore_Tree *tree, void *key, void *value)
{
Ecore_Tree_Node *node = NULL;
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
node = tree_node_find(tree, key);
if (!node)
{
node = ecore_tree_node_new();
ecore_tree_node_key_set(node, key);
if (!ecore_tree_node_add(tree, node))
return FALSE;
}
else
{
if (tree->free_key)
tree->free_key(key);
if (node->value && tree->free_value)
tree->free_value(node->value);
}
ecore_tree_node_value_set(node, value);
for (; node; node = node->parent)
tree_node_balance(tree, node);
return TRUE;
}
/**
* Place a node in the tree.
* @param tree The tree to add @a node.
* @param node The node to add to @a tree.
* @return TRUE on a successful add, FALSE otherwise.
*/
EAPI int
ecore_tree_node_add(Ecore_Tree *tree, Ecore_Tree_Node *node)
{
Ecore_Tree_Node *travel = NULL;
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
CHECK_PARAM_POINTER_RETURN("node", node, FALSE);
/* Find where to put this new node. */
if (!tree->tree)
tree->tree = node;
else
{
travel = tree_node_find_parent(tree, node->key);
node->parent = travel;
/* The node is less than travel */
if (tree->compare_func(node->key, travel->key) < 0)
{
travel->right_child = node;
travel->max_left = 1;
/* The node is greater than travel */
}
else
{
travel->left_child = node;
travel->max_right = 1;
}
}
return TRUE;
}
/**
* Remove the node from the tree.
* @param tree The tree to remove @a node from.
* @param node The node to remove from @a tree.
* @return TRUE on a successful remove, FALSE otherwise.
*/
EAPI int
ecore_tree_node_remove(Ecore_Tree *tree, Ecore_Tree_Node *node)
{
Ecore_Tree_Node *traverse;
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
CHECK_PARAM_POINTER_RETURN("node", node, FALSE);
/*
* Find the nearest value to the balanced one.
*/
if (node->left_child)
{
traverse = node->left_child;
/* Now work our way down right side of the traverse node.
* This will give us the node with the next closest value
* to the current node. If traverse had no right node, then
* this will stop at node's left node. */
while (traverse->right_child)
{
traverse = traverse->right_child;
}
/*
* Hook any dropped leaves into the moved nodes spot
*/
if (traverse->parent)
traverse->parent->left_child = traverse->left_child;
}
else if (node->right_child)
{
traverse = node->right_child;
/* Now work our way down left side of the traverse node.
* This will give us the node with the next closest value
* to the current node. If traverse had no left node, then
* this will stop at node's right node. */
while (traverse->left_child)
{
traverse = traverse->left_child;
}
/*
* Hook any dropped leaves into the moved nodes spot
*/
if (traverse->right_child)
traverse->right_child->parent = traverse->parent;
if (traverse->parent)
traverse->parent->right_child = traverse->right_child;
else
tree->tree = traverse->right_child;
}
else
traverse = NULL;
if (traverse)
{
/*
* Ensure that we don't get a recursive reference.
*/
if (node->right_child && node->right_child != traverse)
{
node->right_child->parent = traverse;
traverse->right_child = node->right_child;
}
if (node->left_child && node->left_child != traverse)
{
node->left_child->parent = traverse;
traverse->left_child = node->left_child;
}
/*
* Unlink the node to be moved from it's parent.
*/
if (traverse->parent)
{
if (traverse->parent->left_child == traverse)
traverse->parent->left_child = NULL;
else
traverse->parent->right_child = NULL;
}
traverse->parent = node->parent;
}
if (node->parent)
{
if (node == node->parent->left_child)
node->parent->left_child = traverse;
else
node->parent->right_child = traverse;
}
if (tree->tree == node)
tree->tree = traverse;
node->parent = node->left_child = node->right_child = NULL;
/*
* Rebalance the tree to ensure short search paths.
*/
while (traverse)
{
tree_node_balance(tree, traverse);
traverse = traverse->parent;
}
return TRUE;
}
/**
* Remove the key from the tree.
* @param tree The tree to remove @a key.
* @param key The key to remove from @a tree.
* @return TRUE on a successful remove, FALSE otherwise.
*/
EAPI int
ecore_tree_remove(Ecore_Tree *tree, const void *key)
{
Ecore_Tree_Node *node;
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
if (!tree->tree)
return FALSE;
/* Find the node we need to remove */
node = tree_node_find(tree, key);
if (!node)
return FALSE;
if (!ecore_tree_node_remove(tree, node))
return FALSE;
ecore_tree_node_destroy(node, tree->free_value, tree->free_key);
return TRUE;
}
/**
* @brief Test to see if the tree has any nodes
* @param tree: the tree to check for nodes
* @return Returns TRUE if no nodes exist, FALSE otherwise
*/
EAPI int
ecore_tree_empty_is(Ecore_Tree *tree)
{
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
if (!tree->tree)
return TRUE;
return FALSE;
}
/**
* @brief Execute function for each value in the tree
* @param tree: the tree to traverse
* @param for_each_func: the function to execute for each value in the tree
* @param user_data: data passed to each for_each_func call
* @return Returns TRUE on success, FALSE on failure.
*/
EAPI int
ecore_tree_for_each_node_value(Ecore_Tree *tree, Ecore_For_Each for_each_func, void *user_data)
{
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
CHECK_PARAM_POINTER_RETURN("for_each_func", for_each_func, FALSE);
if (!tree->tree)
return FALSE;
return tree_for_each_node_value(tree->tree, for_each_func, user_data);
}
/**
* @brief Execute the function for each node in the tree
* @param tree: the tree to traverse
* @param for_each_func: the function to execute for each node
* @param user_data: data passed to each for_each_func call
* @return Returns TRUE on success, FALSE on failure.
*/
EAPI int
ecore_tree_for_each_node(Ecore_Tree *tree, Ecore_For_Each for_each_func, void *user_data)
{
CHECK_PARAM_POINTER_RETURN("tree", tree, FALSE);
CHECK_PARAM_POINTER_RETURN("for_each_func", for_each_func, FALSE);
if (!tree->tree)
return FALSE;
return tree_for_each_node(tree->tree, for_each_func, user_data);
}
/* Find the parent for the key */
static Ecore_Tree_Node *
tree_node_find_parent(Ecore_Tree *tree, const void *key)
{
Ecore_Tree_Node *parent, *travel;
CHECK_PARAM_POINTER_RETURN("tree", tree, NULL);
parent = tree_node_find(tree, key);
if (parent)
parent = parent->parent;
travel = tree->tree;
if (!travel)
return NULL;
while (!parent)
{
int compare;
if ((compare = tree->compare_func(key, travel->key)) < 0)
{
if (!travel->right_child)
parent = travel;
travel = travel->right_child;
}
else
{
if (!travel->left_child)
parent = travel;
travel = travel->left_child;
}
}
return parent;
}
/* Search for the node with a specified key */
static Ecore_Tree_Node *
tree_node_find(Ecore_Tree *tree, const void *key)
{
int compare;
Ecore_Tree_Node *node;
CHECK_PARAM_POINTER_RETURN("tree", tree, NULL);
node = tree->tree;
while (node && (compare = tree->compare_func(key, node->key)) != 0)
{
if (compare < 0)
{
if (!node->right_child)
return NULL;
node = node->right_child;
}
else
{
if (!node->left_child)
return NULL;
node = node->left_child;
}
}
return node;
}
/* Balance the tree with respect to node */
static int
tree_node_balance(Ecore_Tree *tree, Ecore_Tree_Node *top_node)
{
int balance;
CHECK_PARAM_POINTER_RETURN("top_node", top_node, FALSE);
/* Get the height of the left branch. */
if (top_node->right_child)
top_node->max_left = MAX_HEIGHT(top_node->right_child) + 1;
else
top_node->max_left = 0;
/* Get the height of the right branch. */
if (top_node->left_child)
top_node->max_right = MAX_HEIGHT(top_node->left_child) + 1;
else
top_node->max_right = 0;
/* Determine which side has a larger height. */
balance = top_node->max_right - top_node->max_left;
/* if the left side has a height advantage >1 rotate right */
if (balance < -1)
tree_node_rotate_right(tree, top_node);
/* else if the left side has a height advantage >1 rotate left */
else if (balance > 1)
tree_node_rotate_left(tree, top_node);
return TRUE;
}
/* Tree is overbalanced to the left, so rotate nodes to the right. */
static int
tree_node_rotate_right(Ecore_Tree *tree, Ecore_Tree_Node *top_node)
{
Ecore_Tree_Node *temp;
CHECK_PARAM_POINTER_RETURN("top_node", top_node, FALSE);
/* The left branch's right branch becomes the nodes left branch,
* the left branch becomes the top node, and the node becomes the
* right branch. */
temp = top_node->right_child;
top_node->right_child = temp->left_child;
temp->left_child = top_node;
/* Make sure the nodes know who their new parents are and the tree
* structure knows the start of the tree. */
temp->parent = top_node->parent;
if (temp->parent == NULL)
tree->tree = temp;
else
{
if (temp->parent->left_child == top_node)
temp->parent->left_child = temp;
else
temp->parent->right_child = temp;
}
top_node->parent = temp;
/* And recalculate node heights */
tree_node_balance(tree, top_node);
tree_node_balance(tree, temp);
return TRUE;
}
/* The tree is overbalanced to the right, so we rotate nodes to the left */
static int
tree_node_rotate_left(Ecore_Tree *tree, Ecore_Tree_Node *top_node)
{
Ecore_Tree_Node *temp;
CHECK_PARAM_POINTER_RETURN("top_node", top_node, FALSE);
/*
* The right branch's left branch becomes the nodes right branch,
* the right branch becomes the top node, and the node becomes the
* left branch.
*/
temp = top_node->left_child;
top_node->left_child = temp->right_child;
temp->right_child = top_node;
/* Make sure the nodes know who their new parents are. */
temp->parent = top_node->parent;
if (temp->parent == NULL)
tree->tree = temp;
else
{
if (temp->parent->left_child == top_node)
temp->parent->left_child = temp;
else
temp->parent->right_child = temp;
}
top_node->parent = temp;
/* And recalculate node heights */
tree_node_balance(tree, top_node);
tree_node_balance(tree, temp);
return TRUE;
}
/*
* @brief Execute a function for each node below this point in the tree.
* @param node: the highest node in the tree the function will be executed for
* @param for_each_func: the function to pass the nodes as data into
* @param user_data: data passed to each for_each_func call
* @return Returns FALSE if an error condition occurs, otherwise TRUE
*/
static int
tree_for_each_node(Ecore_Tree_Node * node, Ecore_For_Each for_each_func, void *user_data)
{
CHECK_PARAM_POINTER_RETURN("node", node, FALSE);
if (node->right_child)
tree_for_each_node(node->right_child, for_each_func, user_data);
if (node->left_child)
tree_for_each_node(node->left_child, for_each_func, user_data);
for_each_func(node, user_data);
return TRUE;
}
/*
* @brief Execute a function for each node below this point in the tree.
* @param node: the highest node in the tree the function will be executed for
* @param for_each_func: the function to pass the nodes values as data
* @return Returns FALSE if an error condition occurs, otherwise TRUE
*/
static int
tree_for_each_node_value(Ecore_Tree_Node *node, Ecore_For_Each for_each_func, void *user_data)
{
CHECK_PARAM_POINTER_RETURN("node", node, FALSE);
if (node->right_child)
tree_for_each_node_value(node->right_child, for_each_func, user_data);
if (node->left_child)
tree_for_each_node_value(node->left_child, for_each_func, user_data);
for_each_func(node->value, user_data);
return TRUE;
}