diff options
Diffstat (limited to 'dbus/dbus-marshal-recursive-util.c')
| -rw-r--r-- | dbus/dbus-marshal-recursive-util.c | 2963 | 
1 files changed, 2963 insertions, 0 deletions
diff --git a/dbus/dbus-marshal-recursive-util.c b/dbus/dbus-marshal-recursive-util.c new file mode 100644 index 00000000..cd80ea07 --- /dev/null +++ b/dbus/dbus-marshal-recursive-util.c @@ -0,0 +1,2963 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-marshal-recursive-util.c  Would be in dbus-marshal-recursive.c, but only used in bus/tests + * + * Copyright (C) 2004, 2005 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * 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 + * + */ + +#include "dbus-marshal-recursive.h" +#include "dbus-marshal-basic.h" +#include "dbus-internals.h" + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include "dbus-list.h" +#include <stdio.h> +#include <stdlib.h> + +static int +first_type_in_signature (const DBusString *str, +                         int               pos) +{ +  unsigned char t; + +  t = _dbus_string_get_byte (str, pos); + +  if (t == DBUS_STRUCT_BEGIN_CHAR) +    return DBUS_TYPE_STRUCT; +  else +    return t; +} + +/* Whether to do the OOM stuff (only with other expensive tests) */ +#define TEST_OOM_HANDLING 0 +/* We do start offset 0 through 9, to get various alignment cases. Still this + * obviously makes the test suite run 10x as slow. + */ +#define MAX_INITIAL_OFFSET 9 + +/* Largest iteration count to test copying, realignment, + * etc. with. i.e. we only test this stuff with some of the smaller + * data sets. + */ +#define MAX_ITERATIONS_FOR_EXPENSIVE_TESTS 1000 + +typedef struct +{ +  int byte_order; +  int initial_offset; +  DBusString signature; +  DBusString body; +} DataBlock; + +typedef struct +{ +  int saved_sig_len; +  int saved_body_len; +} DataBlockState; + +#define N_FENCE_BYTES 5 +#define FENCE_BYTES_STR "abcde" +#define INITIAL_PADDING_BYTE '\0' + +static dbus_bool_t +data_block_init (DataBlock *block, +                 int        byte_order, +                 int        initial_offset) +{ +  if (!_dbus_string_init (&block->signature)) +    return FALSE; + +  if (!_dbus_string_init (&block->body)) +    { +      _dbus_string_free (&block->signature); +      return FALSE; +    } + +  if (!_dbus_string_insert_bytes (&block->signature, 0, initial_offset, +                                  INITIAL_PADDING_BYTE) || +      !_dbus_string_insert_bytes (&block->body, 0, initial_offset, +                                  INITIAL_PADDING_BYTE) || +      !_dbus_string_append (&block->signature, FENCE_BYTES_STR) || +      !_dbus_string_append (&block->body, FENCE_BYTES_STR)) +    { +      _dbus_string_free (&block->signature); +      _dbus_string_free (&block->body); +      return FALSE; +    } + +  block->byte_order = byte_order; +  block->initial_offset = initial_offset; + +  return TRUE; +} + +static void +data_block_save (DataBlock      *block, +                 DataBlockState *state) +{ +  state->saved_sig_len = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES; +  state->saved_body_len = _dbus_string_get_length (&block->body) - N_FENCE_BYTES; +} + +static void +data_block_restore (DataBlock      *block, +                    DataBlockState *state) +{ +  _dbus_string_delete (&block->signature, +                       state->saved_sig_len, +                       _dbus_string_get_length (&block->signature) - state->saved_sig_len - N_FENCE_BYTES); +  _dbus_string_delete (&block->body, +                       state->saved_body_len, +                       _dbus_string_get_length (&block->body) - state->saved_body_len - N_FENCE_BYTES); +} + +static void +data_block_verify (DataBlock *block) +{ +  if (!_dbus_string_ends_with_c_str (&block->signature, +                                     FENCE_BYTES_STR)) +    { +      int offset; + +      offset = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - 8; +      if (offset < 0) +        offset = 0; + +      _dbus_verbose_bytes_of_string (&block->signature, +                                     offset, +                                     _dbus_string_get_length (&block->signature) - offset); +      _dbus_assert_not_reached ("block did not verify: bad bytes at end of signature"); +    } +  if (!_dbus_string_ends_with_c_str (&block->body, +                                     FENCE_BYTES_STR)) +    { +      int offset; + +      offset = _dbus_string_get_length (&block->body) - N_FENCE_BYTES - 8; +      if (offset < 0) +        offset = 0; + +      _dbus_verbose_bytes_of_string (&block->body, +                                     offset, +                                     _dbus_string_get_length (&block->body) - offset); +      _dbus_assert_not_reached ("block did not verify: bad bytes at end of body"); +    } + +  _dbus_assert (_dbus_string_validate_nul (&block->signature, +                                           0, block->initial_offset)); +  _dbus_assert (_dbus_string_validate_nul (&block->body, +                                           0, block->initial_offset)); +} + +static void +data_block_free (DataBlock *block) +{ +  data_block_verify (block); + +  _dbus_string_free (&block->signature); +  _dbus_string_free (&block->body); +} + +static void +data_block_reset (DataBlock *block) +{ +  data_block_verify (block); + +  _dbus_string_delete (&block->signature, +                       block->initial_offset, +                       _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - block->initial_offset); +  _dbus_string_delete (&block->body, +                       block->initial_offset, +                       _dbus_string_get_length (&block->body) - N_FENCE_BYTES - block->initial_offset); + +  data_block_verify (block); +} + +static void +data_block_init_reader_writer (DataBlock      *block, +                               DBusTypeReader *reader, +                               DBusTypeWriter *writer) +{ +  if (reader) +    _dbus_type_reader_init (reader, +                            block->byte_order, +                            &block->signature, +                            block->initial_offset, +                            &block->body, +                            block->initial_offset); + +  if (writer) +    _dbus_type_writer_init (writer, +                            block->byte_order, +                            &block->signature, +                            _dbus_string_get_length (&block->signature) - N_FENCE_BYTES, +                            &block->body, +                            _dbus_string_get_length (&block->body) - N_FENCE_BYTES); +} + +static void +real_check_expected_type (DBusTypeReader *reader, +                          int             expected, +                          const char     *funcname, +                          int             line) +{ +  int t; + +  t = _dbus_type_reader_get_current_type (reader); + +  if (t != expected) +    { +      _dbus_warn ("Read type %s while expecting %s at %s line %d\n", +                  _dbus_type_to_string (t), +                  _dbus_type_to_string (expected), +                  funcname, line); + +      _dbus_assert_not_reached ("read wrong type"); +    } +} + +#define check_expected_type(reader, expected) real_check_expected_type (reader, expected, _DBUS_FUNCTION_NAME, __LINE__) + +#define NEXT_EXPECTING_TRUE(reader)  do { if (!_dbus_type_reader_next (reader))         \ + {                                                                                      \ +    _dbus_warn ("_dbus_type_reader_next() should have returned TRUE at %s %d\n",        \ +                              _DBUS_FUNCTION_NAME, __LINE__);                           \ +    _dbus_assert_not_reached ("test failed");                                           \ + }                                                                                      \ +} while (0) + +#define NEXT_EXPECTING_FALSE(reader) do { if (_dbus_type_reader_next (reader))          \ + {                                                                                      \ +    _dbus_warn ("_dbus_type_reader_next() should have returned FALSE at %s %d\n",       \ +                              _DBUS_FUNCTION_NAME, __LINE__);                           \ +    _dbus_assert_not_reached ("test failed");                                           \ + }                                                                                      \ + check_expected_type (reader, DBUS_TYPE_INVALID);                                       \ +} while (0) + +typedef struct TestTypeNode               TestTypeNode; +typedef struct TestTypeNodeClass          TestTypeNodeClass; +typedef struct TestTypeNodeContainer      TestTypeNodeContainer; +typedef struct TestTypeNodeContainerClass TestTypeNodeContainerClass; + +struct TestTypeNode +{ +  const TestTypeNodeClass *klass; +}; + +struct TestTypeNodeContainer +{ +  TestTypeNode base; +  DBusList    *children; +}; + +struct TestTypeNodeClass +{ +  int typecode; + +  int instance_size; + +  int subclass_detail; /* a bad hack to avoid a bunch of subclass casting */ + +  dbus_bool_t   (* construct)     (TestTypeNode   *node); +  void          (* destroy)       (TestTypeNode   *node); + +  dbus_bool_t (* write_value)     (TestTypeNode   *node, +                                   DataBlock      *block, +                                   DBusTypeWriter *writer, +                                   int             seed); +  dbus_bool_t (* read_value)      (TestTypeNode   *node, +                                   DBusTypeReader *reader, +                                   int             seed); +  dbus_bool_t (* set_value)       (TestTypeNode   *node, +                                   DBusTypeReader *reader, +                                   DBusTypeReader *realign_root, +                                   int             seed); +  dbus_bool_t (* build_signature) (TestTypeNode   *node, +                                   DBusString     *str); +  dbus_bool_t (* write_multi)     (TestTypeNode   *node, +                                   DataBlock      *block, +                                   DBusTypeWriter *writer, +                                   int             seed, +                                   int             count); +  dbus_bool_t (* read_multi)      (TestTypeNode   *node, +                                   DBusTypeReader *reader, +                                   int             seed, +                                   int             count); +}; + +struct TestTypeNodeContainerClass +{ +  TestTypeNodeClass base; +}; + +/* FIXME this could be chilled out substantially by unifying + * the basic types into basic_write_value/basic_read_value + * and by merging read_value and set_value into one function + * taking a flag argument. + */ +static dbus_bool_t int32_write_value       (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t int32_read_value        (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t int32_set_value         (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t int32_write_multi       (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed, +                                            int             count); +static dbus_bool_t int32_read_multi        (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed, +                                            int             count); +static dbus_bool_t int64_write_value       (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t int64_read_value        (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t int64_set_value         (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t string_write_value      (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t string_read_value       (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t string_set_value        (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t bool_write_value        (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t bool_read_value         (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t bool_set_value          (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t byte_write_value        (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t byte_read_value         (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t byte_set_value          (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t double_write_value      (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t double_read_value       (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t double_set_value        (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t object_path_write_value (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t object_path_read_value  (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t object_path_set_value   (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t signature_write_value   (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t signature_read_value    (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t signature_set_value     (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t struct_write_value      (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t struct_read_value       (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t struct_set_value        (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t struct_build_signature  (TestTypeNode   *node, +                                            DBusString     *str); +static dbus_bool_t array_write_value       (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t array_read_value        (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t array_set_value         (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static dbus_bool_t array_build_signature   (TestTypeNode   *node, +                                            DBusString     *str); +static dbus_bool_t variant_write_value     (TestTypeNode   *node, +                                            DataBlock      *block, +                                            DBusTypeWriter *writer, +                                            int             seed); +static dbus_bool_t variant_read_value      (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            int             seed); +static dbus_bool_t variant_set_value       (TestTypeNode   *node, +                                            DBusTypeReader *reader, +                                            DBusTypeReader *realign_root, +                                            int             seed); +static void        container_destroy       (TestTypeNode   *node); + + +static const TestTypeNodeClass int32_class = { +  DBUS_TYPE_INT32, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  int32_write_value, +  int32_read_value, +  int32_set_value, +  NULL, +  int32_write_multi, +  int32_read_multi +}; + +static const TestTypeNodeClass uint32_class = { +  DBUS_TYPE_UINT32, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  int32_write_value, /* recycle from int32 */ +  int32_read_value,  /* recycle from int32 */ +  int32_set_value,   /* recycle from int32 */ +  NULL, +  int32_write_multi, /* recycle from int32 */ +  int32_read_multi   /* recycle from int32 */ +}; + +static const TestTypeNodeClass int64_class = { +  DBUS_TYPE_INT64, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  int64_write_value, +  int64_read_value, +  int64_set_value, +  NULL, +  NULL, /* FIXME */ +  NULL  /* FIXME */ +}; + +static const TestTypeNodeClass uint64_class = { +  DBUS_TYPE_UINT64, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  int64_write_value, /* recycle from int64 */ +  int64_read_value,  /* recycle from int64 */ +  int64_set_value,   /* recycle from int64 */ +  NULL, +  NULL, /* FIXME */ +  NULL  /* FIXME */ +}; + +static const TestTypeNodeClass string_0_class = { +  DBUS_TYPE_STRING, +  sizeof (TestTypeNode), +  0, /* string length */ +  NULL, +  NULL, +  string_write_value, +  string_read_value, +  string_set_value, +  NULL, +  NULL, +  NULL +}; + +static const TestTypeNodeClass string_1_class = { +  DBUS_TYPE_STRING, +  sizeof (TestTypeNode), +  1, /* string length */ +  NULL, +  NULL, +  string_write_value, +  string_read_value, +  string_set_value, +  NULL, +  NULL, +  NULL +}; + +/* with nul, a len 3 string should fill 4 bytes and thus is "special" */ +static const TestTypeNodeClass string_3_class = { +  DBUS_TYPE_STRING, +  sizeof (TestTypeNode), +  3, /* string length */ +  NULL, +  NULL, +  string_write_value, +  string_read_value, +  string_set_value, +  NULL, +  NULL, +  NULL +}; + +/* with nul, a len 8 string should fill 9 bytes and thus is "special" (far-fetched I suppose) */ +static const TestTypeNodeClass string_8_class = { +  DBUS_TYPE_STRING, +  sizeof (TestTypeNode), +  8, /* string length */ +  NULL, +  NULL, +  string_write_value, +  string_read_value, +  string_set_value, +  NULL, +  NULL, +  NULL +}; + +static const TestTypeNodeClass bool_class = { +  DBUS_TYPE_BOOLEAN, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  bool_write_value, +  bool_read_value, +  bool_set_value, +  NULL, +  NULL, /* FIXME */ +  NULL  /* FIXME */ +}; + +static const TestTypeNodeClass byte_class = { +  DBUS_TYPE_BYTE, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  byte_write_value, +  byte_read_value, +  byte_set_value, +  NULL, +  NULL, /* FIXME */ +  NULL  /* FIXME */ +}; + +static const TestTypeNodeClass double_class = { +  DBUS_TYPE_DOUBLE, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  double_write_value, +  double_read_value, +  double_set_value, +  NULL, +  NULL, /* FIXME */ +  NULL  /* FIXME */ +}; + +static const TestTypeNodeClass object_path_class = { +  DBUS_TYPE_OBJECT_PATH, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  object_path_write_value, +  object_path_read_value, +  object_path_set_value, +  NULL, +  NULL, +  NULL +}; + +static const TestTypeNodeClass signature_class = { +  DBUS_TYPE_SIGNATURE, +  sizeof (TestTypeNode), +  0, +  NULL, +  NULL, +  signature_write_value, +  signature_read_value, +  signature_set_value, +  NULL, +  NULL, +  NULL +}; + +static const TestTypeNodeClass struct_1_class = { +  DBUS_TYPE_STRUCT, +  sizeof (TestTypeNodeContainer), +  1, /* number of times children appear as fields */ +  NULL, +  container_destroy, +  struct_write_value, +  struct_read_value, +  struct_set_value, +  struct_build_signature, +  NULL, +  NULL +}; + +static const TestTypeNodeClass struct_2_class = { +  DBUS_TYPE_STRUCT, +  sizeof (TestTypeNodeContainer), +  2, /* number of times children appear as fields */ +  NULL, +  container_destroy, +  struct_write_value, +  struct_read_value, +  struct_set_value, +  struct_build_signature, +  NULL, +  NULL +}; + +static dbus_bool_t arrays_write_fixed_in_blocks = FALSE; + +static const TestTypeNodeClass array_0_class = { +  DBUS_TYPE_ARRAY, +  sizeof (TestTypeNodeContainer), +  0, /* number of array elements */ +  NULL, +  container_destroy, +  array_write_value, +  array_read_value, +  array_set_value, +  array_build_signature, +  NULL, +  NULL +}; + +static const TestTypeNodeClass array_1_class = { +  DBUS_TYPE_ARRAY, +  sizeof (TestTypeNodeContainer), +  1, /* number of array elements */ +  NULL, +  container_destroy, +  array_write_value, +  array_read_value, +  array_set_value, +  array_build_signature, +  NULL, +  NULL +}; + +static const TestTypeNodeClass array_2_class = { +  DBUS_TYPE_ARRAY, +  sizeof (TestTypeNodeContainer), +  2, /* number of array elements */ +  NULL, +  container_destroy, +  array_write_value, +  array_read_value, +  array_set_value, +  array_build_signature, +  NULL, +  NULL +}; + +static const TestTypeNodeClass array_9_class = { +  DBUS_TYPE_ARRAY, +  sizeof (TestTypeNodeContainer), +  9, /* number of array elements */ +  NULL, +  container_destroy, +  array_write_value, +  array_read_value, +  array_set_value, +  array_build_signature, +  NULL, +  NULL +}; + +static const TestTypeNodeClass variant_class = { +  DBUS_TYPE_VARIANT, +  sizeof (TestTypeNodeContainer), +  0, +  NULL, +  container_destroy, +  variant_write_value, +  variant_read_value, +  variant_set_value, +  NULL, +  NULL, +  NULL +}; + +static const TestTypeNodeClass* const +basic_nodes[] = { +  &int32_class, +  &uint32_class, +  &int64_class, +  &uint64_class, +  &bool_class, +  &byte_class, +  &double_class, +  &string_0_class, +  &string_1_class, +  &string_3_class, +  &string_8_class, +  &object_path_class, +  &signature_class +}; +#define N_BASICS (_DBUS_N_ELEMENTS (basic_nodes)) + +static const TestTypeNodeClass* const +container_nodes[] = { +  &struct_1_class, +  &array_1_class, +  &struct_2_class, +  &array_0_class, +  &array_2_class, +  &variant_class +  /* array_9_class is omitted on purpose, it's too slow; +   * we only use it in one hardcoded test below +   */ +}; +#define N_CONTAINERS (_DBUS_N_ELEMENTS (container_nodes)) + +static TestTypeNode* +node_new (const TestTypeNodeClass *klass) +{ +  TestTypeNode *node; + +  node = dbus_malloc0 (klass->instance_size); +  if (node == NULL) +    return NULL; + +  node->klass = klass; + +  if (klass->construct) +    { +      if (!(* klass->construct) (node)) +        { +          dbus_free (node); +          return FALSE; +        } +    } + +  return node; +} + +static void +node_destroy (TestTypeNode *node) +{ +  if (node->klass->destroy) +    (* node->klass->destroy) (node); +  dbus_free (node); +} + +static dbus_bool_t +node_write_value (TestTypeNode   *node, +                  DataBlock      *block, +                  DBusTypeWriter *writer, +                  int             seed) +{ +  dbus_bool_t retval; + +  retval = (* node->klass->write_value) (node, block, writer, seed); + +#if 0 +  /* Handy to see where things break, but too expensive to do all the time */ +  data_block_verify (block); +#endif + +  return retval; +} + +static dbus_bool_t +node_read_value (TestTypeNode   *node, +                 DBusTypeReader *reader, +                 int             seed) +{ +  DBusTypeMark mark; +  DBusTypeReader restored; + +  _dbus_type_reader_save_mark (reader, &mark); + +  if (!(* node->klass->read_value) (node, reader, seed)) +    return FALSE; + +  _dbus_type_reader_init_from_mark (&restored, +                                    reader->byte_order, +                                    reader->type_str, +                                    reader->value_str, +                                    &mark); + +  if (!(* node->klass->read_value) (node, &restored, seed)) +    return FALSE; + +  return TRUE; +} + +/* Warning: if this one fails due to OOM, it has side effects (can + * modify only some of the sub-values). OK in a test suite, but we + * never do this in real code. + */ +static dbus_bool_t +node_set_value (TestTypeNode   *node, +                DBusTypeReader *reader, +                DBusTypeReader *realign_root, +                int             seed) +{ +  if (!(* node->klass->set_value) (node, reader, realign_root, seed)) +    return FALSE; + +  return TRUE; +} + +static dbus_bool_t +node_build_signature (TestTypeNode *node, +                      DBusString   *str) +{ +  if (node->klass->build_signature) +    return (* node->klass->build_signature) (node, str); +  else +    return _dbus_string_append_byte (str, node->klass->typecode); +} + +static dbus_bool_t +node_append_child (TestTypeNode *node, +                   TestTypeNode *child) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + +  _dbus_assert (node->klass->instance_size >= (int) sizeof (TestTypeNodeContainer)); + +  if (!_dbus_list_append (&container->children, child)) +    _dbus_assert_not_reached ("no memory"); /* we never check the return value on node_append_child anyhow - it's run from outside the malloc-failure test code */ + +  return TRUE; +} + +static dbus_bool_t +node_write_multi (TestTypeNode   *node, +                  DataBlock      *block, +                  DBusTypeWriter *writer, +                  int             seed, +                  int             n_copies) +{ +  dbus_bool_t retval; + +  _dbus_assert (node->klass->write_multi != NULL); +  retval = (* node->klass->write_multi) (node, block, writer, seed, n_copies); + +#if 0 +  /* Handy to see where things break, but too expensive to do all the time */ +  data_block_verify (block); +#endif + +  return retval; +} + +static dbus_bool_t +node_read_multi (TestTypeNode   *node, +                 DBusTypeReader *reader, +                 int             seed, +                 int             n_copies) +{ +  _dbus_assert (node->klass->read_multi != NULL); + +  if (!(* node->klass->read_multi) (node, reader, seed, n_copies)) +    return FALSE; + +  return TRUE; +} + +static int n_iterations_completed_total = 0; +static int n_iterations_completed_this_test = 0; +static int n_iterations_expected_this_test = 0; + +typedef struct +{ +  const DBusString   *signature; +  DataBlock          *block; +  int                 type_offset; +  TestTypeNode      **nodes; +  int                 n_nodes; +} NodeIterationData; + +static dbus_bool_t +run_test_copy (NodeIterationData *nid) +{ +  DataBlock *src; +  DataBlock dest; +  dbus_bool_t retval; +  DBusTypeReader reader; +  DBusTypeWriter writer; + +  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + +  src = nid->block; + +  retval = FALSE; + +  if (!data_block_init (&dest, src->byte_order, src->initial_offset)) +    return FALSE; + +  data_block_init_reader_writer (src, &reader, NULL); +  data_block_init_reader_writer (&dest, NULL, &writer); + +  /* DBusTypeWriter assumes it's writing into an existing signature, +   * so doesn't add nul on its own. We have to do that. +   */ +  if (!_dbus_string_insert_byte (&dest.signature, +                                 dest.initial_offset, '\0')) +    goto out; + +  if (!_dbus_type_writer_write_reader (&writer, &reader)) +    goto out; + +  /* Data blocks should now be identical */ +  if (!_dbus_string_equal (&src->signature, &dest.signature)) +    { +      _dbus_verbose ("SOURCE\n"); +      _dbus_verbose_bytes_of_string (&src->signature, 0, +                                     _dbus_string_get_length (&src->signature)); +      _dbus_verbose ("DEST\n"); +      _dbus_verbose_bytes_of_string (&dest.signature, 0, +                                     _dbus_string_get_length (&dest.signature)); +      _dbus_assert_not_reached ("signatures did not match"); +    } + +  if (!_dbus_string_equal (&src->body, &dest.body)) +    { +      _dbus_verbose ("SOURCE\n"); +      _dbus_verbose_bytes_of_string (&src->body, 0, +                                     _dbus_string_get_length (&src->body)); +      _dbus_verbose ("DEST\n"); +      _dbus_verbose_bytes_of_string (&dest.body, 0, +                                     _dbus_string_get_length (&dest.body)); +      _dbus_assert_not_reached ("bodies did not match"); +    } + +  retval = TRUE; + + out: + +  data_block_free (&dest); + +  return retval; +} + +static dbus_bool_t +run_test_values_only_write (NodeIterationData *nid) +{ +  DBusTypeReader reader; +  DBusTypeWriter writer; +  int i; +  dbus_bool_t retval; +  int sig_len; + +  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + +  retval = FALSE; + +  data_block_reset (nid->block); + +  sig_len = _dbus_string_get_length (nid->signature); + +  _dbus_type_writer_init_values_only (&writer, +                                      nid->block->byte_order, +                                      nid->signature, 0, +                                      &nid->block->body, +                                      _dbus_string_get_length (&nid->block->body) - N_FENCE_BYTES); +  _dbus_type_reader_init (&reader, +                          nid->block->byte_order, +                          nid->signature, 0, +                          &nid->block->body, +                          nid->block->initial_offset); + +  i = 0; +  while (i < nid->n_nodes) +    { +      if (!node_write_value (nid->nodes[i], nid->block, &writer, i)) +        goto out; + +      ++i; +    } + +  /* if we wrote any typecodes then this would fail */ +  _dbus_assert (sig_len == _dbus_string_get_length (nid->signature)); + +  /* But be sure we wrote out the values correctly */ +  i = 0; +  while (i < nid->n_nodes) +    { +      if (!node_read_value (nid->nodes[i], &reader, i)) +        goto out; + +      if (i + 1 == nid->n_nodes) +        NEXT_EXPECTING_FALSE (&reader); +      else +        NEXT_EXPECTING_TRUE (&reader); + +      ++i; +    } + +  retval = TRUE; + + out: +  data_block_reset (nid->block); +  return retval; +} + +/* offset the seed for setting, so we set different numbers than + * we originally wrote. Don't offset by a huge number since in + * some cases it's value = possibilities[seed % n_possibilities] + * and we don't want to wrap around. bool_from_seed + * is just seed % 2 even. + */ +#define SET_SEED 1 +static dbus_bool_t +run_test_set_values (NodeIterationData *nid) +{ +  DBusTypeReader reader; +  DBusTypeReader realign_root; +  dbus_bool_t retval; +  int i; + +  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + +  retval = FALSE; + +  data_block_init_reader_writer (nid->block, +                                 &reader, NULL); + +  realign_root = reader; + +  i = 0; +  while (i < nid->n_nodes) +    { +      if (!node_set_value (nid->nodes[i], +                           &reader, &realign_root, +                           i + SET_SEED)) +        goto out; + +      if (i + 1 == nid->n_nodes) +        NEXT_EXPECTING_FALSE (&reader); +      else +        NEXT_EXPECTING_TRUE (&reader); + +      ++i; +    } + +  /* Check that the new values were set */ + +  reader = realign_root; + +  i = 0; +  while (i < nid->n_nodes) +    { +      if (!node_read_value (nid->nodes[i], &reader, +                            i + SET_SEED)) +        goto out; + +      if (i + 1 == nid->n_nodes) +        NEXT_EXPECTING_FALSE (&reader); +      else +        NEXT_EXPECTING_TRUE (&reader); + +      ++i; +    } + +  retval = TRUE; + + out: +  return retval; +} + +static dbus_bool_t +run_test_delete_values (NodeIterationData *nid) +{ +  DBusTypeReader reader; +  dbus_bool_t retval; +  int t; + +  _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME); + +  retval = FALSE; + +  data_block_init_reader_writer (nid->block, +                                 &reader, NULL); + +  while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID) +    { +      /* Right now, deleting only works on array elements.  We delete +       * all array elements, and then verify that there aren't any +       * left. +       */ +      if (t == DBUS_TYPE_ARRAY) +        { +          DBusTypeReader array; +          int n_elements; +          int elem_type; + +          _dbus_type_reader_recurse (&reader, &array); +          n_elements = 0; +          while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) +            { +              n_elements += 1; +              _dbus_type_reader_next (&array); +            } + +          /* reset to start of array */ +          _dbus_type_reader_recurse (&reader, &array); +          _dbus_verbose ("recursing into deletion loop reader.value_pos = %d array.value_pos = %d array.u.start_pos = %d\n", +                         reader.value_pos, array.value_pos, array.u.array.start_pos); +          while ((elem_type = _dbus_type_reader_get_current_type (&array)) != DBUS_TYPE_INVALID) +            { +              /* We don't want to always delete from the same part of the array. */ +              static int cycle = 0; +              int elem; + +              _dbus_assert (n_elements > 0); + +              elem = cycle; +              if (elem == 3 || elem >= n_elements) /* end of array */ +                elem = n_elements - 1; + +              _dbus_verbose ("deleting array element %d of %d type %s cycle %d reader pos %d elem pos %d\n", +                             elem, n_elements, _dbus_type_to_string (elem_type), +                             cycle, reader.value_pos, array.value_pos); +              while (elem > 0) +                { +                  if (!_dbus_type_reader_next (&array)) +                    _dbus_assert_not_reached ("should have had another element\n"); +                  --elem; +                } + +              if (!_dbus_type_reader_delete (&array, &reader)) +                goto out; + +              n_elements -= 1; + +              /* reset */ +              _dbus_type_reader_recurse (&reader, &array); + +              if (cycle > 2) +                cycle = 0; +              else +                cycle += 1; +            } +        } +      _dbus_type_reader_next (&reader); +    } + +  /* Check that there are no array elements left */ +  data_block_init_reader_writer (nid->block, +                                 &reader, NULL); + +  while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID) +    { +      _dbus_type_reader_next (&reader); +    } + +  retval = TRUE; + + out: +  return retval; +} + +static dbus_bool_t +run_test_nodes_iteration (void *data) +{ +  NodeIterationData *nid = data; +  DBusTypeReader reader; +  DBusTypeWriter writer; +  int i; +  dbus_bool_t retval; + +  /* Stuff to do: +   * 1. write the value +   * 2. strcmp-compare with the signature we built +   * 3. read the value +   * 4. type-iterate the signature and the value and see if they are the same type-wise +   */ +  retval = FALSE; + +  data_block_init_reader_writer (nid->block, +                                 &reader, &writer); + +  /* DBusTypeWriter assumes it's writing into an existing signature, +   * so doesn't add nul on its own. We have to do that. +   */ +  if (!_dbus_string_insert_byte (&nid->block->signature, +                                 nid->type_offset, '\0')) +    goto out; + +  i = 0; +  while (i < nid->n_nodes) +    { +      if (!node_write_value (nid->nodes[i], nid->block, &writer, i)) +        goto out; + +      ++i; +    } + +  if (!_dbus_string_equal_substring (nid->signature, 0, _dbus_string_get_length (nid->signature), +                                     &nid->block->signature, nid->type_offset)) +    { +      _dbus_warn ("Expected signature '%s' and got '%s' with initial offset %d\n", +                  _dbus_string_get_const_data (nid->signature), +                  _dbus_string_get_const_data_len (&nid->block->signature, nid->type_offset, 0), +                  nid->type_offset); +      _dbus_assert_not_reached ("wrong signature"); +    } + +  i = 0; +  while (i < nid->n_nodes) +    { +      if (!node_read_value (nid->nodes[i], &reader, i)) +        goto out; + +      if (i + 1 == nid->n_nodes) +        NEXT_EXPECTING_FALSE (&reader); +      else +        NEXT_EXPECTING_TRUE (&reader); + +      ++i; +    } + +  if (n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS) +    { +      /* this set values test uses code from copy and +       * values_only_write so would ideally be last so you get a +       * simpler test case for problems with copying or values_only +       * writing; but it also needs an already-written DataBlock so it +       * has to go first. Comment it out if it breaks, and see if the +       * later tests also break - debug them first if so. +       */ +      if (!run_test_set_values (nid)) +        goto out; + +      if (!run_test_delete_values (nid)) +        goto out; + +      if (!run_test_copy (nid)) +        goto out; + +      if (!run_test_values_only_write (nid)) +        goto out; +    } + +  /* FIXME type-iterate both signature and value and compare the resulting +   * tree to the node tree perhaps +   */ + +  retval = TRUE; + + out: + +  data_block_reset (nid->block); + +  return retval; +} + +static void +run_test_nodes_in_one_configuration (TestTypeNode    **nodes, +                                     int               n_nodes, +                                     const DBusString *signature, +                                     int               byte_order, +                                     int               initial_offset) +{ +  DataBlock block; +  NodeIterationData nid; + +  if (!data_block_init (&block, byte_order, initial_offset)) +    _dbus_assert_not_reached ("no memory"); + +  nid.signature = signature; +  nid.block = █ +  nid.type_offset = initial_offset; +  nid.nodes = nodes; +  nid.n_nodes = n_nodes; + +  if (TEST_OOM_HANDLING && +      n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS) +    { +      _dbus_test_oom_handling ("running test node", +                               run_test_nodes_iteration, +                               &nid); +    } +  else +    { +      if (!run_test_nodes_iteration (&nid)) +        _dbus_assert_not_reached ("no memory"); +    } + +  data_block_free (&block); +} + +static void +run_test_nodes (TestTypeNode **nodes, +                int            n_nodes) +{ +  int i; +  DBusString signature; + +  if (!_dbus_string_init (&signature)) +    _dbus_assert_not_reached ("no memory"); + +  i = 0; +  while (i < n_nodes) +    { +      if (! node_build_signature (nodes[i], &signature)) +        _dbus_assert_not_reached ("no memory"); + +      ++i; +    } + +  _dbus_verbose (">>> test nodes with signature '%s'\n", +                 _dbus_string_get_const_data (&signature)); + +  i = 0; +  while (i <= MAX_INITIAL_OFFSET) +    { +      run_test_nodes_in_one_configuration (nodes, n_nodes, &signature, +                                           DBUS_LITTLE_ENDIAN, i); +      run_test_nodes_in_one_configuration (nodes, n_nodes, &signature, +                                           DBUS_BIG_ENDIAN, i); + +      ++i; +    } + +  n_iterations_completed_this_test += 1; +  n_iterations_completed_total += 1; + +  if (n_iterations_completed_this_test == n_iterations_expected_this_test) +    { +      fprintf (stderr, " 100%% %d this test (%d cumulative)\n", +               n_iterations_completed_this_test, +               n_iterations_completed_total); +    } +  /* this happens to turn out well with mod == 1 */ +  else if ((n_iterations_completed_this_test % +            (int)(n_iterations_expected_this_test / 10.0)) == 1) +    { +      fprintf (stderr, " %d%% ", (int) (n_iterations_completed_this_test / (double) n_iterations_expected_this_test * 100)); +    } + +  _dbus_string_free (&signature); +} + +#define N_VALUES (N_BASICS * N_CONTAINERS + N_BASICS) + +static TestTypeNode* +value_generator (int *ip) +{ +  int i = *ip; +  const TestTypeNodeClass *child_klass; +  const TestTypeNodeClass *container_klass; +  TestTypeNode *child; +  TestTypeNode *node; + +  _dbus_assert (i <= N_VALUES); + +  if (i == N_VALUES) +    { +      return NULL; +    } +  else if (i < N_BASICS) +    { +      node = node_new (basic_nodes[i]); +    } +  else +    { +      /* imagine an array: +       * container 0 of basic 0 +       * container 0 of basic 1 +       * container 0 of basic 2 +       * container 1 of basic 0 +       * container 1 of basic 1 +       * container 1 of basic 2 +       */ +      i -= N_BASICS; + +      container_klass = container_nodes[i / N_BASICS]; +      child_klass = basic_nodes[i % N_BASICS]; + +      node = node_new (container_klass); +      child = node_new (child_klass); + +      node_append_child (node, child); +    } + +  *ip += 1; /* increment the generator */ + +  return node; +} + +static void +make_and_run_values_inside_container (const TestTypeNodeClass *container_klass, +                                      int                      n_nested) +{ +  TestTypeNode *root; +  TestTypeNode *container; +  TestTypeNode *child; +  int i; + +  root = node_new (container_klass); +  container = root; +  for (i = 1; i < n_nested; i++) +    { +      child = node_new (container_klass); +      node_append_child (container, child); +      container = child; +    } + +  /* container should now be the most-nested container */ + +  i = 0; +  while ((child = value_generator (&i))) +    { +      node_append_child (container, child); + +      run_test_nodes (&root, 1); + +      _dbus_list_clear (&((TestTypeNodeContainer*)container)->children); +      node_destroy (child); +    } + +  node_destroy (root); +} + +static void +start_next_test (const char *format, +                 int         expected) +{ +  n_iterations_completed_this_test = 0; +  n_iterations_expected_this_test = expected; + +  fprintf (stderr, ">>> >>> "); +  fprintf (stderr, format, +           n_iterations_expected_this_test); +} + +static void +make_and_run_test_nodes (void) +{ +  int i, j, k, m; + +  /* We try to do this in order of "complicatedness" so that test +   * failures tend to show up in the simplest test case that +   * demonstrates the failure.  There are also some tests that run +   * more than once for this reason, first while going through simple +   * cases, second while going through a broader range of complex +   * cases. +   */ +  /* Each basic node. The basic nodes should include: +   * +   * - each fixed-size type (in such a way that it has different values each time, +   *                         so we can tell if we mix two of them up) +   * - strings of various lengths +   * - object path +   * - signature +   */ +  /* Each container node. The container nodes should include: +   * +   *  struct with 1 and 2 copies of the contained item +   *  array with 0, 1, 2 copies of the contained item +   *  variant +   */ +  /*  Let a "value" be a basic node, or a container containing a single basic node. +   *  Let n_values be the number of such values i.e. (n_container * n_basic + n_basic) +   *  When iterating through all values to make combinations, do the basic types +   *  first and the containers second. +   */ +  /* Each item is shown with its number of iterations to complete so +   * we can keep a handle on this unit test +   */ + +  /* FIXME test just an empty body, no types at all */ + +  start_next_test ("Each value by itself %d iterations\n", N_VALUES); +  { +    TestTypeNode *node; +    i = 0; +    while ((node = value_generator (&i))) +      { +        run_test_nodes (&node, 1); + +        node_destroy (node); +      } +  } + +  start_next_test ("Each value by itself with arrays as blocks %d iterations\n", N_VALUES); +  arrays_write_fixed_in_blocks = TRUE; +  { +    TestTypeNode *node; +    i = 0; +    while ((node = value_generator (&i))) +      { +        run_test_nodes (&node, 1); + +        node_destroy (node); +      } +  } +  arrays_write_fixed_in_blocks = FALSE; + +  start_next_test ("All values in one big toplevel %d iteration\n", 1); +  { +    TestTypeNode *nodes[N_VALUES]; + +    i = 0; +    while ((nodes[i] = value_generator (&i))) +      ; + +    run_test_nodes (nodes, N_VALUES); + +    for (i = 0; i < N_VALUES; i++) +      node_destroy (nodes[i]); +  } + +  start_next_test ("Each value,value pair combination as toplevel, in both orders %d iterations\n", +                   N_VALUES * N_VALUES); +  { +    TestTypeNode *nodes[2]; + +    i = 0; +    while ((nodes[0] = value_generator (&i))) +      { +        j = 0; +        while ((nodes[1] = value_generator (&j))) +          { +            run_test_nodes (nodes, 2); + +            node_destroy (nodes[1]); +          } + +        node_destroy (nodes[0]); +      } +  } + +  start_next_test ("Each container containing each value %d iterations\n", +                   N_CONTAINERS * N_VALUES); +  for (i = 0; i < N_CONTAINERS; i++) +    { +      const TestTypeNodeClass *container_klass = container_nodes[i]; + +      make_and_run_values_inside_container (container_klass, 1); +    } + +  start_next_test ("Each container containing each value with arrays as blocks %d iterations\n", +                   N_CONTAINERS * N_VALUES); +  arrays_write_fixed_in_blocks = TRUE; +  for (i = 0; i < N_CONTAINERS; i++) +    { +      const TestTypeNodeClass *container_klass = container_nodes[i]; + +      make_and_run_values_inside_container (container_klass, 1); +    } +  arrays_write_fixed_in_blocks = FALSE; + +  start_next_test ("Each container of same container of each value %d iterations\n", +                   N_CONTAINERS * N_VALUES); +  for (i = 0; i < N_CONTAINERS; i++) +    { +      const TestTypeNodeClass *container_klass = container_nodes[i]; + +      make_and_run_values_inside_container (container_klass, 2); +    } + +  start_next_test ("Each container of same container of same container of each value %d iterations\n", +                   N_CONTAINERS * N_VALUES); +  for (i = 0; i < N_CONTAINERS; i++) +    { +      const TestTypeNodeClass *container_klass = container_nodes[i]; + +      make_and_run_values_inside_container (container_klass, 3); +    } + +  start_next_test ("Each value,value pair inside a struct %d iterations\n", +                   N_VALUES * N_VALUES); +  { +    TestTypeNode *val1, *val2; +    TestTypeNode *node; + +    node = node_new (&struct_1_class); + +    i = 0; +    while ((val1 = value_generator (&i))) +      { +        j = 0; +        while ((val2 = value_generator (&j))) +          { +            TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + +            node_append_child (node, val1); +            node_append_child (node, val2); + +            run_test_nodes (&node, 1); + +            _dbus_list_clear (&container->children); +            node_destroy (val2); +          } +        node_destroy (val1); +      } +    node_destroy (node); +  } + +  start_next_test ("All values in one big struct %d iteration\n", +                   1); +  { +    TestTypeNode *node; +    TestTypeNode *child; + +    node = node_new (&struct_1_class); + +    i = 0; +    while ((child = value_generator (&i))) +      node_append_child (node, child); + +    run_test_nodes (&node, 1); + +    node_destroy (node); +  } + +  start_next_test ("Each value in a large array %d iterations\n", +                   N_VALUES); +  { +    TestTypeNode *val; +    TestTypeNode *node; + +    node = node_new (&array_9_class); + +    i = 0; +    while ((val = value_generator (&i))) +      { +        TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + +        node_append_child (node, val); + +        run_test_nodes (&node, 1); + +        _dbus_list_clear (&container->children); +        node_destroy (val); +      } + +    node_destroy (node); +  } + +  start_next_test ("Each container of each container of each value %d iterations\n", +                   N_CONTAINERS * N_CONTAINERS * N_VALUES); +  for (i = 0; i < N_CONTAINERS; i++) +    { +      const TestTypeNodeClass *outer_container_klass = container_nodes[i]; +      TestTypeNode *outer_container = node_new (outer_container_klass); + +      for (j = 0; j < N_CONTAINERS; j++) +        { +          TestTypeNode *child; +          const TestTypeNodeClass *inner_container_klass = container_nodes[j]; +          TestTypeNode *inner_container = node_new (inner_container_klass); + +          node_append_child (outer_container, inner_container); + +          m = 0; +          while ((child = value_generator (&m))) +            { +              node_append_child (inner_container, child); + +              run_test_nodes (&outer_container, 1); + +              _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children); +              node_destroy (child); +            } +          _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children); +          node_destroy (inner_container); +        } +      node_destroy (outer_container); +    } + +  start_next_test ("Each container of each container of each container of each value %d iterations\n", +                   N_CONTAINERS * N_CONTAINERS * N_CONTAINERS * N_VALUES); +  for (i = 0; i < N_CONTAINERS; i++) +    { +      const TestTypeNodeClass *outer_container_klass = container_nodes[i]; +      TestTypeNode *outer_container = node_new (outer_container_klass); + +      for (j = 0; j < N_CONTAINERS; j++) +        { +          const TestTypeNodeClass *inner_container_klass = container_nodes[j]; +          TestTypeNode *inner_container = node_new (inner_container_klass); + +          node_append_child (outer_container, inner_container); + +          for (k = 0; k < N_CONTAINERS; k++) +            { +              TestTypeNode *child; +              const TestTypeNodeClass *center_container_klass = container_nodes[k]; +              TestTypeNode *center_container = node_new (center_container_klass); + +              node_append_child (inner_container, center_container); + +              m = 0; +              while ((child = value_generator (&m))) +                { +                  node_append_child (center_container, child); + +                  run_test_nodes (&outer_container, 1); + +                  _dbus_list_clear (&((TestTypeNodeContainer*)center_container)->children); +                  node_destroy (child); +                } +              _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children); +              node_destroy (center_container); +            } +          _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children); +          node_destroy (inner_container); +        } +      node_destroy (outer_container); +    } + +#if 0 +  /* This one takes a really long time, so comment it out for now */ +  start_next_test ("Each value,value,value triplet combination as toplevel, in all orders %d iterations\n", +                   N_VALUES * N_VALUES * N_VALUES); +  { +    TestTypeNode *nodes[3]; + +    i = 0; +    while ((nodes[0] = value_generator (&i))) +      { +        j = 0; +        while ((nodes[1] = value_generator (&j))) +          { +            k = 0; +            while ((nodes[2] = value_generator (&k))) +              { +                run_test_nodes (nodes, 3); + +                node_destroy (nodes[2]); +              } +            node_destroy (nodes[1]); +          } +        node_destroy (nodes[0]); +      } +  } +#endif /* #if 0 expensive test */ + +  fprintf (stderr, "%d total iterations of recursive marshaling tests\n", +           n_iterations_completed_total); +  fprintf (stderr, "each iteration ran at initial offsets 0 through %d in both big and little endian\n", +           MAX_INITIAL_OFFSET); +  fprintf (stderr, "out of memory handling %s tested\n", +           TEST_OOM_HANDLING ? "was" : "was not"); +} + +dbus_bool_t +_dbus_marshal_recursive_test (void) +{ +  make_and_run_test_nodes (); + +  return TRUE; +} + +/* + * + * + *         Implementations of each type node class + * + * + * + */ +#define MAX_MULTI_COUNT 5 + + +#define SAMPLE_INT32           12345678 +#define SAMPLE_INT32_ALTERNATE 53781429 +static dbus_int32_t +int32_from_seed (int seed) +{ +  /* Generate an integer value that's predictable from seed.  We could +   * just use seed itself, but that would only ever touch one byte of +   * the int so would miss some kinds of bug. +   */ +  dbus_int32_t v; + +  v = 42; /* just to quiet compiler afaik */ +  switch (seed % 5) +    { +    case 0: +      v = SAMPLE_INT32; +      break; +    case 1: +      v = SAMPLE_INT32_ALTERNATE; +      break; +    case 2: +      v = -1; +      break; +    case 3: +      v = _DBUS_INT_MAX; +      break; +    case 4: +      v = 1; +      break; +    } + +  if (seed > 1) +    v *= seed; /* wraps around eventually, which is fine */ + +  return v; +} + +static dbus_bool_t +int32_write_value (TestTypeNode   *node, +                   DataBlock      *block, +                   DBusTypeWriter *writer, +                   int             seed) +{ +  /* also used for uint32 */ +  dbus_int32_t v; + +  v = int32_from_seed (seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v); +} + +static dbus_bool_t +int32_read_value (TestTypeNode   *node, +                  DBusTypeReader *reader, +                  int             seed) +{ +  /* also used for uint32 */ +  dbus_int32_t v; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (dbus_int32_t*) &v); + +  _dbus_assert (v == int32_from_seed (seed)); + +  return TRUE; +} + +static dbus_bool_t +int32_set_value (TestTypeNode   *node, +                 DBusTypeReader *reader, +                 DBusTypeReader *realign_root, +                 int             seed) +{ +  /* also used for uint32 */ +  dbus_int32_t v; + +  v = int32_from_seed (seed); + +  return _dbus_type_reader_set_basic (reader, +                                      &v, +                                      realign_root); +} + +static dbus_bool_t +int32_write_multi (TestTypeNode   *node, +                   DataBlock      *block, +                   DBusTypeWriter *writer, +                   int             seed, +                   int             count) +{ +  /* also used for uint32 */ +  dbus_int32_t values[MAX_MULTI_COUNT]; +  dbus_int32_t *v_ARRAY_INT32 = values; +  int i; + +  for (i = 0; i < count; ++i) +    values[i] = int32_from_seed (seed + i); + +  return _dbus_type_writer_write_fixed_multi (writer, +                                              node->klass->typecode, +                                              &v_ARRAY_INT32, count); +} + +static dbus_bool_t +int32_read_multi (TestTypeNode   *node, +                  DBusTypeReader *reader, +                  int             seed, +                  int             count) +{ +  /* also used for uint32 */ +  dbus_int32_t *values; +  int n_elements; +  int i; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_fixed_multi (reader, +                                      &values, +                                      &n_elements); + +  if (n_elements != count) +    _dbus_warn ("got %d elements expected %d\n", n_elements, count); +  _dbus_assert (n_elements == count); + +  for (i = 0; i < count; i++) +    _dbus_assert (((int)_dbus_unpack_uint32 (reader->byte_order, +                                             (const unsigned char*)values + (i * 4))) == +                  int32_from_seed (seed + i)); + +  return TRUE; +} + +#ifdef DBUS_HAVE_INT64 +static dbus_int64_t +int64_from_seed (int seed) +{ +  dbus_int32_t v32; +  dbus_int64_t v; + +  v32 = int32_from_seed (seed); + +  v = - (dbus_int32_t) ~ v32; +  v |= (((dbus_int64_t)v32) << 32); + +  return v; +} +#endif + +static dbus_bool_t +int64_write_value (TestTypeNode   *node, +                   DataBlock      *block, +                   DBusTypeWriter *writer, +                   int             seed) +{ +#ifdef DBUS_HAVE_INT64 +  /* also used for uint64 */ +  dbus_int64_t v; + +  v = int64_from_seed (seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v); +#else +  return TRUE; +#endif +} + +static dbus_bool_t +int64_read_value (TestTypeNode   *node, +                  DBusTypeReader *reader, +                  int             seed) +{ +#ifdef DBUS_HAVE_INT64 +  /* also used for uint64 */ +  dbus_int64_t v; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (dbus_int64_t*) &v); + +  _dbus_assert (v == int64_from_seed (seed)); + +  return TRUE; +#else +  return TRUE; +#endif +} + +static dbus_bool_t +int64_set_value (TestTypeNode   *node, +                 DBusTypeReader *reader, +                 DBusTypeReader *realign_root, +                 int             seed) +{ +#ifdef DBUS_HAVE_INT64 +  /* also used for uint64 */ +  dbus_int64_t v; + +  v = int64_from_seed (seed); + +  return _dbus_type_reader_set_basic (reader, +                                      &v, +                                      realign_root); +#else +  return TRUE; +#endif +} + +#define MAX_SAMPLE_STRING_LEN 10 +static void +string_from_seed (char *buf, +                  int   len, +                  int   seed) +{ +  int i; +  unsigned char v; + +  _dbus_assert (len < MAX_SAMPLE_STRING_LEN); + +  /* vary the length slightly, though we also have multiple string +   * value types for this, varying it here tests the set_value code +   */ +  switch (seed % 3) +    { +    case 1: +      len += 2; +      break; +    case 2: +      len -= 2; +      break; +    } +  if (len < 0) +    len = 0; + +  v = (unsigned char) ('A' + seed); + +  i = 0; +  while (i < len) +    { +      if (v < 'A' || v > 'z') +        v = 'A'; + +      buf[i] = v; + +      v += 1; +      ++i; +    } + +  buf[i] = '\0'; +} + +static dbus_bool_t +string_write_value (TestTypeNode   *node, +                    DataBlock      *block, +                    DBusTypeWriter *writer, +                    int             seed) +{ +  char buf[MAX_SAMPLE_STRING_LEN]; +  const char *v_string = buf; + +  string_from_seed (buf, node->klass->subclass_detail, +                    seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v_string); +} + +static dbus_bool_t +string_read_value (TestTypeNode   *node, +                   DBusTypeReader *reader, +                   int             seed) +{ +  const char *v; +  char buf[MAX_SAMPLE_STRING_LEN]; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (const char **) &v); + +  string_from_seed (buf, node->klass->subclass_detail, +                    seed); + +  if (strcmp (buf, v) != 0) +    { +      _dbus_warn ("read string '%s' expected '%s'\n", +                  v, buf); +      _dbus_assert_not_reached ("test failed"); +    } + +  return TRUE; +} + +static dbus_bool_t +string_set_value (TestTypeNode   *node, +                  DBusTypeReader *reader, +                  DBusTypeReader *realign_root, +                  int             seed) +{ +  char buf[MAX_SAMPLE_STRING_LEN]; +  const char *v_string = buf; + +  string_from_seed (buf, node->klass->subclass_detail, +                    seed); + +#if RECURSIVE_MARSHAL_WRITE_TRACE + { +   const char *old; +   _dbus_type_reader_read_basic (reader, &old); +   _dbus_verbose ("SETTING new string '%s' len %d in place of '%s' len %d\n", +                  v_string, strlen (v_string), old, strlen (old)); + } +#endif + +  return _dbus_type_reader_set_basic (reader, +                                      &v_string, +                                      realign_root); +} + +#define BOOL_FROM_SEED(seed) (seed % 2) + +static dbus_bool_t +bool_write_value (TestTypeNode   *node, +                  DataBlock      *block, +                  DBusTypeWriter *writer, +                  int             seed) +{ +  unsigned char v; + +  v = BOOL_FROM_SEED (seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v); +} + +static dbus_bool_t +bool_read_value (TestTypeNode   *node, +                 DBusTypeReader *reader, +                 int             seed) +{ +  unsigned char v; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (unsigned char*) &v); + +  _dbus_assert (v == BOOL_FROM_SEED (seed)); + +  return TRUE; +} + +static dbus_bool_t +bool_set_value (TestTypeNode   *node, +                DBusTypeReader *reader, +                DBusTypeReader *realign_root, +                int             seed) +{ +  unsigned char v; + +  v = BOOL_FROM_SEED (seed); + +  return _dbus_type_reader_set_basic (reader, +                                      &v, +                                      realign_root); +} + +#define BYTE_FROM_SEED(seed) ((unsigned char) int32_from_seed (seed)) + +static dbus_bool_t +byte_write_value (TestTypeNode   *node, +                  DataBlock      *block, +                  DBusTypeWriter *writer, +                  int             seed) +{ +  unsigned char v; + +  v = BYTE_FROM_SEED (seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v); +} + +static dbus_bool_t +byte_read_value (TestTypeNode   *node, +                 DBusTypeReader *reader, +                 int             seed) +{ +  unsigned char v; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (unsigned char*) &v); + +  _dbus_assert (v == BYTE_FROM_SEED (seed)); + +  return TRUE; +} + + +static dbus_bool_t +byte_set_value (TestTypeNode   *node, +                DBusTypeReader *reader, +                DBusTypeReader *realign_root, +                int             seed) +{ +  unsigned char v; + +  v = BYTE_FROM_SEED (seed); + +  return _dbus_type_reader_set_basic (reader, +                                      &v, +                                      realign_root); +} + +static double +double_from_seed (int seed) +{ +  return SAMPLE_INT32 * (double) seed + 0.3; +} + +static dbus_bool_t +double_write_value (TestTypeNode   *node, +                    DataBlock      *block, +                    DBusTypeWriter *writer, +                    int             seed) +{ +  double v; + +  v = double_from_seed (seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v); +} + +static dbus_bool_t +double_read_value (TestTypeNode   *node, +                   DBusTypeReader *reader, +                   int             seed) +{ +  double v; +  double expected; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (double*) &v); + +  expected = double_from_seed (seed); + +  if (!_DBUS_DOUBLES_BITWISE_EQUAL (v, expected)) +    { +#ifdef DBUS_HAVE_INT64 +      _dbus_warn ("Expected double %g got %g\n bits = 0x%llx vs.\n bits = 0x%llx)\n", +                  expected, v, +                  *(dbus_uint64_t*)(char*)&expected, +                  *(dbus_uint64_t*)(char*)&v); +#endif +      _dbus_assert_not_reached ("test failed"); +    } + +  return TRUE; +} + +static dbus_bool_t +double_set_value (TestTypeNode   *node, +                DBusTypeReader *reader, +                DBusTypeReader *realign_root, +                int             seed) +{ +  double v; + +  v = double_from_seed (seed); + +  return _dbus_type_reader_set_basic (reader, +                                      &v, +                                      realign_root); +} + +#define MAX_SAMPLE_OBJECT_PATH_LEN 10 +static void +object_path_from_seed (char *buf, +                       int   seed) +{ +  int i; +  unsigned char v; +  int len; + +  len = seed % 9; +  _dbus_assert (len < MAX_SAMPLE_OBJECT_PATH_LEN); + +  v = (unsigned char) ('A' + seed); + +  i = 0; +  while (i + 1 < len) +    { +      if (v < 'A' || v > 'z') +        v = 'A'; + +      buf[i] = '/'; +      ++i; +      buf[i] = v; +      ++i; + +      v += 1; +    } + +  buf[i] = '\0'; +} + +static dbus_bool_t +object_path_write_value (TestTypeNode   *node, +                         DataBlock      *block, +                         DBusTypeWriter *writer, +                         int             seed) +{ +  char buf[MAX_SAMPLE_OBJECT_PATH_LEN]; +  const char *v_string = buf; + +  object_path_from_seed (buf, seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v_string); +} + +static dbus_bool_t +object_path_read_value (TestTypeNode   *node, +                        DBusTypeReader *reader, +                        int             seed) +{ +  const char *v; +  char buf[MAX_SAMPLE_OBJECT_PATH_LEN]; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (const char **) &v); + +  object_path_from_seed (buf, seed); + +  if (strcmp (buf, v) != 0) +    { +      _dbus_warn ("read object path '%s' expected '%s'\n", +                  v, buf); +      _dbus_assert_not_reached ("test failed"); +    } + +  return TRUE; +} + +static dbus_bool_t +object_path_set_value (TestTypeNode   *node, +                       DBusTypeReader *reader, +                       DBusTypeReader *realign_root, +                       int             seed) +{ +  char buf[MAX_SAMPLE_OBJECT_PATH_LEN]; +  const char *v_string = buf; + +  object_path_from_seed (buf, seed); + +  return _dbus_type_reader_set_basic (reader, +                                      &v_string, +                                      realign_root); +} + +#define MAX_SAMPLE_SIGNATURE_LEN 10 +static void +signature_from_seed (char *buf, +                     int   seed) +{ +  int i; +  const char *s; +  /* try to avoid ascending, descending, or alternating length to help find bugs */ +  const char *sample_signatures[] = { +    "asax" +    "", +    "asau(xxxx)", +    "x", +    "ai", +    "a(ii)" +  }; + +  s = sample_signatures[seed % _DBUS_N_ELEMENTS(sample_signatures)]; + +  for (i = 0; s[i]; i++) +    { +      buf[i] = s[i]; +    } +  buf[i] = '\0'; +} + +static dbus_bool_t +signature_write_value (TestTypeNode   *node, +                       DataBlock      *block, +                       DBusTypeWriter *writer, +                       int             seed) +{ +  char buf[MAX_SAMPLE_SIGNATURE_LEN]; +  const char *v_string = buf; + +  signature_from_seed (buf, seed); + +  return _dbus_type_writer_write_basic (writer, +                                        node->klass->typecode, +                                        &v_string); +} + +static dbus_bool_t +signature_read_value (TestTypeNode   *node, +                      DBusTypeReader *reader, +                      int             seed) +{ +  const char *v; +  char buf[MAX_SAMPLE_SIGNATURE_LEN]; + +  check_expected_type (reader, node->klass->typecode); + +  _dbus_type_reader_read_basic (reader, +                                (const char **) &v); + +  signature_from_seed (buf, seed); + +  if (strcmp (buf, v) != 0) +    { +      _dbus_warn ("read signature value '%s' expected '%s'\n", +                  v, buf); +      _dbus_assert_not_reached ("test failed"); +    } + +  return TRUE; +} + + +static dbus_bool_t +signature_set_value (TestTypeNode   *node, +                     DBusTypeReader *reader, +                     DBusTypeReader *realign_root, +                     int             seed) +{ +  char buf[MAX_SAMPLE_SIGNATURE_LEN]; +  const char *v_string = buf; + +  signature_from_seed (buf, seed); + +  return _dbus_type_reader_set_basic (reader, +                                      &v_string, +                                      realign_root); +} + +static dbus_bool_t +struct_write_value (TestTypeNode   *node, +                    DataBlock      *block, +                    DBusTypeWriter *writer, +                    int             seed) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  DataBlockState saved; +  DBusTypeWriter sub; +  int i; +  int n_copies; + +  n_copies = node->klass->subclass_detail; + +  _dbus_assert (container->children != NULL); + +  data_block_save (block, &saved); + +  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT, +                                  NULL, 0, +                                  &sub)) +    return FALSE; + +  i = 0; +  while (i < n_copies) +    { +      DBusList *link; + +      link = _dbus_list_get_first_link (&container->children); +      while (link != NULL) +        { +          TestTypeNode *child = link->data; +          DBusList *next = _dbus_list_get_next_link (&container->children, link); + +          if (!node_write_value (child, block, &sub, seed + i)) +            { +              data_block_restore (block, &saved); +              return FALSE; +            } + +          link = next; +        } + +      ++i; +    } + +  if (!_dbus_type_writer_unrecurse (writer, &sub)) +    { +      data_block_restore (block, &saved); +      return FALSE; +    } + +  return TRUE; +} + +static dbus_bool_t +struct_read_or_set_value (TestTypeNode   *node, +                          DBusTypeReader *reader, +                          DBusTypeReader *realign_root, +                          int             seed) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  DBusTypeReader sub; +  int i; +  int n_copies; + +  n_copies = node->klass->subclass_detail; + +  check_expected_type (reader, DBUS_TYPE_STRUCT); + +  _dbus_type_reader_recurse (reader, &sub); + +  i = 0; +  while (i < n_copies) +    { +      DBusList *link; + +      link = _dbus_list_get_first_link (&container->children); +      while (link != NULL) +        { +          TestTypeNode *child = link->data; +          DBusList *next = _dbus_list_get_next_link (&container->children, link); + +          if (realign_root == NULL) +            { +              if (!node_read_value (child, &sub, seed + i)) +                return FALSE; +            } +          else +            { +              if (!node_set_value (child, &sub, realign_root, seed + i)) +                return FALSE; +            } + +          if (i == (n_copies - 1) && next == NULL) +            NEXT_EXPECTING_FALSE (&sub); +          else +            NEXT_EXPECTING_TRUE (&sub); + +          link = next; +        } + +      ++i; +    } + +  return TRUE; +} + +static dbus_bool_t +struct_read_value (TestTypeNode   *node, +                   DBusTypeReader *reader, +                   int             seed) +{ +  return struct_read_or_set_value (node, reader, NULL, seed); +} + +static dbus_bool_t +struct_set_value (TestTypeNode   *node, +                  DBusTypeReader *reader, +                  DBusTypeReader *realign_root, +                  int             seed) +{ +  return struct_read_or_set_value (node, reader, realign_root, seed); +} + +static dbus_bool_t +struct_build_signature (TestTypeNode   *node, +                        DBusString     *str) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  int i; +  int orig_len; +  int n_copies; + +  n_copies = node->klass->subclass_detail; + +  orig_len = _dbus_string_get_length (str); + +  if (!_dbus_string_append_byte (str, DBUS_STRUCT_BEGIN_CHAR)) +    goto oom; + +  i = 0; +  while (i < n_copies) +    { +      DBusList *link; + +      link = _dbus_list_get_first_link (&container->children); +      while (link != NULL) +        { +          TestTypeNode *child = link->data; +          DBusList *next = _dbus_list_get_next_link (&container->children, link); + +          if (!node_build_signature (child, str)) +            goto oom; + +          link = next; +        } + +      ++i; +    } + +  if (!_dbus_string_append_byte (str, DBUS_STRUCT_END_CHAR)) +    goto oom; + +  return TRUE; + + oom: +  _dbus_string_set_length (str, orig_len); +  return FALSE; +} + +static dbus_bool_t +array_write_value (TestTypeNode   *node, +                   DataBlock      *block, +                   DBusTypeWriter *writer, +                   int             seed) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  DataBlockState saved; +  DBusTypeWriter sub; +  DBusString element_signature; +  int i; +  int n_copies; +  int element_type; +  TestTypeNode *child; + +  n_copies = node->klass->subclass_detail; + +  _dbus_assert (container->children != NULL); + +  data_block_save (block, &saved); + +  if (!_dbus_string_init (&element_signature)) +    return FALSE; + +  child = _dbus_list_get_first (&container->children); + +  if (!node_build_signature (child, +                             &element_signature)) +    goto oom; + +  element_type = first_type_in_signature (&element_signature, 0); + +  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_ARRAY, +                                  &element_signature, 0, +                                  &sub)) +    goto oom; + +  if (arrays_write_fixed_in_blocks && +      _dbus_type_is_fixed (element_type) && +      child->klass->write_multi) +    { +      if (!node_write_multi (child, block, &sub, seed, n_copies)) +        goto oom; +    } +  else +    { +      i = 0; +      while (i < n_copies) +        { +          DBusList *link; + +          link = _dbus_list_get_first_link (&container->children); +          while (link != NULL) +            { +              TestTypeNode *child = link->data; +              DBusList *next = _dbus_list_get_next_link (&container->children, link); + +              if (!node_write_value (child, block, &sub, seed + i)) +                goto oom; + +              link = next; +            } + +          ++i; +        } +    } + +  if (!_dbus_type_writer_unrecurse (writer, &sub)) +    goto oom; + +  _dbus_string_free (&element_signature); +  return TRUE; + + oom: +  data_block_restore (block, &saved); +  _dbus_string_free (&element_signature); +  return FALSE; +} + +static dbus_bool_t +array_read_or_set_value (TestTypeNode   *node, +                         DBusTypeReader *reader, +                         DBusTypeReader *realign_root, +                         int             seed) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  DBusTypeReader sub; +  int i; +  int n_copies; +  TestTypeNode *child; + +  n_copies = node->klass->subclass_detail; + +  check_expected_type (reader, DBUS_TYPE_ARRAY); + +  child = _dbus_list_get_first (&container->children); + +  if (n_copies > 0) +    { +      _dbus_type_reader_recurse (reader, &sub); + +      if (realign_root == NULL && arrays_write_fixed_in_blocks && +          _dbus_type_is_fixed (_dbus_type_reader_get_element_type (reader)) && +          child->klass->read_multi) +        { +          if (!node_read_multi (child, &sub, seed, n_copies)) +            return FALSE; +        } +      else +        { +          i = 0; +          while (i < n_copies) +            { +              DBusList *link; + +              link = _dbus_list_get_first_link (&container->children); +              while (link != NULL) +                { +                  TestTypeNode *child = link->data; +                  DBusList *next = _dbus_list_get_next_link (&container->children, link); + +                  _dbus_assert (child->klass->typecode == +                                _dbus_type_reader_get_element_type (reader)); + +                  if (realign_root == NULL) +                    { +                      if (!node_read_value (child, &sub, seed + i)) +                        return FALSE; +                    } +                  else +                    { +                      if (!node_set_value (child, &sub, realign_root, seed + i)) +                        return FALSE; +                    } + +                  if (i == (n_copies - 1) && next == NULL) +                    NEXT_EXPECTING_FALSE (&sub); +                  else +                    NEXT_EXPECTING_TRUE (&sub); + +                  link = next; +                } + +              ++i; +            } +        } +    } + +  return TRUE; +} + +static dbus_bool_t +array_read_value (TestTypeNode   *node, +                  DBusTypeReader *reader, +                  int             seed) +{ +  return array_read_or_set_value (node, reader, NULL, seed); +} + +static dbus_bool_t +array_set_value (TestTypeNode   *node, +                 DBusTypeReader *reader, +                 DBusTypeReader *realign_root, +                 int             seed) +{ +  return array_read_or_set_value (node, reader, realign_root, seed); +} + +static dbus_bool_t +array_build_signature (TestTypeNode   *node, +                       DBusString     *str) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  int orig_len; + +  orig_len = _dbus_string_get_length (str); + +  if (!_dbus_string_append_byte (str, DBUS_TYPE_ARRAY)) +    goto oom; + +  if (!node_build_signature (_dbus_list_get_first (&container->children), +                             str)) +    goto oom; + +  return TRUE; + + oom: +  _dbus_string_set_length (str, orig_len); +  return FALSE; +} + + /* 10 is random just to add another seed that we use in the suite */ +#define VARIANT_SEED 10 + +static dbus_bool_t +variant_write_value (TestTypeNode   *node, +                     DataBlock      *block, +                     DBusTypeWriter *writer, +                     int             seed) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  DataBlockState saved; +  DBusTypeWriter sub; +  DBusString content_signature; +  TestTypeNode *child; + +  _dbus_assert (container->children != NULL); +  _dbus_assert (_dbus_list_length_is_one (&container->children)); + +  child = _dbus_list_get_first (&container->children); + +  data_block_save (block, &saved); + +  if (!_dbus_string_init (&content_signature)) +    return FALSE; + +  if (!node_build_signature (child, +                             &content_signature)) +    goto oom; + +  if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_VARIANT, +                                  &content_signature, 0, +                                  &sub)) +    goto oom; + +  if (!node_write_value (child, block, &sub, seed + VARIANT_SEED)) +    goto oom; + +  if (!_dbus_type_writer_unrecurse (writer, &sub)) +    goto oom; + +  _dbus_string_free (&content_signature); +  return TRUE; + + oom: +  data_block_restore (block, &saved); +  _dbus_string_free (&content_signature); +  return FALSE; +} + +static dbus_bool_t +variant_read_or_set_value (TestTypeNode   *node, +                           DBusTypeReader *reader, +                           DBusTypeReader *realign_root, +                           int             seed) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  DBusTypeReader sub; +  TestTypeNode *child; + +  _dbus_assert (container->children != NULL); +  _dbus_assert (_dbus_list_length_is_one (&container->children)); + +  child = _dbus_list_get_first (&container->children); + +  check_expected_type (reader, DBUS_TYPE_VARIANT); + +  _dbus_type_reader_recurse (reader, &sub); + +  if (realign_root == NULL) +    { +      if (!node_read_value (child, &sub, seed + VARIANT_SEED)) +        return FALSE; +    } +  else +    { +      if (!node_set_value (child, &sub, realign_root, seed + VARIANT_SEED)) +        return FALSE; +    } + +  NEXT_EXPECTING_FALSE (&sub); + +  return TRUE; +} + +static dbus_bool_t +variant_read_value (TestTypeNode   *node, +                    DBusTypeReader *reader, +                    int             seed) +{ +  return variant_read_or_set_value (node, reader, NULL, seed); +} + +static dbus_bool_t +variant_set_value (TestTypeNode   *node, +                   DBusTypeReader *reader, +                   DBusTypeReader *realign_root, +                   int             seed) +{ +  return variant_read_or_set_value (node, reader, realign_root, seed); +} + +static void +container_destroy (TestTypeNode *node) +{ +  TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; +  DBusList *link; + +  link = _dbus_list_get_first_link (&container->children); +  while (link != NULL) +    { +      TestTypeNode *child = link->data; +      DBusList *next = _dbus_list_get_next_link (&container->children, link); + +      node_destroy (child); + +      _dbus_list_free_link (link); + +      link = next; +    } +} + +#endif /* DBUS_BUILD_TESTS */  | 
