ghash: Add APIs to get hash table keys and values as GPtrArray

GPtrArray's are faster than lists and provide more flexibility, so add
APIs to get hash keys and values using these containers too.

Given that we know the size at array initialization we can optimize the
allocation quite a bit, making it faster than the API using GList both at
creation time and for consumers.
This commit is contained in:
Marco Trevisan (Treviño) 2022-12-14 03:18:44 +01:00
parent a0dbaeed2f
commit d2c3f7f513
4 changed files with 145 additions and 0 deletions

View File

@ -2584,7 +2584,9 @@ g_hash_table_remove_all
g_hash_table_steal_all
g_hash_table_get_keys
g_hash_table_get_values
g_hash_table_get_values_as_ptr_array
g_hash_table_get_keys_as_array
g_hash_table_get_keys_as_ptr_array
GHRFunc
g_hash_table_freeze
g_hash_table_thaw

View File

@ -2273,6 +2273,45 @@ g_hash_table_get_keys_as_array (GHashTable *hash_table,
return result;
}
/**
* g_hash_table_get_keys_as_ptr_array: (skip)
* @hash_table: a #GHashTable
*
* Retrieves every key inside @hash_table, as a #GPtrArray.
* The returned data is valid until changes to the hash release those keys.
*
* This iterates over every entry in the hash table to build its return value.
* To iterate over the entries in a #GHashTable more efficiently, use a
* #GHashTableIter.
*
* You should always unref the returned array with g_ptr_array_unref().
*
* Returns: (transfer container): a #GPtrArray containing each key from
* the table. Unref with with g_ptr_array_unref() when done.
*
* Since: 2.76
**/
GPtrArray *
g_hash_table_get_keys_as_ptr_array (GHashTable *hash_table)
{
GPtrArray *array;
g_return_val_if_fail (hash_table != NULL, NULL);
array = g_ptr_array_sized_new (hash_table->size);
for (gsize i = 0; i < hash_table->size; ++i)
{
if (HASH_IS_REAL (hash_table->hashes[i]))
{
g_ptr_array_add (array, g_hash_table_fetch_key_or_value (
hash_table->keys, i, hash_table->have_big_keys));
}
}
g_assert (array->len == (guint) hash_table->nnodes);
return array;
}
/**
* g_hash_table_get_values:
* @hash_table: a #GHashTable
@ -2309,6 +2348,45 @@ g_hash_table_get_values (GHashTable *hash_table)
return retval;
}
/**
* g_hash_table_get_values_as_ptr_array: (skip)
* @hash_table: a #GHashTable
*
* Retrieves every value inside @hash_table, as a #GPtrArray.
* The returned data is valid until changes to the hash release those values.
*
* This iterates over every entry in the hash table to build its return value.
* To iterate over the entries in a #GHashTable more efficiently, use a
* #GHashTableIter.
*
* You should always unref the returned array with g_ptr_array_unref().
*
* Returns: (transfer container): a #GPtrArray containing each value from
* the table. Unref with with g_ptr_array_unref() when done.
*
* Since: 2.76
**/
GPtrArray *
g_hash_table_get_values_as_ptr_array (GHashTable *hash_table)
{
GPtrArray *array;
g_return_val_if_fail (hash_table != NULL, NULL);
array = g_ptr_array_sized_new (hash_table->size);
for (gsize i = 0; i < hash_table->size; ++i)
{
if (HASH_IS_REAL (hash_table->hashes[i]))
{
g_ptr_array_add (array, g_hash_table_fetch_key_or_value (
hash_table->values, i, hash_table->have_big_values));
}
}
g_assert (array->len == (guint) hash_table->nnodes);
return array;
}
/* Hash functions.
*/

View File

@ -32,6 +32,7 @@
#endif
#include <glib/gtypes.h>
#include <glib/garray.h>
#include <glib/glist.h>
G_BEGIN_DECLS
@ -129,6 +130,11 @@ GList * g_hash_table_get_values (GHashTable *hash_table);
GLIB_AVAILABLE_IN_2_40
gpointer * g_hash_table_get_keys_as_array (GHashTable *hash_table,
guint *length);
GLIB_AVAILABLE_IN_2_76
GPtrArray * g_hash_table_get_keys_as_ptr_array (GHashTable *hash_table);
GLIB_AVAILABLE_IN_2_76
GPtrArray * g_hash_table_get_values_as_ptr_array (GHashTable *hash_table);
GLIB_AVAILABLE_IN_ALL
void g_hash_table_iter_init (GHashTableIter *iter,

View File

@ -1692,6 +1692,63 @@ test_set_to_strv (void)
g_strfreev (strv);
}
static void
test_set_get_keys_as_ptr_array (void)
{
GHashTable *set;
GPtrArray *array;
set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_hash_table_add (set, g_strdup ("xyz"));
g_hash_table_add (set, g_strdup ("xyz"));
g_hash_table_add (set, g_strdup ("abc"));
array = g_hash_table_get_keys_as_ptr_array (set);
g_hash_table_steal_all (set);
g_hash_table_unref (set);
g_ptr_array_set_free_func (array, g_free);
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);
}
static void
test_set_get_values_as_ptr_array (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_get_values_as_ptr_array (table);
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);
}
static gboolean
is_prime (guint p)
{
@ -1774,6 +1831,8 @@ main (int argc, char *argv[])
g_test_add_func ("/hash/iter-replace", test_iter_replace);
g_test_add_func ("/hash/set-insert-corruption", test_set_insert_corruption);
g_test_add_func ("/hash/set-to-strv", test_set_to_strv);
g_test_add_func ("/hash/get-keys-as-ptr-array", test_set_get_keys_as_ptr_array);
g_test_add_func ("/hash/get-values-as-ptr-array", test_set_get_values_as_ptr_array);
g_test_add_func ("/hash/primes", test_primes);
return g_test_run ();