From d0a48f26c765b7f5a3aa26773b7d1475a1643f1c Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 19 Apr 2018 14:54:41 +0100 Subject: [PATCH] garray: Add g_ptr_array_steal_index*() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These make it easy to steal elements from pointer arrays without having the array’s GDestroyNotify called on them, similarly to what’s possible with g_hash_table_steal(). Signed-off-by: Philip Withnall https://bugzilla.gnome.org/show_bug.cgi?id=795376 --- glib/garray.c | 51 +++++++++++++++++++++++++++++++++++--- glib/garray.h | 6 +++++ glib/tests/array-test.c | 54 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) diff --git a/glib/garray.c b/glib/garray.c index d8f96db90..914eaee34 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -1194,7 +1194,8 @@ g_ptr_array_set_size (GPtrArray *array, static gpointer ptr_array_remove_index (GPtrArray *array, guint index_, - gboolean fast) + gboolean fast, + gboolean free_element) { GRealPtrArray *rarray = (GRealPtrArray *) array; gpointer result; @@ -1206,7 +1207,7 @@ ptr_array_remove_index (GPtrArray *array, result = rarray->pdata[index_]; - if (rarray->element_free_func != NULL) + if (rarray->element_free_func != NULL && free_element) rarray->element_free_func (rarray->pdata[index_]); if (index_ != rarray->len - 1 && !fast) @@ -1240,7 +1241,7 @@ gpointer g_ptr_array_remove_index (GPtrArray *array, guint index_) { - return ptr_array_remove_index (array, index_, FALSE); + return ptr_array_remove_index (array, index_, FALSE, TRUE); } /** @@ -1262,7 +1263,49 @@ gpointer g_ptr_array_remove_index_fast (GPtrArray *array, guint index_) { - return ptr_array_remove_index (array, index_, TRUE); + return ptr_array_remove_index (array, index_, TRUE, TRUE); +} + +/** + * g_ptr_array_steal_index: + * @array: a #GPtrArray + * @index_: the index of the pointer to steal + * + * Removes the pointer at the given index from the pointer array. + * The following elements are moved down one place. The #GDestroyNotify for + * @array is *not* called on the removed element; ownership is transferred to + * the caller of this function. + * + * Returns: (transfer full) (nullable): the pointer which was removed + * Since: 2.58 + */ +gpointer +g_ptr_array_steal_index (GPtrArray *array, + guint index_) +{ + return ptr_array_remove_index (array, index_, FALSE, FALSE); +} + +/** + * g_ptr_array_steal_index_fast: + * @array: a #GPtrArray + * @index_: the index of the pointer to steal + * + * Removes the pointer at the given index from the pointer array. + * The last element in the array is used to fill in the space, so + * this function does not preserve the order of the array. But it + * is faster than g_ptr_array_steal_index(). The #GDestroyNotify for @array is + * *not* called on the removed element; ownership is transferred to the caller + * of this function. + * + * Returns: (transfer full) (nullable): the pointer which was removed + * Since: 2.58 + */ +gpointer +g_ptr_array_steal_index_fast (GPtrArray *array, + guint index_) +{ + return ptr_array_remove_index (array, index_, TRUE, FALSE); } /** diff --git a/glib/garray.h b/glib/garray.h index 3490f14f0..6c8c2c363 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -154,6 +154,12 @@ gpointer g_ptr_array_remove_index (GPtrArray *array, GLIB_AVAILABLE_IN_ALL gpointer g_ptr_array_remove_index_fast (GPtrArray *array, guint index_); +GLIB_AVAILABLE_IN_2_58 +gpointer g_ptr_array_steal_index (GPtrArray *array, + guint index_); +GLIB_AVAILABLE_IN_2_58 +gpointer g_ptr_array_steal_index_fast (GPtrArray *array, + guint index_); GLIB_AVAILABLE_IN_ALL gboolean g_ptr_array_remove (GPtrArray *array, gpointer data); diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index 64b996fb8..8c0872aef 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -658,6 +658,59 @@ pointer_array_find_non_empty (void) g_ptr_array_free (array, TRUE); } +static void +steal_destroy_notify (gpointer data) +{ + guint *counter = data; + *counter = *counter + 1; +} + +/* 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) +{ + guint i1 = 0, i2 = 0, i3 = 0, i4 = 0; + gpointer out1, out2; + GPtrArray *array = g_ptr_array_new_with_free_func (steal_destroy_notify); + + g_ptr_array_add (array, &i1); + g_ptr_array_add (array, &i2); + g_ptr_array_add (array, &i3); + g_ptr_array_add (array, &i4); + + g_assert_cmpuint (array->len, ==, 4); + + /* Remove a single element. */ + out1 = g_ptr_array_steal_index (array, 0); + g_assert_true (out1 == &i1); + g_assert_cmpuint (i1, ==, 0); /* should not have been destroyed */ + + /* Following elements should have been moved down. */ + g_assert_cmpuint (array->len, ==, 3); + g_assert_true (g_ptr_array_index (array, 0) == &i2); + g_assert_true (g_ptr_array_index (array, 1) == &i3); + g_assert_true (g_ptr_array_index (array, 2) == &i4); + + /* Remove another element, quickly. */ + out2 = g_ptr_array_steal_index_fast (array, 0); + g_assert_true (out2 == &i2); + g_assert_cmpuint (i2, ==, 0); /* should not have been destroyed */ + + /* Last element should have been swapped in place. */ + g_assert_cmpuint (array->len, ==, 2); + g_assert_true (g_ptr_array_index (array, 0) == &i4); + g_assert_true (g_ptr_array_index (array, 1) == &i3); + + /* Check that destroying the pointer array doesn’t affect the stolen elements. */ + g_ptr_array_unref (array); + + g_assert_cmpuint (i1, ==, 0); + g_assert_cmpuint (i2, ==, 0); + g_assert_cmpuint (i3, ==, 1); + g_assert_cmpuint (i4, ==, 1); +} + static void byte_array_append (void) { @@ -970,6 +1023,7 @@ main (int argc, char *argv[]) g_test_add_func ("/pointerarray/sort-with-data", pointer_array_sort_with_data); 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); /* byte arrays */ g_test_add_func ("/bytearray/append", byte_array_append);