Merge branch 'th/g-ptr-array-set-null-terminated' into 'main'

array: add support for g_ptr_array_null_terminated()

See merge request GNOME/glib!1485
This commit is contained in:
Philip Withnall 2022-05-27 15:08:50 +00:00
commit a922f2f2c7
4 changed files with 312 additions and 57 deletions

View File

@ -3028,7 +3028,9 @@ g_ptr_array_sized_new
g_ptr_array_new_with_free_func g_ptr_array_new_with_free_func
g_ptr_array_copy g_ptr_array_copy
g_ptr_array_new_full g_ptr_array_new_full
g_ptr_array_new_null_terminated
g_ptr_array_set_free_func g_ptr_array_set_free_func
g_ptr_array_is_null_terminated
g_ptr_array_ref g_ptr_array_ref
g_ptr_array_unref g_ptr_array_unref
g_ptr_array_add g_ptr_array_add

View File

@ -1071,6 +1071,7 @@ struct _GRealPtrArray
guint len; guint len;
guint alloc; guint alloc;
gatomicrefcount ref_count; gatomicrefcount ref_count;
guint8 null_terminated; /* always either 0 or 1, so it can be added to array lengths */
GDestroyNotify element_free_func; GDestroyNotify element_free_func;
}; };
@ -1090,9 +1091,17 @@ struct _GRealPtrArray
static void g_ptr_array_maybe_expand (GRealPtrArray *array, static void g_ptr_array_maybe_expand (GRealPtrArray *array,
guint len); guint len);
static void
ptr_array_null_terminate (GRealPtrArray *rarray)
{
if (G_UNLIKELY (rarray->null_terminated))
rarray->pdata[rarray->len] = NULL;
}
static GPtrArray * static GPtrArray *
ptr_array_new (guint reserved_size, ptr_array_new (guint reserved_size,
GDestroyNotify element_free_func) GDestroyNotify element_free_func,
gboolean null_terminated)
{ {
GRealPtrArray *array; GRealPtrArray *array;
@ -1101,12 +1110,25 @@ ptr_array_new (guint reserved_size,
array->pdata = NULL; array->pdata = NULL;
array->len = 0; array->len = 0;
array->alloc = 0; array->alloc = 0;
array->null_terminated = null_terminated ? 1 : 0;
array->element_free_func = element_free_func; array->element_free_func = element_free_func;
g_atomic_ref_count_init (&array->ref_count); g_atomic_ref_count_init (&array->ref_count);
if (reserved_size != 0) 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; return (GPtrArray *) array;
} }
@ -1121,7 +1143,7 @@ ptr_array_new (guint reserved_size,
GPtrArray* GPtrArray*
g_ptr_array_new (void) 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 * the underlying array is preserved for use elsewhere and returned
* to the caller. * 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 * Even if set, the #GDestroyNotify function will never be called
* on the current contents of the array and the caller is * on the current contents of the array and the caller is
* responsible for freeing the array elements. * responsible for freeing the array elements.
@ -1171,8 +1197,9 @@ g_ptr_array_new (void)
* g_assert (chunk_buffer->len == 0); * g_assert (chunk_buffer->len == 0);
* ]| * ]|
* *
* Returns: (transfer full): the element data, which should be * Returns: (transfer full) (nullable): the element data, which should be
* freed using g_free(). * freed using g_free(). This may be %NULL if the array doesnt have any
* elements (i.e. if `*len` is zero).
* *
* Since: 2.64 * Since: 2.64
*/ */
@ -1215,7 +1242,8 @@ g_ptr_array_steal (GPtrArray *array,
* pointing to) are copied to the new #GPtrArray. * pointing to) are copied to the new #GPtrArray.
* *
* The copy of @array will have the same #GDestroyNotify for its elements as * 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. * Returns: (transfer full): a deep copy of the initial #GPtrArray.
* *
@ -1226,27 +1254,39 @@ g_ptr_array_copy (GPtrArray *array,
GCopyFunc func, GCopyFunc func,
gpointer user_data) gpointer user_data)
{ {
GRealPtrArray *rarray = (GRealPtrArray *) array;
GPtrArray *new_array; GPtrArray *new_array;
g_return_val_if_fail (array != NULL, NULL); g_return_val_if_fail (array != NULL, NULL);
new_array = ptr_array_new (array->len, new_array = ptr_array_new (0,
((GRealPtrArray *) array)->element_free_func); 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++) if (array->len > 0)
new_array->pdata[i] = func (array->pdata[i], user_data); {
} if (func != NULL)
else if (array->len > 0) {
{ guint i;
memcpy (new_array->pdata, array->pdata,
array->len * sizeof (*array->pdata));
}
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; return new_array;
} }
@ -1265,7 +1305,7 @@ g_ptr_array_copy (GPtrArray *array,
GPtrArray* GPtrArray*
g_ptr_array_sized_new (guint reserved_size) 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 * either via g_ptr_array_unref(), when g_ptr_array_free() is called with
* @free_segment set to %TRUE or when removing elements. * @free_segment set to %TRUE or when removing elements.
* *
* Returns: A new #GPtrArray * Returns: (transfer full): A new #GPtrArray
* *
* Since: 2.22 * Since: 2.22
*/ */
GPtrArray* GPtrArray*
g_ptr_array_new_with_free_func (GDestroyNotify element_free_func) 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 * g_ptr_array_unref(), when g_ptr_array_free() is called with
* @free_segment set to %TRUE or when removing elements. * @free_segment set to %TRUE or when removing elements.
* *
* Returns: A new #GPtrArray * Returns: (transfer full): A new #GPtrArray
* *
* Since: 2.30 * Since: 2.30
*/ */
@ -1341,7 +1381,45 @@ GPtrArray*
g_ptr_array_new_full (guint reserved_size, g_ptr_array_new_full (guint reserved_size,
GDestroyNotify element_free_func) 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; 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: * g_ptr_array_ref:
* @array: a #GPtrArray * @array: a #GPtrArray
@ -1430,6 +1531,10 @@ g_ptr_array_unref (GPtrArray *array)
* be freed separately if @free_seg is %TRUE and no #GDestroyNotify * be freed separately if @free_seg is %TRUE and no #GDestroyNotify
* function has been set for @array. * 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 * 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() * threads, use only the atomic g_ptr_array_ref() and g_ptr_array_unref()
* functions. * functions.
@ -1485,7 +1590,11 @@ ptr_array_free (GPtrArray *array,
segment = NULL; segment = NULL;
} }
else else
segment = rarray->pdata; {
segment = rarray->pdata;
if (!segment && rarray->null_terminated)
segment = (gpointer *) g_new0 (char *, 1);
}
if (flags & PRESERVE_WRAPPER) if (flags & PRESERVE_WRAPPER)
{ {
@ -1556,8 +1665,14 @@ g_ptr_array_set_size (GPtrArray *array,
if (length_unsigned > rarray->len) if (length_unsigned > rarray->len)
{ {
guint i; 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, * memset (array->pdata + array->len, 0,
* sizeof (gpointer) * (length_unsigned - array->len)); * sizeof (gpointer) * (length_unsigned - array->len));
* to make it really portable. Remember (void*)NULL needn't be * 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++) for (i = rarray->len; i < length_unsigned; i++)
rarray->pdata[i] = NULL; rarray->pdata[i] = NULL;
rarray->len = length_unsigned;
ptr_array_null_terminate (rarray);
} }
else if (length_unsigned < rarray->len) else if (length_unsigned < rarray->len)
g_ptr_array_remove_range (array, length_unsigned, rarray->len - length_unsigned); g_ptr_array_remove_range (array, length_unsigned, rarray->len - length_unsigned);
rarray->len = length_unsigned;
} }
static gpointer static gpointer
@ -1599,7 +1716,7 @@ ptr_array_remove_index (GPtrArray *array,
rarray->len -= 1; 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; rarray->pdata[rarray->len] = NULL;
return result; 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 != NULL, NULL);
g_return_val_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != 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_ <= 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) if (rarray->element_free_func != NULL)
{ {
@ -1736,6 +1856,8 @@ g_ptr_array_remove_range (GPtrArray *array,
for (i = 0; i < length; i++) for (i = 0; i < length; i++)
rarray->pdata[rarray->len + i] = NULL; rarray->pdata[rarray->len + i] = NULL;
} }
else
ptr_array_null_terminate (rarray);
return array; return array;
} }
@ -1832,9 +1954,11 @@ g_ptr_array_add (GPtrArray *array,
g_return_if_fail (rarray); g_return_if_fail (rarray);
g_return_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL)); 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; 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 * If @func is %NULL, then only the pointers (and not what they are
* pointing to) are copied to the new #GPtrArray. * pointing to) are copied to the new #GPtrArray.
* *
* Whether @array_to_extend is %NULL terminated stays unchanged by this function.
*
* Since: 2.62 * Since: 2.62
**/ **/
void 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_to_extend != NULL);
g_return_if_fail (array != 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) if (func != NULL)
{ {
@ -1887,6 +2020,8 @@ g_ptr_array_extend (GPtrArray *array_to_extend,
} }
rarray_to_extend->len += array->len; 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_ >= -1);
g_return_if_fail (index_ <= (gint)rarray->len); 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) if (index_ < 0)
index_ = rarray->len; index_ = rarray->len;
@ -1956,6 +2091,8 @@ g_ptr_array_insert (GPtrArray *array,
rarray->len++; rarray->len++;
rarray->pdata[index_] = data; rarray->pdata[index_] = data;
ptr_array_null_terminate (rarray);
} }
/* Please keep this doc-comment in sync with pointer_array_sort_example() /* Please keep this doc-comment in sync with pointer_array_sort_example()

View File

@ -154,6 +154,10 @@ GPtrArray* g_ptr_array_sized_new (guint reserved_size);
GLIB_AVAILABLE_IN_ALL GLIB_AVAILABLE_IN_ALL
GPtrArray* g_ptr_array_new_full (guint reserved_size, GPtrArray* g_ptr_array_new_full (guint reserved_size,
GDestroyNotify element_free_func); 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 GLIB_AVAILABLE_IN_ALL
gpointer* g_ptr_array_free (GPtrArray *array, gpointer* g_ptr_array_free (GPtrArray *array,
gboolean free_seg); gboolean free_seg);
@ -225,6 +229,8 @@ gboolean g_ptr_array_find_with_equal_func (GPtrArray *haystack,
GEqualFunc equal_func, GEqualFunc equal_func,
guint *index_); 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, /* Byte arrays, an array of guint8. Implemented as a GArray,
* but type-safe. * but type-safe.

View File

@ -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 */ /* Check g_ptr_array_steal() function */
static void static void
pointer_array_steal (void) pointer_array_steal (void)
@ -927,6 +940,30 @@ pointer_array_steal (void)
g_free (pdata); g_free (pdata);
g_ptr_array_free (gparray, TRUE); 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 static void
@ -977,16 +1014,26 @@ pointer_array_insert (void)
} }
static 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 *gparray;
GPtrArray *gparray2; GPtrArray *gparray2;
gint i; gint i;
gint sum = 0; 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++) 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 */ /* check we can ref, unref and still access the array */
gparray2 = g_ptr_array_ref (gparray); gparray2 = g_ptr_array_ref (gparray);
@ -995,6 +1042,8 @@ pointer_array_ref_count (void)
for (i = 0; i < 10000; i++) for (i = 0; i < 10000; i++)
g_assert (g_ptr_array_index (gparray, i) == GINT_TO_POINTER (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_ptr_array_foreach (gparray, sum_up, &sum);
g_assert (sum == 49995000); g_assert (sum == 49995000);
@ -1003,6 +1052,8 @@ pointer_array_ref_count (void)
g_ptr_array_free (gparray, TRUE); g_ptr_array_free (gparray, TRUE);
g_assert_cmpint (gparray2->len, ==, 0); g_assert_cmpint (gparray2->len, ==, 0);
assert_ptr_array_null_terminated (gparray, null_terminated);
g_ptr_array_unref (gparray2); g_ptr_array_unref (gparray2);
} }
@ -1108,8 +1159,9 @@ ptr_array_copy_func (gconstpointer src, gpointer userdata)
/* Test the g_ptr_array_copy() function */ /* Test the g_ptr_array_copy() function */
static void 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; GPtrArray *ptr_array, *ptr_array2;
gsize i; gsize i;
const gsize array_size = 100; const gsize array_size = 100;
@ -1134,16 +1186,18 @@ pointer_array_copy (void)
array_test[i] = i; array_test[i] = i;
/* Test copy an empty array */ /* 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); ptr_array2 = g_ptr_array_copy (ptr_array, NULL, NULL);
g_assert_cmpuint (ptr_array2->len, ==, ptr_array->len); 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_array);
g_ptr_array_unref (ptr_array2); g_ptr_array_unref (ptr_array2);
/* Test simple copy */ /* 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++) for (i = 0; i < array_size; i++)
g_ptr_array_add (ptr_array, &array_test[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), ==, g_assert_cmpuint ((gsize) g_ptr_array_index (ptr_array, i), ==,
(gsize) g_ptr_array_index (ptr_array2, 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); g_ptr_array_free (ptr_array2, TRUE);
/* Test copy through GCopyFunc */ /* Test copy through GCopyFunc */
@ -1172,6 +1229,9 @@ pointer_array_copy (void)
g_assert_cmpuint ((gsize) g_ptr_array_index (ptr_array, i), !=, g_assert_cmpuint ((gsize) g_ptr_array_index (ptr_array, i), !=,
(gsize) g_ptr_array_index (ptr_array2, 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); g_ptr_array_free (ptr_array2, TRUE);
/* Final cleanup */ /* Final cleanup */
@ -1181,8 +1241,9 @@ pointer_array_copy (void)
/* Test the g_ptr_array_extend() function */ /* Test the g_ptr_array_extend() function */
static void 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; GPtrArray *ptr_array, *ptr_array2;
gsize i; gsize i;
const gsize array_size = 100; const gsize array_size = 100;
@ -1210,20 +1271,23 @@ pointer_array_extend (void)
array_test[i] = i; array_test[i] = i;
/* Testing extend with array of size zero */ /* Testing extend with array of size zero */
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_sized_new (0); ptr_array2 = g_ptr_array_new_null_terminated (0, NULL, null_terminated);
g_ptr_array_extend (ptr_array, ptr_array2, NULL, NULL); g_ptr_array_extend (ptr_array, ptr_array2, NULL, NULL);
g_assert_cmpuint (ptr_array->len, ==, 0); g_assert_cmpuint (ptr_array->len, ==, 0);
g_assert_cmpuint (ptr_array2->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_array);
g_ptr_array_unref (ptr_array2); g_ptr_array_unref (ptr_array2);
/* Testing extend an array of size zero */ /* Testing extend an array of size zero */
ptr_array = g_ptr_array_sized_new (array_size); ptr_array = g_ptr_array_new_null_terminated (array_size, NULL, null_terminated);
ptr_array2 = g_ptr_array_sized_new (0); ptr_array2 = g_ptr_array_new_null_terminated (0, NULL, null_terminated);
for (i = 0; i < array_size; i++) for (i = 0; i < array_size; i++)
{ {
@ -1235,12 +1299,15 @@ pointer_array_extend (void)
for (i = 0; i < array_size; i++) for (i = 0; i < array_size; i++)
g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, 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_array);
g_ptr_array_unref (ptr_array2); g_ptr_array_unref (ptr_array2);
/* Testing extend an array of size zero */ /* Testing extend an array of size zero */
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_sized_new (array_size); ptr_array2 = g_ptr_array_new_null_terminated (array_size, NULL, null_terminated);
for (i = 0; i < array_size; i++) for (i = 0; i < array_size; i++)
{ {
@ -1252,12 +1319,15 @@ pointer_array_extend (void)
for (i = 0; i < array_size; i++) for (i = 0; i < array_size; i++)
g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, 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_array);
g_ptr_array_unref (ptr_array2); g_ptr_array_unref (ptr_array2);
/* Testing simple extend */ /* Testing simple extend */
ptr_array = 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_sized_new (array_size / 2); ptr_array2 = g_ptr_array_new_null_terminated (array_size / 2, NULL, null_terminated);
for (i = 0; i < array_size / 2; i++) for (i = 0; i < array_size / 2; i++)
{ {
@ -1270,12 +1340,15 @@ pointer_array_extend (void)
for (i = 0; i < array_size; i++) for (i = 0; i < array_size; i++)
g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, 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_array);
g_ptr_array_unref (ptr_array2); g_ptr_array_unref (ptr_array2);
/* Testing extend with GCopyFunc */ /* Testing extend with GCopyFunc */
ptr_array = 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_sized_new (array_size / 2); ptr_array2 = g_ptr_array_new_null_terminated (array_size / 2, NULL, null_terminated);
for (i = 0; i < array_size / 2; i++) for (i = 0; i < array_size / 2; i++)
{ {
@ -1288,6 +1361,9 @@ pointer_array_extend (void)
for (i = 0; i < array_size; i++) for (i = 0; i < array_size; i++)
g_assert_cmpuint (*((gsize *) g_ptr_array_index (ptr_array, i)), ==, 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 */ /* Clean-up memory */
for (i = array_size / 2; i < array_size; i++) for (i = array_size / 2; i < array_size; i++)
g_free (g_ptr_array_index (ptr_array, 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); 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 static void
steal_destroy_notify (gpointer data) 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 /* 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. */ * remove elements from a pointer array without the #GDestroyNotify being called. */
static void 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; guint i1 = 0, i2 = 0, i3 = 0, i4 = 0;
gpointer out1, out2; 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, &i1);
g_ptr_array_add (array, &i2); 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, &i3);
g_ptr_array_add (array, &i4); g_ptr_array_add (array, &i4);
g_assert_cmpuint (array->len, ==, 4); g_assert_cmpuint (array->len, ==, 4);
assert_ptr_array_null_terminated (array, null_terminated);
/* Remove a single element. */ /* Remove a single element. */
out1 = g_ptr_array_steal_index (array, 0); out1 = g_ptr_array_steal_index (array, 0);
g_assert_true (out1 == &i1); 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, 1) == &i3);
g_assert_true (g_ptr_array_index (array, 2) == &i4); g_assert_true (g_ptr_array_index (array, 2) == &i4);
assert_ptr_array_null_terminated (array, null_terminated);
/* Remove another element, quickly. */ /* Remove another element, quickly. */
out2 = g_ptr_array_steal_index_fast (array, 0); out2 = g_ptr_array_steal_index_fast (array, 0);
g_assert_true (out2 == &i2); 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, 0) == &i4);
g_assert_true (g_ptr_array_index (array, 1) == &i3); g_assert_true (g_ptr_array_index (array, 1) == &i3);
assert_ptr_array_null_terminated (array, null_terminated);
/* Check that destroying the pointer array doesnt affect the stolen elements. */ /* Check that destroying the pointer array doesnt affect the stolen elements. */
g_ptr_array_unref (array); g_ptr_array_unref (array);
@ -2079,12 +2183,16 @@ main (int argc, char *argv[])
} }
/* pointer arrays */ /* 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/add", pointer_array_add);
g_test_add_func ("/pointerarray/insert", pointer_array_insert); 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/free-func", pointer_array_free_func);
g_test_add_func ("/pointerarray/array_copy", pointer_array_copy); g_test_add_data_func ("/pointerarray/array_copy/not-null-terminated", GINT_TO_POINTER (0), pointer_array_copy);
g_test_add_func ("/pointerarray/array_extend", pointer_array_extend); 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/array_extend_and_steal", pointer_array_extend_and_steal);
g_test_add_func ("/pointerarray/sort", pointer_array_sort); g_test_add_func ("/pointerarray/sort", pointer_array_sort);
g_test_add_func ("/pointerarray/sort/example", pointer_array_sort_example); 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/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/empty", pointer_array_find_empty);
g_test_add_func ("/pointerarray/find/non-empty", pointer_array_find_non_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", 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 */ /* byte arrays */
g_test_add_func ("/bytearray/steal", byte_array_steal); g_test_add_func ("/bytearray/steal", byte_array_steal);