[glib] GDBus: Fix serialization of empty arrays



commit bb6530eb34a16cbf34ce130c21071a25666a704b
Author: David Zeuthen <davidz redhat com>
Date:   Fri May 14 12:49:51 2010 -0400

    GDBus: Fix serialization of empty arrays
    
    It turns out that we didn't observe padding (neither when reading nor
    writing) for empty arrays which (apparently) is needed according to
    the D-Bus spec and reference implementation. A simple test case to
    provoke this behavior is as follows (notice the lack of 4 bytes worth
    of padding at position 0x0064):
    
     Error calling dbus_message_demarshal() on this blob: org.freedesktop.DBus.Error.InvalidArgs: Message is corrupted (Alignment padding not null)
     0000: 6c 01 00 01  2e 00 00 00  41 00 00 00  37 00 00 00    l.......A...7...
     0010: 08 01 67 00  08 73 61 7b  73 76 7d 61  73 00 00 00    ..g..sa{sv}as...
     0020: 01 01 6f 00  08 00 00 00  2f 66 6f 6f  2f 62 61 72    ..o...../foo/bar
     0030: 00 00 00 00  00 00 00 00  03 01 73 00  06 00 00 00    ..........s.....
     0040: 4d 65 6d 62  65 72 00 00  11 00 00 00  30 31 32 33    Member......0123
     0050: 34 35 36 37  38 39 30 31  32 33 34 35  36 00 00 00    4567890123456...
     0060: 00 00 00 00  0e 00 00 00  09 00 00 00  53 6f 6d 65    ............Some
     0070: 74 68 69 6e  67 00                                    thing.
    
     The blob was generated from the following GVariant value:
     ('01234567890123456', @a{sv} {}, ['Something'])
    
     If the blob was encoded using DBusMessageIter, the payload would have been:
    
     0000: 6c 01 00 01  32 00 00 00  41 00 00 00  36 00 00 00    l...2...A...6...
     0010: 01 01 6f 00  08 00 00 00  2f 66 6f 6f  2f 62 61 72    ..o...../foo/bar
     0020: 00 00 00 00  00 00 00 00  03 01 73 00  06 00 00 00    ..........s.....
     0030: 4d 65 6d 62  65 72 00 00  08 01 67 00  08 73 61 7b    Member....g..sa{
     0040: 73 76 7d 61  73 00 00 00  11 00 00 00  30 31 32 33    sv}as.......0123
     0050: 34 35 36 37  38 39 30 31  32 33 34 35  36 00 00 00    4567890123456...
     0060: 00 00 00 00  00 00 00 00  0e 00 00 00  09 00 00 00    ................
     0070: 53 6f 6d 65  74 68 69 6e  67 00                       Something.
     ** ERROR:gdbus-serialization.c:547:check_serialization: code should not be reached
     Aborted
    
    and this is now in the libdbus-1-using serialization test case.
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 gio/gdbusmessage.c              |  680 ++++++++++++++++++++++++---------------
 gio/tests/gdbus-serialization.c |   12 +
 2 files changed, 439 insertions(+), 253 deletions(-)
---
diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
index 0f1e0d2..4d98e3e 100644
--- a/gio/gdbusmessage.c
+++ b/gio/gdbusmessage.c
@@ -682,6 +682,8 @@ ensure_input_padding (GMemoryInputStream   *mis,
   offset = g_seekable_tell (G_SEEKABLE (mis));
   wanted_offset = ((offset + padding_size - 1) / padding_size) * padding_size;
 
+  /*g_debug ("ensure_input_padding(%d) pushed offset 0x%04x to 0x%04x", (gint) padding_size, (gint) offset, (gint) wanted_offset);*/
+
   return g_seekable_seek (G_SEEKABLE (mis), wanted_offset, G_SEEK_SET, NULL, error);
 }
 
@@ -751,164 +753,206 @@ read_string (GMemoryInputStream    *mis,
   return NULL;
 }
 
