From 5463c8cedbdf9ea2829f83c53876f21b34f57250 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Fri, 4 Jul 2014 15:00:49 -0400 Subject: [PATCH] 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 --- gio/gdbusmessage.c | 145 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 114 insertions(+), 31 deletions(-) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 50105e8..b9cb03e 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -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) + NULL); + g_assert (item == NULL); + } + else + { + offset = buf->pos; + target = offset + array_len; + while (offset < target) { - g_variant_builder_clear (&builder); - goto fail; + 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; } - 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: -- 2.7.4