From 7bada8394dae1128558734fb655a5abb529a630c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 30 Jul 2019 19:22:05 +0200 Subject: [PATCH] Add g_array_steal(), g_ptr_array_steal() and g_byte_array_steal() Closes issue #285 --- docs/reference/glib/glib-sections.txt | 3 + glib/garray.c | 113 ++++++++++++++++++++++ glib/garray.h | 9 ++ glib/tests/array-test.c | 134 +++++++++++++++++++++++++- 4 files changed, 258 insertions(+), 1 deletion(-) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index c67af683d..08ff50ac2 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2805,6 +2805,7 @@ g_string_chunk_free arrays GArray g_array_new +g_array_steal g_array_sized_new g_array_copy g_array_ref @@ -2833,6 +2834,7 @@ g_array_free arrays_pointer GPtrArray g_ptr_array_new +g_ptr_array_steal g_ptr_array_sized_new g_ptr_array_new_with_free_func g_ptr_array_copy @@ -2868,6 +2870,7 @@ g_ptr_array_find_with_equal_func GByteArray g_byte_array_new +g_byte_array_steal g_byte_array_new_take g_byte_array_sized_new g_byte_array_ref diff --git a/glib/garray.c b/glib/garray.c index 38f64b82d..0ffbe3de6 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -165,6 +165,57 @@ g_array_new (gboolean zero_terminated, return g_array_sized_new (zero_terminated, clear, elt_size, 0); } +/** + * g_array_steal: + * @array: a #GArray. + * @len: (optional) (out caller-allocates): pointer to retrieve the number of + * elements of the original array + * + * Frees the data in the array and resets the size to zero, while + * the underlying array is preserved for use elsewhere and returned + * to the caller. + * + * If the array was created with the @zero_terminate property + * set to %TRUE, the returned data is zero terminated too. + * + * If array elements contain dynamically-allocated memory, + * the array elements should also be freed by the caller. + * + * A short example of use: + * |[ + * ... + * gpointer data; + * gsize data_len; + * data = g_array_steal (some_array, &data_len); + * ... + * ]| + + * Returns: (transfer full): the element data, which should be + * freed using g_free(). + * + * Since: 2.64 + */ +gpointer +g_array_steal (GArray *array, + gsize *len) +{ + GRealArray *rarray; + gpointer segment; + + g_return_val_if_fail (array != NULL, NULL); + + rarray = (GRealArray *) array; + segment = (gpointer) rarray->data; + + if (len != NULL) + *len = rarray->len; + + rarray->data = NULL; + rarray->len = 0; + rarray->alloc = 0; + return segment; +} + /** * g_array_sized_new: * @zero_terminated: %TRUE if the array should have an extra element at @@ -1013,6 +1064,46 @@ g_ptr_array_new (void) return g_ptr_array_sized_new (0); } +/** + * g_ptr_array_steal: + * @array: a #GPtrArray. + * @len: (optional) (out caller-allocates): pointer to retrieve the number of + * elements of the original array + * + * Frees the data in the array and resets the size to zero, while + * the underlying array is preserved for use elsewhere and returned + * to the caller. + * + * Even if set, the #GDestroyNotify function will never be called + * on the current contents of the array and the caller is + * responsible for freeing the array elements. + * + * Returns: (transfer full): the element data, which should be + * freed using g_free(). + * + * Since: 2.64 + */ +gpointer * +g_ptr_array_steal (GPtrArray *array, + gsize *len) +{ + GRealPtrArray *rarray; + gpointer *segment; + + g_return_val_if_fail (array != NULL, NULL); + + rarray = (GRealPtrArray *) array; + segment = (gpointer *) rarray->pdata; + + if (len != NULL) + *len = rarray->len; + + rarray->pdata = NULL; + rarray->len = 0; + rarray->alloc = 0; + return segment; +} + /** * g_ptr_array_copy: * @array: #GPtrArray to duplicate @@ -2002,6 +2093,28 @@ g_byte_array_new (void) return (GByteArray *)g_array_sized_new (FALSE, FALSE, 1, 0); } +/** + * g_byte_array_steal: + * @array: a #GByteArray. + * @len: (optional) (out caller-allocates): pointer to retrieve the number of + * elements of the original array + * + * Frees the data in the array and resets the size to zero, while + * the underlying array is preserved for use elsewhere and returned + * to the caller. + * + * Returns: (transfer full): the element data, which should be + * freed using g_free(). + * + * Since: 2.64 + */ +guint8 * +g_byte_array_steal (GByteArray *array, + gsize *len) +{ + return (guint8 *) g_array_steal ((GArray *) array, len); +} + /** * g_byte_array_new_take: * @data: (transfer full) (array length=len): byte data for the array diff --git a/glib/garray.h b/glib/garray.h index 3e7ce7732..67131b5b3 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -70,6 +70,9 @@ GLIB_AVAILABLE_IN_ALL GArray* g_array_new (gboolean zero_terminated, gboolean clear_, guint element_size); +GLIB_AVAILABLE_IN_2_64 +gpointer g_array_steal (GArray *array, + gsize *len); GLIB_AVAILABLE_IN_ALL GArray* g_array_sized_new (gboolean zero_terminated, gboolean clear_, @@ -137,6 +140,9 @@ 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_64 +gpointer* g_ptr_array_steal (GPtrArray *array, + gsize *len); GLIB_AVAILABLE_IN_2_62 GPtrArray *g_ptr_array_copy (GPtrArray *array, GCopyFunc func, @@ -227,6 +233,9 @@ GByteArray* g_byte_array_new (void); GLIB_AVAILABLE_IN_ALL GByteArray* g_byte_array_new_take (guint8 *data, gsize len); +GLIB_AVAILABLE_IN_2_64 +guint8* g_byte_array_steal (GByteArray *array, + gsize *len); GLIB_AVAILABLE_IN_ALL GByteArray* g_byte_array_sized_new (guint reserved_size); GLIB_AVAILABLE_IN_ALL diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index b26704e25..01372a030 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -140,6 +140,57 @@ array_new_zero_terminated (void) g_free (out_str); } +/* Check g_array_steal() function */ +static void +array_steal (void) +{ + const guint array_size = 10000; + GArray *garray; + gint *adata; + guint i; + gsize len, past_len; + + garray = g_array_new (FALSE, FALSE, sizeof (gint)); + adata = (gint *) g_array_steal (garray, NULL); + g_assert_null (adata); + + adata = (gint *) g_array_steal (garray, &len); + g_assert_null (adata); + g_assert_cmpint (len, ==, 0); + + for (i = 0; i < array_size; i++) + g_array_append_val (garray, i); + + for (i = 0; i < array_size; i++) + g_assert_cmpint (g_array_index (garray, gint, i), ==, i); + + + past_len = garray->len; + adata = (gint *) g_array_steal (garray, &len); + for (i = 0; i < array_size; i++) + g_assert_cmpint (adata[i], ==, i); + + g_assert_cmpint (past_len, ==, len); + g_assert_cmpint (garray->len, ==, 0); + + g_array_append_val (garray, i); + + g_assert_cmpint (adata[0], ==, 0); + g_assert_cmpint (g_array_index (garray, gint, 0), ==, array_size); + g_assert_cmpint (garray->len, ==, 1); + + g_array_remove_index (garray, 0); + + for (i = 0; i < array_size; i++) + g_array_append_val (garray, i); + + g_assert_cmpint (garray->len, ==, array_size); + g_assert_cmpmem (adata, array_size * sizeof (gint), + garray->data, array_size * sizeof (gint)); + g_free (adata); + g_array_free (garray, TRUE); +} + /* Check that g_array_append_val() works correctly for various #GArray * configurations. */ static void @@ -760,6 +811,49 @@ test_array_binary_search (void) g_array_free (garray, TRUE); } +/* Check g_ptr_array_steal() function */ +static void +pointer_array_steal (void) +{ + const guint array_size = 10000; + GPtrArray *gparray; + gpointer *pdata; + guint i; + gsize len, past_len; + + gparray = g_ptr_array_new (); + pdata = g_ptr_array_steal (gparray, NULL); + g_assert_null (pdata); + + pdata = g_ptr_array_steal (gparray, &len); + g_assert_null (pdata); + g_assert_cmpint (len, ==, 0); + + for (i = 0; i < array_size; i++) + g_ptr_array_add (gparray, GINT_TO_POINTER (i)); + + past_len = gparray->len; + pdata = g_ptr_array_steal (gparray, &len); + g_assert_cmpint (gparray->len, ==, 0); + g_assert_cmpint (past_len, ==, len); + g_ptr_array_add (gparray, GINT_TO_POINTER (10)); + + g_assert_cmpint ((gsize) pdata[0], ==, (gsize) GINT_TO_POINTER (0)); + g_assert_cmpint ((gsize) g_ptr_array_index (gparray, 0), ==, + (gsize) GINT_TO_POINTER (10)); + g_assert_cmpint (gparray->len, ==, 1); + + g_ptr_array_remove_index (gparray, 0); + + for (i = 0; i < array_size; i++) + g_ptr_array_add (gparray, GINT_TO_POINTER (i)); + g_assert_cmpmem (pdata, array_size * sizeof (gpointer), + gparray->pdata, array_size * sizeof (gpointer)); + g_free (pdata); + + g_ptr_array_free (gparray, TRUE); +} + static void pointer_array_add (void) { @@ -1318,7 +1412,7 @@ steal_destroy_notify (gpointer data) /* Test that g_ptr_array_steal_index() and g_ptr_array_steal_index_fast() can * remove elements from a pointer array without the #GDestroyNotify being called. */ static void -pointer_array_steal (void) +pointer_array_steal_index (void) { guint i1 = 0, i2 = 0, i3 = 0, i4 = 0; gpointer out1, out2; @@ -1361,6 +1455,41 @@ pointer_array_steal (void) g_assert_cmpuint (i4, ==, 1); } +static void +byte_array_steal (void) +{ + const guint array_size = 10000; + GByteArray *gbarray; + guint8 *bdata; + guint i; + gsize len, past_len; + + gbarray = g_byte_array_new (); + bdata = g_byte_array_steal (gbarray, NULL); + g_assert_cmpint ((gsize) bdata, ==, (gsize) gbarray->data); + g_free (bdata); + + for (i = 0; i < array_size; i++) + g_byte_array_append (gbarray, (guint8 *) "abcd", 4); + + past_len = gbarray->len; + bdata = g_byte_array_steal (gbarray, &len); + + g_assert_cmpint (len, ==, past_len); + g_assert_cmpint (gbarray->len, ==, 0); + + g_byte_array_append (gbarray, (guint8 *) "@", 1); + + g_assert_cmpint (bdata[0], ==, 'a'); + g_assert_cmpint (gbarray->data[0], ==, '@'); + g_assert_cmpint (gbarray->len, ==, 1); + + g_byte_array_remove_index (gbarray, 0); + + g_free (bdata); + g_byte_array_free (gbarray, TRUE); +} + static void byte_array_append (void) { @@ -1679,6 +1808,7 @@ main (int argc, char *argv[]) /* array tests */ g_test_add_func ("/array/new/zero-terminated", array_new_zero_terminated); g_test_add_func ("/array/ref-count", array_ref_count); + g_test_add_func ("/array/steal", array_steal); g_test_add_func ("/array/clear-func", array_clear_func); g_test_add_func ("/array/binary-search", test_array_binary_search); @@ -1711,8 +1841,10 @@ main (int argc, char *argv[]) g_test_add_func ("/pointerarray/find/empty", pointer_array_find_empty); g_test_add_func ("/pointerarray/find/non-empty", pointer_array_find_non_empty); g_test_add_func ("/pointerarray/steal", pointer_array_steal); + g_test_add_func ("/pointerarray/steal_index", pointer_array_steal_index); /* byte arrays */ + g_test_add_func ("/bytearray/steal", byte_array_steal); g_test_add_func ("/bytearray/append", byte_array_append); g_test_add_func ("/bytearray/prepend", byte_array_prepend); g_test_add_func ("/bytearray/remove", byte_array_remove);