Documentation expanded
[platform/upstream/dbus.git] / dbus / dbus-marshal-recursive.c
index 09bf174..4adfd2e 100644 (file)
@@ -1,4 +1,4 @@
-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* dbus-marshal-recursive.c  Marshalling routines for recursive types
  *
  * Copyright (C) 2004, 2005 Red Hat, Inc.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
+#include <config.h>
 #include "dbus-marshal-recursive.h"
 #include "dbus-marshal-basic.h"
+#include "dbus-signature.h"
 #include "dbus-internals.h"
 
 /**
  * @{
  */
 
+static dbus_bool_t _dbus_type_reader_greater_than              (const DBusTypeReader  *lhs,
+                                                                const DBusTypeReader  *rhs);
+
+static void       _dbus_type_writer_set_enabled           (DBusTypeWriter        *writer,
+                                                           dbus_bool_t            enabled);
+static dbus_bool_t _dbus_type_writer_write_reader_partial (DBusTypeWriter        *writer,
+                                                           DBusTypeReader        *reader,
+                                                           const DBusTypeReader  *start_after,
+                                                           int                    start_after_new_pos,
+                                                           int                    start_after_new_len,
+                                                           DBusList             **fixups);
+
 /** turn this on to get deluged in TypeReader verbose spam */
 #define RECURSIVE_MARSHAL_READ_TRACE  0
 
@@ -118,8 +132,6 @@ struct DBusTypeReaderClass
   dbus_bool_t (* check_finished)   (const DBusTypeReader  *reader); /**< check whether reader is at the end */
   void        (* next)             (DBusTypeReader        *reader,
                                     int                    current_type); /**< go to the next value */
-  void        (* init_from_mark)   (DBusTypeReader        *reader,
-                                    const DBusTypeMark    *mark);  /**< uncompress from a mark */
 };
 
 static int
@@ -159,24 +171,26 @@ base_reader_recurse (DBusTypeReader *sub,
 }
 
 static void
-struct_types_only_reader_recurse (DBusTypeReader *sub,
-                                  DBusTypeReader *parent)
+struct_or_dict_entry_types_only_reader_recurse (DBusTypeReader *sub,
+                                                DBusTypeReader *parent)
 {
   base_reader_recurse (sub, parent);
-
+  
   _dbus_assert (_dbus_string_get_byte (sub->type_str,
-                                       sub->type_pos) == DBUS_STRUCT_BEGIN_CHAR);
+                                       sub->type_pos) == DBUS_STRUCT_BEGIN_CHAR ||
+                _dbus_string_get_byte (sub->type_str,
+                                       sub->type_pos) == DBUS_DICT_ENTRY_BEGIN_CHAR);
 
   sub->type_pos += 1;
 }
 
 static void
-struct_reader_recurse (DBusTypeReader *sub,
-                       DBusTypeReader *parent)
+struct_or_dict_entry_reader_recurse (DBusTypeReader *sub,
+                                     DBusTypeReader *parent)
 {
-  struct_types_only_reader_recurse (sub, parent);
+  struct_or_dict_entry_types_only_reader_recurse (sub, parent);
 
-  /* struct has 8 byte alignment */
+  /* struct and dict entry have 8 byte alignment */
   sub->value_pos = _DBUS_ALIGN_VALUE (sub->value_pos, 8);
 }
 
@@ -306,26 +320,43 @@ array_reader_check_finished (const DBusTypeReader *reader)
   return reader->value_pos == end_pos;
 }
 
-/* this is written a little oddly to try and overoptimize */
 static void
 skip_one_complete_type (const DBusString *type_str,
                         int              *type_pos)
 {
+  _dbus_type_signature_next (_dbus_string_get_const_data (type_str),
+                            type_pos);
+}
+
+/**
+ * Skips to the next "complete" type inside a type signature.
+ * The signature is read starting at type_pos, and the next
+ * type position is stored in the same variable.
+ *
+ * @param type_str a type signature (must be valid)
+ * @param type_pos an integer position in the type signature (in and out)
+ */
+void
+_dbus_type_signature_next (const char       *type_str,
+                          int              *type_pos)
+{
   const unsigned char *p;
   const unsigned char *start;
 
   _dbus_assert (type_str != NULL);
   _dbus_assert (type_pos != NULL);
   
-  start = _dbus_string_get_const_data (type_str);
+  start = type_str;
   p = start + *type_pos;
 
   _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
+  _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
   
   while (*p == DBUS_TYPE_ARRAY)
     ++p;
 
   _dbus_assert (*p != DBUS_STRUCT_END_CHAR);
+  _dbus_assert (*p != DBUS_DICT_ENTRY_END_CHAR);
   
   if (*p == DBUS_STRUCT_BEGIN_CHAR)
     {
@@ -354,6 +385,33 @@ skip_one_complete_type (const DBusString *type_str,
             }
         }
     }
