ghash: Add functions to steal all keys and values preserving ownership

Add functions to steal all the keys or values from a ghash (especially
useful when it's used as a set), passing the ownership of then to a
GPtrArray container that preserves the destroy notify functions.
This commit is contained in:
Marco Trevisan (Treviño) 2022-12-14 04:08:10 +01:00
parent d2c3f7f513
commit d68e7bc84a
4 changed files with 182 additions and 0 deletions

View File

@ -2578,6 +2578,8 @@ GHFunc
g_hash_table_remove
g_hash_table_steal
g_hash_table_steal_extended
g_hash_table_steal_all_keys
g_hash_table_steal_all_values
g_hash_table_foreach_remove
g_hash_table_foreach_steal
g_hash_table_remove_all

View File

@ -1942,6 +1942,80 @@ g_hash_table_steal_all (GHashTable *hash_table)
g_hash_table_maybe_resize (hash_table);
}
/**
* g_hash_table_steal_all_keys: (skip)
* @hash_table: a #GHashTable
*
* Removes all keys and their associated values from a #GHashTable
* without calling the key destroy functions, returning the keys
* as a #GPtrArray with the free func set to the @hash_table key
* destroy function.
*
* Returns: (transfer container): a #GPtrArray containing each key of
* the table. Unref with with g_ptr_array_unref() when done.
*
* Since: 2.76
*/
GPtrArray *
g_hash_table_steal_all_keys (GHashTable *hash_table)
{
GPtrArray *array;
GDestroyNotify key_destroy_func;
g_return_val_if_fail (hash_table != NULL, NULL);
array = g_hash_table_get_keys_as_ptr_array (hash_table);
/* Ignore the key destroy notify calls during removal, and use it for the
* array elements instead, but restore it after the hash table has been
* cleared, so that newly added keys will continue using it.
*/
key_destroy_func = g_steal_pointer (&hash_table->key_destroy_func);
g_ptr_array_set_free_func (array, key_destroy_func);
g_hash_table_remove_all (hash_table);
hash_table->key_destroy_func = g_steal_pointer (&key_destroy_func);
return array;
}
/**
* g_hash_table_steal_all_values: (skip)
* @hash_table: a #GHashTable
*
* Removes all keys and their associated values from a #GHashTable
* without calling the value destroy functions, returning the values
* as a #GPtrArray with the free func set to the @hash_table value
* destroy function.
*
* Returns: (transfer container): a #GPtrArray containing each value of
* the table. Unref with with g_ptr_array_unref() when done.
*
* Since: 2.76
*/
GPtrArray *
g_hash_table_steal_all_values (GHashTable *hash_table)
{
GPtrArray *array;
GDestroyNotify value_destroy_func;
g_return_val_if_fail (hash_table != NULL, NULL);
array = g_hash_table_get_values_as_ptr_array (hash_table);
/* Ignore the value destroy notify calls during removal, and use it for the
* array elements instead, but restore it after the hash table has been
* cleared, so that newly added values will continue using it.
*/
value_destroy_func = g_steal_pointer (&hash_table->value_destroy_func);
g_ptr_array_set_free_func (array, value_destroy_func);
g_hash_table_remove_all (hash_table);
hash_table->value_destroy_func = g_steal_pointer (&value_destroy_func);
return array;
}
/*
* g_hash_table_foreach_remove_or_steal:
* @hash_table: a #GHashTable

View File

@ -94,6 +94,10 @@ gboolean g_hash_table_steal_extended (GHashTable *hash_table,
gpointer *stolen_value);
GLIB_AVAILABLE_IN_ALL
void g_hash_table_steal_all (GHashTable *hash_table);
GLIB_AVAILABLE_IN_2_76
GPtrArray * g_hash_table_steal_all_keys (GHashTable *hash_table);
GLIB_AVAILABLE_IN_2_76
GPtrArray * g_hash_table_steal_all_values (GHashTable *hash_table);
GLIB_AVAILABLE_IN_ALL
gpointer g_hash_table_lookup (GHashTable *hash_table,
gconstpointer key);

View File

@ -1749,6 +1749,106 @@ test_set_get_values_as_ptr_array (void)
g_clear_pointer (&array, g_ptr_array_unref);
}
static void
test_steal_all_keys (void)
{
GHashTable *table;
GPtrArray *array;
table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_hash_table_insert (table, g_strdup ("xyz"), GUINT_TO_POINTER (0));
g_hash_table_insert (table, g_strdup ("xyz"), GUINT_TO_POINTER (1));
g_hash_table_insert (table, g_strdup ("abc"), GUINT_TO_POINTER (2));
array = g_hash_table_steal_all_keys (table);
g_assert_cmpuint (g_hash_table_size (table), ==, 0);
g_hash_table_insert (table, g_strdup ("do-not-leak-me"), GUINT_TO_POINTER (5));
g_clear_pointer (&table, g_hash_table_unref);
g_assert_cmpint (array->len, ==, 2);
g_ptr_array_add (array, NULL);
g_assert_true (
g_strv_equal ((const gchar * const[]) { "xyz", "abc", NULL },
(const gchar * const*) array->pdata) ||
g_strv_equal ((const gchar * const[]) { "abc", "xyz", NULL },
(const gchar * const*) array->pdata)
);
g_clear_pointer (&array, g_ptr_array_unref);
table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
g_hash_table_insert (table, GUINT_TO_POINTER (0), g_strdup ("xyz"));
g_hash_table_insert (table, GUINT_TO_POINTER (1), g_strdup ("xyz"));
g_hash_table_insert (table, GUINT_TO_POINTER (2), g_strdup ("abc"));
array = g_hash_table_steal_all_keys (table);
g_assert_cmpuint (g_hash_table_size (table), ==, 0);
g_hash_table_insert (table, GUINT_TO_POINTER (5), g_strdup ("do-not-leak-me"));
g_clear_pointer (&table, g_hash_table_unref);
g_assert_cmpint (array->len, ==, 3);
g_assert_true (g_ptr_array_find (array, GUINT_TO_POINTER (0), NULL));
g_assert_true (g_ptr_array_find (array, GUINT_TO_POINTER (1), NULL));
g_assert_true (g_ptr_array_find (array, GUINT_TO_POINTER (2), NULL));
g_clear_pointer (&array, g_ptr_array_unref);
}
static void
test_steal_all_values (void)
{
GHashTable *table;
GPtrArray *array;
table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_hash_table_insert (table, g_strdup ("xyz"), GUINT_TO_POINTER (0));
g_hash_table_insert (table, g_strdup ("xyz"), GUINT_TO_POINTER (1));
g_hash_table_insert (table, g_strdup ("abc"), GUINT_TO_POINTER (2));
array = g_hash_table_steal_all_values (table);
g_assert_cmpuint (g_hash_table_size (table), ==, 0);
g_hash_table_insert (table, g_strdup ("do-not-leak-me"), GUINT_TO_POINTER (5));
g_clear_pointer (&table, g_hash_table_unref);
g_assert_cmpint (array->len, ==, 2);
g_assert_true (g_ptr_array_find (array, GUINT_TO_POINTER (1), NULL));
g_assert_true (g_ptr_array_find (array, GUINT_TO_POINTER (2), NULL));
g_assert_true (
memcmp ((gpointer []) { GUINT_TO_POINTER (1), GUINT_TO_POINTER (2) },
array->pdata, array->len * sizeof (gpointer)) == 0 ||
memcmp ((gpointer []) { GUINT_TO_POINTER (2), GUINT_TO_POINTER (1) },
array->pdata, array->len * sizeof (gpointer)) == 0
);
g_clear_pointer (&array, g_ptr_array_unref);
table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
g_hash_table_insert (table, GUINT_TO_POINTER (0), g_strdup ("xyz"));
g_hash_table_insert (table, GUINT_TO_POINTER (1), g_strdup ("foo"));
g_hash_table_insert (table, GUINT_TO_POINTER (2), g_strdup ("abc"));
array = g_hash_table_steal_all_values (table);
g_assert_cmpuint (g_hash_table_size (table), ==, 0);
g_hash_table_insert (table, GUINT_TO_POINTER (5), g_strdup ("do-not-leak-me"));
g_clear_pointer (&table, g_hash_table_unref);
g_assert_cmpint (array->len, ==, 3);
g_assert_true (
g_ptr_array_find_with_equal_func (array, "xyz", g_str_equal, NULL));
g_assert_true (
g_ptr_array_find_with_equal_func (array, "foo", g_str_equal, NULL));
g_assert_true (
g_ptr_array_find_with_equal_func (array, "abc", g_str_equal, NULL));
g_clear_pointer (&array, g_ptr_array_unref);
}
static gboolean
is_prime (guint p)
{
@ -1821,6 +1921,8 @@ main (int argc, char *argv[])
g_test_add_func ("/hash/foreach-steal", test_foreach_steal);
g_test_add_func ("/hash/steal-extended", test_steal_extended);
g_test_add_func ("/hash/steal-extended/optional", test_steal_extended_optional);
g_test_add_func ("/hash/steal-all-keys", test_steal_all_keys);
g_test_add_func ("/hash/steal-all-values", test_steal_all_values);
g_test_add_func ("/hash/lookup-extended", test_lookup_extended);
g_test_add_func ("/hash/new-similar", test_new_similar);