mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-26 05:56:14 +01:00
garray: Add g_array_new_take() and g_array_new_take_zero_terminated()
Make it easy to handle C arrays using GArray API stealing data from other sources.
This commit is contained in:
parent
9ea56848c2
commit
b15040215a
@ -2680,6 +2680,8 @@ g_string_chunk_free
|
||||
<FILE>arrays</FILE>
|
||||
GArray
|
||||
g_array_new
|
||||
g_array_new_take
|
||||
g_array_new_take_zero_terminated
|
||||
g_array_steal
|
||||
g_array_sized_new
|
||||
g_array_copy
|
||||
|
114
glib/garray.c
114
glib/garray.c
@ -35,6 +35,7 @@
|
||||
|
||||
#include "garray.h"
|
||||
|
||||
#include "galloca.h"
|
||||
#include "gbytes.h"
|
||||
#include "ghash.h"
|
||||
#include "gslice.h"
|
||||
@ -190,6 +191,119 @@ g_array_new (gboolean zero_terminated,
|
||||
return g_array_sized_new (zero_terminated, clear, elt_size, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_array_new_take: (skip)
|
||||
* @data: (array length=len) (transfer full) (nullable): an array of
|
||||
* elements of @element_size, or %NULL for an empty array
|
||||
* @len: the number of elements in @data
|
||||
* @clear: %TRUE if #GArray elements should be automatically cleared
|
||||
* to 0 when they are allocated
|
||||
* @element_size: the size of each element in bytes
|
||||
*
|
||||
* Creates a new #GArray with @data as array data, @len as length and a
|
||||
* reference count of 1.
|
||||
*
|
||||
* This avoids having to copy the data manually, when it can just be
|
||||
* inherited. @data will eventually be freed using g_free(), so must
|
||||
* have been allocated with a suitable allocator.
|
||||
*
|
||||
* In case the elements need to be cleared when the array is freed, use
|
||||
* g_array_set_clear_func() to set a #GDestroyNotify function to perform
|
||||
* such task.
|
||||
*
|
||||
* Do not use it if @len or @element_size are greater than %G_MAXUINT.
|
||||
* #GArray stores the length of its data in #guint, which may be shorter
|
||||
* than #gsize.
|
||||
*
|
||||
* Returns: (transfer full): A new #GArray
|
||||
*
|
||||
* Since: 2.76
|
||||
*/
|
||||
GArray *
|
||||
g_array_new_take (gpointer data,
|
||||
gsize len,
|
||||
gboolean clear,
|
||||
gsize element_size)
|
||||
{
|
||||
GRealArray *rarray;
|
||||
GArray *array;
|
||||
|
||||
g_return_val_if_fail (data != NULL || len == 0, NULL);
|
||||
g_return_val_if_fail (len <= G_MAXUINT, NULL);
|
||||
g_return_val_if_fail (element_size <= G_MAXUINT, NULL);
|
||||
|
||||
array = g_array_sized_new (FALSE, clear, element_size, 0);
|
||||
rarray = (GRealArray *) array;
|
||||
rarray->data = (guint8 *) g_steal_pointer (&data);
|
||||
rarray->len = len;
|
||||
rarray->elt_capacity = len;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_array_new_take_zero_terminated: (skip)
|
||||
* @data: (array zero-terminated=1): an array of elements of @element_size
|
||||
* @clear: %TRUE if #GArray elements should be automatically cleared
|
||||
* to 0 when they are allocated
|
||||
* @element_size: the size of each element in bytes
|
||||
*
|
||||
* Creates a new #GArray with @data as array data, computing the length of it
|
||||
* and setting the reference count to 1.
|
||||
*
|
||||
* This avoids having to copy the data manually, when it can just be
|
||||
* inherited. @data will eventually be freed using g_free(), so must
|
||||
* have been allocated with a suitable allocator.
|
||||
*
|
||||
* The length is calculated by iterating through @data until the first %NULL
|
||||
* element is found.
|
||||
*
|
||||
* In case the elements need to be cleared when the array is freed, use
|
||||
* g_array_set_clear_func() to set a #GDestroyNotify function to perform
|
||||
* such task.
|
||||
*
|
||||
* Do not use it if @data length or @element_size are greater than %G_MAXUINT.
|
||||
* #GArray stores the length of its data in #guint, which may be shorter
|
||||
* than #gsize.
|
||||
*
|
||||
* Returns: (transfer full): A new #GArray
|
||||
*
|
||||
* Since: 2.76
|
||||
*/
|
||||
GArray *
|
||||
g_array_new_take_zero_terminated (gpointer data,
|
||||
gboolean clear,
|
||||
gsize element_size)
|
||||
{
|
||||
GArray *array;
|
||||
gsize len = 0;
|
||||
|
||||
g_return_val_if_fail (element_size <= G_MAXUINT, NULL);
|
||||
|
||||
if (data != NULL)
|
||||
{
|
||||
guint8 *array_data = data;
|
||||
|
||||
for (gsize i = 0; ; ++i)
|
||||
{
|
||||
const guint8 *element_start = array_data + (i * element_size);
|
||||
|
||||
if (*element_start == 0 &&
|
||||
memcmp (element_start, element_start + 1, element_size - 1) == 0)
|
||||
break;
|
||||
|
||||
len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
g_return_val_if_fail (len <= G_MAXUINT, NULL);
|
||||
|
||||
array = g_array_new_take (data, len, clear, element_size);
|
||||
((GRealArray *)array)->zero_terminated = TRUE;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_array_steal:
|
||||
* @array: a #GArray.
|
||||
|
@ -72,6 +72,15 @@ GLIB_AVAILABLE_IN_ALL
|
||||
GArray* g_array_new (gboolean zero_terminated,
|
||||
gboolean clear_,
|
||||
guint element_size);
|
||||
GLIB_AVAILABLE_IN_2_76
|
||||
GArray* g_array_new_take (gpointer data,
|
||||
gsize len,
|
||||
gboolean clear,
|
||||
gsize element_size);
|
||||
GLIB_AVAILABLE_IN_2_76
|
||||
GArray* g_array_new_take_zero_terminated (gpointer data,
|
||||
gboolean clear,
|
||||
gsize element_size);
|
||||
GLIB_AVAILABLE_IN_2_64
|
||||
gpointer g_array_steal (GArray *array,
|
||||
gsize *len);
|
||||
|
@ -142,6 +142,196 @@ array_new_zero_terminated (void)
|
||||
g_free (out_str);
|
||||
}
|
||||
|
||||
static void
|
||||
array_new_take (void)
|
||||
{
|
||||
const size_t array_size = 10000;
|
||||
GArray *garray;
|
||||
gpointer *data;
|
||||
gpointer *old_data_copy;
|
||||
gsize len;
|
||||
|
||||
garray = g_array_new (FALSE, FALSE, sizeof (size_t));
|
||||
for (size_t i = 0; i < array_size; i++)
|
||||
g_array_append_val (garray, i);
|
||||
|
||||
data = g_array_steal (garray, &len);
|
||||
g_assert_cmpuint (array_size, ==, len);
|
||||
g_assert_nonnull (data);
|
||||
g_clear_pointer (&garray, g_array_unref);
|
||||
|
||||
old_data_copy = g_memdup2 (data, len * sizeof (size_t));
|
||||
garray = g_array_new_take (g_steal_pointer (&data), len, FALSE, sizeof (size_t));
|
||||
g_assert_cmpuint (garray->len, ==, array_size);
|
||||
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, 0), ==, 0);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, 10), ==, 10);
|
||||
|
||||
g_assert_cmpmem (old_data_copy, array_size * sizeof (size_t),
|
||||
garray->data, array_size * sizeof (size_t));
|
||||
|
||||
size_t val = 55;
|
||||
g_array_append_val (garray, val);
|
||||
val = 33;
|
||||
g_array_prepend_val (garray, val);
|
||||
|
||||
g_assert_cmpuint (garray->len, ==, array_size + 2);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, 0), ==, 33);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, garray->len - 1), ==, 55);
|
||||
|
||||
g_array_remove_index (garray, 0);
|
||||
g_assert_cmpuint (garray->len, ==, array_size + 1);
|
||||
g_array_remove_index (garray, garray->len - 1);
|
||||
g_assert_cmpuint (garray->len, ==, array_size);
|
||||
|
||||
g_assert_cmpmem (old_data_copy, array_size * sizeof (size_t),
|
||||
garray->data, array_size * sizeof (size_t));
|
||||
|
||||
g_array_unref (garray);
|
||||
g_free (old_data_copy);
|
||||
}
|
||||
|
||||
static void
|
||||
array_new_take_empty (void)
|
||||
{
|
||||
GArray *garray;
|
||||
size_t empty_array[] = {0};
|
||||
|
||||
garray = g_array_new_take (
|
||||
g_memdup2 (&empty_array, sizeof (size_t)), 0, FALSE, sizeof (size_t));
|
||||
g_assert_cmpuint (garray->len, ==, 0);
|
||||
|
||||
g_clear_pointer (&garray, g_array_unref);
|
||||
|
||||
garray = g_array_new_take (NULL, 0, FALSE, sizeof (size_t));
|
||||
g_assert_cmpuint (garray->len, ==, 0);
|
||||
|
||||
g_clear_pointer (&garray, g_array_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
array_new_take_zero_terminated (void)
|
||||
{
|
||||
size_t array_size = 10000;
|
||||
GArray *garray;
|
||||
gpointer *data;
|
||||
gpointer *old_data_copy;
|
||||
gsize len;
|
||||
|
||||
garray = g_array_new (TRUE, FALSE, sizeof (size_t));
|
||||
for (size_t i = 1; i <= array_size; i++)
|
||||
g_array_append_val (garray, i);
|
||||
|
||||
data = g_array_steal (garray, &len);
|
||||
g_assert_cmpuint (array_size, ==, len);
|
||||
g_assert_nonnull (data);
|
||||
g_clear_pointer (&garray, g_array_unref);
|
||||
|
||||
old_data_copy = g_memdup2 (data, len * sizeof (size_t));
|
||||
garray = g_array_new_take_zero_terminated (
|
||||
g_steal_pointer (&data), FALSE, sizeof (size_t));
|
||||
g_assert_cmpuint (garray->len, ==, array_size);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, garray->len), ==, 0);
|
||||
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, 0), ==, 1);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, 10), ==, 11);
|
||||
|
||||
g_assert_cmpmem (old_data_copy, array_size * sizeof (size_t),
|
||||
garray->data, array_size * sizeof (size_t));
|
||||
|
||||
size_t val = 55;
|
||||
g_array_append_val (garray, val);
|
||||
val = 33;
|
||||
g_array_prepend_val (garray, val);
|
||||
|
||||
g_assert_cmpuint (garray->len, ==, array_size + 2);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, 0), ==, 33);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, garray->len - 1), ==, 55);
|
||||
|
||||
g_array_remove_index (garray, 0);
|
||||
g_assert_cmpuint (garray->len, ==, array_size + 1);
|
||||
g_array_remove_index (garray, garray->len - 1);
|
||||
g_assert_cmpuint (garray->len, ==, array_size);
|
||||
g_assert_cmpuint (g_array_index (garray, size_t, garray->len), ==, 0);
|
||||
|
||||
g_assert_cmpmem (old_data_copy, array_size * sizeof (size_t),
|
||||
garray->data, array_size * sizeof (size_t));
|
||||
|
||||
g_clear_pointer (&garray, g_array_unref);
|
||||
g_clear_pointer (&old_data_copy, g_free);
|
||||
|
||||
array_size = G_MAXUINT8;
|
||||
garray = g_array_new (TRUE, FALSE, sizeof (guint8));
|
||||
for (guint8 i = 1; i < array_size; i++)
|
||||
g_array_append_val (garray, i);
|
||||
|
||||
val = G_MAXUINT8 / 2;
|
||||
g_array_append_val (garray, val);
|
||||
|
||||
data = g_array_steal (garray, &len);
|
||||
g_assert_cmpuint (array_size, ==, len);
|
||||
g_assert_nonnull (data);
|
||||
g_clear_pointer (&garray, g_array_unref);
|
||||
|
||||
old_data_copy = g_memdup2 (data, len * sizeof (guint8));
|
||||
garray = g_array_new_take_zero_terminated (
|
||||
g_steal_pointer (&data), FALSE, sizeof (guint8));
|
||||
g_assert_cmpuint (garray->len, ==, array_size);
|
||||
g_assert_cmpuint (g_array_index (garray, guint8, garray->len), ==, 0);
|
||||
|
||||
g_assert_cmpuint (g_array_index (garray, guint8, 0), ==, 1);
|
||||
g_assert_cmpuint (g_array_index (garray, guint8, 10), ==, 11);
|
||||
|
||||
g_assert_cmpmem (old_data_copy, array_size * sizeof (guint8),
|
||||
garray->data, array_size * sizeof (guint8));
|
||||
|
||||
val = 55;
|
||||
g_array_append_val (garray, val);
|
||||
val = 33;
|
||||
g_array_prepend_val (garray, val);
|
||||
|
||||
g_assert_cmpuint (garray->len, ==, array_size + 2);
|
||||
g_assert_cmpuint (g_array_index (garray, guint8, 0), ==, 33);
|
||||
g_assert_cmpuint (g_array_index (garray, guint8, garray->len - 1), ==, 55);
|
||||
|
||||
g_array_remove_index (garray, 0);
|
||||
g_assert_cmpuint (garray->len, ==, array_size + 1);
|
||||
g_array_remove_index (garray, garray->len - 1);
|
||||
g_assert_cmpuint (garray->len, ==, array_size);
|
||||
g_assert_cmpuint (g_array_index (garray, guint8, garray->len), ==, 0);
|
||||
|
||||
g_assert_cmpmem (old_data_copy, array_size * sizeof (guint8),
|
||||
garray->data, array_size * sizeof (guint8));
|
||||
|
||||
g_clear_pointer (&garray, g_array_unref);
|
||||
g_clear_pointer (&old_data_copy, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
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_array_new_take (
|
||||
(gpointer) (int []) { 0 }, (gsize) G_MAXUINT + 1, FALSE, sizeof (int)));
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
|
||||
"*assertion 'element_size <= G_MAXUINT' failed");
|
||||
g_assert_null (
|
||||
g_array_new_take (NULL, 0, FALSE, (gsize) G_MAXUINT + 1));
|
||||
g_test_assert_expected_messages ();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check g_array_steal() function */
|
||||
static void
|
||||
array_steal (void)
|
||||
@ -2790,6 +2980,10 @@ main (int argc, char *argv[])
|
||||
|
||||
/* array tests */
|
||||
g_test_add_func ("/array/new/zero-terminated", array_new_zero_terminated);
|
||||
g_test_add_func ("/array/new/take", array_new_take);
|
||||
g_test_add_func ("/array/new/take/empty", array_new_take_empty);
|
||||
g_test_add_func ("/array/new/take/overflow", array_new_take_overflow);
|
||||
g_test_add_func ("/array/new/take-zero-terminated", array_new_take_zero_terminated);
|
||||
g_test_add_func ("/array/ref-count", array_ref_count);
|
||||
g_test_add_func ("/array/steal", array_steal);
|
||||
g_test_add_func ("/array/clear-func", array_clear_func);
|
||||
|
Loading…
Reference in New Issue
Block a user