+  else if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
+    {
+      int depth;
+
+      depth = 1;
+
+      while (TRUE)
+        {
+          _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+          ++p;
+
+          _dbus_assert (*p != DBUS_TYPE_INVALID);
+
+          if (*p == DBUS_DICT_ENTRY_BEGIN_CHAR)
+            depth += 1;
+          else if (*p == DBUS_DICT_ENTRY_END_CHAR)
+            {
+              depth -= 1;
+              if (depth == 0)
+                {
+                  ++p;
+                  break;
+                }
+            }
+        }
+    }
   else
     {
       ++p;
@@ -381,6 +439,7 @@ base_reader_next (DBusTypeReader *reader,
 {
   switch (current_type)
     {
+    case DBUS_TYPE_DICT_ENTRY:
     case DBUS_TYPE_STRUCT:
     case DBUS_TYPE_VARIANT:
       /* Scan forward over the entire container contents */
@@ -461,6 +520,27 @@ struct_reader_next (DBusTypeReader *reader,
 }
 
 static void
+dict_entry_reader_next (DBusTypeReader *reader,
+                        int             current_type)
+{
+  int t;
+
+  base_reader_next (reader, current_type);
+
+  /* for STRUCT containers we return FALSE at the end of the struct,
+   * for INVALID we return FALSE at the end of the signature.
+   * In both cases we arrange for get_current_type() to return INVALID
+   * which is defined to happen iff we're at the end (no more next())
+   */
+  t = _dbus_string_get_byte (reader->type_str, reader->type_pos);
+  if (t == DBUS_DICT_ENTRY_END_CHAR)
+    {
+      reader->type_pos += 1;
+      reader->finished = TRUE;
+    }
+}
+
+static void
 array_types_only_reader_next (DBusTypeReader *reader,
                               int             current_type)
 {
@@ -493,8 +573,9 @@ array_reader_next (DBusTypeReader *reader,
   _dbus_assert (reader->value_pos >= reader->u.array.start_pos);
 
   switch (_dbus_first_type_in_signature (reader->type_str,
-                                   reader->type_pos))
+                                         reader->type_pos))
     {
+    case DBUS_TYPE_DICT_ENTRY:
     case DBUS_TYPE_STRUCT:
     case DBUS_TYPE_VARIANT:
       {
@@ -550,24 +631,12 @@ array_reader_next (DBusTypeReader *reader,
     }
 }
 
-static void
-array_init_from_mark (DBusTypeReader     *reader,
-                      const DBusTypeMark *mark)
-{
-  /* Fill in the array-specific fields from the mark. The general
-   * fields are already filled in.
-   */
-  reader->u.array.start_pos = mark->array_start_pos;
-  reader->array_len_offset = mark->array_len_offset;
-}
-
 static const DBusTypeReaderClass body_reader_class = {
   "body", 0,
   FALSE,
   NULL, /* body is always toplevel, so doesn't get recursed into */
   NULL,
-  base_reader_next,
-  NULL
+  base_reader_next
 };
 
 static const DBusTypeReaderClass body_types_only_reader_class = {
@@ -575,65 +644,79 @@ static const DBusTypeReaderClass body_types_only_reader_class = {
   TRUE,
   NULL, /* body is always toplevel, so doesn't get recursed into */
   NULL,
-  base_reader_next,
-  NULL
+  base_reader_next
 };
 
 static const DBusTypeReaderClass struct_reader_class = {
   "struct", 2,
   FALSE,
-  struct_reader_recurse,
+  struct_or_dict_entry_reader_recurse,
   NULL,
-  struct_reader_next,
-  NULL
+  struct_reader_next
 };
 
 static const DBusTypeReaderClass struct_types_only_reader_class = {
   "struct types", 3,
   TRUE,
-  struct_types_only_reader_recurse,
+  struct_or_dict_entry_types_only_reader_recurse,
+  NULL,
+  struct_reader_next
+};
+
+static const DBusTypeReaderClass dict_entry_reader_class = {
+  "dict_entry", 4,
+  FALSE,
+  struct_or_dict_entry_reader_recurse,
+  NULL,
+  dict_entry_reader_next
+};
+
+static const DBusTypeReaderClass dict_entry_types_only_reader_class = {
+  "dict_entry types", 5,
+  TRUE,
+  struct_or_dict_entry_types_only_reader_recurse,
   NULL,
-  struct_reader_next,
-  NULL
+  dict_entry_reader_next
 };
 
 static const DBusTypeReaderClass array_reader_class = {
-  "array", 4,
+  "array", 6,
   FALSE,
   array_reader_recurse,
   array_reader_check_finished,
-  array_reader_next,
-  array_init_from_mark
+  array_reader_next
 };
 
 static const DBusTypeReaderClass array_types_only_reader_class = {
-  "array types", 5,
+  "array types", 7,
   TRUE,
   array_types_only_reader_recurse,
   NULL,
-  array_types_only_reader_next,
-  NULL
+  array_types_only_reader_next
 };
 
 static const DBusTypeReaderClass variant_reader_class = {
-  "variant", 6,
+  "variant", 8,
   FALSE,
   variant_reader_recurse,
   NULL,
-  base_reader_next,
-  NULL
+  base_reader_next
 };
 
-static const DBusTypeReaderClass const *
+#ifndef DBUS_DISABLE_ASSERT
+static const DBusTypeReaderClass * const
 all_reader_classes[] = {
   &body_reader_class,
   &body_types_only_reader_class,
   &struct_reader_class,
   &struct_types_only_reader_class,
+  &dict_entry_reader_class,
+  &dict_entry_types_only_reader_class,
   &array_reader_class,
   &array_types_only_reader_class,
   &variant_reader_class
 };
+#endif
 
 /**
  * Initializes a type reader.
@@ -666,41 +749,6 @@ _dbus_type_reader_init (DBusTypeReader    *reader,
 }
 
 /**
- * Initializes a type reader that's been compressed into a
- * DBusTypeMark.  The args have to be the same as those passed in to
- * create the original #DBusTypeReader.
- *
- * @param reader the reader
- * @param byte_order the byte order of the value block
- * @param type_str string containing the type signature
- * @param value_str string containing the values block
- * @param mark the mark to decompress from
- */
-void
-_dbus_type_reader_init_from_mark (DBusTypeReader     *reader,
-                                  int                 byte_order,
-                                  const DBusString   *type_str,
-                                  const DBusString   *value_str,
-                                  const DBusTypeMark *mark)
-{
-  reader->klass = all_reader_classes[mark->container_type];
-
-  reader_init (reader, byte_order,
-               mark->type_pos_in_value_str ? value_str : type_str,
-               mark->type_pos,
-               value_str, mark->value_pos);
-
-  if (reader->klass->init_from_mark)
-    (* reader->klass->init_from_mark) (reader, mark);
-
-#if RECURSIVE_MARSHAL_READ_TRACE
-  _dbus_verbose ("  type reader %p init from mark type_pos = %d value_pos = %d remaining sig '%s'\n",
-                 reader, reader->type_pos, reader->value_pos,
-                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
-#endif
-}
-
-/**
  * Like _dbus_type_reader_init() but the iteration is over the
  * signature, not over values.
  *
@@ -726,60 +774,6 @@ _dbus_type_reader_init_types_only (DBusTypeReader    *reader,
 }
 
 /**
- * Like _dbus_type_reader_init_from_mark() but only iterates over
- * the signature, not the values.
- *
- * @param reader the reader
- * @param type_str the signature string
- * @param mark the mark to decompress from
- */
-void
-_dbus_type_reader_init_types_only_from_mark (DBusTypeReader     *reader,
-                                             const DBusString   *type_str,
-                                             const DBusTypeMark *mark)
-{
-  reader->klass = all_reader_classes[mark->container_type];
-  _dbus_assert (reader->klass->types_only);
-  _dbus_assert (!mark->type_pos_in_value_str);
-
-  reader_init (reader, DBUS_COMPILER_BYTE_ORDER, /* irrelevant */
-               type_str, mark->type_pos,
-               NULL, _DBUS_INT_MAX /* crashes if we screw up */);
-
-  if (reader->klass->init_from_mark)
-    (* reader->klass->init_from_mark) (reader, mark);
-
-#if RECURSIVE_MARSHAL_READ_TRACE
-  _dbus_verbose ("  type reader %p init types only from mark type_pos = %d remaining sig '%s'\n",
-                 reader, reader->type_pos,
-                 _dbus_string_get_const_data_len (reader->type_str, reader->type_pos, 0));
-#endif
-}
-
-/**
- * Compresses a type reader into a #DBusTypeMark, useful for example
- * if you want to cache a bunch of positions in a block of values.
- *
- * @param reader the reader
- * @param mark the mark to init
- */
-void
-_dbus_type_reader_save_mark (const DBusTypeReader *reader,
-                             DBusTypeMark         *mark)
-{
-  mark->type_pos_in_value_str = (reader->type_str == reader->value_str);
-  mark->container_type = reader->klass->id;
-  _dbus_assert (all_reader_classes[reader->klass->id] == reader->klass);
-
-  mark->type_pos = reader->type_pos;
-  mark->value_pos = reader->value_pos;
-
-  /* these are just junk if the reader isn't really an array of course */
-  mark->array_len_offset = reader->array_len_offset;
-  mark->array_start_pos = reader->u.array.start_pos;
-}
-
-/**
  * Gets the type of the value the reader is currently pointing to;
  * or for a types-only reader gets the type it's currently pointing to.
  * If the reader is at the end of a block or end of a container such
@@ -798,11 +792,13 @@ _dbus_type_reader_get_current_type (const DBusTypeReader *reader)
     t = DBUS_TYPE_INVALID;
   else
     t = _dbus_first_type_in_signature (reader->type_str,
-                                 reader->type_pos);
+                                       reader->type_pos);
 
   _dbus_assert (t != DBUS_STRUCT_END_CHAR);
   _dbus_assert (t != DBUS_STRUCT_BEGIN_CHAR);
-
+  _dbus_assert (t != DBUS_DICT_ENTRY_END_CHAR);
+  _dbus_assert (t != DBUS_DICT_ENTRY_BEGIN_CHAR);
+  
 #if 0
   _dbus_verbose ("  type reader %p current type_pos = %d type = %s\n",
                  reader, reader->type_pos,
@@ -894,6 +890,21 @@ _dbus_type_reader_read_basic (const DBusTypeReader    *reader,
 }
 
 /**
+ * Returns the number of bytes in the array.
+ *
+ * @param reader the reader to read from
+ * @returns the number of bytes in the array
+ */
+int
+_dbus_type_reader_get_array_length (const DBusTypeReader  *reader)
+{
+  _dbus_assert (!reader->klass->types_only);
+  _dbus_assert (reader->klass == &array_reader_class);
+
+  return array_reader_get_array_len (reader);
+}
+
+/**
  * Reads a block of fixed-length basic values, from the current point
  * in an array to the end of the array.  Does not work for arrays of
  * string or container types.
@@ -926,7 +937,7 @@ _dbus_type_reader_read_fixed_multi (const DBusTypeReader  *reader,
                                                 reader->type_pos);
 
   _dbus_assert (element_type != DBUS_TYPE_INVALID); /* why we don't use get_current_type() */
-  _dbus_assert (_dbus_type_is_fixed (element_type));
+  _dbus_assert (dbus_type_is_fixed (element_type));
 
   alignment = _dbus_type_get_alignment (element_type);
 
@@ -989,6 +1000,12 @@ _dbus_type_reader_recurse (DBusTypeReader *reader,
       else
         sub->klass = &struct_reader_class;
       break;
+    case DBUS_TYPE_DICT_ENTRY:
+      if (reader->klass->types_only)
+        sub->klass = &dict_entry_types_only_reader_class;
+      else
+        sub->klass = &dict_entry_reader_class;
+      break;
     case DBUS_TYPE_ARRAY:
       if (reader->klass->types_only)
         sub->klass = &array_types_only_reader_class;
@@ -1005,7 +1022,7 @@ _dbus_type_reader_recurse (DBusTypeReader *reader,
       _dbus_verbose ("recursing into type %s\n", _dbus_type_to_string (t));
 #ifndef DBUS_DISABLE_CHECKS
       if (t == DBUS_TYPE_INVALID)
-        _dbus_warn ("You can't recurse into an empty array or off the end of a message body\n");
+        _dbus_warn_check_failed ("You can't recurse into an empty array or off the end of a message body\n");
 #endif /* DBUS_DISABLE_CHECKS */
 
       _dbus_assert_not_reached ("don't yet handle recursing into this type");
@@ -1114,8 +1131,8 @@ _dbus_type_reader_get_signature (const DBusTypeReader  *reader,
 
 typedef struct
 {
-  DBusString replacement;
-  int padding;
+  DBusString replacement; /**< Marshaled value including alignment padding */
+  int padding;            /**< How much of the replacement block is padding */
 } ReplacementBlock;
 
 static dbus_bool_t
@@ -1328,7 +1345,7 @@ reader_set_basic_fixed_length (DBusTypeReader *reader,
  * type and value strings and set_basic would be a method on that
  * object... this would also make DBusTypeReader the same thing as
  * DBusTypeMark. But since DBusMessage is effectively that object for
- * D-BUS it doesn't seem worth creating some random object.)
+ * D-Bus it doesn't seem worth creating some random object.)
  *
  * @todo optimize this by only rewriting until the old and new values
  * are at the same alignment. Frequently this should result in only
@@ -1364,9 +1381,9 @@ _dbus_type_reader_set_basic (DBusTypeReader       *reader,
                                  realign_root->value_pos);
 #endif
 
-  _dbus_assert (_dbus_type_is_basic (current_type));
+  _dbus_assert (dbus_type_is_basic (current_type));
 
-  if (_dbus_type_is_fixed (current_type))
+  if (dbus_type_is_fixed (current_type))
     {
       reader_set_basic_fixed_length (reader, current_type, value);
       return TRUE;
@@ -1423,7 +1440,7 @@ _dbus_type_reader_delete (DBusTypeReader        *reader,
   return retval;
 }
 
-/**
+/*
  * Compares two readers, which must be iterating over the same value data.
  * Returns #TRUE if the first parameter is further along than the second parameter.
  *
@@ -1431,7 +1448,7 @@ _dbus_type_reader_delete (DBusTypeReader        *reader,
  * @param rhs left-hand-side (first) parameter
  * @returns whether lhs is greater than rhs
  */
-dbus_bool_t
+static dbus_bool_t
 _dbus_type_reader_greater_than (const DBusTypeReader  *lhs,
                                 const DBusTypeReader  *rhs)
 {
@@ -1451,7 +1468,7 @@ _dbus_type_reader_greater_than (const DBusTypeReader  *lhs,
 
 /**
  * Initialize a write iterator, which is used to write out values in
- * serialized D-BUS format.
+ * serialized D-Bus format.
  *
  * The type_pos passed in is expected to be inside an already-valid,
  * though potentially empty, type signature. This means that the byte
@@ -1643,9 +1660,18 @@ writer_recurse_init_and_check (DBusTypeWriter *writer,
 
       if (expected != sub->container_type)
         {
-          _dbus_warn ("Writing an element of type %s, but the expected type here is %s\n",
-                      _dbus_type_to_string (sub->container_type),
-                      _dbus_type_to_string (expected));
+          if (expected != DBUS_TYPE_INVALID)
+            _dbus_warn_check_failed ("Writing an element of type %s, but the expected type here is %s\n"
+                                     "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                     _dbus_type_to_string (sub->container_type),
+                                     _dbus_type_to_string (expected),
+                                     _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+          else
+            _dbus_warn_check_failed ("Writing an element of type %s, but no value is expected here\n"
+                                     "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                     _dbus_type_to_string (sub->container_type),
+                                     _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+          
           _dbus_assert_not_reached ("bad array element or variant content written");
         }
     }
@@ -1700,8 +1726,16 @@ write_or_verify_typecode (DBusTypeWriter *writer,
 
         if (expected != typecode)
           {
-            _dbus_warn ("Array or variant type requires that type %s be written, but %s was written\n",
-                        _dbus_type_to_string (expected), _dbus_type_to_string (typecode));
+            if (expected != DBUS_TYPE_INVALID)
+              _dbus_warn_check_failed ("Array or variant type requires that type %s be written, but %s was written.\n"
+                                       "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                       _dbus_type_to_string (expected), _dbus_type_to_string (typecode),
+                                       _dbus_string_get_const_data (writer->type_str), writer->type_pos);
+            else
+              _dbus_warn_check_failed ("Array or variant type wasn't expecting any more values to be written into it, but a value %s was written.\n"
+                                       "The overall signature expected here was '%s' and we are on byte %d of that signature.\n",
+                                       _dbus_type_to_string (typecode),
+                                       _dbus_string_get_const_data (writer->type_str), writer->type_pos);
             _dbus_assert_not_reached ("bad type inserted somewhere inside an array or variant");
           }
       }
@@ -1734,11 +1768,12 @@ write_or_verify_typecode (DBusTypeWriter *writer,
 }
 
 static dbus_bool_t
-writer_recurse_struct (DBusTypeWriter   *writer,
-                       const DBusString *contained_type,
-                       int               contained_type_start,
-                       int               contained_type_len,
-                       DBusTypeWriter   *sub)
+writer_recurse_struct_or_dict_entry (DBusTypeWriter   *writer,
+                                     int               begin_char,
+                                     const DBusString *contained_type,
+                                     int               contained_type_start,
+                                     int               contained_type_len,
+                                     DBusTypeWriter   *sub)
 {
   /* FIXME right now contained_type is ignored; we could probably
    * almost trivially fix the code so if it's present we
@@ -1752,7 +1787,7 @@ writer_recurse_struct (DBusTypeWriter   *writer,
         return FALSE;
     }
 
-  if (!write_or_verify_typecode (sub, DBUS_STRUCT_BEGIN_CHAR))
+  if (!write_or_verify_typecode (sub, begin_char))
     _dbus_assert_not_reached ("failed to insert struct typecode after prealloc");
 
   if (writer->enabled)
@@ -1791,10 +1826,10 @@ writer_recurse_array (DBusTypeWriter   *writer,
                                          writer->type_str,
                                          writer->u.array.element_type_pos + 1))
         {
-          _dbus_warn ("Writing an array of '%s' but this is incompatible with the expected type of elements in the parent array\n",
-                      _dbus_string_get_const_data_len (contained_type,
-                                                       contained_type_start,
-                                                       contained_type_len));
+          _dbus_warn_check_failed ("Writing an array of '%s' but this is incompatible with the expected type of elements in the parent array\n",
+                                   _dbus_string_get_const_data_len (contained_type,
+                                                                    contained_type_start,
+                                                                    contained_type_len));
           _dbus_assert_not_reached ("incompatible type for child array");
         }
     }
@@ -2027,9 +2062,18 @@ _dbus_type_writer_recurse_contained_len (DBusTypeWriter   *writer,
   switch (container_type)
     {
     case DBUS_TYPE_STRUCT:
-      return writer_recurse_struct (writer,
-                                    contained_type, contained_type_start, contained_type_len,
-                                    sub);
+      return writer_recurse_struct_or_dict_entry (writer,
+                                                  DBUS_STRUCT_BEGIN_CHAR,
+                                                  contained_type,
+                                                  contained_type_start, contained_type_len,
+                                                  sub);
+      break;
+    case DBUS_TYPE_DICT_ENTRY:
+      return writer_recurse_struct_or_dict_entry (writer,
+                                                  DBUS_DICT_ENTRY_BEGIN_CHAR,
+                                                  contained_type,
+                                                  contained_type_start, contained_type_len,
+                                                  sub);
       break;
     case DBUS_TYPE_ARRAY:
       return writer_recurse_array (writer,
@@ -2151,6 +2195,11 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
       if (!write_or_verify_typecode (sub, DBUS_STRUCT_END_CHAR))
         return FALSE;
     }
+  else if (sub->container_type == DBUS_TYPE_DICT_ENTRY)
+    {
+      if (!write_or_verify_typecode (sub, DBUS_DICT_ENTRY_END_CHAR))
+        return FALSE;
+    }
   else if (sub->container_type == DBUS_TYPE_ARRAY)
     {
       if (sub->u.array.len_pos >= 0) /* len_pos == -1 if we weren't enabled when we passed it */
@@ -2217,11 +2266,16 @@ _dbus_type_writer_unrecurse (DBusTypeWriter *writer,
    *   parent makes no difference since there's only one value
    *   and we just finished writing it and won't use type_pos again
    *       writer->type_pos should remain as-is
+   *
+   *
+   * For all these, DICT_ENTRY is the same as STRUCT
    */
   if (writer->type_str != NULL)
     {
-      if (sub->container_type == DBUS_TYPE_STRUCT &&
+      if ((sub->container_type == DBUS_TYPE_STRUCT ||
+           sub->container_type == DBUS_TYPE_DICT_ENTRY) &&
           (writer->container_type == DBUS_TYPE_STRUCT ||
+           writer->container_type == DBUS_TYPE_DICT_ENTRY ||
            writer->container_type == DBUS_TYPE_INVALID))
         {
           /* Advance the parent to the next struct field */
@@ -2286,7 +2340,7 @@ _dbus_type_writer_write_basic (DBusTypeWriter *writer,
 
 /**
  * Writes a block of fixed-length basic values, i.e. those that are
- * both _dbus_type_is_fixed() and _dbus_type_is_basic(). The block
+ * both dbus_type_is_fixed() and _dbus_type_is_basic(). The block
  * must be written inside an array.
  *
  * The value parameter should be the address of said array of values,
@@ -2305,7 +2359,7 @@ _dbus_type_writer_write_fixed_multi (DBusTypeWriter        *writer,
                                      int                    n_elements)
 {
   _dbus_assert (writer->container_type == DBUS_TYPE_ARRAY);
-  _dbus_assert (_dbus_type_is_fixed (element_type));
+  _dbus_assert (dbus_type_is_fixed (element_type));
   _dbus_assert (writer->type_pos_is_expectation);
   _dbus_assert (n_elements >= 0);
 
@@ -2398,7 +2452,7 @@ writer_write_reader_helper (DBusTypeWriter       *writer,
 
   while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID)
     {
-      if (_dbus_type_is_container (current_type))
+      if (dbus_type_is_container (current_type))
         {
           DBusTypeReader subreader;
           DBusTypeWriter subwriter;
@@ -2544,7 +2598,7 @@ writer_write_reader_helper (DBusTypeWriter       *writer,
         {
           DBusBasicValue val;
 
-          _dbus_assert (_dbus_type_is_basic (current_type));
+          _dbus_assert (dbus_type_is_basic (current_type));
 
 #if RECURSIVE_MARSHAL_WRITE_TRACE
           _dbus_verbose ("Reading basic value %s at %d\n",
@@ -2585,7 +2639,7 @@ writer_write_reader_helper (DBusTypeWriter       *writer,
   return FALSE;
 }
 
-/**
+/*
  * Iterate through all values in the given reader, writing a copy of
  * each value to the writer.  The reader will be moved forward to its
  * end position.
@@ -2616,7 +2670,7 @@ writer_write_reader_helper (DBusTypeWriter       *writer,
  * @param fixups list to append #DBusArrayLenFixup if the write was partial
  * @returns #FALSE if no memory
  */
-dbus_bool_t
+static dbus_bool_t
 _dbus_type_writer_write_reader_partial (DBusTypeWriter       *writer,
                                         DBusTypeReader       *reader,
                                         const DBusTypeReader *start_after,
@@ -2677,7 +2731,7 @@ _dbus_type_writer_write_reader (DBusTypeWriter       *writer,
   return _dbus_type_writer_write_reader_partial (writer, reader, NULL, 0, 0, NULL);
 }
 
-/**
+/*
  * If disabled, a writer can still be iterated forward and recursed/unrecursed
  * but won't write any values. Types will still be written unless the
  * writer is a "values only" writer, because the writer needs access to
@@ -2686,7 +2740,7 @@ _dbus_type_writer_write_reader (DBusTypeWriter       *writer,
  * @param writer the type writer
  * @param enabled #TRUE if values should be written
  */
-void
+static void
 _dbus_type_writer_set_enabled (DBusTypeWriter   *writer,
                                dbus_bool_t       enabled)
 {