diff options
author | Marco Trevisan (Treviño) <mail@3v1n0.net> | 2022-12-07 13:35:04 +0100 |
---|---|---|
committer | Marco Trevisan (Treviño) <mail@3v1n0.net> | 2022-12-19 19:55:28 +0100 |
commit | 14ded2ef9430eb38c745da21417e06e002e6678d (patch) | |
tree | a1e4521d21264e20e9221d157e0dce11656788a4 | |
parent | 4eb9b09014612401b98c9fe2fef4f1c779956089 (diff) | |
download | glib-14ded2ef9430eb38c745da21417e06e002e6678d.tar.gz |
garray: Add g_ptr_array_new_take() to take a C array without copies
GPtrArray is a nice interface to handle pointer arrays, however if a classic
array needs to be converted into a GPtrArray is currently needed to manually
go through all its elements and do new allocations that could be avoided.
So add g_ptr_array_new_take() which steals the data from an array of
pointers and allows to manage it using the GPtrArray API.
-rw-r--r-- | docs/reference/glib/glib-sections.txt.in | 1 | ||||
-rw-r--r-- | glib/garray.c | 47 | ||||
-rw-r--r-- | glib/garray.h | 4 | ||||
-rw-r--r-- | glib/tests/array-test.c | 145 |
4 files changed, 197 insertions, 0 deletions
diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index 9fbce402c..93931e7e0 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -2715,6 +2715,7 @@ g_ptr_array_new_with_free_func g_ptr_array_copy g_ptr_array_new_full g_ptr_array_new_null_terminated +g_ptr_array_new_take g_ptr_array_set_free_func g_ptr_array_is_null_terminated g_ptr_array_ref diff --git a/glib/garray.c b/glib/garray.c index 0db46bf5f..57c621e53 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -1151,6 +1151,53 @@ g_ptr_array_new (void) } /** + * g_ptr_array_new_take: (skip) + * @data: (array length=len) (transfer full) (nullable): an array of pointers, + * or %NULL for an empty array + * @len: the number of pointers in @data + * @element_free_func: (nullable): A function to free elements on @array + * destruction or %NULL + * + * Creates a new #GPtrArray with @data as pointers, @len as length and a + * reference count of 1. + * + * This avoids having to copy such data manually. @data will eventually be + * freed using g_free(), so must have been allocated with a suitable allocator. + * + * It also sets @element_free_func for freeing each element when the array is + * destroyed either via g_ptr_array_unref(), when g_ptr_array_free() is called + * with @free_segment set to %TRUE or when removing elements. + * + * Do not use it if @len is greater than %G_MAXUINT. #GPtrArray + * stores the length of its data in #guint, which may be shorter than + * #gsize. + * + * Returns: (transfer full): A new #GPtrArray + * + * Since: 2.76 + */ +GPtrArray * +g_ptr_array_new_take (gpointer *data, + gsize len, + GDestroyNotify element_free_func) +{ + GPtrArray *array; + GRealPtrArray *rarray; + + g_return_val_if_fail (data != NULL || len == 0, NULL); + g_return_val_if_fail (len <= G_MAXUINT, NULL); + + array = ptr_array_new (0, element_free_func, FALSE); + rarray = (GRealPtrArray *)array; + + rarray->pdata = g_steal_pointer (&data); + rarray->len = len; + rarray->alloc = len; + + return array; +} + +/** * g_ptr_array_steal: * @array: a #GPtrArray. * @len: (optional) (out): pointer to retrieve the number of diff --git a/glib/garray.h b/glib/garray.h index 2300e5f58..97a2b0575 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -142,6 +142,10 @@ GLIB_AVAILABLE_IN_ALL GPtrArray* g_ptr_array_new (void); GLIB_AVAILABLE_IN_ALL GPtrArray* g_ptr_array_new_with_free_func (GDestroyNotify element_free_func); +GLIB_AVAILABLE_IN_2_76 +GPtrArray* g_ptr_array_new_take (gpointer *data, + gsize len, + GDestroyNotify element_free_func); GLIB_AVAILABLE_IN_2_64 gpointer* g_ptr_array_steal (GPtrArray *array, gsize *len); diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index cd617200a..944f5ad84 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -1014,6 +1014,147 @@ pointer_array_insert (void) } static void +pointer_array_new_take (void) +{ + const size_t array_size = 10000; + GPtrArray *gparray; + gpointer *pdata; + gpointer *old_pdata_copy; + gsize len; + + gparray = g_ptr_array_new (); + for (size_t i = 0; i < array_size; i++) + g_ptr_array_add (gparray, GUINT_TO_POINTER (i)); + + pdata = g_ptr_array_steal (gparray, &len); + g_assert_cmpuint (array_size, ==, len); + g_assert_nonnull (pdata); + g_clear_pointer (&gparray, g_ptr_array_unref); + + old_pdata_copy = g_memdup2 (pdata, len * sizeof (gpointer)); + gparray = g_ptr_array_new_take (g_steal_pointer (&pdata), len, NULL); + g_assert_false (g_ptr_array_is_null_terminated (gparray)); + g_assert_cmpuint (gparray->len, ==, array_size); + + g_assert_cmpuint (GPOINTER_TO_UINT (g_ptr_array_index (gparray, 0)), ==, 0); + g_assert_cmpuint (GPOINTER_TO_UINT (g_ptr_array_index (gparray, 10)), ==, 10); + + g_assert_cmpmem (old_pdata_copy, array_size * sizeof (gpointer), + gparray->pdata, array_size * sizeof (gpointer)); + + g_ptr_array_add (gparray, GUINT_TO_POINTER (55)); + g_ptr_array_insert (gparray, 0, GUINT_TO_POINTER (33)); + + g_assert_cmpuint (gparray->len, ==, array_size + 2); + g_assert_cmpuint (GPOINTER_TO_UINT (g_ptr_array_index (gparray, 0)), ==, 33); + g_assert_cmpuint ( + GPOINTER_TO_UINT (g_ptr_array_index (gparray, gparray->len - 1)), ==, 55); + + g_ptr_array_remove_index (gparray, 0); + g_assert_cmpuint (gparray->len, ==, array_size + 1); + g_ptr_array_remove_index (gparray, gparray->len - 1); + g_assert_cmpuint (gparray->len, ==, array_size); + + g_assert_cmpmem (old_pdata_copy, array_size * sizeof (gpointer), + gparray->pdata, array_size * sizeof (gpointer)); + + g_ptr_array_unref (gparray); + g_free (old_pdata_copy); +} + +static void +pointer_array_new_take_empty (void) +{ + GPtrArray *gparray; + gpointer empty_array[] = {0}; + + gparray = g_ptr_array_new_take ( + g_memdup2 (&empty_array, sizeof (gpointer)), 0, NULL); + g_assert_false (g_ptr_array_is_null_terminated (gparray)); + g_assert_cmpuint (gparray->len, ==, 0); + + g_clear_pointer (&gparray, g_ptr_array_unref); + + gparray = g_ptr_array_new_take (NULL, 0, NULL); + g_assert_false (g_ptr_array_is_null_terminated (gparray)); + g_assert_cmpuint (gparray->len, ==, 0); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*data*!=*NULL*||*len*==*0*"); + g_assert_null (g_ptr_array_new_take (NULL, 10, NULL)); + g_test_assert_expected_messages (); + + g_clear_pointer (&gparray, g_ptr_array_unref); +} + +static void +pointer_array_new_take_overflow (void) +{ +#if SIZE_WIDTH <= UINT_WIDTH + g_test_skip ("Overflow test requires UINT_WIDTH > SIZE_WIDTH."); +#else + if (!g_test_undefined ()) + return; + + /* Check for overflow should happen before data is accessed. */ + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion 'len <= G_MAXUINT' failed"); + g_assert_null (g_ptr_array_new_take ( + (gpointer []) { NULL }, (gsize) G_MAXUINT + 1, NULL)); + g_test_assert_expected_messages (); +#endif +} + +static void +pointer_array_new_take_with_free_func (void) +{ + const size_t array_size = 10000; + GPtrArray *gparray; + gpointer *pdata; + gpointer *old_pdata_copy; + gsize len; + + gparray = g_ptr_array_new_with_free_func (g_free); + for (size_t i = 0; i < array_size; i++) + g_ptr_array_add (gparray, g_strdup_printf ("%" G_GSIZE_FORMAT, i)); + + pdata = g_ptr_array_steal (gparray, &len); + g_assert_cmpuint (array_size, ==, len); + g_assert_nonnull (pdata); + g_clear_pointer (&gparray, g_ptr_array_unref); + + old_pdata_copy = g_memdup2 (pdata, len * sizeof (gpointer)); + gparray = g_ptr_array_new_take (g_steal_pointer (&pdata), len, g_free); + g_assert_false (g_ptr_array_is_null_terminated (gparray)); + g_assert_cmpuint (gparray->len, ==, array_size); + + g_assert_cmpstr ((const char *) g_ptr_array_index (gparray, 0), ==, "0"); + g_assert_cmpstr ((const char *) g_ptr_array_index (gparray, 101), ==, "101"); + + g_assert_cmpmem (old_pdata_copy, array_size * sizeof (gpointer), + gparray->pdata, array_size * sizeof (gpointer)); + + g_ptr_array_add (gparray, g_strdup_printf ("%d", 55)); + g_ptr_array_insert (gparray, 0, g_strdup_printf ("%d", 33)); + + g_assert_cmpuint (gparray->len, ==, array_size + 2); + g_assert_cmpstr ((const char *) g_ptr_array_index (gparray, 0), ==, "33"); + g_assert_cmpstr ( + (const char *) g_ptr_array_index (gparray, gparray->len - 1), ==, "55"); + + g_ptr_array_remove_index (gparray, 0); + g_assert_cmpuint (gparray->len, ==, array_size + 1); + g_ptr_array_remove_index (gparray, gparray->len - 1); + g_assert_cmpuint (gparray->len, ==, array_size); + + g_assert_cmpmem (old_pdata_copy, array_size * sizeof (gpointer), + gparray->pdata, array_size * sizeof (gpointer)); + + g_ptr_array_unref (gparray); + g_free (old_pdata_copy); +} + +static void pointer_array_ref_count (gconstpointer test_data) { const gboolean null_terminated = GPOINTER_TO_INT (test_data); @@ -2186,6 +2327,10 @@ main (int argc, char *argv[]) g_test_add_func ("/pointerarray/free/null-terminated", pointer_array_free_null_terminated); g_test_add_func ("/pointerarray/add", pointer_array_add); g_test_add_func ("/pointerarray/insert", pointer_array_insert); + g_test_add_func ("/pointerarray/new-take", pointer_array_new_take); + g_test_add_func ("/pointerarray/new-take/empty", pointer_array_new_take_empty); + g_test_add_func ("/pointerarray/new-take/overflow", pointer_array_new_take_overflow); + g_test_add_func ("/pointerarray/new-take/with-free-func", pointer_array_new_take_with_free_func); g_test_add_data_func ("/pointerarray/ref-count/not-null-terminated", GINT_TO_POINTER (0), pointer_array_ref_count); g_test_add_data_func ("/pointerarray/ref-count/null-terminated", GINT_TO_POINTER (1), pointer_array_ref_count); g_test_add_func ("/pointerarray/free-func", pointer_array_free_func); |