mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-21 16:38:54 +02: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:
@@ -2982,21 +2982,36 @@ g_bytes_get_type
|
|||||||
<TITLE>Balanced Binary Trees</TITLE>
|
<TITLE>Balanced Binary Trees</TITLE>
|
||||||
<FILE>trees-binary</FILE>
|
<FILE>trees-binary</FILE>
|
||||||
GTree
|
GTree
|
||||||
|
GTreeNode
|
||||||
g_tree_new
|
g_tree_new
|
||||||
g_tree_ref
|
g_tree_ref
|
||||||
g_tree_unref
|
g_tree_unref
|
||||||
g_tree_new_with_data
|
g_tree_new_with_data
|
||||||
g_tree_new_full
|
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_insert
|
||||||
|
g_tree_replace_node
|
||||||
g_tree_replace
|
g_tree_replace
|
||||||
|
g_tree_node_key
|
||||||
|
g_tree_node_value
|
||||||
g_tree_nnodes
|
g_tree_nnodes
|
||||||
g_tree_height
|
g_tree_height
|
||||||
|
g_tree_lookup_node
|
||||||
g_tree_lookup
|
g_tree_lookup
|
||||||
g_tree_lookup_extended
|
g_tree_lookup_extended
|
||||||
|
g_tree_foreach_node
|
||||||
g_tree_foreach
|
g_tree_foreach
|
||||||
g_tree_traverse
|
g_tree_traverse
|
||||||
GTraverseFunc
|
GTraverseFunc
|
||||||
|
GTraverseNodeFunc
|
||||||
|
g_tree_search_node
|
||||||
g_tree_search
|
g_tree_search
|
||||||
|
g_tree_lower_bound
|
||||||
|
g_tree_upper_bound
|
||||||
g_tree_remove
|
g_tree_remove
|
||||||
g_tree_steal
|
g_tree_steal
|
||||||
g_tree_destroy
|
g_tree_destroy
|
||||||
|
481
glib/gtree.c
481
glib/gtree.c
@@ -66,12 +66,8 @@
|
|||||||
* To destroy a #GTree, use g_tree_destroy().
|
* To destroy a #GTree, use g_tree_destroy().
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#undef G_TREE_DEBUG
|
|
||||||
|
|
||||||
#define MAX_GTREE_HEIGHT 40
|
#define MAX_GTREE_HEIGHT 40
|
||||||
|
|
||||||
typedef struct _GTreeNode GTreeNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GTree:
|
* GTree:
|
||||||
*
|
*
|
||||||
@@ -104,10 +100,10 @@ struct _GTreeNode
|
|||||||
|
|
||||||
static GTreeNode* g_tree_node_new (gpointer key,
|
static GTreeNode* g_tree_node_new (gpointer key,
|
||||||
gpointer value);
|
gpointer value);
|
||||||
static void g_tree_insert_internal (GTree *tree,
|
static GTreeNode *g_tree_insert_internal (GTree *tree,
|
||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value,
|
gpointer value,
|
||||||
gboolean replace);
|
gboolean replace);
|
||||||
static gboolean g_tree_remove_internal (GTree *tree,
|
static gboolean g_tree_remove_internal (GTree *tree,
|
||||||
gconstpointer key,
|
gconstpointer key,
|
||||||
gboolean steal);
|
gboolean steal);
|
||||||
@@ -123,9 +119,9 @@ static gint g_tree_node_in_order (GTreeNode *node,
|
|||||||
static gint g_tree_node_post_order (GTreeNode *node,
|
static gint g_tree_node_post_order (GTreeNode *node,
|
||||||
GTraverseFunc traverse_func,
|
GTraverseFunc traverse_func,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
static gpointer g_tree_node_search (GTreeNode *node,
|
static GTreeNode *g_tree_node_search (GTreeNode *node,
|
||||||
GCompareFunc search_func,
|
GCompareFunc search_func,
|
||||||
gconstpointer data);
|
gconstpointer data);
|
||||||
static GTreeNode* g_tree_node_rotate_left (GTreeNode *node);
|
static GTreeNode* g_tree_node_rotate_left (GTreeNode *node);
|
||||||
static GTreeNode* g_tree_node_rotate_right (GTreeNode *node);
|
static GTreeNode* g_tree_node_rotate_right (GTreeNode *node);
|
||||||
#ifdef G_TREE_DEBUG
|
#ifdef G_TREE_DEBUG
|
||||||
@@ -230,11 +226,24 @@ g_tree_new_full (GCompareDataFunc key_compare_func,
|
|||||||
return tree;
|
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;
|
GTreeNode *tmp;
|
||||||
|
|
||||||
|
g_return_val_if_fail (tree != NULL, NULL);
|
||||||
|
|
||||||
if (!tree->root)
|
if (!tree->root)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -244,13 +253,55 @@ g_tree_first_node (GTree *tree)
|
|||||||
tmp = tmp->left;
|
tmp = tmp->left;
|
||||||
|
|
||||||
return tmp;
|
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)
|
g_tree_node_previous (GTreeNode *node)
|
||||||
{
|
{
|
||||||
GTreeNode *tmp;
|
GTreeNode *tmp;
|
||||||
|
|
||||||
|
g_return_val_if_fail (node != NULL, NULL);
|
||||||
|
|
||||||
tmp = node->left;
|
tmp = node->left;
|
||||||
|
|
||||||
if (node->left_child)
|
if (node->left_child)
|
||||||
@@ -260,11 +311,24 @@ g_tree_node_previous (GTreeNode *node)
|
|||||||
return tmp;
|
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)
|
g_tree_node_next (GTreeNode *node)
|
||||||
{
|
{
|
||||||
GTreeNode *tmp;
|
GTreeNode *tmp;
|
||||||
|
|
||||||
|
g_return_val_if_fail (node != NULL, NULL);
|
||||||
|
|
||||||
tmp = node->right;
|
tmp = node->right;
|
||||||
|
|
||||||
if (node->right_child)
|
if (node->right_child)
|
||||||
@@ -282,7 +346,7 @@ g_tree_remove_all (GTree *tree)
|
|||||||
|
|
||||||
g_return_if_fail (tree != NULL);
|
g_return_if_fail (tree != NULL);
|
||||||
|
|
||||||
node = g_tree_first_node (tree);
|
node = g_tree_node_first (tree);
|
||||||
|
|
||||||
while (node)
|
while (node)
|
||||||
{
|
{
|
||||||
@@ -294,11 +358,22 @@ g_tree_remove_all (GTree *tree)
|
|||||||
tree->value_destroy_func (node->value);
|
tree->value_destroy_func (node->value);
|
||||||
g_slice_free (GTreeNode, node);
|
g_slice_free (GTreeNode, node);
|
||||||
|
|
||||||
|
#ifdef G_TREE_DEBUG
|
||||||
|
g_assert (tree->nnodes > 0);
|
||||||
|
tree->nnodes--;
|
||||||
|
#endif
|
||||||
|
|
||||||
node = next;
|
node = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef G_TREE_DEBUG
|
||||||
|
g_assert (tree->nnodes == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
tree->root = NULL;
|
tree->root = NULL;
|
||||||
|
#ifndef G_TREE_DEBUG
|
||||||
tree->nnodes = 0;
|
tree->nnodes = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -369,7 +444,7 @@ g_tree_destroy (GTree *tree)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_tree_insert:
|
* g_tree_insert_node:
|
||||||
* @tree: a #GTree
|
* @tree: a #GTree
|
||||||
* @key: the key to insert
|
* @key: the key to insert
|
||||||
* @value: the value corresponding to the key
|
* @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
|
* 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
|
* result in a O(n log(n)) operation where most of the other operations
|
||||||
* are O(log(n)).
|
* 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
|
void
|
||||||
g_tree_insert (GTree *tree,
|
g_tree_insert (GTree *tree,
|
||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value)
|
gpointer value)
|
||||||
{
|
{
|
||||||
g_return_if_fail (tree != NULL);
|
g_tree_insert_node (tree, key, value);
|
||||||
|
|
||||||
g_tree_insert_internal (tree, key, value, FALSE);
|
|
||||||
|
|
||||||
#ifdef G_TREE_DEBUG
|
|
||||||
g_tree_node_check (tree->root);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_tree_replace:
|
* g_tree_replace_node:
|
||||||
* @tree: a #GTree
|
* @tree: a #GTree
|
||||||
* @key: the key to insert
|
* @key: the key to insert
|
||||||
* @value: the value corresponding to the key
|
* @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
|
* 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
|
* 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
|
* 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,
|
* 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.
|
* 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
|
void
|
||||||
g_tree_replace (GTree *tree,
|
g_tree_replace (GTree *tree,
|
||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value)
|
gpointer value)
|
||||||
{
|
{
|
||||||
g_return_if_fail (tree != NULL);
|
g_tree_replace_node (tree, key, value);
|
||||||
|
|
||||||
g_tree_insert_internal (tree, key, value, TRUE);
|
|
||||||
|
|
||||||
#ifdef G_TREE_DEBUG
|
|
||||||
g_tree_node_check (tree->root);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* internal insert routine */
|
/* internal insert routine */
|
||||||
static void
|
static GTreeNode *
|
||||||
g_tree_insert_internal (GTree *tree,
|
g_tree_insert_internal (GTree *tree,
|
||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value,
|
gpointer value,
|
||||||
gboolean replace)
|
gboolean replace)
|
||||||
{
|
{
|
||||||
GTreeNode *node;
|
GTreeNode *node, *retnode;
|
||||||
GTreeNode *path[MAX_GTREE_HEIGHT];
|
GTreeNode *path[MAX_GTREE_HEIGHT];
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
g_return_if_fail (tree != NULL);
|
g_return_val_if_fail (tree != NULL, NULL);
|
||||||
|
|
||||||
if (!tree->root)
|
if (!tree->root)
|
||||||
{
|
{
|
||||||
tree->root = g_tree_node_new (key, value);
|
tree->root = g_tree_node_new (key, value);
|
||||||
tree->nnodes++;
|
tree->nnodes++;
|
||||||
return;
|
return tree->root;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = 0;
|
idx = 0;
|
||||||
@@ -481,7 +608,7 @@ g_tree_insert_internal (GTree *tree,
|
|||||||
tree->key_destroy_func (key);
|
tree->key_destroy_func (key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return node;
|
||||||
}
|
}
|
||||||
else if (cmp < 0)
|
else if (cmp < 0)
|
||||||
{
|
{
|
||||||
@@ -502,6 +629,7 @@ g_tree_insert_internal (GTree *tree,
|
|||||||
|
|
||||||
tree->nnodes++;
|
tree->nnodes++;
|
||||||
|
|
||||||
|
retnode = child;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,6 +652,7 @@ g_tree_insert_internal (GTree *tree,
|
|||||||
|
|
||||||
tree->nnodes++;
|
tree->nnodes++;
|
||||||
|
|
||||||
|
retnode = child;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -560,6 +689,8 @@ g_tree_insert_internal (GTree *tree,
|
|||||||
|
|
||||||
node = bparent;
|
node = bparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return retnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -835,6 +966,65 @@ g_tree_remove_internal (GTree *tree,
|
|||||||
return TRUE;
|
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:
|
* g_tree_lookup:
|
||||||
* @tree: a #GTree
|
* @tree: a #GTree
|
||||||
@@ -853,10 +1043,8 @@ g_tree_lookup (GTree *tree,
|
|||||||
{
|
{
|
||||||
GTreeNode *node;
|
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;
|
return node ? node->value : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -926,8 +1114,8 @@ g_tree_foreach (GTree *tree,
|
|||||||
if (!tree->root)
|
if (!tree->root)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
node = g_tree_first_node (tree);
|
node = g_tree_node_first (tree);
|
||||||
|
|
||||||
while (node)
|
while (node)
|
||||||
{
|
{
|
||||||
if ((*func) (node->key, node->value, user_data))
|
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:
|
* g_tree_traverse:
|
||||||
* @tree: a #GTree
|
* @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:
|
* g_tree_search:
|
||||||
* @tree: a #GTree
|
* @tree: a #GTree
|
||||||
@@ -1021,12 +1284,119 @@ g_tree_search (GTree *tree,
|
|||||||
GCompareFunc search_func,
|
GCompareFunc search_func,
|
||||||
gconstpointer user_data)
|
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);
|
g_return_val_if_fail (tree != NULL, NULL);
|
||||||
|
|
||||||
if (tree->root)
|
node = tree->root;
|
||||||
return g_tree_node_search (tree->root, search_func, user_data);
|
if (!node)
|
||||||
else
|
|
||||||
return NULL;
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gpointer
|
static GTreeNode *
|
||||||
g_tree_node_search (GTreeNode *node,
|
g_tree_node_search (GTreeNode *node,
|
||||||
GCompareFunc search_func,
|
GCompareFunc search_func,
|
||||||
gconstpointer data)
|
gconstpointer data)
|
||||||
@@ -1217,7 +1587,7 @@ g_tree_node_search (GTreeNode *node,
|
|||||||
{
|
{
|
||||||
dir = (* search_func) (node->key, data);
|
dir = (* search_func) (node->key, data);
|
||||||
if (dir == 0)
|
if (dir == 0)
|
||||||
return node->value;
|
return node;
|
||||||
else if (dir < 0)
|
else if (dir < 0)
|
||||||
{
|
{
|
||||||
if (!node->left_child)
|
if (!node->left_child)
|
||||||
@@ -1388,17 +1758,22 @@ g_tree_node_dump (GTreeNode *node,
|
|||||||
g_print ("%*s%c\n", indent, "", *(char *)node->key);
|
g_print ("%*s%c\n", indent, "", *(char *)node->key);
|
||||||
|
|
||||||
if (node->left_child)
|
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)
|
else if (node->left)
|
||||||
g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key);
|
g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key);
|
||||||
|
|
||||||
if (node->right_child)
|
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)
|
else if (node->right)
|
||||||
g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key);
|
g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
g_tree_dump (GTree *tree)
|
g_tree_dump (GTree *tree)
|
||||||
{
|
{
|
||||||
|
71
glib/gtree.h
71
glib/gtree.h
@@ -33,12 +33,39 @@
|
|||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#undef G_TREE_DEBUG
|
||||||
|
|
||||||
typedef struct _GTree GTree;
|
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,
|
typedef gboolean (*GTraverseFunc) (gpointer key,
|
||||||
gpointer value,
|
gpointer value,
|
||||||
gpointer data);
|
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
|
/* Balanced binary trees
|
||||||
*/
|
*/
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
@@ -51,16 +78,32 @@ GTree* g_tree_new_full (GCompareDataFunc key_compare_func,
|
|||||||
gpointer key_compare_data,
|
gpointer key_compare_data,
|
||||||
GDestroyNotify key_destroy_func,
|
GDestroyNotify key_destroy_func,
|
||||||
GDestroyNotify value_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
|
GLIB_AVAILABLE_IN_ALL
|
||||||
GTree* g_tree_ref (GTree *tree);
|
GTree* g_tree_ref (GTree *tree);
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
void g_tree_unref (GTree *tree);
|
void g_tree_unref (GTree *tree);
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
void g_tree_destroy (GTree *tree);
|
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
|
GLIB_AVAILABLE_IN_ALL
|
||||||
void g_tree_insert (GTree *tree,
|
void g_tree_insert (GTree *tree,
|
||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value);
|
gpointer value);
|
||||||
|
GLIB_AVAILABLE_IN_2_68
|
||||||
|
GTreeNode *g_tree_replace_node (GTree *tree,
|
||||||
|
gpointer key,
|
||||||
|
gpointer value);
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
void g_tree_replace (GTree *tree,
|
void g_tree_replace (GTree *tree,
|
||||||
gpointer key,
|
gpointer key,
|
||||||
@@ -71,6 +114,13 @@ gboolean g_tree_remove (GTree *tree,
|
|||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
gboolean g_tree_steal (GTree *tree,
|
gboolean g_tree_steal (GTree *tree,
|
||||||
gconstpointer key);
|
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
|
GLIB_AVAILABLE_IN_ALL
|
||||||
gpointer g_tree_lookup (GTree *tree,
|
gpointer g_tree_lookup (GTree *tree,
|
||||||
gconstpointer key);
|
gconstpointer key);
|
||||||
@@ -83,6 +133,10 @@ GLIB_AVAILABLE_IN_ALL
|
|||||||
void g_tree_foreach (GTree *tree,
|
void g_tree_foreach (GTree *tree,
|
||||||
GTraverseFunc func,
|
GTraverseFunc func,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
GLIB_AVAILABLE_IN_2_68
|
||||||
|
void g_tree_foreach_node (GTree *tree,
|
||||||
|
GTraverseNodeFunc func,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
GLIB_DEPRECATED
|
GLIB_DEPRECATED
|
||||||
void g_tree_traverse (GTree *tree,
|
void g_tree_traverse (GTree *tree,
|
||||||
@@ -90,15 +144,32 @@ void g_tree_traverse (GTree *tree,
|
|||||||
GTraverseType traverse_type,
|
GTraverseType traverse_type,
|
||||||
gpointer user_data);
|
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
|
GLIB_AVAILABLE_IN_ALL
|
||||||
gpointer g_tree_search (GTree *tree,
|
gpointer g_tree_search (GTree *tree,
|
||||||
GCompareFunc search_func,
|
GCompareFunc search_func,
|
||||||
gconstpointer user_data);
|
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
|
GLIB_AVAILABLE_IN_ALL
|
||||||
gint g_tree_height (GTree *tree);
|
gint g_tree_height (GTree *tree);
|
||||||
GLIB_AVAILABLE_IN_ALL
|
GLIB_AVAILABLE_IN_ALL
|
||||||
gint g_tree_nnodes (GTree *tree);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __G_TREE_H__ */
|
#endif /* __G_TREE_H__ */
|
||||||
|
189
tests/testglib.c
189
tests/testglib.c
@@ -397,9 +397,139 @@ my_traverse (gpointer key,
|
|||||||
return FALSE;
|
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
|
static void
|
||||||
binary_tree_test (void)
|
binary_tree_test (void)
|
||||||
{
|
{
|
||||||
|
GQueue queue = G_QUEUE_INIT;
|
||||||
GTree *tree;
|
GTree *tree;
|
||||||
char chars[62];
|
char chars[62];
|
||||||
guint i, j;
|
guint i, j;
|
||||||
@@ -409,42 +539,85 @@ binary_tree_test (void)
|
|||||||
for (j = 0; j < 10; j++, i++)
|
for (j = 0; j < 10; j++, i++)
|
||||||
{
|
{
|
||||||
chars[i] = '0' + j;
|
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++)
|
for (j = 0; j < 26; j++, i++)
|
||||||
{
|
{
|
||||||
chars[i] = 'A' + j;
|
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++)
|
for (j = 0; j < 26; j++, i++)
|
||||||
{
|
{
|
||||||
chars[i] = 'a' + j;
|
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);
|
if (g_test_verbose ())
|
||||||
g_assert_cmpint (g_tree_height (tree), ==, 6);
|
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_printerr ("tree: ");
|
||||||
g_tree_foreach (tree, my_traverse, NULL);
|
g_tree_foreach (tree, my_traverse, NULL);
|
||||||
g_printerr ("\n");
|
g_printerr ("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binary_tree_bounds_test (tree, 0);
|
||||||
|
|
||||||
for (i = 0; i < 10; i++)
|
for (i = 0; i < 10; i++)
|
||||||
g_tree_remove (tree, &chars[i]);
|
g_tree_remove (tree, &chars[i]);
|
||||||
|
|
||||||
g_assert_cmpint (g_tree_nnodes (tree), ==, 26 + 26);
|
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_printerr ("tree: ");
|
||||||
g_tree_foreach (tree, my_traverse, NULL);
|
g_tree_foreach (tree, my_traverse, NULL);
|
||||||
g_printerr ("\n");
|
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);
|
g_tree_unref (tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user