/* ---------------------------------------------------------------------------------------------------- */
+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)
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! */
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);
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;
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: