-/* -*- 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
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
}
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);
}
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)
{
}
}
}
+ 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;
{
switch (current_type)
{
+ case DBUS_TYPE_DICT_ENTRY:
case DBUS_TYPE_STRUCT:
case DBUS_TYPE_VARIANT:
/* Scan forward over the entire container contents */
}
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)
{
_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:
{
}
}
-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 = {
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.
}
/**
- * 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.
*
}
/**
- * 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
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,
}
/**
+ * 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.
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);
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;
_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");
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
* 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
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;
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.
*
* @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)
{
/**
* 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
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");
}
}
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");
}
}
}
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
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)
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");
}
}
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,
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 */
* 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 */
/**
* 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,
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);
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;
{
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",
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.
* @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,
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
* @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)
{