+/* if just_align==TRUE, don't read a value, just align the input stream wrt padding */
 static GVariant *
 parse_value_from_blob (GMemoryInputStream    *mis,
                        GDataInputStream      *dis,
                        const GVariantType    *type,
+                       gboolean               just_align,
                        GError               **error)
 {
   GVariant *ret;
   GError *local_error;
 
+  /*g_debug ("Reading type %s from offset 0x%04x", g_variant_type_dup_string (type), (gint) g_seekable_tell (G_SEEKABLE (mis)));*/
+
+  ret = NULL;
+
   local_error = NULL;
   if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
     {
-      gboolean v;
       if (!ensure_input_padding (mis, 4, &local_error))
         goto fail;
-      v = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_boolean (v);
+      if (!just_align)
+        {
+          gboolean v;
+          v = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_boolean (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
     {
-      guchar v;
-      v = g_data_input_stream_read_byte (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_byte (v);
+      if (!just_align)
+        {
+          guchar v;
+          v = g_data_input_stream_read_byte (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_byte (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
     {
-      gint16 v;
       if (!ensure_input_padding (mis, 2, &local_error))
         goto fail;
-      v = g_data_input_stream_read_int16 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_int16 (v);
+      if (!just_align)
+        {
+          gint16 v;
+          v = g_data_input_stream_read_int16 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_int16 (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
     {
-      guint16 v;
       if (!ensure_input_padding (mis, 2, &local_error))
         goto fail;
-      v = g_data_input_stream_read_uint16 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_uint16 (v);
+      if (!just_align)
+        {
+          guint16 v;
+          v = g_data_input_stream_read_uint16 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_uint16 (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
     {
-      gint32 v;
       if (!ensure_input_padding (mis, 4, &local_error))
         goto fail;
-      v = g_data_input_stream_read_int32 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_int32 (v);
+      if (!just_align)
+        {
+          gint32 v;
+          v = g_data_input_stream_read_int32 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_int32 (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
     {
-      guint32 v;
       if (!ensure_input_padding (mis, 4, &local_error))
         goto fail;
-      v = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_uint32 (v);
+      if (!just_align)
+        {
+          guint32 v;
+          v = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_uint32 (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
     {
-      gint64 v;
       if (!ensure_input_padding (mis, 8, &local_error))
         goto fail;
-      v = g_data_input_stream_read_int64 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_int64 (v);
+      if (!just_align)
+        {
+          gint64 v;
+          v = g_data_input_stream_read_int64 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_int64 (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
     {
-      guint64 v;
       if (!ensure_input_padding (mis, 8, &local_error))
         goto fail;
-      v = g_data_input_stream_read_uint64 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      ret = g_variant_new_uint64 (v);
+      if (!just_align)
+        {
+          guint64 v;
+          v = g_data_input_stream_read_uint64 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          ret = g_variant_new_uint64 (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
     {
-      guint64 v;
-      gdouble *encoded;
       if (!ensure_input_padding (mis, 8, &local_error))
         goto fail;
-      v = g_data_input_stream_read_uint64 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      /* TODO: hmm */
-      encoded = (gdouble *) &v;
-      ret = g_variant_new_double (*encoded);
+      if (!just_align)
+        {
+          guint64 v;
+          gdouble *encoded;
+          v = g_data_input_stream_read_uint64 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          /* TODO: hmm */
+          encoded = (gdouble *) &v;
+          ret = g_variant_new_double (*encoded);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
     {
-      guint32 len;
-      gchar *v;
       if (!ensure_input_padding (mis, 4, &local_error))
         goto fail;
-      len = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      v = read_string (mis, dis, (gsize) len, &local_error);
-      if (v == NULL)
-        goto fail;
-      ret = g_variant_new_string (v);
+      if (!just_align)
+        {
+          guint32 len;
+          gchar *v;
+          len = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          v = read_string (mis, dis, (gsize) len, &local_error);
+          if (v == NULL)
+            goto fail;
+          ret = g_variant_new_string (v);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
     {
-      guint32 len;
-      gchar *v;
       if (!ensure_input_padding (mis, 4, &local_error))
         goto fail;
-      len = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      v = read_string (mis, dis, (gsize) len, &local_error);
-      if (v == NULL)
-        goto fail;
-      if (!g_variant_is_object_path (v))
+      if (!just_align)
         {
-          g_set_error (&local_error,
-                       G_IO_ERROR,
-                       G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Parsed value `%s' is not a valid D-Bus object path"),
-                       v);
-          goto fail;
+          guint32 len;
+          gchar *v;
+          len = g_data_input_stream_read_uint32 (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          v = read_string (mis, dis, (gsize) len, &local_error);
+          if (v == NULL)
+            goto fail;
+          if (!g_variant_is_object_path (v))
+            {
+              g_set_error (&local_error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_ARGUMENT,
+                           _("Parsed value `%s' is not a valid D-Bus object path"),
+                           v);
+              goto fail;
+            }
+          ret = g_variant_new_object_path (v);
         }
-      ret = g_variant_new_object_path (v);
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
     {
-      guchar len;
-      gchar *v;
-      len = g_data_input_stream_read_byte (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      v = read_string (mis, dis, (gsize) len, &local_error);
-      if (v == NULL)
-        goto fail;
-      if (!g_variant_is_signature (v))
+      if (!just_align)
         {
-          g_set_error (&local_error,
-                       G_IO_ERROR,
-                       G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Parsed value `%s' is not a valid D-Bus signature"),
+          guchar len;
+          gchar *v;
+          len = g_data_input_stream_read_byte (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          v = read_string (mis, dis, (gsize) len, &local_error);
+          if (v == NULL)
+            goto fail;
+          if (!g_variant_is_signature (v))
+            {
+              g_set_error (&local_error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_ARGUMENT,
+                           _("Parsed value `%s' is not a valid D-Bus signature"),
                        v);
-          goto fail;
+              goto fail;
+            }
+          ret = g_variant_new_signature (v);
         }
-      ret = g_variant_new_signature (v);
     }
   else if (g_variant_type_is_array (type))
     {
@@ -935,24 +979,47 @@ parse_value_from_blob (GMemoryInputStream    *mis,
       builder = g_variant_builder_new (type);
       element_type = g_variant_type_element (type);
 
-      /* TODO: optimize array of primitive types */
-
-      offset = g_seekable_tell (G_SEEKABLE (mis));
-      target = offset + array_len;
-      while (offset < target)
+      if (array_len == 0)
         {
           GVariant *item;
-          item = parse_value_from_blob (mis, dis, element_type, &local_error);
-          if (item == NULL)
+          item = parse_value_from_blob (mis,
+                                        dis,
+                                        element_type,
+                                        TRUE,
+                                        &local_error);
+          g_assert (item == NULL);
+        }
+      else
+        {
+          /* TODO: optimize array of primitive types */
+          offset = g_seekable_tell (G_SEEKABLE (mis));
+          target = offset + array_len;
+          while (offset < target)
             {
-              g_variant_builder_unref (builder);
-              goto fail;
+              GVariant *item;
+              item = parse_value_from_blob (mis,
+                                            dis,
+                                            element_type,
+                                            FALSE,
+                                            &local_error);
+              if (item == NULL)
+                {
+                  g_variant_builder_unref (builder);
+                  goto fail;
+                }
+              g_variant_builder_add_value (builder, item);
+              offset = g_seekable_tell (G_SEEKABLE (mis));
             }
-          g_variant_builder_add_value (builder, item);
-          offset = g_seekable_tell (G_SEEKABLE (mis));
         }
 
-      ret = g_variant_builder_end (builder);
+      if (!just_align)
+        {
+          ret = g_variant_builder_end (builder);
+        }
+      else
+        {
+          g_variant_builder_unref (builder);
+        }
     }
   else if (g_variant_type_is_dict_entry (type))
     {
@@ -964,73 +1031,98 @@ parse_value_from_blob (GMemoryInputStream    *mis,
       if (!ensure_input_padding (mis, 8, &local_error))
         goto fail;
 
-      key_type = g_variant_type_key (type);
-      key = parse_value_from_blob (mis, dis, key_type, &local_error);
-      if (key == NULL)
-        goto fail;
-
-      value_type = g_variant_type_value (type);
-      value = parse_value_from_blob (mis, dis, value_type, &local_error);
-      if (value == NULL)
+      if (!just_align)
         {
-          g_variant_unref (key);
-          goto fail;
+          key_type = g_variant_type_key (type);
+          key = parse_value_from_blob (mis,
+                                       dis,
+                                       key_type,
+                                       FALSE,
+                                       &local_error);
+          if (key == NULL)
+            goto fail;
+
+          value_type = g_variant_type_value (type);
+          value = parse_value_from_blob (mis,
+                                         dis,
+                                         value_type,
+                                         FALSE,
+                                         &local_error);
+          if (value == NULL)
+            {
+              g_variant_unref (key);
+              goto fail;
+            }
+          ret = g_variant_new_dict_entry (key, value);
         }
-      ret = g_variant_new_dict_entry (key, value);
     }
   else if (g_variant_type_is_tuple (type))
     {
-      const GVariantType *element_type;
-      GVariantBuilder *builder;
-
       if (!ensure_input_padding (mis, 8, &local_error))
         goto fail;
 
-      builder = g_variant_builder_new (type);
-      element_type = g_variant_type_first (type);
-      while (element_type != NULL)
+      if (!just_align)
         {
-          GVariant *item;
-          item = parse_value_from_blob (mis, dis, element_type, &local_error);
-          if (item == NULL)
+          const GVariantType *element_type;
+          GVariantBuilder *builder;
+
+          builder = g_variant_builder_new (type);
+          element_type = g_variant_type_first (type);
+          while (element_type != NULL)
             {
-              g_variant_builder_unref (builder);
-              goto fail;
-            }
-          g_variant_builder_add_value (builder, item);
+              GVariant *item;
+              item = parse_value_from_blob (mis,
+                                            dis,
+                                            element_type,
+                                            FALSE,
+                                            &local_error);
+              if (item == NULL)
+                {
+                  g_variant_builder_unref (builder);
+                  goto fail;
+                }
+              g_variant_builder_add_value (builder, item);
 
-          element_type = g_variant_type_next (element_type);
+              element_type = g_variant_type_next (element_type);
+            }
+          ret = g_variant_builder_end (builder);
         }
-      ret = g_variant_builder_end (builder);
     }
   else if (g_variant_type_is_variant (type))
     {
-      guchar siglen;
-      gchar *sig;
-      GVariantType *variant_type;
-      GVariant *value;
-
-      siglen = g_data_input_stream_read_byte (dis, NULL, &local_error);
-      if (local_error != NULL)
-        goto fail;
-      sig = read_string (mis, dis, (gsize) siglen, &local_error);
-      if (sig == NULL)
-        goto fail;
-      if (!g_variant_is_signature (sig))
+      if (!just_align)
         {
-          g_set_error (&local_error,
-                       G_IO_ERROR,
-                       G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Parsed value `%s' for variant is not a valid D-Bus signature"),
-                       sig);
-          goto fail;
+          guchar siglen;
+          gchar *sig;
+          GVariantType *variant_type;
+          GVariant *value;
+
+          siglen = g_data_input_stream_read_byte (dis, NULL, &local_error);
+          if (local_error != NULL)
+            goto fail;
+          sig = read_string (mis, dis, (gsize) siglen, &local_error);
+          if (sig == NULL)
+            goto fail;
+          if (!g_variant_is_signature (sig))
+            {
+              g_set_error (&local_error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_ARGUMENT,
+                           _("Parsed value `%s' for variant is not a valid D-Bus signature"),
+                           sig);
+              goto fail;
+            }
+          variant_type = g_variant_type_new (sig);
+          value = parse_value_from_blob (mis,
+                                         dis,
+                                         variant_type,
+                                         FALSE,
+                                         &local_error);
+          g_variant_type_free (variant_type);
+          if (value == NULL)
+            goto fail;
+          ret = g_variant_new_variant (value);
         }
-      variant_type = g_variant_type_new (sig);
-      value = parse_value_from_blob (mis, dis, variant_type, &local_error);
-      g_variant_type_free (variant_type);
-      if (value == NULL)
-        goto fail;
-      ret = g_variant_new_variant (value);
     }
   else
     {
@@ -1045,7 +1137,7 @@ parse_value_from_blob (GMemoryInputStream    *mis,
       goto fail;
     }
 
-  g_assert (ret != NULL);
+  g_assert ((just_align && ret == NULL) || (!just_align && ret != NULL));
   return ret;
 
  fail:
@@ -1204,6 +1296,7 @@ g_dbus_message_new_from_blob (guchar                *blob,
   headers = parse_value_from_blob (mis,
                                    dis,
                                    G_VARIANT_TYPE ("a{yv}"),
+                                   FALSE,
                                    error);
   if (headers == NULL)
     goto out;
@@ -1260,6 +1353,7 @@ g_dbus_message_new_from_blob (guchar                *blob,
           message->priv->body = parse_value_from_blob (mis,
                                                        dis,
                                                        variant_type,
+                                                       FALSE,
                                                        error);
           if (message->priv->body == NULL)
             {
@@ -1325,105 +1419,141 @@ ensure_output_padding (GMemoryOutputStream  *mos,
   return padding_needed;
 }
 
+/* note that value can be NULL for e.g. empty arrays - type is never NULL */
 static gboolean
 append_value_to_blob (GVariant             *value,
+                      const GVariantType   *type,
                       GMemoryOutputStream  *mos,
                       GDataOutputStream    *dos,
                       gsize                *out_padding_added,
                       GError              **error)
 {
-  const GVariantType *type;
   gsize padding_added;
 
   padding_added = 0;
 
-  type = g_variant_get_type (value);
   if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
     {
-      gboolean v = g_variant_get_boolean (value);
       padding_added = ensure_output_padding (mos, dos, 4);
-      g_data_output_stream_put_uint32 (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          gboolean v = g_variant_get_boolean (value);
+          g_data_output_stream_put_uint32 (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
     {
-      guint8 v = g_variant_get_byte (value);
-      g_data_output_stream_put_byte (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          guint8 v = g_variant_get_byte (value);
+          g_data_output_stream_put_byte (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
     {
-      gint16 v = g_variant_get_int16 (value);
       padding_added = ensure_output_padding (mos, dos, 2);
-      g_data_output_stream_put_int16 (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          gint16 v = g_variant_get_int16 (value);
+          g_data_output_stream_put_int16 (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
     {
-      guint16 v = g_variant_get_uint16 (value);
       padding_added = ensure_output_padding (mos, dos, 2);
-      g_data_output_stream_put_uint16 (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          guint16 v = g_variant_get_uint16 (value);
+          g_data_output_stream_put_uint16 (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
     {
-      gint32 v = g_variant_get_int32 (value);
       padding_added = ensure_output_padding (mos, dos, 4);
-      g_data_output_stream_put_int32 (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          gint32 v = g_variant_get_int32 (value);
+          g_data_output_stream_put_int32 (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
     {
-      guint32 v = g_variant_get_uint32 (value);
       padding_added = ensure_output_padding (mos, dos, 4);
-      g_data_output_stream_put_uint32 (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          guint32 v = g_variant_get_uint32 (value);
+          g_data_output_stream_put_uint32 (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
     {
-      gint64 v = g_variant_get_int64 (value);
       padding_added = ensure_output_padding (mos, dos, 8);
-      g_data_output_stream_put_int64 (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          gint64 v = g_variant_get_int64 (value);
+          g_data_output_stream_put_int64 (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
     {
-      guint64 v = g_variant_get_uint64 (value);
       padding_added = ensure_output_padding (mos, dos, 8);
-      g_data_output_stream_put_uint64 (dos, v, NULL, NULL);
+      if (value != NULL)
+        {
+          guint64 v = g_variant_get_uint64 (value);
+          g_data_output_stream_put_uint64 (dos, v, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
     {
-      guint64 *encoded;
-      gdouble v = g_variant_get_double (value);
       padding_added = ensure_output_padding (mos, dos, 8);
-      /* TODO: hmm */
-      encoded = (guint64 *) &v;
-      g_data_output_stream_put_uint64 (dos, *encoded, NULL, NULL);
+      if (value != NULL)
+        {
+          guint64 *encoded;
+          gdouble v = g_variant_get_double (value);
+          /* TODO: hmm */
+          encoded = (guint64 *) &v;
+          g_data_output_stream_put_uint64 (dos, *encoded, NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
     {
-      const gchar *v = g_variant_get_string (value, NULL);
-      gsize len;
       padding_added = ensure_output_padding (mos, dos, 4);
-      len = strlen (v);
-      g_data_output_stream_put_uint32 (dos, len, NULL, NULL);
-      g_data_output_stream_put_string (dos, v, NULL, NULL);
-      g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
+      if (value != NULL)
+        {
+          const gchar *v = g_variant_get_string (value, NULL);
+          gsize len;
+          len = strlen (v);
+          g_data_output_stream_put_uint32 (dos, len, NULL, NULL);
+          g_data_output_stream_put_string (dos, v, NULL, NULL);
+          g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
     {
-      /* TODO: validate object path */
-      const gchar *v = g_variant_get_string (value, NULL);
-      gsize len;
       padding_added = ensure_output_padding (mos, dos, 4);
-      len = strlen (v);
-      g_data_output_stream_put_uint32 (dos, len, NULL, NULL);
-      g_data_output_stream_put_string (dos, v, NULL, NULL);
-      g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
+      if (value != NULL)
+        {
+          /* TODO: validate object path */
+          const gchar *v = g_variant_get_string (value, NULL);
+          gsize len;
+          len = strlen (v);
+          g_data_output_stream_put_uint32 (dos, len, NULL, NULL);
+          g_data_output_stream_put_string (dos, v, NULL, NULL);
+          g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
+        }
     }
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
     {
-      /* TODO: validate signature (including max len being 255) */
-      const gchar *v = g_variant_get_string (value, NULL);
-      gsize len;
-      len = strlen (v);
-      g_data_output_stream_put_byte (dos, len, NULL, NULL);
-      g_data_output_stream_put_string (dos, v, NULL, NULL);
-      g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
+      if (value != NULL)
+        {
+          /* TODO: validate signature (including max len being 255) */
+          const gchar *v = g_variant_get_string (value, NULL);
+          gsize len;
+          len = strlen (v);
+          g_data_output_stream_put_byte (dos, len, NULL, NULL);
+          g_data_output_stream_put_string (dos, v, NULL, NULL);
+          g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
+        }
     }
   else if (g_variant_type_is_array (type))
     {
@@ -1433,86 +1563,121 @@ append_value_to_blob (GVariant             *value,
       goffset array_payload_begin_offset;
       goffset cur_offset;
       gsize array_len;
-      guint n;
 
       padding_added = ensure_output_padding (mos, dos, 4);
-
-      /* array length - will be filled in later */
-      array_len_offset = g_memory_output_stream_get_data_size (mos);
-      g_data_output_stream_put_uint32 (dos, 0xF00DFACE, NULL, NULL);
-
-      /* From the D-Bus spec:
-       *
-       *   "A UINT32 giving the length of the array data in bytes,
-       *    followed by alignment padding to the alignment boundary of
-       *    the array element type, followed by each array element. The
-       *    array length is from the end of the alignment padding to
-       *    the end of the last element, i.e. it does not include the
-       *    padding after the length, or any padding after the last
-       *    element."
-       *
-       * Thus, we need to count how much padding the first element
-       * contributes and subtract that from the array length.
-       */
-      array_payload_begin_offset = g_memory_output_stream_get_data_size (mos);
-
-      g_variant_iter_init (&iter, value);
-      n = 0;
-      while ((item = g_variant_iter_next_value (&iter)))
+      if (value != NULL)
         {
-          gsize padding_added_for_item;
-          if (!append_value_to_blob (item, mos, dos, &padding_added_for_item, error))
-            goto fail;
-          if (n == 0)
+          /* array length - will be filled in later */
+          array_len_offset = g_memory_output_stream_get_data_size (mos);
+          g_data_output_stream_put_uint32 (dos, 0xF00DFACE, NULL, NULL);
+
+          /* From the D-Bus spec:
+           *
+           *   "A UINT32 giving the length of the array data in bytes,
+           *    followed by alignment padding to the alignment boundary of
+           *    the array element type, followed by each array element. The
+           *    array length is from the end of the alignment padding to
+           *    the end of the last element, i.e. it does not include the
+           *    padding after the length, or any padding after the last
+           *    element."
+           *
+           * Thus, we need to count how much padding the first element
+           * contributes and subtract that from the array length.
+           */
+          array_payload_begin_offset = g_memory_output_stream_get_data_size (mos);
+
+          if (g_variant_n_children (value) == 0)
             {
+              gsize padding_added_for_item;
+              if (!append_value_to_blob (NULL,
+                                         g_variant_type_element (type),
+                                         mos,
+                                         dos,
+                                         &padding_added_for_item,
+                                         error))
+                goto fail;
               array_payload_begin_offset += padding_added_for_item;
             }
-          n++;
-        }
+          else
+            {
+              guint n;
+              n = 0;
+              g_variant_iter_init (&iter, value);
+              while ((item = g_variant_iter_next_value (&iter)))
+                {
+                  gsize padding_added_for_item;
+                  if (!append_value_to_blob (item,
+                                             g_variant_get_type (item),
+                                             mos,
+                                             dos,
+                                             &padding_added_for_item,
+                                             error))
+                    goto fail;
+                  if (n == 0)
+                    {
+                      array_payload_begin_offset += padding_added_for_item;
+                    }
+                  n++;
+                }
+            }
 
-      cur_offset = g_memory_output_stream_get_data_size (mos);
+          cur_offset = g_memory_output_stream_get_data_size (mos);
 
-      array_len = cur_offset - array_payload_begin_offset;
+          array_len = cur_offset - array_payload_begin_offset;
 
-      if (!g_seekable_seek (G_SEEKABLE (mos), array_len_offset, G_SEEK_SET, NULL, error))
-        goto fail;
+          if (!g_seekable_seek (G_SEEKABLE (mos), array_len_offset, G_SEEK_SET, NULL, error))
+            goto fail;
 
-      g_data_output_stream_put_uint32 (dos, array_len, NULL, NULL);
+          g_data_output_stream_put_uint32 (dos, array_len, NULL, NULL);
 
-      if (!g_seekable_seek (G_SEEKABLE (mos), cur_offset, G_SEEK_SET, NULL, error))
-        goto fail;
+          if (!g_seekable_seek (G_SEEKABLE (mos), cur_offset, G_SEEK_SET, NULL, error))
+            goto fail;
+        }
     }
   else if (g_variant_type_is_dict_entry (type) || g_variant_type_is_tuple (type))
     {
-      GVariant *item;
-      GVariantIter iter;
-
       padding_added = ensure_output_padding (mos, dos, 8);
-
-      g_variant_iter_init (&iter, value);
-
-      while ((item = g_variant_iter_next_value (&iter)))
+      if (value != NULL)
         {
-          if (!append_value_to_blob (item, mos, dos, NULL, error))
-            goto fail;
+          GVariant *item;
+          GVariantIter iter;
+          g_variant_iter_init (&iter, value);
+          while ((item = g_variant_iter_next_value (&iter)))
+            {
+              if (!append_value_to_blob (item,
+                                         g_variant_get_type (item),
+                                         mos,
+                                         dos,
+                                         NULL,
+                                         error))
+                goto fail;
+            }
         }
     }
   else if (g_variant_type_is_variant (type))
     {
-      GVariant *child;
-      const gchar *signature;
-      child = g_variant_get_child_value (value, 0);
-      signature = g_variant_get_type_string (child);
-      /* TODO: validate signature (including max len being 255) */
-      g_data_output_stream_put_byte (dos, strlen (signature), NULL, NULL);
-      g_data_output_stream_put_string (dos, signature, NULL, NULL);
-      g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
-      if (!append_value_to_blob (child, mos, dos, NULL, error))
+      if (value != NULL)
         {
+          GVariant *child;
+          const gchar *signature;
+          child = g_variant_get_child_value (value, 0);
+          signature = g_variant_get_type_string (child);
+          /* TODO: validate signature (including max len being 255) */
+          g_data_output_stream_put_byte (dos, strlen (signature), NULL, NULL);
+          g_data_output_stream_put_string (dos, signature, NULL, NULL);
+          g_data_output_stream_put_byte (dos, '\0', NULL, NULL);
+          if (!append_value_to_blob (child,
+                                     g_variant_get_type (child),
+                                     mos,
+                                     dos,
+                                     NULL,
+                                     error))
+            {
+              g_variant_unref (child);
+              goto fail;
+            }
           g_variant_unref (child);
-          goto fail;
         }
-      g_variant_unref (child);
     }
   else
     {
@@ -1557,7 +1722,12 @@ append_body_to_blob (GVariant             *value,
   g_variant_iter_init (&iter, value);
   while ((item = g_variant_iter_next_value (&iter)))
     {
-      if (!append_value_to_blob (item, mos, dos, NULL, error))
+      if (!append_value_to_blob (item,
+                                 g_variant_get_type (item),
+                                 mos,
+                                 dos,
+                                 NULL,
+                                 error))
         goto fail;
     }
   return TRUE;
@@ -1660,7 +1830,11 @@ g_dbus_message_to_blob (GDBusMessage          *message,
     }
   header_fields = g_variant_new ("a{yv}", builder);
 
-  if (!append_value_to_blob (header_fields, mos, dos, NULL, error))
+  if (!append_value_to_blob (header_fields,
+                             g_variant_get_type (header_fields),
+                             mos, dos,
+                             NULL,
+                             error))
     {
       g_variant_unref (header_fields);
       goto out;
diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c
index 90e380f..3a49f07 100644
--- a/gio/tests/gdbus-serialization.c
+++ b/gio/tests/gdbus-serialization.c
@@ -618,6 +618,7 @@ message_serialize_complex (void)
   GVariant *value;
 
   error = NULL;
+
   value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
                            "([1, 2, 3], {'one': 'white', 'two': 'black'})",
                            NULL, NULL, &error);
@@ -635,6 +636,17 @@ message_serialize_complex (void)
                        "    dict_entry:\n"
                        "      string: `two'\n"
                        "      string: `black'\n");
+
+  value = g_variant_parse (G_VARIANT_TYPE ("(sa{sv}as)"),
+                           "('01234567890123456', {}, ['Something'])",
+                           NULL, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (value != NULL);
+  check_serialization (value,
+                       "value 0:   string: `01234567890123456'\n"
+                       "value 1:   array:\n"
+                       "value 2:   array:\n"
+                       "    string: `Something'\n");
 }
 
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]