mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-13 15:56:23 +01:00
Merge branch 'gtree-add-iterators' into 'master'
GTree: add an ability to iterate over a tree and a node-based API See merge request GNOME/glib!1509
This commit is contained in:
commit
7bfd6278b9
@ -2982,21 +2982,36 @@ g_bytes_get_type
|
||||
<TITLE>Balanced Binary Trees</TITLE>
|
||||
<FILE>trees-binary</FILE>
|
||||
GTree
|
||||
GTreeNode
|
||||
g_tree_new
|
||||
g_tree_ref
|
||||
g_tree_unref
|
||||
g_tree_new_with_data
|
||||
g_tree_new_full
|
||||
g_tree_node_first
|
||||
g_tree_node_last
|
||||
g_tree_node_previous
|
||||
g_tree_node_next
|
||||
g_tree_insert_node
|
||||
g_tree_insert
|
||||
g_tree_replace_node
|
||||
g_tree_replace
|
||||
g_tree_node_key
|
||||
g_tree_node_value
|
||||
g_tree_nnodes
|
||||
g_tree_height
|
||||
g_tree_lookup_node
|
||||
g_tree_lookup
|
||||
g_tree_lookup_extended
|
||||
g_tree_foreach_node
|
||||
g_tree_foreach
|
||||
g_tree_traverse
|
||||
GTraverseFunc
|
||||
GTraverseNodeFunc
|
||||
g_tree_search_node
|
||||
g_tree_search
|
||||
g_tree_lower_bound
|
||||
g_tree_upper_bound
|
||||
g_tree_remove
|
||||
g_tree_steal
|
||||
g_tree_destroy
|
||||
|
481
glib/gtree.c
481
glib/gtree.c
@ -66,12 +66,8 @@
|
||||
* To destroy a #GTree, use g_tree_destroy().
|
||||
**/
|
||||
|
||||
#undef G_TREE_DEBUG
|
||||
|
||||
#define MAX_GTREE_HEIGHT 40
|
||||
|
||||
typedef struct _GTreeNode GTreeNode;
|
||||
|
||||
/**
|
||||
* GTree:
|
||||
*
|
||||
@ -104,10 +100,10 @@ struct _GTreeNode
|
||||
|
||||
static GTreeNode* g_tree_node_new (gpointer key,
|
||||
gpointer value);
|
||||
static void g_tree_insert_internal (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value,
|
||||
gboolean replace);
|
||||
static GTreeNode *g_tree_insert_internal (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value,
|
||||
gboolean replace);
|
||||
static gboolean g_tree_remove_internal (GTree *tree,
|
||||
gconstpointer key,
|
||||
gboolean steal);
|
||||
@ -123,9 +119,9 @@ static gint g_tree_node_in_order (GTreeNode *node,
|
||||
static gint g_tree_node_post_order (GTreeNode *node,
|
||||
GTraverseFunc traverse_func,
|
||||
gpointer data);
|
||||
static gpointer g_tree_node_search (GTreeNode *node,
|
||||
GCompareFunc search_func,
|
||||
gconstpointer data);
|
||||
static GTreeNode *g_tree_node_search (GTreeNode *node,
|
||||
GCompareFunc search_func,
|
||||
gconstpointer data);
|
||||
static GTreeNode* g_tree_node_rotate_left (GTreeNode *node);
|
||||
static GTreeNode* g_tree_node_rotate_right (GTreeNode *node);
|
||||
#ifdef G_TREE_DEBUG
|
||||
@ -230,11 +226,24 @@ g_tree_new_full (GCompareDataFunc key_compare_func,
|
||||
return tree;
|
||||
}
|
||||
|
||||
static inline GTreeNode *
|
||||
g_tree_first_node (GTree *tree)
|
||||
/**
|
||||
* g_tree_node_first:
|
||||
* @tree: a #GTree
|
||||
*
|
||||
* Returns the first in-order node of the tree, or %NULL
|
||||
* for an empty tree.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the first node in the tree
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_node_first (GTree *tree)
|
||||
{
|
||||
GTreeNode *tmp;
|
||||
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
if (!tree->root)
|
||||
return NULL;
|
||||
|
||||
@ -244,13 +253,55 @@ g_tree_first_node (GTree *tree)
|
||||
tmp = tmp->left;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GTreeNode *
|
||||
/**
|
||||
* g_tree_node_last:
|
||||
* @tree: a #GTree
|
||||
*
|
||||
* Returns the last in-order node of the tree, or %NULL
|
||||
* for an empty tree.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the last node in the tree
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_node_last (GTree *tree)
|
||||
{
|
||||
GTreeNode *tmp;
|
||||
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
if (!tree->root)
|
||||
return NULL;
|
||||
|
||||
tmp = tree->root;
|
||||
|
||||
while (tmp->right_child)
|
||||
tmp = tmp->right;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_node_previous
|
||||
* @node: a #GTree node
|
||||
*
|
||||
* Returns the previous in-order node of the tree, or %NULL
|
||||
* if the passed node was already the first one.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the previous node in the tree
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_node_previous (GTreeNode *node)
|
||||
{
|
||||
GTreeNode *tmp;
|
||||
|
||||
g_return_val_if_fail (node != NULL, NULL);
|
||||
|
||||
tmp = node->left;
|
||||
|
||||
if (node->left_child)
|
||||
@ -260,11 +311,24 @@ g_tree_node_previous (GTreeNode *node)
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static inline GTreeNode *
|
||||
/**
|
||||
* g_tree_node_next
|
||||
* @node: a #GTree node
|
||||
*
|
||||
* Returns the next in-order node of the tree, or %NULL
|
||||
* if the passed node was already the last one.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the next node in the tree
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_node_next (GTreeNode *node)
|
||||
{
|
||||
GTreeNode *tmp;
|
||||
|
||||
g_return_val_if_fail (node != NULL, NULL);
|
||||
|
||||
tmp = node->right;
|
||||
|
||||
if (node->right_child)
|
||||
@ -282,7 +346,7 @@ g_tree_remove_all (GTree *tree)
|
||||
|
||||
g_return_if_fail (tree != NULL);
|
||||
|
||||
node = g_tree_first_node (tree);
|
||||
node = g_tree_node_first (tree);
|
||||
|
||||
while (node)
|
||||
{
|
||||
@ -294,11 +358,22 @@ g_tree_remove_all (GTree *tree)
|
||||
tree->value_destroy_func (node->value);
|
||||
g_slice_free (GTreeNode, node);
|
||||
|
||||
#ifdef G_TREE_DEBUG
|
||||
g_assert (tree->nnodes > 0);
|
||||
tree->nnodes--;
|
||||
#endif
|
||||
|
||||
node = next;
|
||||
}
|
||||
|
||||
#ifdef G_TREE_DEBUG
|
||||
g_assert (tree->nnodes == 0);
|
||||
#endif
|
||||
|
||||
tree->root = NULL;
|
||||
#ifndef G_TREE_DEBUG
|
||||
tree->nnodes = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@ -369,7 +444,7 @@ g_tree_destroy (GTree *tree)
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_insert:
|
||||
* g_tree_insert_node:
|
||||
* @tree: a #GTree
|
||||
* @key: the key to insert
|
||||
* @value: the value corresponding to the key
|
||||
@ -387,28 +462,55 @@ g_tree_destroy (GTree *tree)
|
||||
* The cost of maintaining a balanced tree while inserting new key/value
|
||||
* result in a O(n log(n)) operation where most of the other operations
|
||||
* are O(log(n)).
|
||||
*
|
||||
* Returns: (transfer none): the inserted (or set) node.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_insert_node (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value)
|
||||
{
|
||||
GTreeNode *node;
|
||||
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
node = g_tree_insert_internal (tree, key, value, FALSE);
|
||||
|
||||
#ifdef G_TREE_DEBUG
|
||||
g_tree_node_check (tree->root);
|
||||
#endif
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_insert:
|
||||
* @tree: a #GTree
|
||||
* @key: the key to insert
|
||||
* @value: the value corresponding to the key
|
||||
*
|
||||
* Inserts a key/value pair into a #GTree.
|
||||
*
|
||||
* Inserts a new key and value into a #GTree as g_tree_insert_node() does,
|
||||
* only this function does not return the inserted or set node.
|
||||
*/
|
||||
void
|
||||
g_tree_insert (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value)
|
||||
{
|
||||
g_return_if_fail (tree != NULL);
|
||||
|
||||
g_tree_insert_internal (tree, key, value, FALSE);
|
||||
|
||||
#ifdef G_TREE_DEBUG
|
||||
g_tree_node_check (tree->root);
|
||||
#endif
|
||||
g_tree_insert_node (tree, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_replace:
|
||||
* g_tree_replace_node:
|
||||
* @tree: a #GTree
|
||||
* @key: the key to insert
|
||||
* @value: the value corresponding to the key
|
||||
*
|
||||
* Inserts a new key and value into a #GTree similar to g_tree_insert().
|
||||
*
|
||||
* Inserts a new key and value into a #GTree similar to g_tree_insert_node().
|
||||
* The difference is that if the key already exists in the #GTree, it gets
|
||||
* replaced by the new key. If you supplied a @value_destroy_func when
|
||||
* creating the #GTree, the old value is freed using that function. If you
|
||||
@ -417,39 +519,64 @@ g_tree_insert (GTree *tree,
|
||||
*
|
||||
* The tree is automatically 'balanced' as new key/value pairs are added,
|
||||
* so that the distance from the root to every leaf is as small as possible.
|
||||
*
|
||||
* Returns: (transfer none): the inserted (or set) node.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_replace_node (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value)
|
||||
{
|
||||
GTreeNode *node;
|
||||
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
node = g_tree_insert_internal (tree, key, value, TRUE);
|
||||
|
||||
#ifdef G_TREE_DEBUG
|
||||
g_tree_node_check (tree->root);
|
||||
#endif
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_replace:
|
||||
* @tree: a #GTree
|
||||
* @key: the key to insert
|
||||
* @value: the value corresponding to the key
|
||||
*
|
||||
* Inserts a new key and value into a #GTree as g_tree_replace_node() does,
|
||||
* only this function does not return the inserted or set node.
|
||||
*/
|
||||
void
|
||||
g_tree_replace (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value)
|
||||
{
|
||||
g_return_if_fail (tree != NULL);
|
||||
|
||||
g_tree_insert_internal (tree, key, value, TRUE);
|
||||
|
||||
#ifdef G_TREE_DEBUG
|
||||
g_tree_node_check (tree->root);
|
||||
#endif
|
||||
g_tree_replace_node (tree, key, value);
|
||||
}
|
||||
|
||||
/* internal insert routine */
|
||||
static void
|
||||
static GTreeNode *
|
||||
g_tree_insert_internal (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value,
|
||||
gboolean replace)
|
||||
{
|
||||
GTreeNode *node;
|
||||
GTreeNode *node, *retnode;
|
||||
GTreeNode *path[MAX_GTREE_HEIGHT];
|
||||
int idx;
|
||||
|
||||
g_return_if_fail (tree != NULL);
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
if (!tree->root)
|
||||
{
|
||||
tree->root = g_tree_node_new (key, value);
|
||||
tree->nnodes++;
|
||||
return;
|
||||
return tree->root;
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
@ -481,7 +608,7 @@ g_tree_insert_internal (GTree *tree,
|
||||
tree->key_destroy_func (key);
|
||||
}
|
||||
|
||||
return;
|
||||
return node;
|
||||
}
|
||||
else if (cmp < 0)
|
||||
{
|
||||
@ -502,6 +629,7 @@ g_tree_insert_internal (GTree *tree,
|
||||
|
||||
tree->nnodes++;
|
||||
|
||||
retnode = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -524,6 +652,7 @@ g_tree_insert_internal (GTree *tree,
|
||||
|
||||
tree->nnodes++;
|
||||
|
||||
retnode = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -560,6 +689,8 @@ g_tree_insert_internal (GTree *tree,
|
||||
|
||||
node = bparent;
|
||||
}
|
||||
|
||||
return retnode;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -835,6 +966,65 @@ g_tree_remove_internal (GTree *tree,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_node_key:
|
||||
* @node: a #GTree node
|
||||
*
|
||||
* Gets the key stored at a particular tree node.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the key at the node.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
gpointer
|
||||
g_tree_node_key (GTreeNode *node)
|
||||
{
|
||||
g_return_val_if_fail (node != NULL, NULL);
|
||||
|
||||
return node->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_node_value:
|
||||
* @node: a #GTree node
|
||||
*
|
||||
* Gets the value stored at a particular tree node.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the value at the node.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
gpointer
|
||||
g_tree_node_value (GTreeNode *node)
|
||||
{
|
||||
g_return_val_if_fail (node != NULL, NULL);
|
||||
|
||||
return node->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_lookup_node:
|
||||
* @tree: a #GTree
|
||||
* @key: the key to look up
|
||||
*
|
||||
* Gets the tree node corresponding to the given key. Since a #GTree is
|
||||
* automatically balanced as key/value pairs are added, key lookup
|
||||
* is O(log n) (where n is the number of key/value pairs in the tree).
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the tree node corresponding to
|
||||
* the key, or %NULL if the key was not found
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_lookup_node (GTree *tree,
|
||||
gconstpointer key)
|
||||
{
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
return g_tree_find_node (tree, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_lookup:
|
||||
* @tree: a #GTree
|
||||
@ -853,10 +1043,8 @@ g_tree_lookup (GTree *tree,
|
||||
{
|
||||
GTreeNode *node;
|
||||
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
node = g_tree_lookup_node (tree, key);
|
||||
|
||||
node = g_tree_find_node (tree, key);
|
||||
|
||||
return node ? node->value : NULL;
|
||||
}
|
||||
|
||||
@ -926,8 +1114,8 @@ g_tree_foreach (GTree *tree,
|
||||
if (!tree->root)
|
||||
return;
|
||||
|
||||
node = g_tree_first_node (tree);
|
||||
|
||||
node = g_tree_node_first (tree);
|
||||
|
||||
while (node)
|
||||
{
|
||||
if ((*func) (node->key, node->value, user_data))
|
||||
@ -937,6 +1125,47 @@ g_tree_foreach (GTree *tree,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_foreach_node:
|
||||
* @tree: a #GTree
|
||||
* @func: the function to call for each node visited.
|
||||
* If this function returns %TRUE, the traversal is stopped.
|
||||
* @user_data: user data to pass to the function
|
||||
*
|
||||
* Calls the given function for each of the nodes in the #GTree.
|
||||
* The function is passed the pointer to the particular node, and the given
|
||||
* @data parameter. The tree traversal happens in-order.
|
||||
*
|
||||
* The tree may not be modified while iterating over it (you can't
|
||||
* add/remove items). To remove all items matching a predicate, you need
|
||||
* to add each item to a list in your #GTraverseFunc as you walk over
|
||||
* the tree, then walk the list and remove each item.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
void
|
||||
g_tree_foreach_node (GTree *tree,
|
||||
GTraverseNodeFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTreeNode *node;
|
||||
|
||||
g_return_if_fail (tree != NULL);
|
||||
|
||||
if (!tree->root)
|
||||
return;
|
||||
|
||||
node = g_tree_node_first (tree);
|
||||
|
||||
while (node)
|
||||
{
|
||||
if ((*func) (node, user_data))
|
||||
break;
|
||||
|
||||
node = g_tree_node_next (node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_traverse:
|
||||
* @tree: a #GTree
|
||||
@ -997,6 +1226,40 @@ g_tree_traverse (GTree *tree,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_search_node:
|
||||
* @tree: a #GTree
|
||||
* @search_func: a function used to search the #GTree
|
||||
* @user_data: the data passed as the second argument to @search_func
|
||||
*
|
||||
* Searches a #GTree using @search_func.
|
||||
*
|
||||
* The @search_func is called with a pointer to the key of a key/value
|
||||
* pair in the tree, and the passed in @user_data. If @search_func returns
|
||||
* 0 for a key/value pair, then the corresponding node is returned as
|
||||
* the result of g_tree_search(). If @search_func returns -1, searching
|
||||
* will proceed among the key/value pairs that have a smaller key; if
|
||||
* @search_func returns 1, searching will proceed among the key/value
|
||||
* pairs that have a larger key.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the node corresponding to the
|
||||
* found key, or %NULL if the key was not found
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_search_node (GTree *tree,
|
||||
GCompareFunc search_func,
|
||||
gconstpointer user_data)
|
||||
{
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
if (!tree->root)
|
||||
return NULL;
|
||||
|
||||
return g_tree_node_search (tree->root, search_func, user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_search:
|
||||
* @tree: a #GTree
|
||||
@ -1021,12 +1284,119 @@ g_tree_search (GTree *tree,
|
||||
GCompareFunc search_func,
|
||||
gconstpointer user_data)
|
||||
{
|
||||
GTreeNode *node;
|
||||
|
||||
node = g_tree_search_node (tree, search_func, user_data);
|
||||
|
||||
return node ? node->value : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_lower_bound:
|
||||
* @tree: a #GTree
|
||||
* @key: the key to calculate the lower bound for
|
||||
*
|
||||
* Gets the lower bound node corresponding to the given key,
|
||||
* or %NULL if the tree is empty or all the nodes in the tree
|
||||
* have keys that are strictly lower than the searched key.
|
||||
*
|
||||
* The lower bound is the first node that has its key greater
|
||||
* than or equal to the searched key.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the tree node corresponding to
|
||||
* the lower bound, or %NULL if the tree is empty or has only
|
||||
* keys strictly lower than the searched key.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_lower_bound (GTree *tree,
|
||||
gconstpointer key)
|
||||
{
|
||||
GTreeNode *node, *result;
|
||||
gint cmp;
|
||||
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
if (tree->root)
|
||||
return g_tree_node_search (tree->root, search_func, user_data);
|
||||
else
|
||||
node = tree->root;
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
result = NULL;
|
||||
while (1)
|
||||
{
|
||||
cmp = tree->key_compare (key, node->key, tree->key_compare_data);
|
||||
if (cmp <= 0)
|
||||
{
|
||||
result = node;
|
||||
|
||||
if (!node->left_child)
|
||||
return result;
|
||||
|
||||
node = node->left;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!node->right_child)
|
||||
return result;
|
||||
|
||||
node = node->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_tree_upper_bound:
|
||||
* @tree: a #GTree
|
||||
* @key: the key to calculate the upper bound for
|
||||
*
|
||||
* Gets the upper bound node corresponding to the given key,
|
||||
* or %NULL if the tree is empty or all the nodes in the tree
|
||||
* have keys that are lower than or equal to the searched key.
|
||||
*
|
||||
* The upper bound is the first node that has its key strictly greater
|
||||
* than the searched key.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the tree node corresponding to the
|
||||
* upper bound, or %NULL if the tree is empty or has only keys
|
||||
* lower than or equal to the searched key.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
GTreeNode *
|
||||
g_tree_upper_bound (GTree *tree,
|
||||
gconstpointer key)
|
||||
{
|
||||
GTreeNode *node, *result;
|
||||
gint cmp;
|
||||
|
||||
g_return_val_if_fail (tree != NULL, NULL);
|
||||
|
||||
node = tree->root;
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
result = NULL;
|
||||
while (1)
|
||||
{
|
||||
cmp = tree->key_compare (key, node->key, tree->key_compare_data);
|
||||
if (cmp < 0)
|
||||
{
|
||||
result = node;
|
||||
|
||||
if (!node->left_child)
|
||||
return result;
|
||||
|
||||
node = node->left;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!node->right_child)
|
||||
return result;
|
||||
|
||||
node = node->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1203,7 +1573,7 @@ g_tree_node_post_order (GTreeNode *node,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
static GTreeNode *
|
||||
g_tree_node_search (GTreeNode *node,
|
||||
GCompareFunc search_func,
|
||||
gconstpointer data)
|
||||
@ -1217,7 +1587,7 @@ g_tree_node_search (GTreeNode *node,
|
||||
{
|
||||
dir = (* search_func) (node->key, data);
|
||||
if (dir == 0)
|
||||
return node->value;
|
||||
return node;
|
||||
else if (dir < 0)
|
||||
{
|
||||
if (!node->left_child)
|
||||
@ -1388,17 +1758,22 @@ g_tree_node_dump (GTreeNode *node,
|
||||
g_print ("%*s%c\n", indent, "", *(char *)node->key);
|
||||
|
||||
if (node->left_child)
|
||||
g_tree_node_dump (node->left, indent + 2);
|
||||
{
|
||||
g_print ("%*sLEFT\n", indent, "");
|
||||
g_tree_node_dump (node->left, indent + 2);
|
||||
}
|
||||
else if (node->left)
|
||||
g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key);
|
||||
|
||||
if (node->right_child)
|
||||
g_tree_node_dump (node->right, indent + 2);
|
||||
{
|
||||
g_print ("%*sRIGHT\n", indent, "");
|
||||
g_tree_node_dump (node->right, indent + 2);
|
||||
}
|
||||
else if (node->right)
|
||||
g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
g_tree_dump (GTree *tree)
|
||||
{
|
||||
|
71
glib/gtree.h
71
glib/gtree.h
@ -33,12 +33,39 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#undef G_TREE_DEBUG
|
||||
|
||||
typedef struct _GTree GTree;
|
||||
|
||||
/**
|
||||
* GTreeNode:
|
||||
*
|
||||
* An opaque type which identifies a specific node in a #GTree.
|
||||
*
|
||||
* Since: 2.68
|
||||
*/
|
||||
typedef struct _GTreeNode GTreeNode;
|
||||
|
||||
typedef gboolean (*GTraverseFunc) (gpointer key,
|
||||
gpointer value,
|
||||
gpointer data);
|
||||
|
||||
/**
|
||||
* GTraverseNodeFunc:
|
||||
* @node: a #GTreeNode
|
||||
* @data: user data passed to g_tree_foreach_node()
|
||||
*
|
||||
* Specifies the type of function passed to g_tree_foreach_node(). It is
|
||||
* passed each node, together with the @user_data parameter passed to
|
||||
* g_tree_foreach_node(). If the function returns %TRUE, the traversal is
|
||||
* stopped.
|
||||
*
|
||||
* Returns: %TRUE to stop the traversal
|
||||
* Since: 2.68
|
||||
*/
|
||||
typedef gboolean (*GTraverseNodeFunc) (GTreeNode *node,
|
||||
gpointer data);
|
||||
|
||||
/* Balanced binary trees
|
||||
*/
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
@ -51,16 +78,32 @@ GTree* g_tree_new_full (GCompareDataFunc key_compare_func,
|
||||
gpointer key_compare_data,
|
||||
GDestroyNotify key_destroy_func,
|
||||
GDestroyNotify value_destroy_func);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_node_first (GTree *tree);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_node_last (GTree *tree);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_node_previous (GTreeNode *node);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_node_next (GTreeNode *node);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GTree* g_tree_ref (GTree *tree);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_tree_unref (GTree *tree);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_tree_destroy (GTree *tree);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_insert_node (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_tree_insert (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_replace_node (GTree *tree,
|
||||
gpointer key,
|
||||
gpointer value);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
void g_tree_replace (GTree *tree,
|
||||
gpointer key,
|
||||
@ -71,6 +114,13 @@ gboolean g_tree_remove (GTree *tree,
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gboolean g_tree_steal (GTree *tree,
|
||||
gconstpointer key);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
gpointer g_tree_node_key (GTreeNode *node);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
gpointer g_tree_node_value (GTreeNode *node);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_lookup_node (GTree *tree,
|
||||
gconstpointer key);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gpointer g_tree_lookup (GTree *tree,
|
||||
gconstpointer key);
|
||||
@ -83,6 +133,10 @@ GLIB_AVAILABLE_IN_ALL
|
||||
void g_tree_foreach (GTree *tree,
|
||||
GTraverseFunc func,
|
||||
gpointer user_data);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
void g_tree_foreach_node (GTree *tree,
|
||||
GTraverseNodeFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GLIB_DEPRECATED
|
||||
void g_tree_traverse (GTree *tree,
|
||||
@ -90,15 +144,32 @@ void g_tree_traverse (GTree *tree,
|
||||
GTraverseType traverse_type,
|
||||
gpointer user_data);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_search_node (GTree *tree,
|
||||
GCompareFunc search_func,
|
||||
gconstpointer user_data);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gpointer g_tree_search (GTree *tree,
|
||||
GCompareFunc search_func,
|
||||
gconstpointer user_data);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_lower_bound (GTree *tree,
|
||||
gconstpointer key);
|
||||
GLIB_AVAILABLE_IN_2_68
|
||||
GTreeNode *g_tree_upper_bound (GTree *tree,
|
||||
gconstpointer key);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gint g_tree_height (GTree *tree);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gint g_tree_nnodes (GTree *tree);
|
||||
|
||||
#ifdef G_TREE_DEBUG
|
||||
/*< private >*/
|
||||
#ifndef __GTK_DOC_IGNORE__
|
||||
void g_tree_dump (GTree *tree);
|
||||
#endif /* !__GTK_DOC_IGNORE__ */
|
||||
#endif /* G_TREE_DEBUG */
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_TREE_H__ */
|
||||
|
189
tests/testglib.c
189
tests/testglib.c
@ -397,9 +397,139 @@ my_traverse (gpointer key,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
binary_tree_bound (GTree *tree,
|
||||
char c,
|
||||
char expected,
|
||||
int lower)
|
||||
{
|
||||
GTreeNode *node;
|
||||
|
||||
if (lower)
|
||||
node = g_tree_lower_bound (tree, &c);
|
||||
else
|
||||
node = g_tree_upper_bound (tree, &c);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("%c %s: ", c, lower ? "lower" : "upper");
|
||||
|
||||
if (!node)
|
||||
{
|
||||
if (!g_tree_nnodes (tree))
|
||||
{
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("empty tree");
|
||||
}
|
||||
else
|
||||
{
|
||||
GTreeNode *last = g_tree_node_last (tree);
|
||||
|
||||
g_assert (last);
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("past end last %c",
|
||||
*(char *) g_tree_node_key (last));
|
||||
}
|
||||
g_assert (expected == '\x00');
|
||||
}
|
||||
else
|
||||
{
|
||||
GTreeNode *begin = g_tree_node_first (tree);
|
||||
GTreeNode *last = g_tree_node_last (tree);
|
||||
GTreeNode *prev = g_tree_node_previous (node);
|
||||
GTreeNode *next = g_tree_node_next (node);
|
||||
|
||||
g_assert (expected != '\x00');
|
||||
g_assert (expected == *(char *) g_tree_node_key (node));
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("%c", *(char *) g_tree_node_key (node));
|
||||
|
||||
if (node != begin)
|
||||
{
|
||||
g_assert (prev);
|
||||
if (g_test_verbose ())
|
||||
g_printerr (" prev %c", *(char *) g_tree_node_key (prev));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (!prev);
|
||||
if (g_test_verbose ())
|
||||
g_printerr (" no prev, it's the first one");
|
||||
}
|
||||
|
||||
if (node != last)
|
||||
{
|
||||
g_assert (next);
|
||||
if (g_test_verbose ())
|
||||
g_printerr (" next %c", *(char *) g_tree_node_key (next));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (!next);
|
||||
if (g_test_verbose ())
|
||||
g_printerr (" no next, it's the last one");
|
||||
}
|
||||
}
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
binary_tree_bounds (GTree *tree,
|
||||
char c,
|
||||
int mode)
|
||||
{
|
||||
char expectedl, expectedu;
|
||||
char first = mode == 0 ? '0' : mode == 1 ? 'A' : 'z';
|
||||
|
||||
g_assert (mode >= 0 && mode <= 3);
|
||||
|
||||
if (c < first)
|
||||
expectedl = first;
|
||||
else if (c > 'z')
|
||||
expectedl = '\x00';
|
||||
else
|
||||
expectedl = c;
|
||||
|
||||
if (c < first)
|
||||
expectedu = first;
|
||||
else if (c >= 'z')
|
||||
expectedu = '\x00';
|
||||
else
|
||||
expectedu = c == '9' ? 'A' : c == 'Z' ? 'a' : c + 1;
|
||||
|
||||
if (mode == 3)
|
||||
{
|
||||
expectedl = '\x00';
|
||||
expectedu = '\x00';
|
||||
}
|
||||
|
||||
binary_tree_bound (tree, c, expectedl, 1);
|
||||
binary_tree_bound (tree, c, expectedu, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
binary_tree_bounds_test (GTree *tree,
|
||||
int mode)
|
||||
{
|
||||
binary_tree_bounds (tree, 'a', mode);
|
||||
binary_tree_bounds (tree, 'A', mode);
|
||||
binary_tree_bounds (tree, 'z', mode);
|
||||
binary_tree_bounds (tree, 'Z', mode);
|
||||
binary_tree_bounds (tree, 'Y', mode);
|
||||
binary_tree_bounds (tree, '0', mode);
|
||||
binary_tree_bounds (tree, '9', mode);
|
||||
binary_tree_bounds (tree, '0' - 1, mode);
|
||||
binary_tree_bounds (tree, 'z' + 1, mode);
|
||||
binary_tree_bounds (tree, '0' - 2, mode);
|
||||
binary_tree_bounds (tree, 'z' + 2, mode);
|
||||
}
|
||||
|
||||
static void
|
||||
binary_tree_test (void)
|
||||
{
|
||||
GQueue queue = G_QUEUE_INIT;
|
||||
GTree *tree;
|
||||
char chars[62];
|
||||
guint i, j;
|
||||
@ -409,42 +539,85 @@ binary_tree_test (void)
|
||||
for (j = 0; j < 10; j++, i++)
|
||||
{
|
||||
chars[i] = '0' + j;
|
||||
g_tree_insert (tree, &chars[i], &chars[i]);
|
||||
g_queue_push_tail (&queue, &chars[i]);
|
||||
}
|
||||
for (j = 0; j < 26; j++, i++)
|
||||
{
|
||||
chars[i] = 'A' + j;
|
||||
g_tree_insert (tree, &chars[i], &chars[i]);
|
||||
g_queue_push_tail (&queue, &chars[i]);
|
||||
}
|
||||
for (j = 0; j < 26; j++, i++)
|
||||
{
|
||||
chars[i] = 'a' + j;
|
||||
g_tree_insert (tree, &chars[i], &chars[i]);
|
||||
g_queue_push_tail (&queue, &chars[i]);
|
||||
}
|
||||
|
||||
g_assert_cmpint (g_tree_nnodes (tree), ==, 10 + 26 + 26);
|
||||
g_assert_cmpint (g_tree_height (tree), ==, 6);
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("tree insert: ");
|
||||
while (!g_queue_is_empty (&queue))
|
||||
{
|
||||
gint32 which = g_random_int_range (0, g_queue_get_length (&queue));
|
||||
gpointer elem = g_queue_pop_nth (&queue, which);
|
||||
GTreeNode *node;
|
||||
|
||||
if (g_test_verbose())
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("%c ", *(char *) elem);
|
||||
|
||||
node = g_tree_insert_node (tree, elem, elem);
|
||||
g_assert (g_tree_node_key (node) == elem);
|
||||
g_assert (g_tree_node_value (node) == elem);
|
||||
}
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("\n");
|
||||
|
||||
g_assert_cmpint (g_tree_nnodes (tree), ==, 10 + 26 + 26);
|
||||
g_assert_cmpint (g_tree_height (tree), >=, 6);
|
||||
g_assert_cmpint (g_tree_height (tree), <=, 8);
|
||||
|
||||
if (g_test_verbose ())
|
||||
{
|
||||
g_printerr ("tree: ");
|
||||
g_tree_foreach (tree, my_traverse, NULL);
|
||||
g_printerr ("\n");
|
||||
}
|
||||
|
||||
binary_tree_bounds_test (tree, 0);
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
g_tree_remove (tree, &chars[i]);
|
||||
|
||||
g_assert_cmpint (g_tree_nnodes (tree), ==, 26 + 26);
|
||||
g_assert_cmpint (g_tree_height (tree), ==, 6);
|
||||
g_assert_cmpint (g_tree_height (tree), >=, 6);
|
||||
g_assert_cmpint (g_tree_height (tree), <=, 8);
|
||||
|
||||
if (g_test_verbose())
|
||||
if (g_test_verbose ())
|
||||
{
|
||||
g_printerr ("tree: ");
|
||||
g_tree_foreach (tree, my_traverse, NULL);
|
||||
g_printerr ("\n");
|
||||
}
|
||||
|
||||
binary_tree_bounds_test (tree, 1);
|
||||
|
||||
for (i = 10; i < 10 + 26 + 26 - 1; i++)
|
||||
g_tree_remove (tree, &chars[i]);
|
||||
|
||||
if (g_test_verbose ())
|
||||
{
|
||||
g_printerr ("tree: ");
|
||||
g_tree_foreach (tree, my_traverse, NULL);
|
||||
g_printerr ("\n");
|
||||
}
|
||||
|
||||
binary_tree_bounds_test (tree, 2);
|
||||
|
||||
g_tree_remove (tree, &chars[10 + 26 + 26 - 1]);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_printerr ("empty tree\n");
|
||||
|
||||
binary_tree_bounds_test (tree, 3);
|
||||
|
||||
g_tree_unref (tree);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user