From 7bf62e31a3c820852271768fafc04ba95c31a19f Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Mon, 17 Jan 2005 03:53:40 +0000 Subject: 2005-01-16 Havoc Pennington This is about it on what can be disabled/deleted from libdbus easily, back below 150K anyhow. Deeper cuts are more work than just turning the code off as I've done here. * dbus/dbus-marshal-basic.c (_dbus_pack_int32): we don't need the signed int convenience funcs * dbus/dbus-internals.c (_dbus_verbose_real): omit when not in verbose mode * dbus/dbus-string-util.c, dbus/dbus-string.c: more breaking things out of libdbus * dbus/dbus-sysdeps.c, dbus/dbus-sysdeps-util.c: same * dbus/dbus-hash.c: purge the TWO_STRINGS crap (well, make it tests-enabled-only, though it should probably be deleted) * dbus/dbus-message-util.c: same stuff * dbus/dbus-auth-util.c: same stuff --- dbus/dbus-marshal-recursive-util.c | 2963 ++++++++++++++++++++++++++++++++++++ 1 file changed, 2963 insertions(+) create mode 100644 dbus/dbus-marshal-recursive-util.c (limited to 'dbus/dbus-marshal-recursive-util.c') 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 +#include + +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 */ -- cgit