ghash: Add g_hash_table_steal_extended()

This is a combination of g_hash_table_lookup_extended() and
g_hash_table_steal(), so that users can combine the two to reduce code
and eliminate a pointless second hash table lookup by
g_hash_table_steal().

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://bugzilla.gnome.org/show_bug.cgi?id=795302
This commit is contained in:
Philip Withnall 2018-04-16 16:02:13 +01:00
parent 864cb71524
commit 6acece5074
4 changed files with 112 additions and 0 deletions

View File

@ -2453,6 +2453,7 @@ g_hash_table_find
GHFunc
g_hash_table_remove
g_hash_table_steal
g_hash_table_steal_extended
g_hash_table_foreach_remove
g_hash_table_foreach_steal
g_hash_table_remove_all

View File

@ -1421,6 +1421,67 @@ g_hash_table_steal (GHashTable *hash_table,
return g_hash_table_remove_internal (hash_table, key, FALSE);
}
/**
* g_hash_table_steal_extended:
* @hash_table: a #GHashTable
* @lookup_key: the key to look up
* @stolen_key: (out) (optional) (transfer full): return location for the
* original key
* @stolen_value: (out) (optional) (nullable) (transfer full): return location
* for the value associated with the key
*
* Looks up a key in the #GHashTable, stealing the original key and the
* associated value and returning %TRUE if the key was found. If the key was
* not found, %FALSE is returned.
*
* If found, the stolen key and value are removed from the hash table without
* calling the key and value destroy functions, and ownership is transferred to
* the caller of this method; as with g_hash_table_steal().
*
* You can pass %NULL for @lookup_key, provided the hash and equal functions
* of @hash_table are %NULL-safe.
*
* Returns: %TRUE if the key was found in the #GHashTable
* Since: 2.58
*/
gboolean
g_hash_table_steal_extended (GHashTable *hash_table,
gconstpointer lookup_key,
gpointer *stolen_key,
gpointer *stolen_value)
{
guint node_index;
guint node_hash;
g_return_val_if_fail (hash_table != NULL, FALSE);
node_index = g_hash_table_lookup_node (hash_table, lookup_key, &node_hash);
if (!HASH_IS_REAL (hash_table->hashes[node_index]))
{
if (stolen_key != NULL)
*stolen_key = NULL;
if (stolen_value != NULL)
*stolen_value = NULL;
return FALSE;
}
if (stolen_key != NULL)
*stolen_key = g_steal_pointer (&hash_table->keys[node_index]);
if (stolen_value != NULL)
*stolen_value = g_steal_pointer (&hash_table->values[node_index]);
g_hash_table_remove_node (hash_table, node_index, FALSE);
g_hash_table_maybe_resize (hash_table);
#ifndef G_DISABLE_ASSERT
hash_table->version++;
#endif
return TRUE;
}
/**
* g_hash_table_remove_all:
* @hash_table: a #GHashTable

View File

@ -82,6 +82,11 @@ void g_hash_table_remove_all (GHashTable *hash_table);
GLIB_AVAILABLE_IN_ALL
gboolean g_hash_table_steal (GHashTable *hash_table,
gconstpointer key);
GLIB_AVAILABLE_IN_2_58
gboolean g_hash_table_steal_extended (GHashTable *hash_table,
gconstpointer lookup_key,
gpointer *stolen_key,
gpointer *stolen_value);
GLIB_AVAILABLE_IN_ALL
void g_hash_table_steal_all (GHashTable *hash_table);
GLIB_AVAILABLE_IN_ALL

View File

@ -1189,6 +1189,50 @@ test_foreach_steal (void)
g_hash_table_unref (hash2);
}
/* Test g_hash_table_steal_extended() works properly with existing and
* non-existing keys. */
static void
test_steal_extended (void)
{
GHashTable *hash;
gchar *stolen_key = NULL, *stolen_value = NULL;
hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
g_hash_table_insert (hash, g_strdup ("a"), g_strdup ("A"));
g_hash_table_insert (hash, g_strdup ("b"), g_strdup ("B"));
g_hash_table_insert (hash, g_strdup ("c"), g_strdup ("C"));
g_hash_table_insert (hash, g_strdup ("d"), g_strdup ("D"));
g_hash_table_insert (hash, g_strdup ("e"), g_strdup ("E"));
g_hash_table_insert (hash, g_strdup ("f"), g_strdup ("F"));
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);
g_clear_pointer (&stolen_value, g_free);
g_assert_cmpuint (g_hash_table_size (hash), ==, 5);
g_assert_false (g_hash_table_steal_extended (hash, "a",
(gpointer *) &stolen_key,
(gpointer *) &stolen_value));
g_assert_null (stolen_key);
g_assert_null (stolen_value);
g_assert_false (g_hash_table_steal_extended (hash, "never a key",
(gpointer *) &stolen_key,
(gpointer *) &stolen_value));
g_assert_null (stolen_key);
g_assert_null (stolen_value);
g_assert_cmpuint (g_hash_table_size (hash), ==, 5);
g_hash_table_unref (hash);
}
struct _GHashTable
{
gint size;
@ -1513,6 +1557,7 @@ main (int argc, char *argv[])
g_test_add_func ("/hash/find", test_find);
g_test_add_func ("/hash/foreach", test_foreach);
g_test_add_func ("/hash/foreach-steal", test_foreach_steal);
g_test_add_func ("/hash/steal-extended", test_steal_extended);
/* tests for individual bugs */
g_test_add_func ("/hash/lookup-null-key", test_lookup_null_key);