+ gsize offset_size;
+ gsize prev_end;
+ gsize i, n;
+
+ n = g_variant_type_info_n_members (type_info);
+
+ /* An empty tuple (n = 0) is always encoded as a single byte, which
+ * means that we should not be attempting to unpack it from multiple
+ * vectors.
+ */
+ if (n == 0)
+ return FALSE;
+
+ offset_size = gvs_get_offset_size (total_size);
+
+ prev_end = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ const GVariantMemberInfo *member_info;
+ GVariantUnpacked unpacked;
+ gsize fixed_size;
+ guint alignment;
+ gsize start;
+ gsize end;
+
+ member_info = g_variant_type_info_member_info (type_info, i);
+ g_variant_type_info_query (member_info->type_info, &alignment, &fixed_size);
+
+ start = prev_end + ((-prev_end) & alignment);
+
+ switch (member_info->ending_type)
+ {
+ case G_VARIANT_MEMBER_ENDING_FIXED:
+ end = start + fixed_size;
+ break;
+
+ case G_VARIANT_MEMBER_ENDING_LAST:
+ end = total_size;
+ break;
+
+ case G_VARIANT_MEMBER_ENDING_OFFSET:
+ if (end_size < offset_size)
+ return FALSE;
+
+ end_pointer -= offset_size;
+ total_size -= offset_size;
+ end_size -= offset_size;
+
+ end = gvs_read_unaligned_le (end_pointer, offset_size);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (start < prev_end || end < start)
+ return FALSE;
+
+ unpacked.type_info = g_variant_type_info_ref (member_info->type_info);
+ unpacked.skip = start - prev_end;
+ unpacked.size = end - start;
+
+ g_array_append_val (results, unpacked);
+
+ prev_end = end;
+ }
+
+ g_assert (prev_end == total_size);
+
+ return TRUE;