Merge branch 'th/hash-steal-extended-set' into 'main'

ghash: fix g_hash_table_steal_extended() when requesting key and value of a set

See merge request GNOME/glib!2980
This commit is contained in:
Michael Catanzaro 2024-08-09 18:54:15 +00:00
commit d8514c4b8d
2 changed files with 58 additions and 6 deletions

View File

@ -1785,8 +1785,9 @@ g_hash_table_steal (GHashTable *hash_table,
* of @hash_table are %NULL-safe. * of @hash_table are %NULL-safe.
* *
* The dictionary implementation optimizes for having all values identical to * The dictionary implementation optimizes for having all values identical to
* their keys, for example by using g_hash_table_add(). When stealing both the * their keys, for example by using g_hash_table_add(). Before 2.82, when
* key and the value from such a dictionary, the value will be %NULL. * stealing both the key and the value from such a dictionary, the value was
* %NULL. Since 2.82, the returned value and key will be the same.
* *
* Returns: %TRUE if the key was found in the #GHashTable * Returns: %TRUE if the key was found in the #GHashTable
* Since: 2.58 * Since: 2.58
@ -1820,10 +1821,15 @@ g_hash_table_steal_extended (GHashTable *hash_table,
} }
if (stolen_value != NULL) if (stolen_value != NULL)
{ {
*stolen_value = g_hash_table_fetch_key_or_value (hash_table->values, node_index, hash_table->have_big_values); if (stolen_key && hash_table->keys == hash_table->values)
g_hash_table_assign_key_or_value (hash_table->values, node_index, hash_table->have_big_values, NULL); *stolen_value = *stolen_key;
} else
{
*stolen_value = g_hash_table_fetch_key_or_value (hash_table->values, node_index, hash_table->have_big_values);
g_hash_table_assign_key_or_value (hash_table->values, node_index, hash_table->have_big_values, NULL);
}
}
g_hash_table_remove_node (hash_table, node_index, FALSE); g_hash_table_remove_node (hash_table, node_index, FALSE);
g_hash_table_maybe_resize (hash_table); g_hash_table_maybe_resize (hash_table);

View File

@ -1271,6 +1271,52 @@ test_steal_extended (void)
g_assert_cmpuint (g_hash_table_size (hash), ==, 5); g_assert_cmpuint (g_hash_table_size (hash), ==, 5);
g_hash_table_unref (hash); g_hash_table_unref (hash);
hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_hash_table_add (hash, g_strdup ("a"));
g_hash_table_add (hash, g_strdup ("b"));
g_hash_table_add (hash, g_strdup ("c"));
g_hash_table_add (hash, g_strdup ("d"));
g_hash_table_add (hash, g_strdup ("e"));
g_assert_true (g_hash_table_steal_extended (hash, "a", (gpointer *) &stolen_key,
(gpointer *) &stolen_value));
g_assert_cmpstr (stolen_key, ==, "a");
g_assert_cmpstr (stolen_value, ==, "a");
g_clear_pointer (&stolen_key, g_free);
stolen_value = NULL;
g_assert_true (g_hash_table_steal_extended (hash, "b", (gpointer *) &stolen_key,
NULL));
g_assert_cmpstr (stolen_key, ==, "b");
g_clear_pointer (&stolen_key, g_free);
g_assert_true (g_hash_table_steal_extended (hash, "c", NULL,
(gpointer *) &stolen_value));
g_assert_cmpstr (stolen_value, ==, "c");
g_clear_pointer (&stolen_value, g_free);
g_assert_true (g_hash_table_steal_extended (hash, "d", (gpointer *) &stolen_key,
(gpointer *) &stolen_value));
g_assert_cmpstr (stolen_key, ==, "d");
g_assert_cmpstr (stolen_value, ==, "d");
g_clear_pointer (&stolen_key, g_free);
stolen_value = NULL;
/* So far, the GHashTable was used like a set (g_hash_table_add()), where all key/values were
* identical. Adding one entry where key/value differs, blows the internal representation
* up, and the hash table tracks two separate key/value arrays. */
g_hash_table_replace (hash, g_strdup ("x"), NULL);
g_assert_true (g_hash_table_steal_extended (hash, "e", (gpointer *) &stolen_key,
(gpointer *) &stolen_value));
g_assert_cmpstr (stolen_key, ==, "e");
g_assert_cmpstr (stolen_value, ==, "e");
g_clear_pointer (&stolen_key, g_free);
stolen_value = NULL;
g_hash_table_unref (hash);
} }
/* Test that passing %NULL to the optional g_hash_table_steal_extended() /* Test that passing %NULL to the optional g_hash_table_steal_extended()