Merge branch 'wip/chergert/gvariant-inline-suffix-data' into 'main'

glib/gvariant: Inline small gvariant data using C99 flexible arrays

See merge request GNOME/glib!4301
This commit is contained in:
Philip Withnall 2024-09-27 12:35:05 +00:00
commit 019de2cbbc
3 changed files with 95 additions and 12 deletions

View File

@ -75,8 +75,15 @@ struct _GVariant
gint state;
gatomicrefcount ref_count;
gsize depth;
guint8 suffix[];
};
/* Ensure our suffix data aligns to largest guaranteed offset
* within GVariant, of 8 bytes.
*/
G_STATIC_ASSERT (G_STRUCT_OFFSET (GVariant, suffix) % 8 == 0);
/* struct GVariant:
*
* There are two primary forms of GVariant instances: "serialized form"
@ -543,21 +550,31 @@ g_variant_ensure_serialised (GVariant *value)
* @type: the type of the new instance
* @serialised: if the instance will be in serialised form
* @trusted: if the instance will be trusted
* @suffix_size: amount of extra bytes to add to allocation
*
* Allocates a #GVariant instance and does some common work (such as
* looking up and filling in the type info), setting the state field,
* and setting the ref_count to 1.
*
* Use @suffix_size when you want to store data inside of the GVariant
* without having to add an additional GBytes allocation.
*
* Returns: a new #GVariant with a floating reference
*/
static GVariant *
g_variant_alloc (const GVariantType *type,
gboolean serialised,
gboolean trusted)
gboolean trusted,
gsize suffix_size)
{
G_GNUC_UNUSED gboolean size_check;
GVariant *value;
gsize size;
value = g_slice_new (GVariant);
size_check = g_size_checked_add (&size, sizeof *value, suffix_size);
g_assert (size_check);
value = g_malloc (size);
value->type_info = g_variant_type_info_get (type);
value->state = (serialised ? STATE_SERIALISED : 0) |
(trusted ? STATE_TRUSTED : 0) |
@ -599,6 +616,54 @@ g_variant_new_from_bytes (const GVariantType *type,
/* -- internal -- */
/* < internal >
* g_variant_new_preallocated_trusted:
* @data: data to copy
* @size: the size of data
*
* Creates a new #GVariant for simple types such as int32, double, or
* bytes.
*
* Instead of allocating a GBytes, the data will be stored at the tail of
* the GVariant structures allocation. This can save considerable malloc
* overhead.
*
* The data is always aligned to the maximum alignment GVariant provides
* which is 8 bytes and therefore does not need to verify alignment based
* on the the @type provided.
*
* This should only be used for creating GVariant with trusted data.
*
* Returns: a new #GVariant with a floating reference
*/
GVariant *
g_variant_new_preallocated_trusted (const GVariantType *type,
gconstpointer data,
gsize size)
{
GVariant *value;
gsize expected_size;
guint alignment;
value = g_variant_alloc (type, TRUE, TRUE, size);
g_variant_type_info_query (value->type_info, &alignment, &expected_size);
g_assert (expected_size == 0 || size == expected_size);
value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE;
value->contents.serialised.checked_offsets_up_to = G_MAXSIZE;
value->contents.serialised.bytes = NULL;
value->contents.serialised.data = value->suffix;
value->size = size;
memcpy (value->suffix, data, size);
TRACE(GLIB_VARIANT_FROM_BUFFER(value, value->type_info, value->ref_count, value->state));
return value;
}
/* < internal >
* g_variant_new_take_bytes:
* @bytes: (transfer full): a #GBytes
@ -620,7 +685,7 @@ g_variant_new_take_bytes (const GVariantType *type,
GBytes *owned_bytes = NULL;
GVariantSerialised serialised;
value = g_variant_alloc (type, TRUE, trusted);
value = g_variant_alloc (type, TRUE, trusted, 0);
g_variant_type_info_query (value->type_info,
&alignment, &size);
@ -728,7 +793,7 @@ g_variant_new_from_children (const GVariantType *type,
{
GVariant *value;
value = g_variant_alloc (type, FALSE, trusted);
value = g_variant_alloc (type, FALSE, trusted, 0);
value->contents.tree.children = children;
value->contents.tree.n_children = n_children;
TRACE(GLIB_VARIANT_FROM_CHILDREN(value, value->type_info, value->ref_count, value->state));
@ -823,7 +888,7 @@ g_variant_unref (GVariant *value)
g_variant_release_children (value);
memset (value, 0, sizeof (GVariant));
g_slice_free (GVariant, value);
g_free (value);
}
}
@ -1068,20 +1133,24 @@ g_variant_get_data (GVariant *value)
* Returns: (transfer full): A new #GBytes representing the variant data
*
* Since: 2.36
*/
*/
GBytes *
g_variant_get_data_as_bytes (GVariant *value)
{
const gchar *bytes_data;
const gchar *data;
gsize bytes_size;
gsize bytes_size = 0;
gsize size;
g_variant_lock (value);
g_variant_ensure_serialised (value);
g_variant_unlock (value);
bytes_data = g_bytes_get_data (value->contents.serialised.bytes, &bytes_size);
if (value->contents.serialised.bytes != NULL)
bytes_data = g_bytes_get_data (value->contents.serialised.bytes, &bytes_size);
else
bytes_data = NULL;
data = value->contents.serialised.data;
size = value->size;
@ -1091,11 +1160,13 @@ g_variant_get_data_as_bytes (GVariant *value)
data = bytes_data;
}
if (data == bytes_data && size == bytes_size)
if (bytes_data != NULL && data == bytes_data && size == bytes_size)
return g_bytes_ref (value->contents.serialised.bytes);
else
else if (bytes_data != NULL)
return g_bytes_new_from_bytes (value->contents.serialised.bytes,
data - bytes_data, size);
else
return g_bytes_new (value->contents.serialised.data, size);
}
@ -1226,7 +1297,7 @@ g_variant_get_child_value (GVariant *value,
}
/* create a new serialized instance out of it */
child = g_slice_new (GVariant);
child = g_new (GVariant, 1);
child->type_info = s_child.type_info;
child->state = (value->state & STATE_TRUSTED) |
STATE_SERIALISED;

View File

@ -25,8 +25,17 @@
#include <glib/gvariant.h>
#include <glib/gbytes.h>
#if GLIB_SIZEOF_VOID_P == 8
# define G_VARIANT_MAX_PREALLOCATED 64
#else
# define G_VARIANT_MAX_PREALLOCATED 32
#endif
/* gvariant-core.c */
GVariant * g_variant_new_preallocated_trusted (const GVariantType *type,
gconstpointer data,
gsize size);
GVariant * g_variant_new_take_bytes (const GVariantType *type,
GBytes *bytes,
gboolean trusted);

View File

@ -321,7 +321,10 @@ g_variant_new_from_trusted (const GVariantType *type,
gconstpointer data,
gsize size)
{
return g_variant_new_take_bytes (type, g_bytes_new (data, size), TRUE);
if (size <= G_VARIANT_MAX_PREALLOCATED)
return g_variant_new_preallocated_trusted (type, data, size);
else
return g_variant_new_take_bytes (type, g_bytes_new (data, size), TRUE);
}
/**