diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 038a97ede..399dfd466 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -3028,7 +3028,9 @@ g_ptr_array_sized_new 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_set_free_func +g_ptr_array_is_null_terminated g_ptr_array_ref g_ptr_array_unref g_ptr_array_add diff --git a/glib/garray.c b/glib/garray.c index 8dfba17b5..920a40258 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -1071,6 +1071,7 @@ struct _GRealPtrArray guint len; guint alloc; gatomicrefcount ref_count; + guint8 null_terminated; /* always either 0 or 1, so it can be added to array lengths */ GDestroyNotify element_free_func; }; @@ -1090,9 +1091,17 @@ struct _GRealPtrArray static void g_ptr_array_maybe_expand (GRealPtrArray *array, guint len); +static void +ptr_array_null_terminate (GRealPtrArray *rarray) +{ + if (G_UNLIKELY (rarray->null_terminated)) + rarray->pdata[rarray->len] = NULL; +} + static GPtrArray * ptr_array_new (guint reserved_size, - GDestroyNotify element_free_func) + GDestroyNotify element_free_func, + gboolean null_terminated) { GRealPtrArray *array; @@ -1101,12 +1110,25 @@ ptr_array_new (guint reserved_size, array->pdata = NULL; array->len = 0; array->alloc = 0; + array->null_terminated = null_terminated ? 1 : 0; array->element_free_func = element_free_func; g_atomic_ref_count_init (&array->ref_count); if (reserved_size != 0) - g_ptr_array_maybe_expand (array, reserved_size); + { + if (G_LIKELY (reserved_size < G_MAXUINT) && + null_terminated) + reserved_size++; + g_ptr_array_maybe_expand (array, reserved_size); + if (null_terminated) + { + /* don't use ptr_array_null_terminate(). It helps the compiler + * to see when @null_terminated is false and thereby inline + * ptr_array_new() and possibly remove the code entirely. */ + array->pdata[0] = NULL; + } + } return (GPtrArray *) array; } @@ -1121,7 +1143,7 @@ ptr_array_new (guint reserved_size, GPtrArray* g_ptr_array_new (void) { - return ptr_array_new (0, NULL); + return ptr_array_new (0, NULL, FALSE); } /** @@ -1134,6 +1156,10 @@ g_ptr_array_new (void) * the underlying array is preserved for use elsewhere and returned * to the caller. * + * Note that if the array is %NULL terminated this may still return + * %NULL if the length of the array was zero and pdata was not yet + * allocated. + * * 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. @@ -1171,8 +1197,9 @@ g_ptr_array_new (void) * g_assert (chunk_buffer->len == 0); * ]| * - * Returns: (transfer full): the element data, which should be - * freed using g_free(). + * Returns: (transfer full) (nullable): the element data, which should be + * freed using g_free(). This may be %NULL if the array doesn’t have any + * elements (i.e. if `*len` is zero). * * Since: 2.64 */ @@ -1215,7 +1242,8 @@ g_ptr_array_steal (GPtrArray *array, * pointing to) are copied to the new #GPtrArray. * * The copy of @array will have the same #GDestroyNotify for its elements as - * @array. + * @array. The copy will also be %NULL terminated if (and only if) the source + * array is. * * Returns: (transfer full): a deep copy of the initial #GPtrArray. * @@ -1226,27 +1254,39 @@ g_ptr_array_copy (GPtrArray *array, GCopyFunc func, gpointer user_data) { + GRealPtrArray *rarray = (GRealPtrArray *) array; GPtrArray *new_array; g_return_val_if_fail (array != NULL, NULL); - new_array = ptr_array_new (array->len, - ((GRealPtrArray *) array)->element_free_func); + new_array = ptr_array_new (0, + rarray->element_free_func, + rarray->null_terminated); - if (func != NULL) + if (rarray->alloc > 0) { - guint i; + g_ptr_array_maybe_expand ((GRealPtrArray *) new_array, array->len + rarray->null_terminated); - for (i = 0; i < array->len; i++) - new_array->pdata[i] = func (array->pdata[i], user_data); - } - else if (array->len > 0) - { - memcpy (new_array->pdata, array->pdata, - array->len * sizeof (*array->pdata)); - } + if (array->len > 0) + { + if (func != NULL) + { + guint i; - new_array->len = array->len; + for (i = 0; i < array->len; i++) + new_array->pdata[i] = func (array->pdata[i], user_data); + } + else + { + memcpy (new_array->pdata, array->pdata, + array->len * sizeof (*array->pdata)); + } + + new_array->len = array->len; + } + + ptr_array_null_terminate (rarray); + } return new_array; } @@ -1265,7 +1305,7 @@ g_ptr_array_copy (GPtrArray *array, GPtrArray* g_ptr_array_sized_new (guint reserved_size) { - return ptr_array_new (reserved_size, NULL); + return ptr_array_new (reserved_size, NULL, FALSE); } /** @@ -1309,14 +1349,14 @@ g_array_copy (GArray *array) * either via g_ptr_array_unref(), when g_ptr_array_free() is called with * @free_segment set to %TRUE or when removing elements. * - * Returns: A new #GPtrArray + * Returns: (transfer full): A new #GPtrArray * * Since: 2.22 */ GPtrArray* g_ptr_array_new_with_free_func (GDestroyNotify element_free_func) { - return ptr_array_new (0, element_free_func); + return ptr_array_new (0, element_free_func, FALSE); } /** @@ -1333,7 +1373,7 @@ g_ptr_array_new_with_free_func (GDestroyNotify element_free_func) * g_ptr_array_unref(), when g_ptr_array_free() is called with * @free_segment set to %TRUE or when removing elements. * - * Returns: A new #GPtrArray + * Returns: (transfer full): A new #GPtrArray * * Since: 2.30 */ @@ -1341,7 +1381,45 @@ GPtrArray* g_ptr_array_new_full (guint reserved_size, GDestroyNotify element_free_func) { - return ptr_array_new (reserved_size, element_free_func); + return ptr_array_new (reserved_size, element_free_func, FALSE); +} + +/** + * g_ptr_array_new_null_terminated: + * @reserved_size: number of pointers preallocated. + * If @null_terminated is %TRUE, the actually allocated + * buffer size is @reserved_size plus 1, unless @reserved_size + * is zero, in which case no initial buffer gets allocated. + * @element_free_func: (nullable): A function to free elements with + * destroy @array or %NULL + * @null_terminated: whether to make the array as %NULL terminated. + * + * Like g_ptr_array_new_full() but also allows to set the array to + * be %NULL terminated. A %NULL terminated pointer array has an + * additional %NULL pointer after the last element, beyond the + * current length. + * + * #GPtrArray created by other constructors are not automatically %NULL + * terminated. + * + * Note that if the @array's length is zero and currently no + * data array is allocated, then pdata will still be %NULL. + * %GPtrArray will only %NULL terminate pdata, if an actual + * array is allocated. It does not guarantee that an array + * is always allocated. In other words, if the length is zero, + * then pdata may either point to a %NULL terminated array of length + * zero or be %NULL. + * + * Returns: (transfer full): A new #GPtrArray + * + * Since: 2.74 + */ +GPtrArray * +g_ptr_array_new_null_terminated (guint reserved_size, + GDestroyNotify element_free_func, + gboolean null_terminated) +{ + return ptr_array_new (reserved_size, element_free_func, null_terminated); } /** @@ -1367,6 +1445,29 @@ g_ptr_array_set_free_func (GPtrArray *array, rarray->element_free_func = element_free_func; } +/** + * g_ptr_array_is_null_terminated: + * @array: the #GPtrArray + * + * Gets whether the @array was constructed as %NULL-terminated. + * + * This will only return %TRUE for arrays constructed by passing %TRUE to the + * `null_terminated` argument of g_ptr_array_new_null_terminated(). It will not + * return %TRUE for normal arrays which have had a %NULL element appended to + * them. + * + * Returns: %TRUE if the array is made to be %NULL terminated. + * + * Since: 2.74 + */ +gboolean +g_ptr_array_is_null_terminated (GPtrArray *array) +{ + g_return_val_if_fail (array, FALSE); + + return ((GRealPtrArray *) array)->null_terminated; +} + /** * g_ptr_array_ref: * @array: a #GPtrArray @@ -1430,6 +1531,10 @@ g_ptr_array_unref (GPtrArray *array) * be freed separately if @free_seg is %TRUE and no #GDestroyNotify * function has been set for @array. * + * Note that if the array is %NULL terminated and @free_seg is %FALSE + * then this will always return an allocated %NULL terminated buffer. + * If pdata is previously %NULL, a new buffer will be allocated. + * * This function is not thread-safe. If using a #GPtrArray from multiple * threads, use only the atomic g_ptr_array_ref() and g_ptr_array_unref() * functions. @@ -1485,7 +1590,11 @@ ptr_array_free (GPtrArray *array, segment = NULL; } else - segment = rarray->pdata; + { + segment = rarray->pdata; + if (!segment && rarray->null_terminated) + segment = (gpointer *) g_new0 (char *, 1); + } if (flags & PRESERVE_WRAPPER) { @@ -1556,8 +1665,14 @@ g_ptr_array_set_size (GPtrArray *array, if (length_unsigned > rarray->len) { guint i; - g_ptr_array_maybe_expand (rarray, (length_unsigned - rarray->len)); - /* This is not + + if (G_UNLIKELY (rarray->null_terminated) && + length_unsigned - rarray->len > G_MAXUINT - 1) + g_error ("array would overflow"); + + g_ptr_array_maybe_expand (rarray, (length_unsigned - rarray->len) + rarray->null_terminated); + + /* This is not * memset (array->pdata + array->len, 0, * sizeof (gpointer) * (length_unsigned - array->len)); * to make it really portable. Remember (void*)NULL needn't be @@ -1565,11 +1680,13 @@ g_ptr_array_set_size (GPtrArray *array, */ for (i = rarray->len; i < length_unsigned; i++) rarray->pdata[i] = NULL; + + rarray->len = length_unsigned; + + ptr_array_null_terminate (rarray); } else if (length_unsigned < rarray->len) g_ptr_array_remove_range (array, length_unsigned, rarray->len - length_unsigned); - - rarray->len = length_unsigned; } static gpointer @@ -1599,7 +1716,7 @@ ptr_array_remove_index (GPtrArray *array, rarray->len -= 1; - if (G_UNLIKELY (g_mem_gc_friendly)) + if (rarray->null_terminated || G_UNLIKELY (g_mem_gc_friendly)) rarray->pdata[rarray->len] = NULL; return result; @@ -1715,7 +1832,10 @@ g_ptr_array_remove_range (GPtrArray *array, g_return_val_if_fail (rarray != NULL, NULL); g_return_val_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL), NULL); g_return_val_if_fail (index_ <= rarray->len, NULL); - g_return_val_if_fail (index_ + length <= rarray->len, NULL); + g_return_val_if_fail (length == 0 || index_ + length <= rarray->len, NULL); + + if (length == 0) + return array; if (rarray->element_free_func != NULL) { @@ -1736,6 +1856,8 @@ g_ptr_array_remove_range (GPtrArray *array, for (i = 0; i < length; i++) rarray->pdata[rarray->len + i] = NULL; } + else + ptr_array_null_terminate (rarray); return array; } @@ -1832,9 +1954,11 @@ g_ptr_array_add (GPtrArray *array, g_return_if_fail (rarray); g_return_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL)); - g_ptr_array_maybe_expand (rarray, 1); + g_ptr_array_maybe_expand (rarray, 1u + rarray->null_terminated); rarray->pdata[rarray->len++] = data; + + ptr_array_null_terminate (rarray); } /** @@ -1857,6 +1981,8 @@ g_ptr_array_add (GPtrArray *array, * If @func is %NULL, then only the pointers (and not what they are * pointing to) are copied to the new #GPtrArray. * + * Whether @array_to_extend is %NULL terminated stays unchanged by this function. + * * Since: 2.62 **/ void @@ -1870,7 +1996,14 @@ g_ptr_array_extend (GPtrArray *array_to_extend, g_return_if_fail (array_to_extend != NULL); g_return_if_fail (array != NULL); - g_ptr_array_maybe_expand (rarray_to_extend, array->len); + if (array->len == 0u) + return; + + if (G_UNLIKELY (array->len == G_MAXUINT) && + rarray_to_extend->null_terminated) + g_error ("adding %u to array would overflow", array->len); + + g_ptr_array_maybe_expand (rarray_to_extend, array->len + rarray_to_extend->null_terminated); if (func != NULL) { @@ -1887,6 +2020,8 @@ g_ptr_array_extend (GPtrArray *array_to_extend, } rarray_to_extend->len += array->len; + + ptr_array_null_terminate (rarray_to_extend); } /** @@ -1944,7 +2079,7 @@ g_ptr_array_insert (GPtrArray *array, g_return_if_fail (index_ >= -1); g_return_if_fail (index_ <= (gint)rarray->len); - g_ptr_array_maybe_expand (rarray, 1); + g_ptr_array_maybe_expand (rarray, 1u + rarray->null_terminated); if (index_ < 0) index_ = rarray->len; @@ -1956,6 +2091,8 @@ g_ptr_array_insert (GPtrArray *array, rarray->len++; rarray->pdata[index_] = data; + + ptr_array_null_terminate (rarray); } /* Please keep this doc-comment in sync with pointer_array_sort_example() diff --git a/glib/garray.h b/glib/garray.h index 6225bc283..2300e5f58 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -154,6 +154,10 @@ GPtrArray* g_ptr_array_sized_new (guint reserved_size); GLIB_AVAILABLE_IN_ALL GPtrArray* g_ptr_array_new_full (guint reserved_size, GDestroyNotify element_free_func); +GLIB_AVAILABLE_IN_2_74 +GPtrArray* g_ptr_array_new_null_terminated (guint reserved_size, + GDestroyNotify element_free_func, + gboolean null_terminated); GLIB_AVAILABLE_IN_ALL gpointer* g_ptr_array_free (GPtrArray *array, gboolean free_seg); @@ -225,6 +229,8 @@ gboolean g_ptr_array_find_with_equal_func (GPtrArray *haystack, GEqualFunc equal_func, guint *index_); +GLIB_AVAILABLE_IN_2_74 +gboolean g_ptr_array_is_null_terminated (GPtrArray *array); /* Byte arrays, an array of guint8. Implemented as a GArray, * but type-safe. diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index 1e61d64ef..9395607ef 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -886,6 +886,19 @@ array_overflow_set_size (void) } } +static void +assert_ptr_array_null_terminated (GPtrArray *array, gboolean null_terminated) +{ + g_assert_cmpint (null_terminated, ==, g_ptr_array_is_null_terminated (array)); + if (array->pdata) + { + if (null_terminated) + g_assert_null (array->pdata[array->len]); + } + else + g_assert_cmpint (array->len, ==, 0); +} + /* Check g_ptr_array_steal() function */ static void pointer_array_steal (void) @@ -927,6 +940,30 @@ pointer_array_steal (void) g_free (pdata); g_ptr_array_free (gparray, TRUE); + + gparray = g_ptr_array_new_null_terminated (0, NULL, TRUE); + pdata = g_ptr_array_steal (gparray, NULL); + g_assert_null (pdata); + g_ptr_array_unref (gparray); +} + +static void +pointer_array_free_null_terminated (void) +{ + GPtrArray *parray = NULL; + gpointer *segment; + + g_test_summary ("Check that g_ptr_array_free() on an empty array returns a NULL-terminated empty array"); + + parray = g_ptr_array_new_null_terminated (0, NULL, TRUE); + g_assert_nonnull (parray); + assert_ptr_array_null_terminated (parray, TRUE); + + segment = g_ptr_array_free (parray, FALSE); + g_assert_nonnull (segment); + g_assert_null (segment[0]); + + g_free (segment); } static void @@ -977,16 +1014,26 @@ pointer_array_insert (void) } static void -pointer_array_ref_count (void) +pointer_array_ref_count (gconstpointer test_data) { + const gboolean null_terminated = GPOINTER_TO_INT (test_data); GPtrArray *gparray; GPtrArray *gparray2; gint i; gint sum = 0; - gparray = g_ptr_array_new (); + if (null_terminated) + gparray = g_ptr_array_new_null_terminated (0, NULL, null_terminated); + else + gparray = g_ptr_array_new (); + + assert_ptr_array_null_terminated (gparray, null_terminated); + for (i = 0; i < 10000; i++) - g_ptr_array_add (gparray, GINT_TO_POINTER (i)); + { + g_ptr_array_add (gparray, GINT_TO_POINTER (i)); + assert_ptr_array_null_terminated (gparray, null_terminated); + } /* check we can ref, unref and still access the array */ gparray2 = g_ptr_array_ref (gparray); @@ -995,6 +1042,8 @@ pointer_array_ref_count (void) for (i = 0; i < 10000; i++) g_assert (g_ptr_array_index (gparray, i) == GINT_TO_POINTER (i)); + assert_ptr_array_null_terminated (gparray, null_terminated); + g_ptr_array_foreach (gparray, sum_up, &sum); g_assert (sum == 49995000); @@ -1003,6 +1052,8 @@ pointer_array_ref_count (void) g_ptr_array_free (gparray, TRUE); g_assert_cmpint (gparray2->len, ==, 0); + assert_ptr_array_null_terminated (gparray, null_terminated); + g_ptr_array_unref (gparray2); } @@ -1108,8 +1159,9 @@ ptr_array_copy_func (gconstpointer src, gpointer userdata) /* Test the g_ptr_array_copy() function */ static void -pointer_array_copy (void) +pointer_array_copy (gconstpointer test_data) { + const gboolean null_terminated = GPOINTER_TO_INT (test_data); GPtrArray *ptr_array, *ptr_array2; gsize i; const gsize array_size = 100; @@ -1134,16 +1186,18 @@ pointer_array_copy (void) array_test[i] = i; /* Test copy an empty array */ - ptr_array = g_ptr_array_sized_new (0); + ptr_array = g_ptr_array_new_null_terminated (0, NULL, null_terminated); ptr_array2 = g_ptr_array_copy (ptr_array, NULL, NULL); g_assert_cmpuint (ptr_array2->len, ==, ptr_array->len); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); g_ptr_array_unref (ptr_array); g_ptr_array_unref (ptr_array2); /* Test simple copy */ - ptr_array = g_ptr_array_sized_new (array_size); + ptr_array = g_ptr_array_new_null_terminated (array_size, NULL, null_terminated); for (i = 0; i < array_size; i++) g_ptr_array_add (ptr_array, &array_test[i]); @@ -1158,6 +1212,9 @@ pointer_array_copy (void) g_assert_cmpuint ((gsize) g_ptr_array_index (ptr_array, i), ==, (gsize) g_ptr_array_index (ptr_array2, i)); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); + g_ptr_array_free (ptr_array2, TRUE); /* Test copy through GCopyFunc */ @@ -1172,6 +1229,9 @@ pointer_array_copy (void) g_assert_cmpuint ((gsize) g_ptr_array_index (ptr_array, i), !=, (gsize) g_ptr_array_index (ptr_array2, i)); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); + g_ptr_array_free (ptr_array2, TRUE); /* Final cleanup */ @@ -1181,8 +1241,9 @@ pointer_array_copy (void) /* Test the g_ptr_array_extend() function */ static void -pointer_array_extend (void) +pointer_array_extend (gconstpointer test_data) { + gboolean null_terminated = GPOINTER_TO_INT (test_data); GPtrArray *ptr_array, *ptr_array2; gsize i; const gsize array_size = 100; @@ -1210,20 +1271,23 @@ pointer_array_extend (void) array_test[i] = i; /* Testing extend with array of size zero */ - ptr_array = g_ptr_array_sized_new (0); - ptr_array2 = g_ptr_array_sized_new (0); + ptr_array = g_ptr_array_new_null_terminated (0, NULL, null_terminated); + ptr_array2 = g_ptr_array_new_null_terminated (0, NULL, null_terminated); g_ptr_array_extend (ptr_array, ptr_array2, NULL, NULL); g_assert_cmpuint (ptr_array->len, ==, 0); g_assert_cmpuint (ptr_array2->len, ==, 0); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); + g_ptr_array_unref (ptr_array); g_ptr_array_unref (ptr_array2); /* Testing extend an array of size zero */ - ptr_array = g_ptr_array_sized_new (array_size); - ptr_array2 = g_ptr_array_sized_new (0); + ptr_array = g_ptr_array_new_null_terminated (array_size, NULL, null_terminated); + ptr_array2 = g_ptr_array_new_null_terminated (0, NULL, null_terminated); for (i = 0; i < array_size; i++) { @@ -1235,12 +1299,15 @@ pointer_array_extend (void) for (i = 0; i < array_size; i++) g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, i); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); + g_ptr_array_unref (ptr_array); g_ptr_array_unref (ptr_array2); /* Testing extend an array of size zero */ - ptr_array = g_ptr_array_sized_new (0); - ptr_array2 = g_ptr_array_sized_new (array_size); + ptr_array = g_ptr_array_new_null_terminated (0, NULL, null_terminated); + ptr_array2 = g_ptr_array_new_null_terminated (array_size, NULL, null_terminated); for (i = 0; i < array_size; i++) { @@ -1252,12 +1319,15 @@ pointer_array_extend (void) for (i = 0; i < array_size; i++) g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, i); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); + g_ptr_array_unref (ptr_array); g_ptr_array_unref (ptr_array2); /* Testing simple extend */ - ptr_array = g_ptr_array_sized_new (array_size / 2); - ptr_array2 = g_ptr_array_sized_new (array_size / 2); + ptr_array = g_ptr_array_new_null_terminated (array_size / 2, NULL, null_terminated); + ptr_array2 = g_ptr_array_new_null_terminated (array_size / 2, NULL, null_terminated); for (i = 0; i < array_size / 2; i++) { @@ -1270,12 +1340,15 @@ pointer_array_extend (void) for (i = 0; i < array_size; i++) g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, i); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); + g_ptr_array_unref (ptr_array); g_ptr_array_unref (ptr_array2); /* Testing extend with GCopyFunc */ - ptr_array = g_ptr_array_sized_new (array_size / 2); - ptr_array2 = g_ptr_array_sized_new (array_size / 2); + ptr_array = g_ptr_array_new_null_terminated (array_size / 2, NULL, null_terminated); + ptr_array2 = g_ptr_array_new_null_terminated (array_size / 2, NULL, null_terminated); for (i = 0; i < array_size / 2; i++) { @@ -1288,6 +1361,9 @@ pointer_array_extend (void) for (i = 0; i < array_size; i++) g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, i); + assert_ptr_array_null_terminated (ptr_array, null_terminated); + assert_ptr_array_null_terminated (ptr_array2, null_terminated); + /* Clean-up memory */ for (i = array_size / 2; i < array_size; i++) g_free (g_ptr_array_index (ptr_array, i)); @@ -1631,6 +1707,17 @@ pointer_array_find_non_empty (void) g_ptr_array_free (array, TRUE); } +static void +pointer_array_remove_range (void) +{ + GPtrArray *parray = NULL; + + /* Try removing an empty range. */ + parray = g_ptr_array_new (); + g_ptr_array_remove_range (parray, 0, 0); + g_ptr_array_unref (parray); +} + static void steal_destroy_notify (gpointer data) { @@ -1641,19 +1728,32 @@ 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_index (void) +pointer_array_steal_index (gconstpointer test_data) { + const gboolean null_terminated = GPOINTER_TO_INT (test_data); guint i1 = 0, i2 = 0, i3 = 0, i4 = 0; gpointer out1, out2; - GPtrArray *array = g_ptr_array_new_with_free_func (steal_destroy_notify); + GPtrArray *array; + + if (null_terminated) + array = g_ptr_array_new_null_terminated (0, steal_destroy_notify, null_terminated); + else + array = g_ptr_array_new_with_free_func (steal_destroy_notify); + + assert_ptr_array_null_terminated (array, null_terminated); g_ptr_array_add (array, &i1); g_ptr_array_add (array, &i2); + + assert_ptr_array_null_terminated (array, null_terminated); + g_ptr_array_add (array, &i3); g_ptr_array_add (array, &i4); g_assert_cmpuint (array->len, ==, 4); + assert_ptr_array_null_terminated (array, null_terminated); + /* Remove a single element. */ out1 = g_ptr_array_steal_index (array, 0); g_assert_true (out1 == &i1); @@ -1665,6 +1765,8 @@ pointer_array_steal_index (void) g_assert_true (g_ptr_array_index (array, 1) == &i3); g_assert_true (g_ptr_array_index (array, 2) == &i4); + assert_ptr_array_null_terminated (array, null_terminated); + /* Remove another element, quickly. */ out2 = g_ptr_array_steal_index_fast (array, 0); g_assert_true (out2 == &i2); @@ -1675,6 +1777,8 @@ pointer_array_steal_index (void) g_assert_true (g_ptr_array_index (array, 0) == &i4); g_assert_true (g_ptr_array_index (array, 1) == &i3); + assert_ptr_array_null_terminated (array, null_terminated); + /* Check that destroying the pointer array doesn’t affect the stolen elements. */ g_ptr_array_unref (array); @@ -2079,12 +2183,16 @@ main (int argc, char *argv[]) } /* pointer arrays */ + 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/ref-count", pointer_array_ref_count); + 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); - g_test_add_func ("/pointerarray/array_copy", pointer_array_copy); - g_test_add_func ("/pointerarray/array_extend", pointer_array_extend); + g_test_add_data_func ("/pointerarray/array_copy/not-null-terminated", GINT_TO_POINTER (0), pointer_array_copy); + g_test_add_data_func ("/pointerarray/array_copy/null-terminated", GINT_TO_POINTER (1), pointer_array_copy); + g_test_add_data_func ("/pointerarray/array_extend/not-null-terminated", GINT_TO_POINTER (0), pointer_array_extend); + g_test_add_data_func ("/pointerarray/array_extend/null-terminated", GINT_TO_POINTER (1), pointer_array_extend); g_test_add_func ("/pointerarray/array_extend_and_steal", pointer_array_extend_and_steal); g_test_add_func ("/pointerarray/sort", pointer_array_sort); g_test_add_func ("/pointerarray/sort/example", pointer_array_sort_example); @@ -2092,8 +2200,10 @@ main (int argc, char *argv[]) g_test_add_func ("/pointerarray/sort-with-data/example", pointer_array_sort_with_data_example); 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/remove-range", pointer_array_remove_range); g_test_add_func ("/pointerarray/steal", pointer_array_steal); - g_test_add_func ("/pointerarray/steal_index", pointer_array_steal_index); + g_test_add_data_func ("/pointerarray/steal_index/not-null-terminated", GINT_TO_POINTER (0), pointer_array_steal_index); + g_test_add_data_func ("/pointerarray/steal_index/null-terminated", GINT_TO_POINTER (1), pointer_array_steal_index); /* byte arrays */ g_test_add_func ("/bytearray/steal", byte_array_steal);