From d2c3f7f513dfa48a72f52adf0bfbbc675e80302b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 14 Dec 2022 03:18:44 +0100 Subject: [PATCH] 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. --- docs/reference/glib/glib-sections.txt.in | 2 + glib/ghash.c | 78 ++++++++++++++++++++++++ glib/ghash.h | 6 ++ glib/tests/hash.c | 59 ++++++++++++++++++ 4 files changed, 145 insertions(+) diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index 9fbce402c..2ac3af6bb 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -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 diff --git a/glib/ghash.c b/glib/ghash.c index 158779911..6b6adc501 100644 --- a/glib/ghash.c +++ b/glib/ghash.c @@ -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. */ diff --git a/glib/ghash.h b/glib/ghash.h index 396148de5..604b07c8c 100644 --- a/glib/ghash.h +++ b/glib/ghash.h @@ -32,6 +32,7 @@ #endif #include +#include #include 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, diff --git a/glib/tests/hash.c b/glib/tests/hash.c index 604dcc049..d929f531e 100644 --- a/glib/tests/hash.c +++ b/glib/tests/hash.c @@ -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 ();