1
0
mirror of https://gitlab.gnome.org/GNOME/glib.git synced 2025-04-03 22:33:08 +02:00

GDBusMessage: fast-path decoding of fixed arrays

Instead of creating a separate GVariant for each of the 'y's in an 'ay',
use g_variant_new_fixed_array().

https://bugzilla.gnome.org/show_bug.cgi?id=732754
This commit is contained in:
Ryan Lortie 2014-07-04 15:00:49 -04:00
parent 958da1e9dc
commit 5463c8cedb

@ -1205,6 +1205,27 @@ g_dbus_message_set_unix_fd_list (GDBusMessage *message,
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */
static guint
get_type_fixed_size (const GVariantType *type)
{
/* NB: we do not treat 'b' as fixed-size here because GVariant and
* D-Bus disagree about the size.
*/
switch (*g_variant_type_peek_string (type))
{
case 'y':
return 1;
case 'n': case 'q':
return 2;
case 'i': case 'u': case 'h':
return 4;
case 'x': case 't': case 'd':
return 8;
default:
return 0;
}
}
static gboolean static gboolean
validate_headers (GDBusMessage *message, validate_headers (GDBusMessage *message,
GError **error) GError **error)
@ -1378,6 +1399,35 @@ read_string (GMemoryBuffer *mbuf,
return str; return str;
} }
static gconstpointer
read_bytes (GMemoryBuffer *mbuf,
gsize len,
GError **error)
{
gconstpointer result;
if (mbuf->pos + len > mbuf->valid_len || mbuf->pos + len < mbuf->pos)
{
mbuf->pos = mbuf->valid_len;
/* G_GSIZE_FORMAT doesn't work with gettext, so we use %lu */
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
g_dngettext (GETTEXT_PACKAGE,
"Wanted to read %lu byte but only got %lu",
"Wanted to read %lu bytes but only got %lu",
(gulong)len),
(gulong)len,
(gulong)(mbuf->valid_len - mbuf->pos));
return NULL;
}
result = mbuf->data + mbuf->pos;
mbuf->pos += len;
return result;
}
/* if just_align==TRUE, don't read a value, just align the input stream wrt padding */ /* if just_align==TRUE, don't read a value, just align the input stream wrt padding */
/* returns a non-floating GVariant! */ /* returns a non-floating GVariant! */
@ -1588,10 +1638,8 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align) if (!just_align)
{ {
guint32 array_len; guint32 array_len;
goffset offset;
goffset target;
const GVariantType *element_type; const GVariantType *element_type;
GVariantBuilder builder; guint fixed_size;
array_len = g_memory_buffer_read_uint32 (buf); array_len = g_memory_buffer_read_uint32 (buf);
@ -1614,44 +1662,82 @@ parse_value_from_blob (GMemoryBuffer *buf,
goto fail; goto fail;
} }
g_variant_builder_init (&builder, type);
element_type = g_variant_type_element (type); element_type = g_variant_type_element (type);
fixed_size = get_type_fixed_size (element_type);
if (array_len == 0) /* Fast-path the cases like 'ay', etc. */
if (fixed_size != 0)
{ {
GVariant *item; gconstpointer array_data;
item = parse_value_from_blob (buf,
element_type, if (array_len % fixed_size != 0)
TRUE, {
indent + 2, g_set_error (&local_error,
NULL); G_IO_ERROR,
g_assert (item == NULL); G_IO_ERROR_INVALID_ARGUMENT,
_("Encountered array of type 'a%c', expected to have a length a multiple "
"of %u bytes, but found to be %u bytes in length"),
g_variant_type_peek_string (element_type)[0], fixed_size, array_len);
goto fail;
}
ensure_input_padding (buf, fixed_size);
array_data = read_bytes (buf, array_len, &local_error);
if (array_data == NULL)
goto fail;
ret = g_variant_new_fixed_array (element_type, array_data, array_len / fixed_size, fixed_size);
if (g_memory_buffer_is_byteswapped (buf))
{
GVariant *tmp = g_variant_ref_sink (ret);
ret = g_variant_byteswap (tmp);
g_variant_unref (tmp);
}
} }
else else
{ {
/* TODO: optimize array of primitive types */ GVariantBuilder builder;
offset = buf->pos; goffset offset;
target = offset + array_len; goffset target;
while (offset < target)
g_variant_builder_init (&builder, type);
if (array_len == 0)
{ {
GVariant *item; GVariant *item;
item = parse_value_from_blob (buf, item = parse_value_from_blob (buf,
element_type, element_type,
FALSE, TRUE,
indent + 2, indent + 2,
&local_error); NULL);
if (item == NULL) g_assert (item == NULL);
{ }
g_variant_builder_clear (&builder); else
goto fail; {
} offset = buf->pos;
g_variant_builder_add_value (&builder, item); target = offset + array_len;
g_variant_unref (item); while (offset < target)
offset = buf->pos; {
GVariant *item;
item = parse_value_from_blob (buf,
element_type,
FALSE,
indent + 2,
&local_error);
if (item == NULL)
{
g_variant_builder_clear (&builder);
goto fail;
}
g_variant_builder_add_value (&builder, item);
g_variant_unref (item);
offset = buf->pos;
}
} }
}
ret = g_variant_builder_end (&builder); ret = g_variant_builder_end (&builder);
}
} }
break; break;
@ -1812,12 +1898,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
is_leaf = is_leaf; /* To avoid -Wunused-but-set-variable */ is_leaf = is_leaf; /* To avoid -Wunused-but-set-variable */
#endif /* DEBUG_SERIALIZER */ #endif /* DEBUG_SERIALIZER */
/* sink the reference */ /* sink the reference, if floating */
if (ret != NULL) if (ret != NULL)
{ g_variant_take_ref (ret);
g_assert (g_variant_is_floating (ret));
g_variant_ref_sink (ret);
}
return ret; return ret;
fail: fail: