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

View File

@ -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
validate_headers (GDBusMessage *message,
GError **error)
@ -1378,6 +1399,35 @@ read_string (GMemoryBuffer *mbuf,
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 */
/* returns a non-floating GVariant! */
@ -1588,10 +1638,8 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
guint32 array_len;
goffset offset;
goffset target;
const GVariantType *element_type;
GVariantBuilder builder;
guint fixed_size;
array_len = g_memory_buffer_read_uint32 (buf);
@ -1614,44 +1662,82 @@ parse_value_from_blob (GMemoryBuffer *buf,
goto fail;
}
g_variant_builder_init (&builder, 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;
item = parse_value_from_blob (buf,
element_type,
TRUE,
indent + 2,
NULL);
g_assert (item == NULL);
gconstpointer array_data;
if (array_len % fixed_size != 0)
{
g_set_error (&local_error,
G_IO_ERROR,
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
{
/* TODO: optimize array of primitive types */
offset = buf->pos;
target = offset + array_len;
while (offset < target)
GVariantBuilder builder;
goffset offset;
goffset target;
g_variant_builder_init (&builder, type);
if (array_len == 0)
{
GVariant *item;
item = parse_value_from_blob (buf,
element_type,
FALSE,
TRUE,
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;
NULL);
g_assert (item == NULL);
}
else
{
offset = buf->pos;
target = offset + array_len;
while (offset < target)
{
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;
@ -1812,12 +1898,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
is_leaf = is_leaf; /* To avoid -Wunused-but-set-variable */
#endif /* DEBUG_SERIALIZER */
/* sink the reference */
/* sink the reference, if floating */
if (ret != NULL)
{
g_assert (g_variant_is_floating (ret));
g_variant_ref_sink (ret);
}
g_variant_take_ref (ret);
return ret;
fail: