mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-04-01 21:33:09 +02:00
Merge branch 'gtree-overflow' into 'main'
GTree: Handle node counter overflow and return it as an unsigned value See merge request GNOME/glib!3521
This commit is contained in:
commit
3029f08ed8
111
glib/gtree.c
111
glib/gtree.c
@ -69,6 +69,8 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
#define MAX_GTREE_HEIGHT 40
|
#define MAX_GTREE_HEIGHT 40
|
||||||
|
/* G_MAXUINT nodes will be covered by tree height of log2(G_MAXUINT) + 2. */
|
||||||
|
G_STATIC_ASSERT ((G_GUINT64_CONSTANT (1) << (MAX_GTREE_HEIGHT - 2)) >= G_MAXUINT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GTree:
|
* GTree:
|
||||||
@ -105,7 +107,8 @@ static GTreeNode* g_tree_node_new (gpointer key,
|
|||||||
static GTreeNode *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,
|
||||||
|
gboolean null_ret_ok);
|
||||||
static gboolean g_tree_remove_internal (GTree *tree,
|
static gboolean g_tree_remove_internal (GTree *tree,
|
||||||
gconstpointer key,
|
gconstpointer key,
|
||||||
gboolean steal);
|
gboolean steal);
|
||||||
@ -454,6 +457,26 @@ g_tree_destroy (GTree *tree)
|
|||||||
g_tree_unref (tree);
|
g_tree_unref (tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GTreeNode *
|
||||||
|
g_tree_insert_replace_node_internal (GTree *tree,
|
||||||
|
gpointer key,
|
||||||
|
gpointer value,
|
||||||
|
gboolean replace,
|
||||||
|
gboolean null_ret_ok)
|
||||||
|
{
|
||||||
|
GTreeNode *node;
|
||||||
|
|
||||||
|
g_return_val_if_fail (tree != NULL, NULL);
|
||||||
|
|
||||||
|
node = g_tree_insert_internal (tree, key, value, replace, null_ret_ok);
|
||||||
|
|
||||||
|
#ifdef G_TREE_DEBUG
|
||||||
|
g_tree_node_check (tree->root);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_tree_insert_node:
|
* g_tree_insert_node:
|
||||||
* @tree: a #GTree
|
* @tree: a #GTree
|
||||||
@ -474,7 +497,8 @@ g_tree_destroy (GTree *tree)
|
|||||||
* 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.
|
* Returns: (transfer none) (nullable): the inserted (or set) node or %NULL
|
||||||
|
* if insertion would overflow the tree node counter.
|
||||||
*
|
*
|
||||||
* Since: 2.68
|
* Since: 2.68
|
||||||
*/
|
*/
|
||||||
@ -483,17 +507,7 @@ g_tree_insert_node (GTree *tree,
|
|||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value)
|
gpointer value)
|
||||||
{
|
{
|
||||||
GTreeNode *node;
|
return g_tree_insert_replace_node_internal (tree, key, value, FALSE, TRUE);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -512,7 +526,7 @@ g_tree_insert (GTree *tree,
|
|||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value)
|
gpointer value)
|
||||||
{
|
{
|
||||||
g_tree_insert_node (tree, key, value);
|
g_tree_insert_replace_node_internal (tree, key, value, FALSE, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -531,7 +545,8 @@ 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.
|
* Returns: (transfer none) (nullable): the inserted (or set) node or %NULL
|
||||||
|
* if insertion would overflow the tree node counter.
|
||||||
*
|
*
|
||||||
* Since: 2.68
|
* Since: 2.68
|
||||||
*/
|
*/
|
||||||
@ -540,17 +555,7 @@ g_tree_replace_node (GTree *tree,
|
|||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value)
|
gpointer value)
|
||||||
{
|
{
|
||||||
GTreeNode *node;
|
return g_tree_insert_replace_node_internal (tree, key, value, TRUE, TRUE);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -567,7 +572,26 @@ g_tree_replace (GTree *tree,
|
|||||||
gpointer key,
|
gpointer key,
|
||||||
gpointer value)
|
gpointer value)
|
||||||
{
|
{
|
||||||
g_tree_replace_node (tree, key, value);
|
g_tree_insert_replace_node_internal (tree, key, value, TRUE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* internal checked nnodes increment routine */
|
||||||
|
static gboolean
|
||||||
|
g_tree_nnodes_inc_checked (GTree *tree, gboolean overflow_fatal)
|
||||||
|
{
|
||||||
|
if (G_UNLIKELY (tree->nnodes == G_MAXUINT))
|
||||||
|
{
|
||||||
|
if (overflow_fatal)
|
||||||
|
{
|
||||||
|
g_error ("Incrementing GTree nnodes counter would overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->nnodes++;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* internal insert routine */
|
/* internal insert routine */
|
||||||
@ -575,7 +599,8 @@ 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,
|
||||||
|
gboolean null_ret_ok)
|
||||||
{
|
{
|
||||||
GTreeNode *node, *retnode;
|
GTreeNode *node, *retnode;
|
||||||
GTreeNode *path[MAX_GTREE_HEIGHT];
|
GTreeNode *path[MAX_GTREE_HEIGHT];
|
||||||
@ -586,7 +611,12 @@ g_tree_insert_internal (GTree *tree,
|
|||||||
if (!tree->root)
|
if (!tree->root)
|
||||||
{
|
{
|
||||||
tree->root = g_tree_node_new (key, value);
|
tree->root = g_tree_node_new (key, value);
|
||||||
|
|
||||||
|
#ifdef G_TREE_DEBUG
|
||||||
|
g_assert (tree->nnodes == 0);
|
||||||
|
#endif
|
||||||
tree->nnodes++;
|
tree->nnodes++;
|
||||||
|
|
||||||
return tree->root;
|
return tree->root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,16 +660,20 @@ g_tree_insert_internal (GTree *tree,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GTreeNode *child = g_tree_node_new (key, value);
|
GTreeNode *child;
|
||||||
|
|
||||||
|
if (!g_tree_nnodes_inc_checked (tree, !null_ret_ok))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = g_tree_node_new (key, value);
|
||||||
child->left = node->left;
|
child->left = node->left;
|
||||||
child->right = node;
|
child->right = node;
|
||||||
node->left = child;
|
node->left = child;
|
||||||
node->left_child = TRUE;
|
node->left_child = TRUE;
|
||||||
node->balance -= 1;
|
node->balance -= 1;
|
||||||
|
|
||||||
tree->nnodes++;
|
|
||||||
|
|
||||||
retnode = child;
|
retnode = child;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -653,16 +687,20 @@ g_tree_insert_internal (GTree *tree,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GTreeNode *child = g_tree_node_new (key, value);
|
GTreeNode *child;
|
||||||
|
|
||||||
|
if (!g_tree_nnodes_inc_checked (tree, !null_ret_ok))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = g_tree_node_new (key, value);
|
||||||
child->right = node->right;
|
child->right = node->right;
|
||||||
child->left = node;
|
child->left = node;
|
||||||
node->right = child;
|
node->right = child;
|
||||||
node->right_child = TRUE;
|
node->right_child = TRUE;
|
||||||
node->balance += 1;
|
node->balance += 1;
|
||||||
|
|
||||||
tree->nnodes++;
|
|
||||||
|
|
||||||
retnode = child;
|
retnode = child;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1454,6 +1492,11 @@ g_tree_height (GTree *tree)
|
|||||||
* Gets the number of nodes in a #GTree.
|
* Gets the number of nodes in a #GTree.
|
||||||
*
|
*
|
||||||
* Returns: the number of nodes in @tree
|
* Returns: the number of nodes in @tree
|
||||||
|
*
|
||||||
|
* The node counter value type is really a #guint,
|
||||||
|
* but it is returned as a #gint due to backward
|
||||||
|
* compatibility issues (can be cast back to #guint to
|
||||||
|
* support its full range of values).
|
||||||
*/
|
*/
|
||||||
gint
|
gint
|
||||||
g_tree_nnodes (GTree *tree)
|
g_tree_nnodes (GTree *tree)
|
||||||
|
@ -237,9 +237,10 @@ static void
|
|||||||
test_tree_remove (void)
|
test_tree_remove (void)
|
||||||
{
|
{
|
||||||
GTree *tree;
|
GTree *tree;
|
||||||
char c, d;
|
char c, d, e, f;
|
||||||
gint i;
|
gint i;
|
||||||
gboolean removed;
|
gboolean removed;
|
||||||
|
GTreeNode *node;
|
||||||
gchar *remove;
|
gchar *remove;
|
||||||
|
|
||||||
tree = g_tree_new_full ((GCompareDataFunc)my_compare, NULL,
|
tree = g_tree_new_full ((GCompareDataFunc)my_compare, NULL,
|
||||||
@ -263,6 +264,12 @@ test_tree_remove (void)
|
|||||||
destroyed_key = NULL;
|
destroyed_key = NULL;
|
||||||
destroyed_value = NULL;
|
destroyed_value = NULL;
|
||||||
|
|
||||||
|
e = '\xff';
|
||||||
|
node = g_tree_insert_node (tree, &e, &e);
|
||||||
|
g_assert (node);
|
||||||
|
g_assert (destroyed_key == NULL);
|
||||||
|
g_assert (destroyed_value == NULL);
|
||||||
|
|
||||||
c = '2';
|
c = '2';
|
||||||
removed = g_tree_remove (tree, &c);
|
removed = g_tree_remove (tree, &c);
|
||||||
g_assert (removed);
|
g_assert (removed);
|
||||||
@ -277,6 +284,14 @@ test_tree_remove (void)
|
|||||||
g_assert (destroyed_key == NULL);
|
g_assert (destroyed_key == NULL);
|
||||||
g_assert (destroyed_value == NULL);
|
g_assert (destroyed_value == NULL);
|
||||||
|
|
||||||
|
f = '4';
|
||||||
|
node = g_tree_replace_node (tree, &f, &f);
|
||||||
|
g_assert (node);
|
||||||
|
g_assert (destroyed_key == &chars[4]);
|
||||||
|
g_assert (destroyed_value == &chars[4]);
|
||||||
|
destroyed_key = NULL;
|
||||||
|
destroyed_value = NULL;
|
||||||
|
|
||||||
remove = "omkjigfedba";
|
remove = "omkjigfedba";
|
||||||
for (i = 0; remove[i]; i++)
|
for (i = 0; remove[i]; i++)
|
||||||
{
|
{
|
||||||
@ -655,6 +670,7 @@ test_tree_bounds (void)
|
|||||||
g_test_message ("%c ", *(char *) elem);
|
g_test_message ("%c ", *(char *) elem);
|
||||||
|
|
||||||
node = g_tree_insert_node (tree, elem, elem);
|
node = g_tree_insert_node (tree, elem, elem);
|
||||||
|
g_assert (node);
|
||||||
g_assert (g_tree_node_key (node) == elem);
|
g_assert (g_tree_node_key (node) == elem);
|
||||||
g_assert (g_tree_node_value (node) == elem);
|
g_assert (g_tree_node_value (node) == elem);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user