diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index 7d99a2783..ac9fab639 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -2718,6 +2718,7 @@ g_ptr_array_new_null_terminated g_ptr_array_new_take g_ptr_array_new_take_null_terminated g_ptr_array_new_from_array +g_ptr_array_new_from_null_terminated_array 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 b7cb7d94e..4419c62e1 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -1238,12 +1238,45 @@ g_ptr_array_new_take_null_terminated (gpointer *data, len += 1; } + g_return_val_if_fail (len <= G_MAXUINT, NULL); + array = g_ptr_array_new_take (g_steal_pointer (&data), len, element_free_func); ((GRealPtrArray *)array)->null_terminated = TRUE; return array; } +static GPtrArray * +ptr_array_new_from_array (gpointer *data, + gsize len, + GCopyFunc copy_func, + gpointer copy_func_user_data, + GDestroyNotify element_free_func, + gboolean null_terminated) +{ + GPtrArray *array; + GRealPtrArray *rarray; + + g_assert (len <= G_MAXUINT); + + array = ptr_array_new (len, element_free_func, null_terminated); + rarray = (GRealPtrArray *)array; + + if (copy_func != NULL) + { + for (gsize i = 0; i < len; i++) + rarray->pdata[i] = copy_func (data[i], copy_func_user_data); + } + else + { + memcpy (rarray->pdata, data, len * sizeof (gpointer)); + } + + rarray->len = len; + + return array; +} + /** * g_ptr_array_new_from_array: (skip) * @data: (array length=len) (transfer none) (nullable): an array of pointers, @@ -1283,28 +1316,58 @@ g_ptr_array_new_from_array (gpointer *data, gpointer copy_func_user_data, 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 (len, element_free_func, FALSE); - rarray = (GRealPtrArray *)array; + return ptr_array_new_from_array ( + data, len, copy_func, copy_func_user_data, element_free_func, FALSE); +} - if (copy_func != NULL) +/** + * g_ptr_array_new_from_null_terminated_array: (skip) + * @data: (array zero-terminated=1) (transfer none) (nullable): an array of + * pointers, %NULL terminated; or %NULL for an empty array + * @copy_func: (nullable): a copy function used to copy every element in the + * array or %NULL. + * @copy_func_user_data: user data passed to @copy_func, or %NULL + * @element_free_func: (nullable): a function to free elements on @array + * destruction or %NULL + * + * Creates a new #GPtrArray copying the pointers from @data after having + * computed the length of it and with a reference count of 1. + * This avoids having to manually add each element one by one. + * If @copy_func is provided, then it is used to copy the data in the new + * array. + * It also set @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 the @data has more than %G_MAXUINT elements. #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_from_null_terminated_array (gpointer *data, + GCopyFunc copy_func, + gpointer copy_func_user_data, + GDestroyNotify element_free_func) +{ + gsize len = 0; + + if (data != NULL) { - for (gsize i = 0; i < len; i++) - rarray->pdata[i] = copy_func (data[i], copy_func_user_data); - } - else - { - memcpy (rarray->pdata, data, len * sizeof (gpointer)); + for (gsize i = 0; data[i] != NULL; ++i) + len += 1; } - rarray->len = len; + g_return_val_if_fail (len <= G_MAXUINT, NULL); - return array; + return ptr_array_new_from_array ( + data, len, copy_func, copy_func_user_data, element_free_func, TRUE); } /** diff --git a/glib/garray.h b/glib/garray.h index 88624d40a..bec7bf6d2 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -171,6 +171,11 @@ GPtrArray* g_ptr_array_new_null_terminated (guint reserved_size, GLIB_AVAILABLE_IN_2_76 GPtrArray* g_ptr_array_new_take_null_terminated (gpointer *data, GDestroyNotify element_free_func); +GLIB_AVAILABLE_IN_2_76 +GPtrArray* g_ptr_array_new_from_null_terminated_array (gpointer *data, + GCopyFunc copy_func, + gpointer copy_func_user_data, + GDestroyNotify element_free_func); GLIB_AVAILABLE_IN_ALL gpointer* g_ptr_array_free (GPtrArray *array, gboolean free_seg); diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index 6adbbb509..f47103497 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -1460,6 +1460,191 @@ pointer_array_new_from_array_with_copy_and_free_func (void) g_free (old_pdata_copy); } +static void +pointer_array_new_from_null_terminated_array (void) +{ + const size_t array_size = 10000; + GPtrArray *source_array; + GPtrArray *gparray; + gpointer *old_pdata_copy; + + source_array = g_ptr_array_new_null_terminated (array_size, NULL, TRUE); + g_assert_true (g_ptr_array_is_null_terminated (source_array)); + + for (size_t i = 0; i < array_size; i++) + g_ptr_array_add (source_array, GUINT_TO_POINTER (i + 1)); + + g_assert_cmpuint (array_size, ==, source_array->len); + g_assert_nonnull (source_array->pdata); + + old_pdata_copy = + g_memdup2 (source_array->pdata, source_array->len * sizeof (gpointer)); + g_assert_nonnull (old_pdata_copy); + + gparray = g_ptr_array_new_from_null_terminated_array (source_array->pdata, + NULL, NULL, NULL); + g_assert_true (g_ptr_array_is_null_terminated (source_array)); + assert_ptr_array_null_terminated (gparray, TRUE); + + g_clear_pointer (&source_array, g_ptr_array_unref); + + g_assert_true (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)), ==, 1); + g_assert_cmpuint (GPOINTER_TO_UINT (g_ptr_array_index (gparray, 10)), ==, 11); + + 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)); + assert_ptr_array_null_terminated (gparray, TRUE); + + g_ptr_array_insert (gparray, 0, GUINT_TO_POINTER (33)); + assert_ptr_array_null_terminated (gparray, TRUE); + + 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); + assert_ptr_array_null_terminated (gparray, TRUE); + + g_ptr_array_remove_index (gparray, gparray->len - 1); + g_assert_cmpuint (gparray->len, ==, array_size); + assert_ptr_array_null_terminated (gparray, TRUE); + + 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_from_null_terminated_array_empty (void) +{ + GPtrArray *gparray; + + gparray = g_ptr_array_new_from_null_terminated_array ( + (gpointer []) { NULL }, NULL, NULL, NULL); + g_assert_true (g_ptr_array_is_null_terminated (gparray)); + assert_ptr_array_null_terminated (gparray, TRUE); + g_assert_cmpuint (gparray->len, ==, 0); + + g_clear_pointer (&gparray, g_ptr_array_unref); + + gparray = g_ptr_array_new_from_null_terminated_array ( + NULL, NULL, NULL, NULL); + g_assert_true (g_ptr_array_is_null_terminated (gparray)); + assert_ptr_array_null_terminated (gparray, TRUE); + g_assert_cmpuint (gparray->len, ==, 0); + + g_clear_pointer (&gparray, g_ptr_array_unref); +} + +static void +pointer_array_new_from_null_terminated_array_with_copy_and_free_func (void) +{ + const size_t array_size = 10000; + GPtrArray *source_array; + GPtrArray *gparray; + GStrv old_pdata_copy; + + source_array = g_ptr_array_new_null_terminated (array_size, g_free, TRUE); + g_assert_true (g_ptr_array_is_null_terminated (source_array)); + + for (size_t i = 0; i < array_size; i++) + g_ptr_array_add (source_array, g_strdup_printf ("%" G_GSIZE_FORMAT, i)); + + g_assert_cmpuint (array_size, ==, source_array->len); + g_assert_nonnull (source_array->pdata); + + old_pdata_copy = g_strdupv ((char **) source_array->pdata); + g_assert_cmpuint (g_strv_length (old_pdata_copy), ==, array_size); + g_assert_nonnull (old_pdata_copy); + g_clear_pointer (&source_array, g_ptr_array_unref); + + gparray = g_ptr_array_new_from_null_terminated_array ( + (gpointer* ) old_pdata_copy, (GCopyFunc) g_strdup, NULL, g_free); + g_assert_true (g_ptr_array_is_null_terminated (gparray)); + assert_ptr_array_null_terminated (gparray, TRUE); + + for (size_t i = 0; i < gparray->len; i++) + { + g_assert_cmpstr ((const char *) g_ptr_array_index (gparray, i), ==, + (const char *) old_pdata_copy[i]); + } + + 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_ptr_array_add (gparray, g_strdup_printf ("%d", 55)); + assert_ptr_array_null_terminated (gparray, TRUE); + + g_ptr_array_insert (gparray, 0, g_strdup_printf ("%d", 33)); + assert_ptr_array_null_terminated (gparray, TRUE); + + 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); + assert_ptr_array_null_terminated (gparray, TRUE); + g_assert_cmpuint (gparray->len, ==, array_size + 1); + + g_ptr_array_remove_index (gparray, gparray->len - 1); + assert_ptr_array_null_terminated (gparray, TRUE); + g_assert_cmpuint (gparray->len, ==, array_size); + + for (size_t i = 0; i < gparray->len; i++) + { + g_assert_cmpstr ((const char *) g_ptr_array_index (gparray, i), ==, + (const char *) old_pdata_copy[i]); + } + + g_ptr_array_unref (gparray); + g_strfreev (old_pdata_copy); +} + +static void +pointer_array_new_from_null_terminated_array_from_gstrv (void) +{ + GPtrArray *gparray; + GStrv strv; + char *joined; + + strv = g_strsplit ("A.dot.separated.string", ".", -1); + gparray = g_ptr_array_new_from_null_terminated_array ( + (gpointer) strv, NULL, NULL, NULL); + + g_assert_cmpstr ( + (const char *) g_ptr_array_index (gparray, 0), ==, "A"); + g_assert_true (g_ptr_array_index (gparray, 0) == strv[0]); + g_assert_cmpstr ( + (const char *) g_ptr_array_index (gparray, 1), ==, "dot"); + g_assert_true (g_ptr_array_index (gparray, 1) == strv[1]); + g_assert_cmpstr ( + (const char *) g_ptr_array_index (gparray, 2), ==, "separated"); + g_assert_true (g_ptr_array_index (gparray, 2) == strv[2]); + g_assert_cmpstr ( + (const char *) g_ptr_array_index (gparray, 3), ==, "string"); + g_assert_true (g_ptr_array_index (gparray, 3) == strv[3]); + + g_assert_null (strv[4]); + g_assert_null (g_ptr_array_index (gparray, 4)); + + joined = g_strjoinv (".", (char **) gparray->pdata); + g_assert_cmpstr (joined, ==, "A.dot.separated.string"); + + g_ptr_array_unref (gparray); + g_strfreev (strv); + g_free (joined); +} + static void pointer_array_ref_count (gconstpointer test_data) { @@ -2645,6 +2830,10 @@ main (int argc, char *argv[]) g_test_add_func ("/pointerarray/new-from-array/empty", pointer_array_new_from_array_empty); g_test_add_func ("/pointerarray/new-from-array/overflow", pointer_array_new_from_array_overflow); g_test_add_func ("/pointerarray/new-from-array/with-copy-and-free-func", pointer_array_new_from_array_with_copy_and_free_func); + g_test_add_func ("/pointerarray/new-from-null-terminated-array", pointer_array_new_from_null_terminated_array); + g_test_add_func ("/pointerarray/new-from-null-terminated-array/empty", pointer_array_new_from_null_terminated_array_empty); + g_test_add_func ("/pointerarray/new-from-null-terminated-array/with-copy-and-free-func", pointer_array_new_from_null_terminated_array_with_copy_and_free_func); + g_test_add_func ("/pointerarray/new-from-null-terminated-array/from-gstrv", pointer_array_new_from_null_terminated_array_from_gstrv); 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);