summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Trevisan (Treviño) <mail@3v1n0.net>2022-12-07 13:35:04 +0100
committerMarco Trevisan (Treviño) <mail@3v1n0.net>2022-12-19 19:55:28 +0100
commit14ded2ef9430eb38c745da21417e06e002e6678d (patch)
treea1e4521d21264e20e9221d157e0dce11656788a4
parent4eb9b09014612401b98c9fe2fef4f1c779956089 (diff)
downloadglib-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.in1
-rw-r--r--glib/garray.c47
-rw-r--r--glib/garray.h4
-rw-r--r--glib/tests/array-test.c145
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);