[glib/gvariant: 5/27] more serialiser changes



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]