[glib/gvariant: 5/27] more serialiser changes
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/gvariant: 5/27] more serialiser changes
- Date: Thu, 25 Feb 2010 08:09:25 +0000 (UTC)
commit ac7e2d740b75f1a8ab494618dbb1052febdc0b59
Author: Ryan Lortie <desrt desrt ca>
Date: Tue Feb 2 23:40:06 2010 -0500
more serialiser changes
glib/gvariant-serialiser.c | 384 +++++++++++++++++++++++++++++++++++++++++---
1 files changed, 358 insertions(+), 26 deletions(-)
---
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
index 34b5885..796fc37 100644
--- a/glib/gvariant-serialiser.c
+++ b/glib/gvariant-serialiser.c
@@ -22,6 +22,7 @@
#include <glib/gtestutils.h>
#include <glib/gstrfuncs.h>
+#include <glib/gtypes.h>
#include <string.h>
@@ -124,7 +125,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
/* proper element size: "Just". recurse to the child. */
value = gvs_fixed_sized_maybe_get_child (value, 0);
- return g_variant_serialiser_is_normal (value);
+ return g_variant_serialised_is_normal (value);
}
/* size of 0: "Nothing" */
@@ -158,6 +159,10 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
value.type_info = g_variant_type_info_element (value.type_info);
value.size--;
+ /* if it's zero-sized then it may as well be NULL */
+ if (value.size == 0)
+ value.data = NULL;
+
return value;
}
@@ -212,7 +217,17 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
* array".
*/
-/* Fixed-sized Array {{{2 */
+/* Fixed-sized Array {{{2
+ *
+ * For fixed sized arrays, the serialised data is simply a concatenation
+ * of the serialised data of each element, in order. Since fixed-sized
+ * values always have a fixed size that is a multiple of their alignment
+ * requirement no extra padding is required.
+ *
+ * In the event that a fixed-sized array is presented with a size that
+ * is not an integer multiple of the element size then the value of the
+ * array must be taken as being empty.
+ */
static inline gsize
gvs_fixed_sized_array_n_children (GVariantSerialised value)
@@ -249,8 +264,7 @@ gvs_fixed_sized_array_needed_size (GVariantTypeInfo *type_info,
{
gsize element_fixed_size;
- g_variant_type_info_query_element (value.type_info, NULL,
- &element_fixed_size);
+ g_variant_type_info_query_element (type_info, NULL, &element_fixed_size);
return element_fixed_size * n_children;
}
@@ -271,7 +285,7 @@ gvs_fixed_sized_array_serialise (GVariantSerialised value,
for (i = 0; i < n_children; i++)
{
gvs_filler (&child, children[i]);
- child_data += element_fixed_size;
+ child.data += child.size;
}
}
@@ -290,24 +304,217 @@ gvs_fixed_sized_array_is_normal (GVariantSerialised value)
child.data < value.data + value.size;
child.data += child.size)
{
- if (!g_variant_serialiser_is_normal (child))
+ if (!g_variant_serialised_is_normal (child))
return FALSE;
}
return TRUE;
}
-/* Variable-sized Array {{{2 */
+/* Variable-sized Array {{{2
+ *
+ * Variable sized arrays, containing variable-sized elements, must be
+ * able to determine the boundaries between the elements. The items
+ * cannot simply be concatenated. Additionally, we are faced with the
+ * fact that non-fixed-sized values do not neccessarily have a size that
+ * is a multiple of their alignment requirement, so we may need to
+ * insert zero-filled padding.
+ *
+ * While it is possible to find the start of an item by starting from
+ * the end of the item before it and padding for alignment, it is not
+ * generally possible to do the reverse operation. For this reason, we
+ * record the end point of each element in the array.
+ *
+ * GVariant works in terms of "offsets". An offset is a pointer to a
+ * boundary between two bytes. In 4 bytes of serialised data, there
+ * would be 5 possible offsets: one at the start ('0'), one between each
+ * pair of adjacent bytes ('1', '2', '3') and one at the end ('4').
+ *
+ * The numeric value of an offset is an unsigned integer given relative
+ * to the start of the serialised data of the array. Offsets are always
+ * stored in little endian byte order and are always only as big as they
+ * need to be. For example, in 255 bytes of serialised data, there are
+ * 256 offsets. All possibilities can be stored in an 8 bit unsigned
+ * integer. In 256 bytes of serialised data, however, there are 257
+ * possible offsets so 16 bit integers must be used. The size of an
+ * offset is always a power of 2.
+ *
+ * The offsets are stored at the end of the serialised data of the
+ * array. They are simply concatenated on without any particular
+ * alignment. The size of the offsets is included in the size of the
+ * serialised data for purposes of determining the size of the offsets.
+ * This presents a possibly ambiguity; in certain cases, a particular
+ * value of array could have two different serialised forms.
+ *
+ * Imagine an array containing a single string of 253 bytes in length
+ * (so, 254 bytes including the nul terminator). Now the offset must be
+ * written. If an 8 bit offset is written, it will bring the size of
+ * the array's serialised data to 255 -- which means that the use of an
+ * 8 bit offset was valid. If a 16 bit offset is used then the total
+ * size of the array will be 256 -- which means that the use of a 16 bit
+ * offset was valid. Although both of these will be accepted by the
+ * deserialiser, only the smaller of the two is considered to be in
+ * normal form and that is the one that the serialiser must produce.
+ */
+
+static inline gsize
+gvs_read_unaligned_le (guchar *bytes,
+ guint size)
+{
+ union
+ {
+ guchar bytes[GLIB_SIZEOF_SIZE_T];
+ gsize integer;
+ } tmpvalue;
+
+ tmpvalue.integer = 0;
+ memcpy (&tmpvalue.bytes, bytes, size);
+
+ return GSIZE_FROM_LE (tmpvalue.integer);
+}
+
+static inline void
+gvs_write_unaligned_le (guchar *bytes,
+ gsize value,
+ guint size)
+{
+ union
+ {
+ guchar bytes[GLIB_SIZEOF_SIZE_T];
+ gsize integer;
+ } tmpvalue;
+
+ tmpvalue.integer = GSIZE_TO_LE (value);
+ memcpy (bytes, &tmpvalue.bytes, size);
+}
+
+/* This is an optimisation.
+ *
+ * We could just as easily have a function that returns '1', '2', '4' or
+ * '8' depending on how large the serialised data is and use that value,
+ * but it would be slower.
+ *
+ * GCC will make many operations much faster if it knows that they are
+ * being performed by a constant power of 2. These include
+ * multiplication, division and memcpy() -- all of which are used during
+ * the deserialisation.
+ *
+ * By using this macro, we spell it out to GCC that within each case,
+ * the offset size will be a constant, and a power of two. It will
+ * optimise accordingly.
+ */
+#define OFFSET_SIZE_CASES(value_size, var_name, code) \
+ if (value_size > G_MAXUINT32) { const guint var_name = 8; code } \
+ if (value_size > G_MAXUINT16) { const guint var_name = 4; code } \
+ if (value_size > G_MAXUINT8) { const guint var_name = 2; code } \
+ if (value_size > 0) { const guint var_name = 1; code }
static inline gsize
gvs_variable_sized_array_n_children (GVariantSerialised value)
{
+ gsize offsets_array_size;
+ gsize last_end;
+
+ OFFSET_SIZE_CASES (value.size, OFFSET_SIZE,
+ {
+ last_end = gvs_read_unaligned_le (value.data + value.size -
+ OFFSET_SIZE, OFFSET_SIZE);
+
+ if (last_end > value.size)
+ return 0;
+
+ offsets_array_size = value.size - last_end;
+
+ if (offsets_array_size % OFFSET_SIZE)
+ return 0;
+
+ return offsets_array_size / OFFSET_SIZE;
+ }
+ )
+
+ else
+ return 0;
}
static inline GVariantSerialised
gvs_variable_sized_array_get_child (GVariantSerialised value,
gsize index_)
{
+ GVariantSerialised child;
+ guint alignment;
+ gsize start;
+ gsize end;
+
+ OFFSET_SIZE_CASES (value.size, OFFSET_SIZE,
+ {
+ gsize last_end;
+
+ last_end = gvs_read_unaligned_le (value.data + value.size -
+ OFFSET_SIZE, OFFSET_SIZE);
+
+ if (index_ > 0)
+ start = gvs_read_unaligned_le (value.data + last_end +
+ (OFFSET_SIZE * (index_ - 1)),
+ OFFSET_SIZE);
+ else
+ start = 0;
+
+ end = gvs_read_unaligned_le (value.data + last_end +
+ (OFFSET_SIZE * index_),
+ OFFSET_SIZE);
+ }
+ )
+
+ else
+ g_assert_not_reached ();
+
+ child.type_info = g_variant_type_info_element (value.type_info);
+ g_variant_type_info_query (child.type_info, &alignment, NULL);
+
+ start += (-start) & alignment;
+ if (start < end && end <= value.size)
+ {
+ child.data = value.data + start;
+ child.size = end - start;
+ }
+ else
+ {
+ child.data = NULL;
+ child.size = 0;
+ }
+
+ return child;
+}
+
+static gsize
+gvs_determine_size (gsize body_size,
+ gsize offsets)
+{
+ if (body_size + 0 * offsets <= 0)
+ return body_size + 0 * offsets;
+
+ if (body_size + 1 * offsets <= G_MAXUINT8)
+ return body_size + 1 * offsets;
+
+ if (body_size + 2 * offsets <= G_MAXUINT16)
+ return body_size + 2 * offsets;
+
+ if (body_size + 4 * offsets <= G_MAXUINT32)
+ return body_size + 4 * offsets;
+
+ return body_size + 8 * offsets;
+}
+
+static guint
+gvfs_get_offset_size (gsize size)
+{
+ OFFSET_SIZE_CASES (size, OFFSET_SIZE,
+ {
+ return OFFSET_SIZE;
+ }
+ )
+ else
+ return 0;
}
static inline gsize
@@ -316,6 +523,22 @@ gvs_variable_sized_array_needed_size (GVariantTypeInfo *type_info,
const gpointer *children,
gsize n_children)
{
+ guint alignment;
+ gsize offset;
+ gsize i;
+
+ g_variant_type_info_query (type_info, &alignment, NULL);
+
+ for (i = 0; i < n_children; i++)
+ {
+ GVariantSerialised child = { };
+
+ offset += (-offset) & alignment;
+ gvs_filler (&child, children[i]);
+ offset += child.size;
+ }
+
+ return gvs_determine_size (offset, n_children);
}
static inline void
@@ -324,46 +547,155 @@ gvs_variable_sized_array_serialise (GVariantSerialised value,
const gpointer *children,
gsize n_children)
{
+ guint alignment;
+ gsize offset;
+
+ g_variant_type_info_query (value.type_info, &alignment, NULL);
+ offset = 0;
+
+ OFFSET_SIZE_CASES (value.size, OFFSET_SIZE,
+ {
+ guchar *offset_ptr;
+ gsize i;
+
+ offset_ptr = value.data + value.size - OFFSET_SIZE * n_children;
+
+ for (i = 0; i < n_children; i++)
+ {
+ GVariantSerialised child = { };
+
+ while (offset & alignment)
+ value.data[offset++] = '\0';
+
+ child.data = value.data + offset;
+ gvs_filler (&child, children[i]);
+ offset += child.size;
+
+ gvs_write_unaligned_le (offset_ptr, offset, OFFSET_SIZE);
+ offset_ptr += OFFSET_SIZE;
+ }
+ }
+ )
}
static inline gboolean
gvs_variable_sized_array_is_normal (GVariantSerialised value)
{
-}
+ GVariantSerialised child = { };
+ gsize offsets_array_size;
+ guchar *offsets_array;
+ guint offset_size;
+ guint alignment;
+ gsize last_end;
+ gsize length;
+ gsize offset;
+ gsize i;
+
+ if (value.size == 0)
+ return TRUE;
+
+ offset_size = gvfs_get_offset_size (value.size);
+ last_end = gvs_read_unaligned_le (value.data + value.size -
+ offset_size, offset_size);
+
+ if (last_end > value.size)
+ return FALSE;
+
+ offsets_array_size = value.size - last_end;
-variable size array stuff
+ if (offsets_array_size % offset_size)
+ return FALSE;
+
+ offsets_array = value.data + value.size - offsets_array_size;
+ length = offsets_array_size / offset_size;
+
+ child.type_info = g_variant_type_info_element (value.type_info);
+ g_variant_type_info_query (child.type_info, &alignment, NULL);
+ offset = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ gsize this_end;
+
+ this_end = gvs_read_unaligned_le (offsets_array + offset_size * i,
+ offset_size);
+
+ if (this_end < offset || this_end > last_end)
+ return FALSE;
+
+ while (offset & alignment)
+ {
+ if (!(offset < this_end && value.data[offset] == '\0'))
+ return FALSE;
+ offset++;
+ }
+
+ child.data = value.data + offset;
+ child.size = this_end - offset;
+
+ if (child.size == 0)
+ child.data = NULL;
+
+ if (!g_variant_serialised_is_normal (child))
+ return FALSE;
+
+ offset = this_end;
+ }
+
+ g_assert (offset == last_end);
+}
/* Tuples {{{1 */
static inline gsize
-gvs_fixed_sized_maybe_n_children (GVariantSerialised value)
+gvs_tuple_n_children (GVariantSerialised value)
{
+ return g_variant_type_info_n_members (value.type_info);
}
static inline GVariantSerialised
-gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
- gsize index_)
+gvs_tuple_get_child (GVariantSerialised value,
+ gsize index_)
{
+ const GVariantMemberInfo *member_info;
+ gsize fixed_size;
+ gsize start, end;
+
+ member_info = g_variant_type_info_member (value.type_info, index_);
+ g_variant_type_info_query (member_info->type_info, NULL, &fixed_size);
+
+ /* XXX handle 'i' */
+ start = 0;
+
+ start += member_info->a;
+ start &= member_info->b;
+ start |= member_info->c;
+
+ if (fixed_size)
+ child.size = fixed_size;
+
+ else
+ {
}
static inline gsize
-gvs_fixed_sized_maybe_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
+gvs_tuple_needed_size (GVariantTypeInfo *type_info,
+ GVariantSerialisedFiller gvs_filler,
+ const gpointer *children,
+ gsize n_children)
{
}
static inline void
-gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
+gvs_tuple_serialise (GVariantSerialised value,
+ GVariantSerialisedFiller gvs_filler,
+ const gpointer *children,
+ gsize n_children)
{
}
static inline gboolean
-gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
+gvs_tuple_is_normal (GVariantSerialised value)
{
}
@@ -372,18 +704,18 @@ tuple stuff
/* Variants {{{1 */
static inline gsize
-gvs_fixed_sized_maybe_n_children (GVariantSerialised value)
+gvs_tuple_n_children (GVariantSerialised value)
{
}
static inline GVariantSerialised
-gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
+gvs_tuple_get_child (GVariantSerialised value,
gsize index_)
{
}
static inline gsize
-gvs_fixed_sized_maybe_needed_size (GVariantTypeInfo *type_info,
+gvs_tuple_needed_size (GVariantTypeInfo *type_info,
GVariantSerialisedFiller gvs_filler,
const gpointer *children,
gsize n_children)
@@ -391,7 +723,7 @@ gvs_fixed_sized_maybe_needed_size (GVariantTypeInfo *type_info,
}
static inline void
-gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
+gvs_tuple_serialise (GVariantSerialised value,
GVariantSerialisedFiller gvs_filler,
const gpointer *children,
gsize n_children)
@@ -399,7 +731,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
}
static inline gboolean
-gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
+gvs_tuple_is_normal (GVariantSerialised value)
{
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]