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 --- ChangeLog | 24 + dbus/Makefile.am | 11 +- dbus/dbus-auth-util.c | 169 ++ dbus/dbus-auth.c | 137 +- dbus/dbus-hash.c | 25 + dbus/dbus-internals.c | 6 + dbus/dbus-internals.h | 8 +- dbus/dbus-list.c | 10 + dbus/dbus-marshal-basic.c | 29 - dbus/dbus-marshal-basic.h | 5 - dbus/dbus-marshal-recursive-util.c | 2963 ++++++++++++++++++++++++++++++++++++ dbus/dbus-marshal-recursive.c | 2923 +---------------------------------- dbus/dbus-message-builder.c | 109 ++ dbus/dbus-message-internal.h | 2 +- dbus/dbus-message-private.h | 119 ++ dbus/dbus-message-util.c | 1309 ++++++++++++++++ dbus/dbus-message.c | 1382 +---------------- dbus/dbus-string-private.h | 56 + dbus/dbus-string-util.c | 727 +++++++++ dbus/dbus-string.c | 871 +---------- dbus/dbus-string.h | 8 +- dbus/dbus-sysdeps-util.c | 871 +++++++++++ dbus/dbus-sysdeps.c | 374 +---- dbus/dbus-userdb-util.c | 1 - dbus/dbus-userdb.c | 2 + test/unused-code-gc.py | 2 + 26 files changed, 6517 insertions(+), 5626 deletions(-) create mode 100644 dbus/dbus-auth-util.c create mode 100644 dbus/dbus-marshal-recursive-util.c create mode 100644 dbus/dbus-message-private.h create mode 100644 dbus/dbus-message-util.c create mode 100644 dbus/dbus-string-util.c create mode 100644 dbus/dbus-sysdeps-util.c diff --git a/ChangeLog b/ChangeLog index e8125720..580ec360 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +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 + 2005-01-16 Havoc Pennington * dbus/dbus-userdb-util.c: split out part of dbus-userdb.c diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 429c9ad9..216b168d 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -62,15 +62,16 @@ DBUS_LIB_SOURCES= \ dbus-marshal-validate.h \ dbus-message.c \ dbus-message-internal.h \ + dbus-message-private.h \ dbus-object-tree.c \ dbus-object-tree.h \ dbus-pending-call.c \ dbus-resources.c \ dbus-resources.h \ dbus-server.c \ - dbus-server-protected.h \ dbus-server-debug-pipe.c \ dbus-server-debug-pipe.h \ + dbus-server-protected.h \ dbus-server-unix.c \ dbus-server-unix.h \ dbus-sha.c \ @@ -121,16 +122,18 @@ DBUS_SHARED_SOURCES= \ ### should be underscore-prefixed but don't really need ### to be unless they move to DBUS_SHARED_SOURCES later) DBUS_UTIL_SOURCES= \ + dbus-auth-util.c \ dbus-mainloop.c \ dbus-mainloop.h \ + dbus-marshal-recursive-util.c \ + dbus-message-util.c \ dbus-spawn.c \ dbus-spawn.h \ + dbus-string-util.c \ dbus-sysdeps-util.c \ - dbus-sysdeps-util.h \ dbus-test.c \ dbus-test.h \ - dbus-userdb-util.c \ - dbus-userdb-util.h + dbus-userdb-util.c libdbus_1_la_SOURCES= \ $(DBUS_LIB_SOURCES) \ diff --git a/dbus/dbus-auth-util.c b/dbus/dbus-auth-util.c new file mode 100644 index 00000000..4d25cf0a --- /dev/null +++ b/dbus/dbus-auth-util.c @@ -0,0 +1,169 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-auth-util.c Would be in dbus-auth.c, but only used for tests/bus + * + * Copyright (C) 2002, 2003, 2004 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-internals.h" +#include "dbus-test.h" +#include "dbus-auth.h" + +/** + * @addtogroup DBusAuth + * @{ + */ + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include "dbus-auth-script.h" +#include + +static dbus_bool_t +process_test_subdir (const DBusString *test_base_dir, + const char *subdir) +{ + DBusString test_directory; + DBusString filename; + DBusDirIter *dir; + dbus_bool_t retval; + DBusError error; + + retval = FALSE; + dir = NULL; + + if (!_dbus_string_init (&test_directory)) + _dbus_assert_not_reached ("didn't allocate test_directory\n"); + + _dbus_string_init_const (&filename, subdir); + + if (!_dbus_string_copy (test_base_dir, 0, + &test_directory, 0)) + _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); + + if (!_dbus_concat_dir_and_file (&test_directory, &filename)) + _dbus_assert_not_reached ("couldn't allocate full path"); + + _dbus_string_free (&filename); + if (!_dbus_string_init (&filename)) + _dbus_assert_not_reached ("didn't allocate filename string\n"); + + dbus_error_init (&error); + dir = _dbus_directory_open (&test_directory, &error); + if (dir == NULL) + { + _dbus_warn ("Could not open %s: %s\n", + _dbus_string_get_const_data (&test_directory), + error.message); + dbus_error_free (&error); + goto failed; + } + + printf ("Testing %s:\n", subdir); + + next: + while (_dbus_directory_get_next_file (dir, &filename, &error)) + { + DBusString full_path; + + if (!_dbus_string_init (&full_path)) + _dbus_assert_not_reached ("couldn't init string"); + + if (!_dbus_string_copy (&test_directory, 0, &full_path, 0)) + _dbus_assert_not_reached ("couldn't copy dir to full_path"); + + if (!_dbus_concat_dir_and_file (&full_path, &filename)) + _dbus_assert_not_reached ("couldn't concat file to dir"); + + if (!_dbus_string_ends_with_c_str (&filename, ".auth-script")) + { + _dbus_verbose ("Skipping non-.auth-script file %s\n", + _dbus_string_get_const_data (&filename)); + _dbus_string_free (&full_path); + goto next; + } + + printf (" %s\n", _dbus_string_get_const_data (&filename)); + + if (!_dbus_auth_script_run (&full_path)) + { + _dbus_string_free (&full_path); + goto failed; + } + else + _dbus_string_free (&full_path); + } + + if (dbus_error_is_set (&error)) + { + _dbus_warn ("Could not get next file in %s: %s\n", + _dbus_string_get_const_data (&test_directory), error.message); + dbus_error_free (&error); + goto failed; + } + + retval = TRUE; + + failed: + + if (dir) + _dbus_directory_close (dir); + _dbus_string_free (&test_directory); + _dbus_string_free (&filename); + + return retval; +} + +static dbus_bool_t +process_test_dirs (const char *test_data_dir) +{ + DBusString test_directory; + dbus_bool_t retval; + + retval = FALSE; + + _dbus_string_init_const (&test_directory, test_data_dir); + + if (!process_test_subdir (&test_directory, "auth")) + goto failed; + + retval = TRUE; + + failed: + + _dbus_string_free (&test_directory); + + return retval; +} + +dbus_bool_t +_dbus_auth_test (const char *test_data_dir) +{ + + if (test_data_dir == NULL) + return TRUE; + + if (!process_test_dirs (test_data_dir)) + return FALSE; + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index b0793e06..eb8d5742 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -2392,139 +2392,4 @@ _dbus_auth_set_context (DBusAuth *auth, /** @} */ -#ifdef DBUS_BUILD_TESTS -#include "dbus-test.h" -#include "dbus-auth-script.h" -#include - -static dbus_bool_t -process_test_subdir (const DBusString *test_base_dir, - const char *subdir) -{ - DBusString test_directory; - DBusString filename; - DBusDirIter *dir; - dbus_bool_t retval; - DBusError error; - - retval = FALSE; - dir = NULL; - - if (!_dbus_string_init (&test_directory)) - _dbus_assert_not_reached ("didn't allocate test_directory\n"); - - _dbus_string_init_const (&filename, subdir); - - if (!_dbus_string_copy (test_base_dir, 0, - &test_directory, 0)) - _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); - - if (!_dbus_concat_dir_and_file (&test_directory, &filename)) - _dbus_assert_not_reached ("couldn't allocate full path"); - - _dbus_string_free (&filename); - if (!_dbus_string_init (&filename)) - _dbus_assert_not_reached ("didn't allocate filename string\n"); - - dbus_error_init (&error); - dir = _dbus_directory_open (&test_directory, &error); - if (dir == NULL) - { - _dbus_warn ("Could not open %s: %s\n", - _dbus_string_get_const_data (&test_directory), - error.message); - dbus_error_free (&error); - goto failed; - } - - printf ("Testing %s:\n", subdir); - - next: - while (_dbus_directory_get_next_file (dir, &filename, &error)) - { - DBusString full_path; - - if (!_dbus_string_init (&full_path)) - _dbus_assert_not_reached ("couldn't init string"); - - if (!_dbus_string_copy (&test_directory, 0, &full_path, 0)) - _dbus_assert_not_reached ("couldn't copy dir to full_path"); - - if (!_dbus_concat_dir_and_file (&full_path, &filename)) - _dbus_assert_not_reached ("couldn't concat file to dir"); - - if (!_dbus_string_ends_with_c_str (&filename, ".auth-script")) - { - _dbus_verbose ("Skipping non-.auth-script file %s\n", - _dbus_string_get_const_data (&filename)); - _dbus_string_free (&full_path); - goto next; - } - - printf (" %s\n", _dbus_string_get_const_data (&filename)); - - if (!_dbus_auth_script_run (&full_path)) - { - _dbus_string_free (&full_path); - goto failed; - } - else - _dbus_string_free (&full_path); - } - - if (dbus_error_is_set (&error)) - { - _dbus_warn ("Could not get next file in %s: %s\n", - _dbus_string_get_const_data (&test_directory), error.message); - dbus_error_free (&error); - goto failed; - } - - retval = TRUE; - - failed: - - if (dir) - _dbus_directory_close (dir); - _dbus_string_free (&test_directory); - _dbus_string_free (&filename); - - return retval; -} - -static dbus_bool_t -process_test_dirs (const char *test_data_dir) -{ - DBusString test_directory; - dbus_bool_t retval; - - retval = FALSE; - - _dbus_string_init_const (&test_directory, test_data_dir); - - if (!process_test_subdir (&test_directory, "auth")) - goto failed; - - retval = TRUE; - - failed: - - _dbus_string_free (&test_directory); - - return retval; -} - -dbus_bool_t -_dbus_auth_test (const char *test_data_dir) -{ - - if (test_data_dir == NULL) - return TRUE; - - if (!process_test_dirs (test_data_dir)) - return FALSE; - - return TRUE; -} - -#endif /* DBUS_BUILD_TESTS */ +/* tests in dbus-auth-util.c */ diff --git a/dbus/dbus-hash.c b/dbus/dbus-hash.c index c2ebb0f0..fa2104b3 100644 --- a/dbus/dbus-hash.c +++ b/dbus/dbus-hash.c @@ -231,13 +231,17 @@ static DBusHashEntry* find_string_function (DBusHashTable *table, dbus_bool_t create_if_not_found, DBusHashEntry ***bucket, DBusPreallocatedHash *preallocated); +#ifdef DBUS_BUILD_TESTS static DBusHashEntry* find_two_strings_function (DBusHashTable *table, void *key, dbus_bool_t create_if_not_found, DBusHashEntry ***bucket, DBusPreallocatedHash *preallocated); +#endif static unsigned int string_hash (const char *str); +#ifdef DBUS_BUILD_TESTS static unsigned int two_strings_hash (const char *str); +#endif static void rebuild_table (DBusHashTable *table); static DBusHashEntry* alloc_entry (DBusHashTable *table); static void remove_entry (DBusHashTable *table, @@ -330,7 +334,9 @@ _dbus_hash_table_new (DBusHashType type, table->find_function = find_string_function; break; case DBUS_HASH_TWO_STRINGS: +#ifdef DBUS_BUILD_TESTS table->find_function = find_two_strings_function; +#endif break; default: _dbus_assert_not_reached ("Unknown hash table type"); @@ -696,6 +702,7 @@ _dbus_hash_iter_get_string_key (DBusHashIter *iter) return real->entry->key; } +#ifdef DBUS_BUILD_TESTS /** * Gets the key for the current entry. * Only works for hash tables of type #DBUS_HASH_TWO_STRINGS @@ -713,6 +720,7 @@ _dbus_hash_iter_get_two_strings_key (DBusHashIter *iter) return real->entry->key; } +#endif /* DBUS_BUILD_TESTS */ /** * A low-level but efficient interface for manipulating the hash @@ -849,6 +857,7 @@ string_hash (const char *str) return h; } +#ifdef DBUS_BUILD_TESTS /* This hashes a memory block with two nul-terminated strings * in it, used in dbus-object-registry.c at the moment. */ @@ -867,6 +876,7 @@ two_strings_hash (const char *str) return h; } +#endif /* DBUS_BUILD_TESTS */ /** Key comparison function */ typedef int (* KeyCompareFunc) (const void *key_a, const void *key_b); @@ -928,6 +938,7 @@ find_string_function (DBusHashTable *table, preallocated); } +#ifdef DBUS_BUILD_TESTS static int two_strings_cmp (const char *a, const char *b) @@ -945,7 +956,9 @@ two_strings_cmp (const char *a, return strcmp (a + len_a + 1, b + len_b + 1); } +#endif +#ifdef DBUS_BUILD_TESTS static DBusHashEntry* find_two_strings_function (DBusHashTable *table, void *key, @@ -961,6 +974,7 @@ find_two_strings_function (DBusHashTable *table, (KeyCompareFunc) two_strings_cmp, create_if_not_found, bucket, preallocated); } +#endif /* DBUS_BUILD_TESTS */ static DBusHashEntry* find_direct_function (DBusHashTable *table, @@ -1077,7 +1091,12 @@ rebuild_table (DBusHashTable *table) idx = string_hash (entry->key) & table->mask; break; case DBUS_HASH_TWO_STRINGS: +#ifdef DBUS_BUILD_TESTS idx = two_strings_hash (entry->key) & table->mask; +#else + idx = 0; + _dbus_assert_not_reached ("two-strings is not enabled"); +#endif break; case DBUS_HASH_INT: case DBUS_HASH_ULONG: @@ -1127,6 +1146,7 @@ _dbus_hash_table_lookup_string (DBusHashTable *table, return NULL; } +#ifdef DBUS_BUILD_TESTS /** * Looks up the value for a given string in a hash table * of type #DBUS_HASH_TWO_STRINGS. Returns %NULL if the value @@ -1151,6 +1171,7 @@ _dbus_hash_table_lookup_two_strings (DBusHashTable *table, else return NULL; } +#endif /* DBUS_BUILD_TESTS */ /** * Looks up the value for a given integer in a hash table @@ -1258,6 +1279,7 @@ _dbus_hash_table_remove_string (DBusHashTable *table, return FALSE; } +#ifdef DBUS_BUILD_TESTS /** * Removes the hash entry for the given key. If no hash entry * for the key exists, does nothing. @@ -1285,6 +1307,7 @@ _dbus_hash_table_remove_two_strings (DBusHashTable *table, else return FALSE; } +#endif /* DBUS_BUILD_TESTS */ /** * Removes the hash entry for the given key. If no hash entry @@ -1407,6 +1430,7 @@ _dbus_hash_table_insert_string (DBusHashTable *table, return TRUE; } +#ifdef DBUS_BUILD_TESTS /** * Creates a hash entry with the given key and value. * The key and value are not copied; they are stored @@ -1447,6 +1471,7 @@ _dbus_hash_table_insert_two_strings (DBusHashTable *table, return TRUE; } +#endif /* DBUS_BUILD_TESTS */ /** * Creates a hash entry with the given key and value. diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c index eba3174d..8d0a968b 100644 --- a/dbus/dbus-internals.c +++ b/dbus/dbus-internals.c @@ -174,6 +174,8 @@ _dbus_warn (const char *format, va_end (args); } +#ifdef DBUS_ENABLE_VERBOSE_MODE + static dbus_bool_t verbose_initted = FALSE; /** @@ -238,6 +240,8 @@ _dbus_verbose_reset_real (void) verbose_initted = FALSE; } +#endif /* DBUS_ENABLE_VERBOSE_MODE */ + /** * Duplicates a string. Result must be freed with * dbus_free(). Returns #NULL if memory allocation fails. @@ -354,6 +358,7 @@ _dbus_string_array_contains (const char **array, return FALSE; } +#ifdef DBUS_BUILD_TESTS /** * Returns a string describing the given name. * @@ -387,6 +392,7 @@ _dbus_header_field_to_string (int header_field) return "unknown"; } } +#endif /* DBUS_BUILD_TESTS */ #ifndef DBUS_DISABLE_CHECKS /** String used in _dbus_return_if_fail macro */ diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 5d6f31f2..2ffd2341 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -39,9 +39,6 @@ DBUS_BEGIN_DECLS void _dbus_warn (const char *format, ...) _DBUS_GNUC_PRINTF (1, 2); -void _dbus_verbose_real (const char *format, - ...) _DBUS_GNUC_PRINTF (1, 2); -void _dbus_verbose_reset_real (void); #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #define _DBUS_FUNCTION_NAME __func__ @@ -79,6 +76,11 @@ void _dbus_verbose_reset_real (void); #endif #ifdef DBUS_ENABLE_VERBOSE_MODE + +void _dbus_verbose_real (const char *format, + ...) _DBUS_GNUC_PRINTF (1, 2); +void _dbus_verbose_reset_real (void); + # define _dbus_verbose _dbus_verbose_real # define _dbus_verbose_reset _dbus_verbose_reset_real #else diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c index 00b03d41..949e70a8 100644 --- a/dbus/dbus-list.c +++ b/dbus/dbus-list.c @@ -126,6 +126,7 @@ link_before (DBusList **list, } } +#ifdef DBUS_BUILD_TESTS static void link_after (DBusList **list, DBusList *after_this_link, @@ -145,6 +146,7 @@ link_after (DBusList **list, link->next->prev = link; } } +#endif /* DBUS_BUILD_TESTS */ /** @} */ @@ -313,6 +315,7 @@ _dbus_list_prepend_link (DBusList **list, link_before (list, *list, link); } +#ifdef DBUS_BUILD_TESTS /** * Inserts data into the list before the given existing link. * @@ -341,7 +344,9 @@ _dbus_list_insert_before (DBusList **list, return TRUE; } +#endif /* DBUS_BUILD_TESTS */ +#ifdef DBUS_BUILD_TESTS /** * Inserts data into the list after the given existing link. * @@ -370,6 +375,7 @@ _dbus_list_insert_after (DBusList **list, return TRUE; } +#endif /* DBUS_BUILD_TESTS */ /** * Inserts a link into the list before the given existing link. @@ -389,6 +395,7 @@ _dbus_list_insert_before_link (DBusList **list, link_before (list, before_this_link, link); } +#ifdef DBUS_BUILD_TESTS /** * Inserts a link into the list after the given existing link. * @@ -406,6 +413,7 @@ _dbus_list_insert_after_link (DBusList **list, else link_after (list, after_this_link, link); } +#endif /* DBUS_BUILD_TESTS */ /** * Removes a value from the list. Only removes the @@ -690,6 +698,7 @@ _dbus_list_pop_last (DBusList **list) return data; } +#ifdef DBUS_BUILD_TESTS /** * Removes the last link in the list and returns it. This is a * constant-time operation. @@ -710,6 +719,7 @@ _dbus_list_pop_last_link (DBusList **list) return link; } +#endif /* DBUS_BUILD_TESTS */ /** * Copies a list. This is a linear-time operation. If there isn't diff --git a/dbus/dbus-marshal-basic.c b/dbus/dbus-marshal-basic.c index df154b54..94161af0 100644 --- a/dbus/dbus-marshal-basic.c +++ b/dbus/dbus-marshal-basic.c @@ -88,21 +88,6 @@ _dbus_pack_uint32 (dbus_uint32_t value, pack_4_octets (value, byte_order, data); } -/** - * Packs a 32 bit signed integer into a data pointer. - * - * @param value the value - * @param byte_order the byte order to use - * @param data the data pointer - */ -void -_dbus_pack_int32 (dbus_int32_t value, - int byte_order, - unsigned char *data) -{ - pack_4_octets ((dbus_uint32_t) value, byte_order, data); -} - #ifndef DBUS_HAVE_INT64 /* from ORBit */ static void @@ -183,20 +168,6 @@ _dbus_unpack_uint32 (int byte_order, } #endif /* _dbus_unpack_uint32 */ -/** - * Unpacks a 32 bit signed integer from a data pointer - * - * @param byte_order The byte order to use - * @param data the data pointer - * @returns the integer - */ -dbus_int32_t -_dbus_unpack_int32 (int byte_order, - const unsigned char *data) -{ - return (dbus_int32_t) _dbus_unpack_uint32 (byte_order, data); -} - static void set_4_octets (DBusString *str, int offset, diff --git a/dbus/dbus-marshal-basic.h b/dbus/dbus-marshal-basic.h index 870fc0f7..f416689c 100644 --- a/dbus/dbus-marshal-basic.h +++ b/dbus/dbus-marshal-basic.h @@ -152,11 +152,6 @@ typedef union DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)(data))) #endif -void _dbus_pack_int32 (dbus_int32_t value, - int byte_order, - unsigned char *data); -dbus_int32_t _dbus_unpack_int32 (int byte_order, - const unsigned char *data); void _dbus_pack_uint32 (dbus_uint32_t value, int byte_order, unsigned char *data); 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 */ diff --git a/dbus/dbus-marshal-recursive.c b/dbus/dbus-marshal-recursive.c index 05b827a0..e25fe249 100644 --- a/dbus/dbus-marshal-recursive.c +++ b/dbus/dbus-marshal-recursive.c @@ -2710,2925 +2710,4 @@ _dbus_type_writer_set_enabled (DBusTypeWriter *writer, /** @} */ /* end of DBusMarshal group */ -#ifdef DBUS_BUILD_TESTS -#include "dbus-test.h" -#include "dbus-list.h" -#include -#include - -/* 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 (_dbus_unpack_int32 (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 */ +/* tests in dbus-marshal-recursive-util.c */ diff --git a/dbus/dbus-message-builder.c b/dbus/dbus-message-builder.c index b5b7564e..58718f6d 100644 --- a/dbus/dbus-message-builder.c +++ b/dbus/dbus-message-builder.c @@ -335,6 +335,115 @@ append_string_field (DBusString *dest, return TRUE; } +#ifdef DBUS_BUILD_TESTS +/** + * Parses a basic type defined by type contained in a DBusString. The + * end_return parameter may be #NULL if you aren't interested in it. The + * type is parsed and stored in value_return. Return parameters are not + * initialized if the function returns #FALSE. + * + * @param str the string + * @param type the type of the basic type + * @param start the byte index of the start of the type + * @param value_return return location of the value or #NULL + * @param end_return return location of the end of the type, or #NULL + * @returns #TRUE on success + */ +static dbus_bool_t +_dbus_string_parse_basic_type (const DBusString *str, + char type, + int start, + void *value, + int *end_return) +{ + int end = start; + + switch (type) + { + case DBUS_TYPE_BOOLEAN: + { + int len = _dbus_string_get_length (str) - start; + if (len >= 5 && _dbus_string_find_to (str, start, start + 5, "false", NULL)) + { + end += 5; + *(unsigned char *) value = TRUE; + } + else if (len >= 4 && _dbus_string_find_to (str, start, start + 4, "true", NULL)) + { + end += 4; + *(unsigned char *) value = FALSE; + } + else + _dbus_warn ("could not parse BOOLEAN\n"); + break; + } + case DBUS_TYPE_BYTE: + { + long val = 0; + + if (_dbus_string_get_byte (str, start) == '\'' && + _dbus_string_get_length (str) >= start + 4 && + _dbus_string_get_byte (str, start + 1) == '\\' && + _dbus_string_get_byte (str, start + 2) == '\'' && + _dbus_string_get_byte (str, start + 3) == '\'') + { + val = '\''; + end += 4; + } + else if (_dbus_string_get_byte (str, start) == '\'' && + _dbus_string_get_length (str) >= start + 3 && + _dbus_string_get_byte (str, start + 2) == '\'') + { + val = _dbus_string_get_byte (str, start + 1); + end += 3; + } + else + { + if (!_dbus_string_parse_int (str, start, &val, &end)) + _dbus_warn ("Failed to parse integer for BYTE\n"); + } + + if (val > 255) + _dbus_warn ("A byte must be in range 0-255 not %ld\n", val); + + *(unsigned char *) value = val; + break; + } + case DBUS_TYPE_INT32: + { + long val; + if (_dbus_string_parse_int (str, start, &val, &end)) + *(dbus_int32_t *)value = val; + break; + } + case DBUS_TYPE_UINT32: + { + unsigned long val; + if (_dbus_string_parse_uint (str, start, &val, &end)) + *(dbus_uint32_t *)value = val; + break; + } +#ifdef DBUS_HAVE_INT64 + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + /* use stroll oull */ + _dbus_assert_not_reached ("string -> [u]int64 not supported yet"); + break; +#endif /* DBUS_HAVE_INT64 */ + case DBUS_TYPE_DOUBLE: + _dbus_string_parse_double (str, start, value, &end); + break; + default: + _dbus_assert_not_reached ("not a basic type"); + break; + } + if (end_return) + *end_return = end; + + return end != start; +} +#endif /* DBUS_BUILD_TESTS */ + static dbus_bool_t parse_basic_type (DBusString *src, char type, DBusString *dest, dbus_bool_t *unalign, diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index f03e13f6..86064d3d 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -71,4 +71,4 @@ long _dbus_message_loader_get_max_message_size (DBusMessageLoader DBUS_END_DECLS -#endif /* DBUS_MESSAGE_H */ +#endif /* DBUS_MESSAGE_INTERNAL_H */ diff --git a/dbus/dbus-message-private.h b/dbus/dbus-message-private.h new file mode 100644 index 00000000..60b0daea --- /dev/null +++ b/dbus/dbus-message-private.h @@ -0,0 +1,119 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-message-private.h header shared between dbus-message.c and dbus-message-util.c + * + * Copyright (C) 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 + * + */ +#ifndef DBUS_MESSAGE_PRIVATE_H +#define DBUS_MESSAGE_PRIVATE_H + +#include +#include +#include +#include +#include + +DBUS_BEGIN_DECLS + +/** + * @addtogroup DBusMessageInternals + * @{ + */ + +/** + * @typedef DBusMessageLoader + * + * The DBusMessageLoader object encapsulates the process of converting + * a byte stream into a series of DBusMessage. It buffers the incoming + * bytes as efficiently as possible, and generates a queue of + * messages. DBusMessageLoader is typically used as part of a + * DBusTransport implementation. The DBusTransport then hands off + * the loaded messages to a DBusConnection, making the messages + * visible to the application. + * + * @todo write tests for break-loader that a) randomly delete header + * fields and b) set string fields to zero-length and other funky + * values. + * + */ + +/** + * Implementation details of DBusMessageLoader. + * All members are private. + */ +struct DBusMessageLoader +{ + int refcount; /**< Reference count. */ + + DBusString data; /**< Buffered data */ + + DBusList *messages; /**< Complete messages. */ + + long max_message_size; /**< Maximum size of a message */ + + unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ + + unsigned int corrupted : 1; /**< We got broken data, and are no longer working */ +}; + + +/** How many bits are in the changed_stamp used to validate iterators */ +#define CHANGED_STAMP_BITS 21 + +/** + * @brief Internals of DBusMessage + * + * Object representing a message received from or to be sent to + * another application. This is an opaque object, all members + * are private. + */ +struct DBusMessage +{ + DBusAtomic refcount; /**< Reference count */ + + DBusHeader header; /**< Header network data and associated cache */ + + DBusString body; /**< Body network data. */ + + char byte_order; /**< Message byte order. */ + + unsigned int locked : 1; /**< Message being sent, no modifications allowed. */ + + DBusList *size_counters; /**< 0-N DBusCounter used to track message size. */ + long size_counter_delta; /**< Size we incremented the size counters by. */ + + dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */ + + DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ + +#ifndef DBUS_DISABLE_CHECKS + int generation; /**< _dbus_current_generation when message was created */ +#endif +}; + +dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args); + +/** @} */ + +DBUS_END_DECLS + +#endif /* DBUS_MESSAGE_H */ diff --git a/dbus/dbus-message-util.c b/dbus/dbus-message-util.c new file mode 100644 index 00000000..47370702 --- /dev/null +++ b/dbus/dbus-message-util.c @@ -0,0 +1,1309 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-message-util.c Would be in dbus-message.c, but only used by bus/tests + * + * Copyright (C) 2002, 2003, 2004, 2005 Red Hat Inc. + * Copyright (C) 2002, 2003 CodeFactory AB + * + * 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-internals.h" +#include "dbus-test.h" +#include "dbus-message-private.h" +#include "dbus-marshal-recursive.h" +#include "dbus-string.h" + +/** + * @addtogroup DBusMessage + * @{ + */ + +#ifdef DBUS_BUILD_TESTS +/** + * Reads arguments from a message iterator given a variable argument + * list. Only arguments of basic type and arrays of fixed-length + * basic type may be read with this function. See + * dbus_message_get_args() for more details. + * + * @todo this is static for now because there's no corresponding + * iter_append_args() and I'm not sure we need this function to be + * public since dbus_message_get_args() is what you usually want + * + * @param iter the message iterator + * @param error error to be filled in on failure + * @param first_arg_type the first argument type + * @param ... location for first argument value, then list of type-location pairs + * @returns #FALSE if the error was set + */ +static dbus_bool_t +dbus_message_iter_get_args (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + ...) +{ + dbus_bool_t retval; + va_list var_args; + + _dbus_return_val_if_fail (iter != NULL, FALSE); + _dbus_return_val_if_error_is_set (error, FALSE); + + va_start (var_args, first_arg_type); + retval = _dbus_message_iter_get_args_valist (iter, error, first_arg_type, var_args); + va_end (var_args); + + return retval; +} +#endif /* DBUS_BUILD_TESTS */ + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include +#include + +static dbus_bool_t +check_have_valid_message (DBusMessageLoader *loader) +{ + DBusMessage *message; + dbus_bool_t retval; + + message = NULL; + retval = FALSE; + + if (!_dbus_message_loader_queue_messages (loader)) + _dbus_assert_not_reached ("no memory to queue messages"); + + if (_dbus_message_loader_get_is_corrupted (loader)) + { + _dbus_warn ("loader corrupted on message that was expected to be valid\n"); + goto failed; + } + + message = _dbus_message_loader_pop_message (loader); + if (message == NULL) + { + _dbus_warn ("didn't load message that was expected to be valid (message not popped)\n"); + goto failed; + } + + if (_dbus_string_get_length (&loader->data) > 0) + { + _dbus_warn ("had leftover bytes from expected-to-be-valid single message\n"); + goto failed; + } + +#if 0 + /* FIXME */ + /* Verify that we're able to properly deal with the message. + * For example, this would detect improper handling of messages + * in nonstandard byte order. + */ + if (!check_message_handling (message)) + goto failed; +#endif + + retval = TRUE; + + failed: + if (message) + dbus_message_unref (message); + + return retval; +} + +static dbus_bool_t +check_invalid_message (DBusMessageLoader *loader) +{ + dbus_bool_t retval; + + retval = FALSE; + + if (!_dbus_message_loader_queue_messages (loader)) + _dbus_assert_not_reached ("no memory to queue messages"); + + if (!_dbus_message_loader_get_is_corrupted (loader)) + { + _dbus_warn ("loader not corrupted on message that was expected to be invalid\n"); + goto failed; + } + + retval = TRUE; + + failed: + return retval; +} + +static dbus_bool_t +check_incomplete_message (DBusMessageLoader *loader) +{ + DBusMessage *message; + dbus_bool_t retval; + + message = NULL; + retval = FALSE; + + if (!_dbus_message_loader_queue_messages (loader)) + _dbus_assert_not_reached ("no memory to queue messages"); + + if (_dbus_message_loader_get_is_corrupted (loader)) + { + _dbus_warn ("loader corrupted on message that was expected to be valid (but incomplete)\n"); + goto failed; + } + + message = _dbus_message_loader_pop_message (loader); + if (message != NULL) + { + _dbus_warn ("loaded message that was expected to be incomplete\n"); + goto failed; + } + + retval = TRUE; + + failed: + if (message) + dbus_message_unref (message); + return retval; +} + +static dbus_bool_t +check_loader_results (DBusMessageLoader *loader, + DBusMessageValidity validity) +{ + if (!_dbus_message_loader_queue_messages (loader)) + _dbus_assert_not_reached ("no memory to queue messages"); + + switch (validity) + { + case _DBUS_MESSAGE_VALID: + return check_have_valid_message (loader); + case _DBUS_MESSAGE_INVALID: + return check_invalid_message (loader); + case _DBUS_MESSAGE_INCOMPLETE: + return check_incomplete_message (loader); + case _DBUS_MESSAGE_UNKNOWN: + return TRUE; + } + + _dbus_assert_not_reached ("bad DBusMessageValidity"); + return FALSE; +} + + +/** + * Loads the message in the given message file. + * + * @param filename filename to load + * @param is_raw if #TRUE load as binary data, if #FALSE as message builder language + * @param data string to load message into + * @returns #TRUE if the message was loaded + */ +dbus_bool_t +dbus_internal_do_not_use_load_message_file (const DBusString *filename, + dbus_bool_t is_raw, + DBusString *data) +{ + dbus_bool_t retval; + + retval = FALSE; + + if (is_raw) + { + DBusError error; + + _dbus_verbose ("Loading raw %s\n", _dbus_string_get_const_data (filename)); + dbus_error_init (&error); + if (!_dbus_file_get_contents (data, filename, &error)) + { + _dbus_warn ("Could not load message file %s: %s\n", + _dbus_string_get_const_data (filename), + error.message); + dbus_error_free (&error); + goto failed; + } + } + else + { + if (FALSE) /* Message builder disabled, probably permanently, + * I want to do it another way + */ + { + _dbus_warn ("Could not load message file %s\n", + _dbus_string_get_const_data (filename)); + goto failed; + } + } + + retval = TRUE; + + failed: + + return retval; +} + +/** + * Tries loading the message in the given message file + * and verifies that DBusMessageLoader can handle it. + * + * @param filename filename to load + * @param is_raw if #TRUE load as binary data, if #FALSE as message builder language + * @param expected_validity what the message has to be like to return #TRUE + * @returns #TRUE if the message has the expected validity + */ +dbus_bool_t +dbus_internal_do_not_use_try_message_file (const DBusString *filename, + dbus_bool_t is_raw, + DBusMessageValidity expected_validity) +{ + DBusString data; + dbus_bool_t retval; + + retval = FALSE; + + if (!_dbus_string_init (&data)) + _dbus_assert_not_reached ("could not allocate string\n"); + + if (!dbus_internal_do_not_use_load_message_file (filename, is_raw, + &data)) + goto failed; + + retval = dbus_internal_do_not_use_try_message_data (&data, expected_validity); + + failed: + + if (!retval) + { + if (_dbus_string_get_length (&data) > 0) + _dbus_verbose_bytes_of_string (&data, 0, + _dbus_string_get_length (&data)); + + _dbus_warn ("Failed message loader test on %s\n", + _dbus_string_get_const_data (filename)); + } + + _dbus_string_free (&data); + + return retval; +} + +/** + * Tries loading the given message data. + * + * + * @param data the message data + * @param expected_validity what the message has to be like to return #TRUE + * @returns #TRUE if the message has the expected validity + */ +dbus_bool_t +dbus_internal_do_not_use_try_message_data (const DBusString *data, + DBusMessageValidity expected_validity) +{ + DBusMessageLoader *loader; + dbus_bool_t retval; + int len; + int i; + + loader = NULL; + retval = FALSE; + + /* Write the data one byte at a time */ + + loader = _dbus_message_loader_new (); + + /* check some trivial loader functions */ + _dbus_message_loader_ref (loader); + _dbus_message_loader_unref (loader); + _dbus_message_loader_get_max_message_size (loader); + + len = _dbus_string_get_length (data); + for (i = 0; i < len; i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, + _dbus_string_get_byte (data, i)); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + if (!check_loader_results (loader, expected_validity)) + goto failed; + + _dbus_message_loader_unref (loader); + loader = NULL; + + /* Write the data all at once */ + + loader = _dbus_message_loader_new (); + + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_copy (data, 0, buffer, + _dbus_string_get_length (buffer)); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + if (!check_loader_results (loader, expected_validity)) + goto failed; + + _dbus_message_loader_unref (loader); + loader = NULL; + + /* Write the data 2 bytes at a time */ + + loader = _dbus_message_loader_new (); + + len = _dbus_string_get_length (data); + for (i = 0; i < len; i += 2) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, + _dbus_string_get_byte (data, i)); + if ((i+1) < len) + _dbus_string_append_byte (buffer, + _dbus_string_get_byte (data, i+1)); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + if (!check_loader_results (loader, expected_validity)) + goto failed; + + _dbus_message_loader_unref (loader); + loader = NULL; + + retval = TRUE; + + failed: + + if (loader) + _dbus_message_loader_unref (loader); + + return retval; +} + +static dbus_bool_t +process_test_subdir (const DBusString *test_base_dir, + const char *subdir, + DBusMessageValidity validity, + DBusForeachMessageFileFunc function, + void *user_data) +{ + DBusString test_directory; + DBusString filename; + DBusDirIter *dir; + dbus_bool_t retval; + DBusError error; + + retval = FALSE; + dir = NULL; + + if (!_dbus_string_init (&test_directory)) + _dbus_assert_not_reached ("didn't allocate test_directory\n"); + + _dbus_string_init_const (&filename, subdir); + + if (!_dbus_string_copy (test_base_dir, 0, + &test_directory, 0)) + _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); + + if (!_dbus_concat_dir_and_file (&test_directory, &filename)) + _dbus_assert_not_reached ("couldn't allocate full path"); + + _dbus_string_free (&filename); + if (!_dbus_string_init (&filename)) + _dbus_assert_not_reached ("didn't allocate filename string\n"); + + dbus_error_init (&error); + dir = _dbus_directory_open (&test_directory, &error); + if (dir == NULL) + { + _dbus_warn ("Could not open %s: %s\n", + _dbus_string_get_const_data (&test_directory), + error.message); + dbus_error_free (&error); + goto failed; + } + + printf ("Testing %s:\n", subdir); + + next: + while (_dbus_directory_get_next_file (dir, &filename, &error)) + { + DBusString full_path; + dbus_bool_t is_raw; + + if (!_dbus_string_init (&full_path)) + _dbus_assert_not_reached ("couldn't init string"); + + if (!_dbus_string_copy (&test_directory, 0, &full_path, 0)) + _dbus_assert_not_reached ("couldn't copy dir to full_path"); + + if (!_dbus_concat_dir_and_file (&full_path, &filename)) + _dbus_assert_not_reached ("couldn't concat file to dir"); + + if (_dbus_string_ends_with_c_str (&filename, ".message")) + is_raw = FALSE; + else if (_dbus_string_ends_with_c_str (&filename, ".message-raw")) + is_raw = TRUE; + else + { + _dbus_verbose ("Skipping non-.message file %s\n", + _dbus_string_get_const_data (&filename)); + _dbus_string_free (&full_path); + goto next; + } + + printf (" %s\n", + _dbus_string_get_const_data (&filename)); + + _dbus_verbose (" expecting %s for %s\n", + validity == _DBUS_MESSAGE_VALID ? "valid" : + (validity == _DBUS_MESSAGE_INVALID ? "invalid" : + (validity == _DBUS_MESSAGE_INCOMPLETE ? "incomplete" : "unknown")), + _dbus_string_get_const_data (&filename)); + + if (! (*function) (&full_path, is_raw, validity, user_data)) + { + _dbus_string_free (&full_path); + goto failed; + } + else + _dbus_string_free (&full_path); + } + + if (dbus_error_is_set (&error)) + { + _dbus_warn ("Could not get next file in %s: %s\n", + _dbus_string_get_const_data (&test_directory), + error.message); + dbus_error_free (&error); + goto failed; + } + + retval = TRUE; + + failed: + + if (dir) + _dbus_directory_close (dir); + _dbus_string_free (&test_directory); + _dbus_string_free (&filename); + + return retval; +} + +/** + * Runs the given function on every message file in the test suite. + * The function should return #FALSE on test failure or fatal error. + * + * @param test_data_dir root dir of the test suite data files (top_srcdir/test/data) + * @param func the function to run + * @param user_data data for function + * @returns #FALSE if there's a failure + */ +dbus_bool_t +dbus_internal_do_not_use_foreach_message_file (const char *test_data_dir, + DBusForeachMessageFileFunc func, + void *user_data) +{ + DBusString test_directory; + dbus_bool_t retval; + + retval = FALSE; + + _dbus_string_init_const (&test_directory, test_data_dir); + + if (!process_test_subdir (&test_directory, "valid-messages", + _DBUS_MESSAGE_VALID, func, user_data)) + goto failed; + + if (!process_test_subdir (&test_directory, "invalid-messages", + _DBUS_MESSAGE_INVALID, func, user_data)) + goto failed; + + if (!process_test_subdir (&test_directory, "incomplete-messages", + _DBUS_MESSAGE_INCOMPLETE, func, user_data)) + goto failed; + + retval = TRUE; + + failed: + + _dbus_string_free (&test_directory); + + return retval; +} + +#define GET_AND_CHECK(iter, typename, literal) \ + do { \ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ + _dbus_assert_not_reached ("got wrong argument type from message iter"); \ + dbus_message_iter_get_basic (&iter, &v_##typename); \ + if (v_##typename != literal) \ + _dbus_assert_not_reached ("got wrong value from message iter"); \ + } while (0) + +#define GET_AND_CHECK_STRCMP(iter, typename, literal) \ + do { \ + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ + _dbus_assert_not_reached ("got wrong argument type from message iter"); \ + dbus_message_iter_get_basic (&iter, &v_##typename); \ + if (strcmp (v_##typename, literal) != 0) \ + _dbus_assert_not_reached ("got wrong value from message iter"); \ + } while (0) + +#define GET_AND_CHECK_AND_NEXT(iter, typename, literal) \ + do { \ + GET_AND_CHECK(iter, typename, literal); \ + if (!dbus_message_iter_next (&iter)) \ + _dbus_assert_not_reached ("failed to move iter to next"); \ + } while (0) + +#define GET_AND_CHECK_STRCMP_AND_NEXT(iter, typename, literal) \ + do { \ + GET_AND_CHECK_STRCMP(iter, typename, literal); \ + if (!dbus_message_iter_next (&iter)) \ + _dbus_assert_not_reached ("failed to move iter to next"); \ + } while (0) + +static void +message_iter_test (DBusMessage *message) +{ + DBusMessageIter iter, array, array2; + const char *v_STRING; + double v_DOUBLE; + dbus_int32_t v_INT32; + dbus_uint32_t v_UINT32; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t v_INT64; + dbus_uint64_t v_UINT64; +#endif + unsigned char v_BYTE; + unsigned char v_BOOLEAN; + + const dbus_int32_t *our_int_array; + int len; + + dbus_message_iter_init (message, &iter); + + GET_AND_CHECK_STRCMP_AND_NEXT (iter, STRING, "Test string"); + GET_AND_CHECK_AND_NEXT (iter, INT32, -0x12345678); + GET_AND_CHECK_AND_NEXT (iter, UINT32, 0xedd1e); + GET_AND_CHECK_AND_NEXT (iter, DOUBLE, 3.14159); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) + _dbus_assert_not_reached ("Argument type not an array"); + + if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DOUBLE) + _dbus_assert_not_reached ("Array type not double"); + + dbus_message_iter_recurse (&iter, &array); + + GET_AND_CHECK_AND_NEXT (array, DOUBLE, 1.5); + GET_AND_CHECK (array, DOUBLE, 2.5); + + if (dbus_message_iter_next (&array)) + _dbus_assert_not_reached ("Didn't reach end of array"); + + if (!dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Reached end of arguments"); + + GET_AND_CHECK_AND_NEXT (iter, BYTE, 0xF0); + + if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) + _dbus_assert_not_reached ("no array"); + + if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_INT32) + _dbus_assert_not_reached ("Array type not int32"); + + /* Empty array */ + dbus_message_iter_recurse (&iter, &array); + + if (dbus_message_iter_next (&array)) + _dbus_assert_not_reached ("Didn't reach end of array"); + + if (!dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Reached end of arguments"); + + GET_AND_CHECK (iter, BYTE, 0xF0); + + if (dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Didn't reach end of arguments"); +} + +static void +verify_test_message (DBusMessage *message) +{ + DBusMessageIter iter; + DBusError error; + dbus_int32_t our_int; + const char *our_str; + double our_double; + unsigned char our_bool; + unsigned char our_byte_1, our_byte_2; + dbus_uint32_t our_uint32; + const dbus_int32_t *our_uint32_array = (void*)0xdeadbeef; + int our_uint32_array_len; + dbus_int32_t *our_int32_array = (void*)0xdeadbeef; + int our_int32_array_len; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t our_int64; + dbus_uint64_t our_uint64; + dbus_int64_t *our_uint64_array = (void*)0xdeadbeef; + int our_uint64_array_len; + const dbus_int64_t *our_int64_array = (void*)0xdeadbeef; + int our_int64_array_len; +#endif + const double *our_double_array = (void*)0xdeadbeef; + int our_double_array_len; + const unsigned char *our_byte_array = (void*)0xdeadbeef; + int our_byte_array_len; + const unsigned char *our_boolean_array = (void*)0xdeadbeef; + int our_boolean_array_len; + + dbus_message_iter_init (message, &iter); + + dbus_error_init (&error); + if (!dbus_message_iter_get_args (&iter, &error, + DBUS_TYPE_INT32, &our_int, +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_INT64, &our_int64, + DBUS_TYPE_UINT64, &our_uint64, +#endif + DBUS_TYPE_STRING, &our_str, + DBUS_TYPE_DOUBLE, &our_double, + DBUS_TYPE_BOOLEAN, &our_bool, + DBUS_TYPE_BYTE, &our_byte_1, + DBUS_TYPE_BYTE, &our_byte_2, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &our_uint32_array, &our_uint32_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, + &our_int32_array, &our_int32_array_len, +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, + &our_uint64_array, &our_uint64_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, + &our_int64_array, &our_int64_array_len, +#endif + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, + &our_double_array, &our_double_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &our_byte_array, &our_byte_array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, + &our_boolean_array, &our_boolean_array_len, + 0)) + { + _dbus_warn ("error: %s - %s\n", error.name, + (error.message != NULL) ? error.message : "no message"); + _dbus_assert_not_reached ("Could not get arguments"); + } + + if (our_int != -0x12345678) + _dbus_assert_not_reached ("integers differ!"); + +#ifdef DBUS_HAVE_INT64 + if (our_int64 != DBUS_INT64_CONSTANT (-0x123456789abcd)) + _dbus_assert_not_reached ("64-bit integers differ!"); + if (our_uint64 != DBUS_UINT64_CONSTANT (0x123456789abcd)) + _dbus_assert_not_reached ("64-bit unsigned integers differ!"); +#endif + + if (our_double != 3.14159) + _dbus_assert_not_reached ("doubles differ!"); + + if (strcmp (our_str, "Test string") != 0) + _dbus_assert_not_reached ("strings differ!"); + + if (!our_bool) + _dbus_assert_not_reached ("booleans differ"); + + if (our_byte_1 != 42) + _dbus_assert_not_reached ("bytes differ!"); + + if (our_byte_2 != 24) + _dbus_assert_not_reached ("bytes differ!"); + + if (our_uint32_array_len != 4 || + our_uint32_array[0] != 0x12345678 || + our_uint32_array[1] != 0x23456781 || + our_uint32_array[2] != 0x34567812 || + our_uint32_array[3] != 0x45678123) + _dbus_assert_not_reached ("uint array differs"); + + if (our_int32_array_len != 4 || + our_int32_array[0] != 0x12345678 || + our_int32_array[1] != -0x23456781 || + our_int32_array[2] != 0x34567812 || + our_int32_array[3] != -0x45678123) + _dbus_assert_not_reached ("int array differs"); + +#ifdef DBUS_HAVE_INT64 + if (our_uint64_array_len != 4 || + our_uint64_array[0] != 0x12345678 || + our_uint64_array[1] != 0x23456781 || + our_uint64_array[2] != 0x34567812 || + our_uint64_array[3] != 0x45678123) + _dbus_assert_not_reached ("uint64 array differs"); + + if (our_int64_array_len != 4 || + our_int64_array[0] != 0x12345678 || + our_int64_array[1] != -0x23456781 || + our_int64_array[2] != 0x34567812 || + our_int64_array[3] != -0x45678123) + _dbus_assert_not_reached ("int64 array differs"); +#endif /* DBUS_HAVE_INT64 */ + + if (our_double_array_len != 3) + _dbus_assert_not_reached ("double array had wrong length"); + + /* On all IEEE machines (i.e. everything sane) exact equality + * should be preserved over the wire + */ + if (our_double_array[0] != 0.1234 || + our_double_array[1] != 9876.54321 || + our_double_array[2] != -300.0) + _dbus_assert_not_reached ("double array had wrong values"); + + if (our_byte_array_len != 4) + _dbus_assert_not_reached ("byte array had wrong length"); + + if (our_byte_array[0] != 'a' || + our_byte_array[1] != 'b' || + our_byte_array[2] != 'c' || + our_byte_array[3] != 234) + _dbus_assert_not_reached ("byte array had wrong values"); + + if (our_boolean_array_len != 5) + _dbus_assert_not_reached ("bool array had wrong length"); + + if (our_boolean_array[0] != TRUE || + our_boolean_array[1] != FALSE || + our_boolean_array[2] != TRUE || + our_boolean_array[3] != TRUE || + our_boolean_array[4] != FALSE) + _dbus_assert_not_reached ("bool array had wrong values"); + + if (dbus_message_iter_next (&iter)) + _dbus_assert_not_reached ("Didn't reach end of arguments"); +} + +/** + * @ingroup DBusMessageInternals + * Unit test for DBusMessage. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_message_test (const char *test_data_dir) +{ + DBusMessage *message; + DBusMessageLoader *loader; + DBusMessageIter iter, child_iter, child_iter2, child_iter3; + int i; + const char *data; + DBusMessage *copy; + const char *name1; + const char *name2; + const dbus_uint32_t our_uint32_array[] = + { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; + const dbus_uint32_t our_int32_array[] = + { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; + const dbus_uint32_t *v_ARRAY_UINT32 = our_uint32_array; + const dbus_int32_t *v_ARRAY_INT32 = our_int32_array; +#ifdef DBUS_HAVE_INT64 + const dbus_uint64_t our_uint64_array[] = + { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; + const dbus_uint64_t our_int64_array[] = + { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; + const dbus_uint64_t *v_ARRAY_UINT64 = our_uint64_array; + const dbus_int64_t *v_ARRAY_INT64 = our_int64_array; +#endif + const char *our_string_array[] = { "Foo", "bar", "", "woo woo woo woo" }; + const char **v_ARRAY_STRING = our_string_array; + const double our_double_array[] = { 0.1234, 9876.54321, -300.0 }; + const double *v_ARRAY_DOUBLE = our_double_array; + const unsigned char our_byte_array[] = { 'a', 'b', 'c', 234 }; + const unsigned char *v_ARRAY_BYTE = our_byte_array; + const unsigned char our_boolean_array[] = { TRUE, FALSE, TRUE, TRUE, FALSE }; + const unsigned char *v_ARRAY_BOOLEAN = our_boolean_array; + char sig[64]; + const char *s; + char *t; + DBusError error; + const char *v_STRING; + double v_DOUBLE; + dbus_int32_t v_INT32; + dbus_uint32_t v_UINT32; +#ifdef DBUS_HAVE_INT64 + dbus_int64_t v_INT64; + dbus_uint64_t v_UINT64; +#endif + unsigned char v_BYTE; + unsigned char v2_BYTE; + unsigned char v_BOOLEAN; + + message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", + "/org/freedesktop/TestPath", + "Foo.TestInterface", + "TestMethod"); + _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService")); + _dbus_assert (dbus_message_is_method_call (message, "Foo.TestInterface", + "TestMethod")); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/org/freedesktop/TestPath") == 0); + _dbus_message_set_serial (message, 1234); + + /* string length including nul byte not a multiple of 4 */ + if (!dbus_message_set_sender (message, "org.foo.bar1")) + _dbus_assert_not_reached ("out of memory"); + + _dbus_assert (dbus_message_has_sender (message, "org.foo.bar1")); + dbus_message_set_reply_serial (message, 5678); + + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + + if (!dbus_message_set_sender (message, NULL)) + _dbus_assert_not_reached ("out of memory"); + + + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + + + _dbus_assert (!dbus_message_has_sender (message, "org.foo.bar1")); + _dbus_assert (dbus_message_get_serial (message) == 1234); + _dbus_assert (dbus_message_get_reply_serial (message) == 5678); + _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService")); + + _dbus_assert (dbus_message_get_no_reply (message) == FALSE); + dbus_message_set_no_reply (message, TRUE); + _dbus_assert (dbus_message_get_no_reply (message) == TRUE); + dbus_message_set_no_reply (message, FALSE); + _dbus_assert (dbus_message_get_no_reply (message) == FALSE); + + /* Set/get some header fields */ + + if (!dbus_message_set_path (message, "/foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/foo") == 0); + + if (!dbus_message_set_interface (message, "org.Foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_interface (message), + "org.Foo") == 0); + + if (!dbus_message_set_member (message, "Bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_member (message), + "Bar") == 0); + + /* Set/get them with longer values */ + if (!dbus_message_set_path (message, "/foo/bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/foo/bar") == 0); + + if (!dbus_message_set_interface (message, "org.Foo.Bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_interface (message), + "org.Foo.Bar") == 0); + + if (!dbus_message_set_member (message, "BarFoo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_member (message), + "BarFoo") == 0); + + /* Realloc shorter again */ + + if (!dbus_message_set_path (message, "/foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_path (message), + "/foo") == 0); + + if (!dbus_message_set_interface (message, "org.Foo")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_interface (message), + "org.Foo") == 0); + + if (!dbus_message_set_member (message, "Bar")) + _dbus_assert_not_reached ("out of memory"); + _dbus_assert (strcmp (dbus_message_get_member (message), + "Bar") == 0); + + dbus_message_unref (message); + + /* Test the vararg functions */ + message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", + "/org/freedesktop/TestPath", + "Foo.TestInterface", + "TestMethod"); + _dbus_message_set_serial (message, 1); + + v_INT32 = -0x12345678; +#ifdef DBUS_HAVE_INT64 + v_INT64 = DBUS_INT64_CONSTANT (-0x123456789abcd); + v_UINT64 = DBUS_UINT64_CONSTANT (0x123456789abcd); +#endif + v_STRING = "Test string"; + v_DOUBLE = 3.14159; + v_BOOLEAN = TRUE; + v_BYTE = 42; + v2_BYTE = 24; + + dbus_message_append_args (message, + DBUS_TYPE_INT32, &v_INT32, +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_INT64, &v_INT64, + DBUS_TYPE_UINT64, &v_UINT64, +#endif + DBUS_TYPE_STRING, &v_STRING, + DBUS_TYPE_DOUBLE, &v_DOUBLE, + DBUS_TYPE_BOOLEAN, &v_BOOLEAN, + DBUS_TYPE_BYTE, &v_BYTE, + DBUS_TYPE_BYTE, &v2_BYTE, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &v_ARRAY_UINT32, + _DBUS_N_ELEMENTS (our_uint32_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY_INT32, + _DBUS_N_ELEMENTS (our_int32_array), +#ifdef DBUS_HAVE_INT64 + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, &v_ARRAY_UINT64, + _DBUS_N_ELEMENTS (our_uint64_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, &v_ARRAY_INT64, + _DBUS_N_ELEMENTS (our_int64_array), +#endif + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &v_ARRAY_DOUBLE, + _DBUS_N_ELEMENTS (our_double_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &v_ARRAY_BYTE, + _DBUS_N_ELEMENTS (our_byte_array), + DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, &v_ARRAY_BOOLEAN, + _DBUS_N_ELEMENTS (our_boolean_array), + DBUS_TYPE_INVALID); + + i = 0; + sig[i++] = DBUS_TYPE_INT32; +#ifdef DBUS_HAVE_INT64 + sig[i++] = DBUS_TYPE_INT64; + sig[i++] = DBUS_TYPE_UINT64; +#endif + sig[i++] = DBUS_TYPE_STRING; + sig[i++] = DBUS_TYPE_DOUBLE; + sig[i++] = DBUS_TYPE_BOOLEAN; + sig[i++] = DBUS_TYPE_BYTE; + sig[i++] = DBUS_TYPE_BYTE; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_UINT32; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_INT32; +#ifdef DBUS_HAVE_INT64 + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_UINT64; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_INT64; +#endif + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_DOUBLE; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_BYTE; + sig[i++] = DBUS_TYPE_ARRAY; + sig[i++] = DBUS_TYPE_BOOLEAN; + sig[i++] = DBUS_TYPE_INVALID; + + _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig)); + + _dbus_verbose ("HEADER\n"); + _dbus_verbose_bytes_of_string (&message->header.data, 0, + _dbus_string_get_length (&message->header.data)); + _dbus_verbose ("BODY\n"); + _dbus_verbose_bytes_of_string (&message->body, 0, + _dbus_string_get_length (&message->body)); + + _dbus_verbose ("Signature expected \"%s\" actual \"%s\"\n", + sig, dbus_message_get_signature (message)); + + s = dbus_message_get_signature (message); + + _dbus_assert (dbus_message_has_signature (message, sig)); + _dbus_assert (strcmp (s, sig) == 0); + + verify_test_message (message); + + copy = dbus_message_copy (message); + + _dbus_assert (dbus_message_get_reply_serial (message) == + dbus_message_get_reply_serial (copy)); + _dbus_assert (message->header.padding == copy->header.padding); + + _dbus_assert (_dbus_string_get_length (&message->header.data) == + _dbus_string_get_length (©->header.data)); + + _dbus_assert (_dbus_string_get_length (&message->body) == + _dbus_string_get_length (©->body)); + + verify_test_message (copy); + + name1 = dbus_message_get_interface (message); + name2 = dbus_message_get_interface (copy); + + _dbus_assert (strcmp (name1, name2) == 0); + + name1 = dbus_message_get_member (message); + name2 = dbus_message_get_member (copy); + + _dbus_assert (strcmp (name1, name2) == 0); + + dbus_message_unref (message); + dbus_message_unref (copy); + +#if 0 + /* FIXME */ + message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", + "/org/freedesktop/TestPath", + "Foo.TestInterface", + "TestMethod"); + + _dbus_message_set_serial (message, 1); + dbus_message_set_reply_serial (message, 0x12345678); + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_string (&iter, "Test string"); + dbus_message_iter_append_int32 (&iter, -0x12345678); + dbus_message_iter_append_uint32 (&iter, 0xedd1e); + dbus_message_iter_append_double (&iter, 3.14159); + + dbus_message_iter_append_array (&iter, &child_iter, DBUS_TYPE_DOUBLE); + dbus_message_iter_append_double (&child_iter, 1.5); + dbus_message_iter_append_double (&child_iter, 2.5); + + /* dict */ + dbus_message_iter_append_dict (&iter, &child_iter); + dbus_message_iter_append_dict_key (&child_iter, "test"); + dbus_message_iter_append_uint32 (&child_iter, 0xDEADBEEF); + + /* dict (in dict) */ + dbus_message_iter_append_dict_key (&child_iter, "testdict"); + dbus_message_iter_append_dict (&child_iter, &child_iter2); + + dbus_message_iter_append_dict_key (&child_iter2, "dictkey"); + dbus_message_iter_append_string (&child_iter2, "dictvalue"); + + /* array of array of int32 (in dict) */ + dbus_message_iter_append_dict_key (&child_iter, "array"); + dbus_message_iter_append_array (&child_iter, &child_iter2, DBUS_TYPE_ARRAY); + dbus_message_iter_append_array (&child_iter2, &child_iter3, DBUS_TYPE_INT32); + dbus_message_iter_append_int32 (&child_iter3, 0x12345678); + dbus_message_iter_append_int32 (&child_iter3, 0x23456781); + _dbus_warn ("next call expected to fail with wrong array type\n"); + _dbus_assert (!dbus_message_iter_append_array (&child_iter2, &child_iter3, DBUS_TYPE_UINT32)); + dbus_message_iter_append_array (&child_iter2, &child_iter3, DBUS_TYPE_INT32); + dbus_message_iter_append_int32 (&child_iter3, 0x34567812); + dbus_message_iter_append_int32 (&child_iter3, 0x45678123); + dbus_message_iter_append_int32 (&child_iter3, 0x56781234); + + dbus_message_iter_append_byte (&iter, 0xF0); + + dbus_message_iter_append_nil (&iter); + + dbus_message_iter_append_custom (&iter, "MyTypeName", + "data", 5); + + dbus_message_iter_append_byte (&iter, 0xF0); + + dbus_message_iter_append_array (&iter, &child_iter, DBUS_TYPE_INT32); + + dbus_message_iter_append_byte (&iter, 0xF0); + + dbus_message_iter_append_dict (&iter, &child_iter); + + dbus_message_iter_append_byte (&iter, 0xF0); + + message_iter_test (message); + + /* Message loader test */ + _dbus_message_lock (message); + loader = _dbus_message_loader_new (); + + /* check ref/unref */ + _dbus_message_loader_ref (loader); + _dbus_message_loader_unref (loader); + + /* Write the header data one byte at a time */ + data = _dbus_string_get_const_data (&message->header); + for (i = 0; i < _dbus_string_get_length (&message->header); i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, data[i]); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + /* Write the body data one byte at a time */ + data = _dbus_string_get_const_data (&message->body); + for (i = 0; i < _dbus_string_get_length (&message->body); i++) + { + DBusString *buffer; + + _dbus_message_loader_get_buffer (loader, &buffer); + _dbus_string_append_byte (buffer, data[i]); + _dbus_message_loader_return_buffer (loader, buffer, 1); + } + + copy = dbus_message_copy (message); /* save for tests below */ + dbus_message_unref (message); + + /* Now pop back the message */ + if (!_dbus_message_loader_queue_messages (loader)) + _dbus_assert_not_reached ("no memory to queue messages"); + + if (_dbus_message_loader_get_is_corrupted (loader)) + _dbus_assert_not_reached ("message loader corrupted"); + + message = _dbus_message_loader_pop_message (loader); + if (!message) + _dbus_assert_not_reached ("received a NULL message"); + + if (dbus_message_get_reply_serial (message) != 0x12345678) + _dbus_assert_not_reached ("reply serial fields differ"); + + message_iter_test (message); + + dbus_message_unref (message); + _dbus_message_loader_unref (loader); + + message = dbus_message_new_method_return (copy); + if (message == NULL) + _dbus_assert_not_reached ("out of memory\n"); + dbus_message_unref (copy); + + if (!dbus_message_append_args (message, + DBUS_TYPE_STRING, "hello", + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("no memory"); + + if (!dbus_message_has_signature (message, "s")) + _dbus_assert_not_reached ("method return has wrong signature"); + + dbus_error_init (&error); + if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, + &t, DBUS_TYPE_INVALID)) + + { + _dbus_warn ("Failed to get expected string arg: %s\n", error.message); + exit (1); + } + dbus_free (t); + + dbus_message_unref (message); + + /* This ServiceAcquired message used to trigger a bug in + * setting header fields, adding to regression test. + */ + message = dbus_message_new_signal (DBUS_PATH_ORG_FREEDESKTOP_DBUS, + DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, + "ServiceAcquired"); + + if (message == NULL) + _dbus_assert_not_reached ("out of memory"); + + _dbus_verbose ("Bytes after creation\n"); + _dbus_verbose_bytes_of_string (&message->header, 0, + _dbus_string_get_length (&message->header)); + + if (!dbus_message_set_destination (message, ":1.0") || + !dbus_message_append_args (message, + DBUS_TYPE_STRING, ":1.0", + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached ("out of memory"); + + _dbus_verbose ("Bytes after set_destination() and append_args()\n"); + _dbus_verbose_bytes_of_string (&message->header, 0, + _dbus_string_get_length (&message->header)); + + if (!dbus_message_set_sender (message, "org.freedesktop.DBus")) + _dbus_assert_not_reached ("out of memory"); + + _dbus_verbose ("Bytes after set_sender()\n"); + _dbus_verbose_bytes_of_string (&message->header, 0, + _dbus_string_get_length (&message->header)); + + /* When the bug happened the above set_destination() would + * corrupt the signature + */ + if (!dbus_message_has_signature (message, "s")) + { + _dbus_warn ("Signature should be 's' but is '%s'\n", + dbus_message_get_signature (message)); + _dbus_assert_not_reached ("signal has wrong signature"); + } + + /* have to set destination again to reproduce the bug */ + if (!dbus_message_set_destination (message, ":1.0")) + _dbus_assert_not_reached ("out of memory"); + + _dbus_verbose ("Bytes after set_destination()\n"); + _dbus_verbose_bytes_of_string (&message->header, 0, + _dbus_string_get_length (&message->header)); + + /* When the bug happened the above set_destination() would + * corrupt the signature + */ + if (!dbus_message_has_signature (message, "s")) + { + _dbus_warn ("Signature should be 's' but is '%s'\n", + dbus_message_get_signature (message)); + _dbus_assert_not_reached ("signal has wrong signature"); + } + + dbus_error_init (&error); + if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, + &t, DBUS_TYPE_INVALID)) + + { + _dbus_warn ("Failed to get expected string arg for signal: %s\n", error.message); + exit (1); + } + dbus_free (t); + + dbus_message_unref (message); + + /* Now load every message in test_data_dir if we have one */ + if (test_data_dir == NULL) + return TRUE; + + return dbus_internal_do_not_use_foreach_message_file (test_data_dir, + (DBusForeachMessageFileFunc) + dbus_internal_do_not_use_try_message_file, + NULL); + +#endif /* Commented out most tests for now */ + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index fc83a07e..8bb48667 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -26,12 +26,10 @@ #include "dbus-marshal-recursive.h" #include "dbus-marshal-validate.h" #include "dbus-marshal-header.h" -#include "dbus-message.h" -#include "dbus-message-internal.h" +#include "dbus-message-private.h" #include "dbus-object-tree.h" #include "dbus-memory.h" #include "dbus-list.h" -#include "dbus-dataslot.h" #include /** @@ -44,54 +42,11 @@ * @{ */ -static dbus_bool_t dbus_message_iter_get_args (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - ...); -static dbus_bool_t dbus_message_iter_get_args_valist (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - va_list var_args); - /* Not thread locked, but strictly const/read-only so should be OK */ /** An static string representing an empty signature */ _DBUS_STRING_DEFINE_STATIC(_dbus_empty_signature_str, ""); -/** How many bits are in the changed_stamp used to validate iterators */ -#define CHANGED_STAMP_BITS 21 - -/** - * @brief Internals of DBusMessage - * - * Object representing a message received from or to be sent to - * another application. This is an opaque object, all members - * are private. - */ -struct DBusMessage -{ - DBusAtomic refcount; /**< Reference count */ - - DBusHeader header; /**< Header network data and associated cache */ - - DBusString body; /**< Body network data. */ - - char byte_order; /**< Message byte order. */ - - unsigned int locked : 1; /**< Message being sent, no modifications allowed. */ - - DBusList *size_counters; /**< 0-N DBusCounter used to track message size. */ - long size_counter_delta; /**< Size we incremented the size counters by. */ - - dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */ - - DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */ - -#ifndef DBUS_DISABLE_CHECKS - int generation; /**< _dbus_current_generation when message was created */ -#endif -}; - /* these have wacky values to help trap uninitialized iterators; * but has to fit in 3 bits */ @@ -1380,42 +1335,7 @@ dbus_message_get_args_valist (DBusMessage *message, _dbus_return_val_if_error_is_set (error, FALSE); dbus_message_iter_init (message, &iter); - return dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args); -} - -/** - * Reads arguments from a message iterator given a variable argument - * list. Only arguments of basic type and arrays of fixed-length - * basic type may be read with this function. See - * dbus_message_get_args() for more details. - * - * @todo this is static for now because there's no corresponding - * iter_append_args() and I'm not sure we need this function to be - * public since dbus_message_get_args() is what you usually want - * - * @param iter the message iterator - * @param error error to be filled in on failure - * @param first_arg_type the first argument type - * @param ... location for first argument value, then list of type-location pairs - * @returns #FALSE if the error was set - */ -static dbus_bool_t -dbus_message_iter_get_args (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - ...) -{ - dbus_bool_t retval; - va_list var_args; - - _dbus_return_val_if_fail (iter != NULL, FALSE); - _dbus_return_val_if_error_is_set (error, FALSE); - - va_start (var_args, first_arg_type); - retval = dbus_message_iter_get_args_valist (iter, error, first_arg_type, var_args); - va_end (var_args); - - return retval; + return _dbus_message_iter_get_args_valist (&iter, error, first_arg_type, var_args); } static void @@ -1700,9 +1620,6 @@ dbus_message_iter_get_fixed_array (DBusMessageIter *iter, * dbus_message_get_args() is the place to go for complete * documentation. * - * @todo this is static for now, should be public if - * dbus_message_iter_get_args_valist() is made public. - * * @see dbus_message_get_args * @param iter the message iter * @param error error to be filled in @@ -1710,18 +1627,17 @@ dbus_message_iter_get_fixed_array (DBusMessageIter *iter, * @param var_args return location for first argument, followed by list of type/location pairs * @returns #FALSE if error was set */ -static dbus_bool_t -dbus_message_iter_get_args_valist (DBusMessageIter *iter, - DBusError *error, - int first_arg_type, - va_list var_args) +dbus_bool_t +_dbus_message_iter_get_args_valist (DBusMessageIter *iter, + DBusError *error, + int first_arg_type, + va_list var_args) { DBusMessageRealIter *real = (DBusMessageRealIter *)iter; int spec_type, msg_type, i; dbus_bool_t retval; - _dbus_return_val_if_fail (_dbus_message_iter_check (real), FALSE); - _dbus_return_val_if_error_is_set (error, FALSE); + _dbus_assert (_dbus_message_iter_check (real)); retval = FALSE; @@ -1749,7 +1665,7 @@ dbus_message_iter_get_args_valist (DBusMessageIter *iter, ptr = va_arg (var_args, DBusBasicValue*); - _dbus_return_val_if_fail (ptr != NULL, FALSE); + _dbus_assert (ptr != NULL); _dbus_type_reader_read_basic (&real->u.reader, ptr); @@ -1782,8 +1698,8 @@ dbus_message_iter_get_args_valist (DBusMessageIter *iter, ptr = va_arg (var_args, const DBusBasicValue**); n_elements_p = va_arg (var_args, int*); - _dbus_return_val_if_fail (ptr != NULL, FALSE); - _dbus_return_val_if_fail (n_elements_p != NULL, FALSE); + _dbus_assert (ptr != NULL); + _dbus_assert (n_elements_p != NULL); _dbus_type_reader_recurse (&real->u.reader, &array); @@ -1801,8 +1717,8 @@ dbus_message_iter_get_args_valist (DBusMessageIter *iter, str_array_p = va_arg (var_args, char***); n_elements_p = va_arg (var_args, int*); - _dbus_return_val_if_fail (str_array_p != NULL, FALSE); - _dbus_return_val_if_fail (n_elements_p != NULL, FALSE); + _dbus_assert (str_array_p != NULL); + _dbus_assert (n_elements_p != NULL); /* Count elements in the array */ _dbus_type_reader_recurse (&real->u.reader, &array); @@ -2937,41 +2853,6 @@ dbus_set_error_from_message (DBusError *error, * * @{ */ -/** - * @typedef DBusMessageLoader - * - * The DBusMessageLoader object encapsulates the process of converting - * a byte stream into a series of DBusMessage. It buffers the incoming - * bytes as efficiently as possible, and generates a queue of - * messages. DBusMessageLoader is typically used as part of a - * DBusTransport implementation. The DBusTransport then hands off - * the loaded messages to a DBusConnection, making the messages - * visible to the application. - * - * @todo write tests for break-loader that a) randomly delete header - * fields and b) set string fields to zero-length and other funky - * values. - * - */ - -/** - * Implementation details of DBusMessageLoader. - * All members are private. - */ -struct DBusMessageLoader -{ - int refcount; /**< Reference count. */ - - DBusString data; /**< Buffered data */ - - DBusList *messages; /**< Complete messages. */ - - long max_message_size; /**< Maximum size of a message */ - - unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ - - unsigned int corrupted : 1; /**< We got broken data, and are no longer working */ -}; /** * The initial buffer size of the message loader. @@ -3579,1240 +3460,5 @@ dbus_message_type_to_string (int type) } /** @} */ -#ifdef DBUS_BUILD_TESTS -#include "dbus-test.h" -#include -#include - -static dbus_bool_t -check_have_valid_message (DBusMessageLoader *loader) -{ - DBusMessage *message; - dbus_bool_t retval; - - message = NULL; - retval = FALSE; - - if (!_dbus_message_loader_queue_messages (loader)) - _dbus_assert_not_reached ("no memory to queue messages"); - - if (_dbus_message_loader_get_is_corrupted (loader)) - { - _dbus_warn ("loader corrupted on message that was expected to be valid\n"); - goto failed; - } - - message = _dbus_message_loader_pop_message (loader); - if (message == NULL) - { - _dbus_warn ("didn't load message that was expected to be valid (message not popped)\n"); - goto failed; - } - - if (_dbus_string_get_length (&loader->data) > 0) - { - _dbus_warn ("had leftover bytes from expected-to-be-valid single message\n"); - goto failed; - } - -#if 0 - /* FIXME */ - /* Verify that we're able to properly deal with the message. - * For example, this would detect improper handling of messages - * in nonstandard byte order. - */ - if (!check_message_handling (message)) - goto failed; -#endif - - retval = TRUE; - - failed: - if (message) - dbus_message_unref (message); - - return retval; -} - -static dbus_bool_t -check_invalid_message (DBusMessageLoader *loader) -{ - dbus_bool_t retval; - - retval = FALSE; - - if (!_dbus_message_loader_queue_messages (loader)) - _dbus_assert_not_reached ("no memory to queue messages"); - - if (!_dbus_message_loader_get_is_corrupted (loader)) - { - _dbus_warn ("loader not corrupted on message that was expected to be invalid\n"); - goto failed; - } - - retval = TRUE; - - failed: - return retval; -} - -static dbus_bool_t -check_incomplete_message (DBusMessageLoader *loader) -{ - DBusMessage *message; - dbus_bool_t retval; - - message = NULL; - retval = FALSE; - - if (!_dbus_message_loader_queue_messages (loader)) - _dbus_assert_not_reached ("no memory to queue messages"); - - if (_dbus_message_loader_get_is_corrupted (loader)) - { - _dbus_warn ("loader corrupted on message that was expected to be valid (but incomplete)\n"); - goto failed; - } - - message = _dbus_message_loader_pop_message (loader); - if (message != NULL) - { - _dbus_warn ("loaded message that was expected to be incomplete\n"); - goto failed; - } - - retval = TRUE; - - failed: - if (message) - dbus_message_unref (message); - return retval; -} - -static dbus_bool_t -check_loader_results (DBusMessageLoader *loader, - DBusMessageValidity validity) -{ - if (!_dbus_message_loader_queue_messages (loader)) - _dbus_assert_not_reached ("no memory to queue messages"); - - switch (validity) - { - case _DBUS_MESSAGE_VALID: - return check_have_valid_message (loader); - case _DBUS_MESSAGE_INVALID: - return check_invalid_message (loader); - case _DBUS_MESSAGE_INCOMPLETE: - return check_incomplete_message (loader); - case _DBUS_MESSAGE_UNKNOWN: - return TRUE; - } - - _dbus_assert_not_reached ("bad DBusMessageValidity"); - return FALSE; -} - - -/** - * Loads the message in the given message file. - * - * @param filename filename to load - * @param is_raw if #TRUE load as binary data, if #FALSE as message builder language - * @param data string to load message into - * @returns #TRUE if the message was loaded - */ -dbus_bool_t -dbus_internal_do_not_use_load_message_file (const DBusString *filename, - dbus_bool_t is_raw, - DBusString *data) -{ - dbus_bool_t retval; - - retval = FALSE; - - if (is_raw) - { - DBusError error; - - _dbus_verbose ("Loading raw %s\n", _dbus_string_get_const_data (filename)); - dbus_error_init (&error); - if (!_dbus_file_get_contents (data, filename, &error)) - { - _dbus_warn ("Could not load message file %s: %s\n", - _dbus_string_get_const_data (filename), - error.message); - dbus_error_free (&error); - goto failed; - } - } - else - { - if (FALSE) /* Message builder disabled, probably permanently, - * I want to do it another way - */ - { - _dbus_warn ("Could not load message file %s\n", - _dbus_string_get_const_data (filename)); - goto failed; - } - } - - retval = TRUE; - - failed: - - return retval; -} - -/** - * Tries loading the message in the given message file - * and verifies that DBusMessageLoader can handle it. - * - * @param filename filename to load - * @param is_raw if #TRUE load as binary data, if #FALSE as message builder language - * @param expected_validity what the message has to be like to return #TRUE - * @returns #TRUE if the message has the expected validity - */ -dbus_bool_t -dbus_internal_do_not_use_try_message_file (const DBusString *filename, - dbus_bool_t is_raw, - DBusMessageValidity expected_validity) -{ - DBusString data; - dbus_bool_t retval; - - retval = FALSE; - - if (!_dbus_string_init (&data)) - _dbus_assert_not_reached ("could not allocate string\n"); - - if (!dbus_internal_do_not_use_load_message_file (filename, is_raw, - &data)) - goto failed; - - retval = dbus_internal_do_not_use_try_message_data (&data, expected_validity); - - failed: - - if (!retval) - { - if (_dbus_string_get_length (&data) > 0) - _dbus_verbose_bytes_of_string (&data, 0, - _dbus_string_get_length (&data)); - - _dbus_warn ("Failed message loader test on %s\n", - _dbus_string_get_const_data (filename)); - } - - _dbus_string_free (&data); - - return retval; -} - -/** - * Tries loading the given message data. - * - * - * @param data the message data - * @param expected_validity what the message has to be like to return #TRUE - * @returns #TRUE if the message has the expected validity - */ -dbus_bool_t -dbus_internal_do_not_use_try_message_data (const DBusString *data, - DBusMessageValidity expected_validity) -{ - DBusMessageLoader *loader; - dbus_bool_t retval; - int len; - int i; - - loader = NULL; - retval = FALSE; - - /* Write the data one byte at a time */ - - loader = _dbus_message_loader_new (); - - /* check some trivial loader functions */ - _dbus_message_loader_ref (loader); - _dbus_message_loader_unref (loader); - _dbus_message_loader_get_max_message_size (loader); - - len = _dbus_string_get_length (data); - for (i = 0; i < len; i++) - { - DBusString *buffer; - - _dbus_message_loader_get_buffer (loader, &buffer); - _dbus_string_append_byte (buffer, - _dbus_string_get_byte (data, i)); - _dbus_message_loader_return_buffer (loader, buffer, 1); - } - - if (!check_loader_results (loader, expected_validity)) - goto failed; - - _dbus_message_loader_unref (loader); - loader = NULL; - - /* Write the data all at once */ - - loader = _dbus_message_loader_new (); - - { - DBusString *buffer; - - _dbus_message_loader_get_buffer (loader, &buffer); - _dbus_string_copy (data, 0, buffer, - _dbus_string_get_length (buffer)); - _dbus_message_loader_return_buffer (loader, buffer, 1); - } - - if (!check_loader_results (loader, expected_validity)) - goto failed; - - _dbus_message_loader_unref (loader); - loader = NULL; - - /* Write the data 2 bytes at a time */ - - loader = _dbus_message_loader_new (); - - len = _dbus_string_get_length (data); - for (i = 0; i < len; i += 2) - { - DBusString *buffer; - - _dbus_message_loader_get_buffer (loader, &buffer); - _dbus_string_append_byte (buffer, - _dbus_string_get_byte (data, i)); - if ((i+1) < len) - _dbus_string_append_byte (buffer, - _dbus_string_get_byte (data, i+1)); - _dbus_message_loader_return_buffer (loader, buffer, 1); - } - - if (!check_loader_results (loader, expected_validity)) - goto failed; - - _dbus_message_loader_unref (loader); - loader = NULL; - - retval = TRUE; - - failed: - - if (loader) - _dbus_message_loader_unref (loader); - - return retval; -} - -static dbus_bool_t -process_test_subdir (const DBusString *test_base_dir, - const char *subdir, - DBusMessageValidity validity, - DBusForeachMessageFileFunc function, - void *user_data) -{ - DBusString test_directory; - DBusString filename; - DBusDirIter *dir; - dbus_bool_t retval; - DBusError error; - - retval = FALSE; - dir = NULL; - - if (!_dbus_string_init (&test_directory)) - _dbus_assert_not_reached ("didn't allocate test_directory\n"); - - _dbus_string_init_const (&filename, subdir); - - if (!_dbus_string_copy (test_base_dir, 0, - &test_directory, 0)) - _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory"); - - if (!_dbus_concat_dir_and_file (&test_directory, &filename)) - _dbus_assert_not_reached ("couldn't allocate full path"); - - _dbus_string_free (&filename); - if (!_dbus_string_init (&filename)) - _dbus_assert_not_reached ("didn't allocate filename string\n"); - - dbus_error_init (&error); - dir = _dbus_directory_open (&test_directory, &error); - if (dir == NULL) - { - _dbus_warn ("Could not open %s: %s\n", - _dbus_string_get_const_data (&test_directory), - error.message); - dbus_error_free (&error); - goto failed; - } - - printf ("Testing %s:\n", subdir); - - next: - while (_dbus_directory_get_next_file (dir, &filename, &error)) - { - DBusString full_path; - dbus_bool_t is_raw; - - if (!_dbus_string_init (&full_path)) - _dbus_assert_not_reached ("couldn't init string"); - - if (!_dbus_string_copy (&test_directory, 0, &full_path, 0)) - _dbus_assert_not_reached ("couldn't copy dir to full_path"); - - if (!_dbus_concat_dir_and_file (&full_path, &filename)) - _dbus_assert_not_reached ("couldn't concat file to dir"); - - if (_dbus_string_ends_with_c_str (&filename, ".message")) - is_raw = FALSE; - else if (_dbus_string_ends_with_c_str (&filename, ".message-raw")) - is_raw = TRUE; - else - { - _dbus_verbose ("Skipping non-.message file %s\n", - _dbus_string_get_const_data (&filename)); - _dbus_string_free (&full_path); - goto next; - } - - printf (" %s\n", - _dbus_string_get_const_data (&filename)); - - _dbus_verbose (" expecting %s for %s\n", - validity == _DBUS_MESSAGE_VALID ? "valid" : - (validity == _DBUS_MESSAGE_INVALID ? "invalid" : - (validity == _DBUS_MESSAGE_INCOMPLETE ? "incomplete" : "unknown")), - _dbus_string_get_const_data (&filename)); - - if (! (*function) (&full_path, is_raw, validity, user_data)) - { - _dbus_string_free (&full_path); - goto failed; - } - else - _dbus_string_free (&full_path); - } - - if (dbus_error_is_set (&error)) - { - _dbus_warn ("Could not get next file in %s: %s\n", - _dbus_string_get_const_data (&test_directory), - error.message); - dbus_error_free (&error); - goto failed; - } - - retval = TRUE; - - failed: - - if (dir) - _dbus_directory_close (dir); - _dbus_string_free (&test_directory); - _dbus_string_free (&filename); - - return retval; -} - -/** - * Runs the given function on every message file in the test suite. - * The function should return #FALSE on test failure or fatal error. - * - * @param test_data_dir root dir of the test suite data files (top_srcdir/test/data) - * @param func the function to run - * @param user_data data for function - * @returns #FALSE if there's a failure - */ -dbus_bool_t -dbus_internal_do_not_use_foreach_message_file (const char *test_data_dir, - DBusForeachMessageFileFunc func, - void *user_data) -{ - DBusString test_directory; - dbus_bool_t retval; - - retval = FALSE; - - _dbus_string_init_const (&test_directory, test_data_dir); - - if (!process_test_subdir (&test_directory, "valid-messages", - _DBUS_MESSAGE_VALID, func, user_data)) - goto failed; - - if (!process_test_subdir (&test_directory, "invalid-messages", - _DBUS_MESSAGE_INVALID, func, user_data)) - goto failed; - - if (!process_test_subdir (&test_directory, "incomplete-messages", - _DBUS_MESSAGE_INCOMPLETE, func, user_data)) - goto failed; - - retval = TRUE; - - failed: - - _dbus_string_free (&test_directory); - - return retval; -} - -#define GET_AND_CHECK(iter, typename, literal) \ - do { \ - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ - _dbus_assert_not_reached ("got wrong argument type from message iter"); \ - dbus_message_iter_get_basic (&iter, &v_##typename); \ - if (v_##typename != literal) \ - _dbus_assert_not_reached ("got wrong value from message iter"); \ - } while (0) - -#define GET_AND_CHECK_STRCMP(iter, typename, literal) \ - do { \ - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_##typename) \ - _dbus_assert_not_reached ("got wrong argument type from message iter"); \ - dbus_message_iter_get_basic (&iter, &v_##typename); \ - if (strcmp (v_##typename, literal) != 0) \ - _dbus_assert_not_reached ("got wrong value from message iter"); \ - } while (0) - -#define GET_AND_CHECK_AND_NEXT(iter, typename, literal) \ - do { \ - GET_AND_CHECK(iter, typename, literal); \ - if (!dbus_message_iter_next (&iter)) \ - _dbus_assert_not_reached ("failed to move iter to next"); \ - } while (0) - -#define GET_AND_CHECK_STRCMP_AND_NEXT(iter, typename, literal) \ - do { \ - GET_AND_CHECK_STRCMP(iter, typename, literal); \ - if (!dbus_message_iter_next (&iter)) \ - _dbus_assert_not_reached ("failed to move iter to next"); \ - } while (0) - -static void -message_iter_test (DBusMessage *message) -{ - DBusMessageIter iter, array, array2; - const char *v_STRING; - double v_DOUBLE; - dbus_int32_t v_INT32; - dbus_uint32_t v_UINT32; -#ifdef DBUS_HAVE_INT64 - dbus_int64_t v_INT64; - dbus_uint64_t v_UINT64; -#endif - unsigned char v_BYTE; - unsigned char v_BOOLEAN; - - const dbus_int32_t *our_int_array; - int len; - - dbus_message_iter_init (message, &iter); - - GET_AND_CHECK_STRCMP_AND_NEXT (iter, STRING, "Test string"); - GET_AND_CHECK_AND_NEXT (iter, INT32, -0x12345678); - GET_AND_CHECK_AND_NEXT (iter, UINT32, 0xedd1e); - GET_AND_CHECK_AND_NEXT (iter, DOUBLE, 3.14159); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) - _dbus_assert_not_reached ("Argument type not an array"); - - if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DOUBLE) - _dbus_assert_not_reached ("Array type not double"); - - dbus_message_iter_recurse (&iter, &array); - - GET_AND_CHECK_AND_NEXT (array, DOUBLE, 1.5); - GET_AND_CHECK (array, DOUBLE, 2.5); - - if (dbus_message_iter_next (&array)) - _dbus_assert_not_reached ("Didn't reach end of array"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - GET_AND_CHECK_AND_NEXT (iter, BYTE, 0xF0); - - if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) - _dbus_assert_not_reached ("no array"); - - if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_INT32) - _dbus_assert_not_reached ("Array type not int32"); - - /* Empty array */ - dbus_message_iter_recurse (&iter, &array); - - if (dbus_message_iter_next (&array)) - _dbus_assert_not_reached ("Didn't reach end of array"); - - if (!dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Reached end of arguments"); - - GET_AND_CHECK (iter, BYTE, 0xF0); - - if (dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Didn't reach end of arguments"); -} - -static void -verify_test_message (DBusMessage *message) -{ - DBusMessageIter iter; - DBusError error; - dbus_int32_t our_int; - const char *our_str; - double our_double; - unsigned char our_bool; - unsigned char our_byte_1, our_byte_2; - dbus_uint32_t our_uint32; - const dbus_int32_t *our_uint32_array = (void*)0xdeadbeef; - int our_uint32_array_len; - dbus_int32_t *our_int32_array = (void*)0xdeadbeef; - int our_int32_array_len; -#ifdef DBUS_HAVE_INT64 - dbus_int64_t our_int64; - dbus_uint64_t our_uint64; - dbus_int64_t *our_uint64_array = (void*)0xdeadbeef; - int our_uint64_array_len; - const dbus_int64_t *our_int64_array = (void*)0xdeadbeef; - int our_int64_array_len; -#endif - const double *our_double_array = (void*)0xdeadbeef; - int our_double_array_len; - const unsigned char *our_byte_array = (void*)0xdeadbeef; - int our_byte_array_len; - const unsigned char *our_boolean_array = (void*)0xdeadbeef; - int our_boolean_array_len; - - dbus_message_iter_init (message, &iter); - - dbus_error_init (&error); - if (!dbus_message_iter_get_args (&iter, &error, - DBUS_TYPE_INT32, &our_int, -#ifdef DBUS_HAVE_INT64 - DBUS_TYPE_INT64, &our_int64, - DBUS_TYPE_UINT64, &our_uint64, -#endif - DBUS_TYPE_STRING, &our_str, - DBUS_TYPE_DOUBLE, &our_double, - DBUS_TYPE_BOOLEAN, &our_bool, - DBUS_TYPE_BYTE, &our_byte_1, - DBUS_TYPE_BYTE, &our_byte_2, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, - &our_uint32_array, &our_uint32_array_len, - DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, - &our_int32_array, &our_int32_array_len, -#ifdef DBUS_HAVE_INT64 - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, - &our_uint64_array, &our_uint64_array_len, - DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, - &our_int64_array, &our_int64_array_len, -#endif - DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, - &our_double_array, &our_double_array_len, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &our_byte_array, &our_byte_array_len, - DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, - &our_boolean_array, &our_boolean_array_len, - 0)) - { - _dbus_warn ("error: %s - %s\n", error.name, - (error.message != NULL) ? error.message : "no message"); - _dbus_assert_not_reached ("Could not get arguments"); - } - - if (our_int != -0x12345678) - _dbus_assert_not_reached ("integers differ!"); - -#ifdef DBUS_HAVE_INT64 - if (our_int64 != DBUS_INT64_CONSTANT (-0x123456789abcd)) - _dbus_assert_not_reached ("64-bit integers differ!"); - if (our_uint64 != DBUS_UINT64_CONSTANT (0x123456789abcd)) - _dbus_assert_not_reached ("64-bit unsigned integers differ!"); -#endif - - if (our_double != 3.14159) - _dbus_assert_not_reached ("doubles differ!"); - - if (strcmp (our_str, "Test string") != 0) - _dbus_assert_not_reached ("strings differ!"); - - if (!our_bool) - _dbus_assert_not_reached ("booleans differ"); - - if (our_byte_1 != 42) - _dbus_assert_not_reached ("bytes differ!"); - - if (our_byte_2 != 24) - _dbus_assert_not_reached ("bytes differ!"); - - if (our_uint32_array_len != 4 || - our_uint32_array[0] != 0x12345678 || - our_uint32_array[1] != 0x23456781 || - our_uint32_array[2] != 0x34567812 || - our_uint32_array[3] != 0x45678123) - _dbus_assert_not_reached ("uint array differs"); - - if (our_int32_array_len != 4 || - our_int32_array[0] != 0x12345678 || - our_int32_array[1] != -0x23456781 || - our_int32_array[2] != 0x34567812 || - our_int32_array[3] != -0x45678123) - _dbus_assert_not_reached ("int array differs"); - -#ifdef DBUS_HAVE_INT64 - if (our_uint64_array_len != 4 || - our_uint64_array[0] != 0x12345678 || - our_uint64_array[1] != 0x23456781 || - our_uint64_array[2] != 0x34567812 || - our_uint64_array[3] != 0x45678123) - _dbus_assert_not_reached ("uint64 array differs"); - - if (our_int64_array_len != 4 || - our_int64_array[0] != 0x12345678 || - our_int64_array[1] != -0x23456781 || - our_int64_array[2] != 0x34567812 || - our_int64_array[3] != -0x45678123) - _dbus_assert_not_reached ("int64 array differs"); -#endif /* DBUS_HAVE_INT64 */ - - if (our_double_array_len != 3) - _dbus_assert_not_reached ("double array had wrong length"); - - /* On all IEEE machines (i.e. everything sane) exact equality - * should be preserved over the wire - */ - if (our_double_array[0] != 0.1234 || - our_double_array[1] != 9876.54321 || - our_double_array[2] != -300.0) - _dbus_assert_not_reached ("double array had wrong values"); - - if (our_byte_array_len != 4) - _dbus_assert_not_reached ("byte array had wrong length"); - - if (our_byte_array[0] != 'a' || - our_byte_array[1] != 'b' || - our_byte_array[2] != 'c' || - our_byte_array[3] != 234) - _dbus_assert_not_reached ("byte array had wrong values"); - - if (our_boolean_array_len != 5) - _dbus_assert_not_reached ("bool array had wrong length"); - - if (our_boolean_array[0] != TRUE || - our_boolean_array[1] != FALSE || - our_boolean_array[2] != TRUE || - our_boolean_array[3] != TRUE || - our_boolean_array[4] != FALSE) - _dbus_assert_not_reached ("bool array had wrong values"); - - if (dbus_message_iter_next (&iter)) - _dbus_assert_not_reached ("Didn't reach end of arguments"); -} - -/** - * @ingroup DBusMessageInternals - * Unit test for DBusMessage. - * - * @returns #TRUE on success. - */ -dbus_bool_t -_dbus_message_test (const char *test_data_dir) -{ - DBusMessage *message; - DBusMessageLoader *loader; - DBusMessageIter iter, child_iter, child_iter2, child_iter3; - int i; - const char *data; - DBusMessage *copy; - const char *name1; - const char *name2; - const dbus_uint32_t our_uint32_array[] = - { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; - const dbus_uint32_t our_int32_array[] = - { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; - const dbus_uint32_t *v_ARRAY_UINT32 = our_uint32_array; - const dbus_int32_t *v_ARRAY_INT32 = our_int32_array; -#ifdef DBUS_HAVE_INT64 - const dbus_uint64_t our_uint64_array[] = - { 0x12345678, 0x23456781, 0x34567812, 0x45678123 }; - const dbus_uint64_t our_int64_array[] = - { 0x12345678, -0x23456781, 0x34567812, -0x45678123 }; - const dbus_uint64_t *v_ARRAY_UINT64 = our_uint64_array; - const dbus_int64_t *v_ARRAY_INT64 = our_int64_array; -#endif - const char *our_string_array[] = { "Foo", "bar", "", "woo woo woo woo" }; - const char **v_ARRAY_STRING = our_string_array; - const double our_double_array[] = { 0.1234, 9876.54321, -300.0 }; - const double *v_ARRAY_DOUBLE = our_double_array; - const unsigned char our_byte_array[] = { 'a', 'b', 'c', 234 }; - const unsigned char *v_ARRAY_BYTE = our_byte_array; - const unsigned char our_boolean_array[] = { TRUE, FALSE, TRUE, TRUE, FALSE }; - const unsigned char *v_ARRAY_BOOLEAN = our_boolean_array; - char sig[64]; - const char *s; - char *t; - DBusError error; - const char *v_STRING; - double v_DOUBLE; - dbus_int32_t v_INT32; - dbus_uint32_t v_UINT32; -#ifdef DBUS_HAVE_INT64 - dbus_int64_t v_INT64; - dbus_uint64_t v_UINT64; -#endif - unsigned char v_BYTE; - unsigned char v2_BYTE; - unsigned char v_BOOLEAN; - - _dbus_assert (sizeof (DBusMessageRealIter) <= sizeof (DBusMessageIter)); - - message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", - "/org/freedesktop/TestPath", - "Foo.TestInterface", - "TestMethod"); - _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService")); - _dbus_assert (dbus_message_is_method_call (message, "Foo.TestInterface", - "TestMethod")); - _dbus_assert (strcmp (dbus_message_get_path (message), - "/org/freedesktop/TestPath") == 0); - _dbus_message_set_serial (message, 1234); - - /* string length including nul byte not a multiple of 4 */ - if (!dbus_message_set_sender (message, "org.foo.bar1")) - _dbus_assert_not_reached ("out of memory"); - - _dbus_assert (dbus_message_has_sender (message, "org.foo.bar1")); - dbus_message_set_reply_serial (message, 5678); - - _dbus_verbose_bytes_of_string (&message->header.data, 0, - _dbus_string_get_length (&message->header.data)); - _dbus_verbose_bytes_of_string (&message->body, 0, - _dbus_string_get_length (&message->body)); - - if (!dbus_message_set_sender (message, NULL)) - _dbus_assert_not_reached ("out of memory"); - - - _dbus_verbose_bytes_of_string (&message->header.data, 0, - _dbus_string_get_length (&message->header.data)); - _dbus_verbose_bytes_of_string (&message->body, 0, - _dbus_string_get_length (&message->body)); - - - _dbus_assert (!dbus_message_has_sender (message, "org.foo.bar1")); - _dbus_assert (dbus_message_get_serial (message) == 1234); - _dbus_assert (dbus_message_get_reply_serial (message) == 5678); - _dbus_assert (dbus_message_has_destination (message, "org.freedesktop.DBus.TestService")); - - _dbus_assert (dbus_message_get_no_reply (message) == FALSE); - dbus_message_set_no_reply (message, TRUE); - _dbus_assert (dbus_message_get_no_reply (message) == TRUE); - dbus_message_set_no_reply (message, FALSE); - _dbus_assert (dbus_message_get_no_reply (message) == FALSE); - - /* Set/get some header fields */ - - if (!dbus_message_set_path (message, "/foo")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_path (message), - "/foo") == 0); - - if (!dbus_message_set_interface (message, "org.Foo")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_interface (message), - "org.Foo") == 0); - - if (!dbus_message_set_member (message, "Bar")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_member (message), - "Bar") == 0); - - /* Set/get them with longer values */ - if (!dbus_message_set_path (message, "/foo/bar")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_path (message), - "/foo/bar") == 0); - - if (!dbus_message_set_interface (message, "org.Foo.Bar")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_interface (message), - "org.Foo.Bar") == 0); - - if (!dbus_message_set_member (message, "BarFoo")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_member (message), - "BarFoo") == 0); - - /* Realloc shorter again */ - - if (!dbus_message_set_path (message, "/foo")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_path (message), - "/foo") == 0); - - if (!dbus_message_set_interface (message, "org.Foo")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_interface (message), - "org.Foo") == 0); - - if (!dbus_message_set_member (message, "Bar")) - _dbus_assert_not_reached ("out of memory"); - _dbus_assert (strcmp (dbus_message_get_member (message), - "Bar") == 0); - - dbus_message_unref (message); - - /* Test the vararg functions */ - message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", - "/org/freedesktop/TestPath", - "Foo.TestInterface", - "TestMethod"); - _dbus_message_set_serial (message, 1); - - v_INT32 = -0x12345678; -#ifdef DBUS_HAVE_INT64 - v_INT64 = DBUS_INT64_CONSTANT (-0x123456789abcd); - v_UINT64 = DBUS_UINT64_CONSTANT (0x123456789abcd); -#endif - v_STRING = "Test string"; - v_DOUBLE = 3.14159; - v_BOOLEAN = TRUE; - v_BYTE = 42; - v2_BYTE = 24; - - dbus_message_append_args (message, - DBUS_TYPE_INT32, &v_INT32, -#ifdef DBUS_HAVE_INT64 - DBUS_TYPE_INT64, &v_INT64, - DBUS_TYPE_UINT64, &v_UINT64, -#endif - DBUS_TYPE_STRING, &v_STRING, - DBUS_TYPE_DOUBLE, &v_DOUBLE, - DBUS_TYPE_BOOLEAN, &v_BOOLEAN, - DBUS_TYPE_BYTE, &v_BYTE, - DBUS_TYPE_BYTE, &v2_BYTE, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &v_ARRAY_UINT32, - _DBUS_N_ELEMENTS (our_uint32_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY_INT32, - _DBUS_N_ELEMENTS (our_int32_array), -#ifdef DBUS_HAVE_INT64 - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, &v_ARRAY_UINT64, - _DBUS_N_ELEMENTS (our_uint64_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, &v_ARRAY_INT64, - _DBUS_N_ELEMENTS (our_int64_array), -#endif - DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &v_ARRAY_DOUBLE, - _DBUS_N_ELEMENTS (our_double_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &v_ARRAY_BYTE, - _DBUS_N_ELEMENTS (our_byte_array), - DBUS_TYPE_ARRAY, DBUS_TYPE_BOOLEAN, &v_ARRAY_BOOLEAN, - _DBUS_N_ELEMENTS (our_boolean_array), - DBUS_TYPE_INVALID); - - i = 0; - sig[i++] = DBUS_TYPE_INT32; -#ifdef DBUS_HAVE_INT64 - sig[i++] = DBUS_TYPE_INT64; - sig[i++] = DBUS_TYPE_UINT64; -#endif - sig[i++] = DBUS_TYPE_STRING; - sig[i++] = DBUS_TYPE_DOUBLE; - sig[i++] = DBUS_TYPE_BOOLEAN; - sig[i++] = DBUS_TYPE_BYTE; - sig[i++] = DBUS_TYPE_BYTE; - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_UINT32; - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_INT32; -#ifdef DBUS_HAVE_INT64 - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_UINT64; - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_INT64; -#endif - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_DOUBLE; - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_BYTE; - sig[i++] = DBUS_TYPE_ARRAY; - sig[i++] = DBUS_TYPE_BOOLEAN; - sig[i++] = DBUS_TYPE_INVALID; - - _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig)); - - _dbus_verbose ("HEADER\n"); - _dbus_verbose_bytes_of_string (&message->header.data, 0, - _dbus_string_get_length (&message->header.data)); - _dbus_verbose ("BODY\n"); - _dbus_verbose_bytes_of_string (&message->body, 0, - _dbus_string_get_length (&message->body)); - - _dbus_verbose ("Signature expected \"%s\" actual \"%s\"\n", - sig, dbus_message_get_signature (message)); - - s = dbus_message_get_signature (message); - - _dbus_assert (dbus_message_has_signature (message, sig)); - _dbus_assert (strcmp (s, sig) == 0); - - verify_test_message (message); - - copy = dbus_message_copy (message); - - _dbus_assert (dbus_message_get_reply_serial (message) == - dbus_message_get_reply_serial (copy)); - _dbus_assert (message->header.padding == copy->header.padding); - - _dbus_assert (_dbus_string_get_length (&message->header.data) == - _dbus_string_get_length (©->header.data)); - - _dbus_assert (_dbus_string_get_length (&message->body) == - _dbus_string_get_length (©->body)); - - verify_test_message (copy); - - name1 = dbus_message_get_interface (message); - name2 = dbus_message_get_interface (copy); - - _dbus_assert (strcmp (name1, name2) == 0); - - name1 = dbus_message_get_member (message); - name2 = dbus_message_get_member (copy); - - _dbus_assert (strcmp (name1, name2) == 0); - - dbus_message_unref (message); - dbus_message_unref (copy); - -#if 0 - /* FIXME */ - message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", - "/org/freedesktop/TestPath", - "Foo.TestInterface", - "TestMethod"); - - _dbus_message_set_serial (message, 1); - dbus_message_set_reply_serial (message, 0x12345678); - - dbus_message_iter_init_append (message, &iter); - dbus_message_iter_append_string (&iter, "Test string"); - dbus_message_iter_append_int32 (&iter, -0x12345678); - dbus_message_iter_append_uint32 (&iter, 0xedd1e); - dbus_message_iter_append_double (&iter, 3.14159); - - dbus_message_iter_append_array (&iter, &child_iter, DBUS_TYPE_DOUBLE); - dbus_message_iter_append_double (&child_iter, 1.5); - dbus_message_iter_append_double (&child_iter, 2.5); - - /* dict */ - dbus_message_iter_append_dict (&iter, &child_iter); - dbus_message_iter_append_dict_key (&child_iter, "test"); - dbus_message_iter_append_uint32 (&child_iter, 0xDEADBEEF); - - /* dict (in dict) */ - dbus_message_iter_append_dict_key (&child_iter, "testdict"); - dbus_message_iter_append_dict (&child_iter, &child_iter2); - - dbus_message_iter_append_dict_key (&child_iter2, "dictkey"); - dbus_message_iter_append_string (&child_iter2, "dictvalue"); - - /* array of array of int32 (in dict) */ - dbus_message_iter_append_dict_key (&child_iter, "array"); - dbus_message_iter_append_array (&child_iter, &child_iter2, DBUS_TYPE_ARRAY); - dbus_message_iter_append_array (&child_iter2, &child_iter3, DBUS_TYPE_INT32); - dbus_message_iter_append_int32 (&child_iter3, 0x12345678); - dbus_message_iter_append_int32 (&child_iter3, 0x23456781); - _dbus_warn ("next call expected to fail with wrong array type\n"); - _dbus_assert (!dbus_message_iter_append_array (&child_iter2, &child_iter3, DBUS_TYPE_UINT32)); - dbus_message_iter_append_array (&child_iter2, &child_iter3, DBUS_TYPE_INT32); - dbus_message_iter_append_int32 (&child_iter3, 0x34567812); - dbus_message_iter_append_int32 (&child_iter3, 0x45678123); - dbus_message_iter_append_int32 (&child_iter3, 0x56781234); - - dbus_message_iter_append_byte (&iter, 0xF0); - - dbus_message_iter_append_nil (&iter); - - dbus_message_iter_append_custom (&iter, "MyTypeName", - "data", 5); - - dbus_message_iter_append_byte (&iter, 0xF0); - - dbus_message_iter_append_array (&iter, &child_iter, DBUS_TYPE_INT32); - - dbus_message_iter_append_byte (&iter, 0xF0); - - dbus_message_iter_append_dict (&iter, &child_iter); - - dbus_message_iter_append_byte (&iter, 0xF0); - - message_iter_test (message); - - /* Message loader test */ - _dbus_message_lock (message); - loader = _dbus_message_loader_new (); - - /* check ref/unref */ - _dbus_message_loader_ref (loader); - _dbus_message_loader_unref (loader); - - /* Write the header data one byte at a time */ - data = _dbus_string_get_const_data (&message->header); - for (i = 0; i < _dbus_string_get_length (&message->header); i++) - { - DBusString *buffer; - - _dbus_message_loader_get_buffer (loader, &buffer); - _dbus_string_append_byte (buffer, data[i]); - _dbus_message_loader_return_buffer (loader, buffer, 1); - } - - /* Write the body data one byte at a time */ - data = _dbus_string_get_const_data (&message->body); - for (i = 0; i < _dbus_string_get_length (&message->body); i++) - { - DBusString *buffer; - - _dbus_message_loader_get_buffer (loader, &buffer); - _dbus_string_append_byte (buffer, data[i]); - _dbus_message_loader_return_buffer (loader, buffer, 1); - } - - copy = dbus_message_copy (message); /* save for tests below */ - dbus_message_unref (message); - - /* Now pop back the message */ - if (!_dbus_message_loader_queue_messages (loader)) - _dbus_assert_not_reached ("no memory to queue messages"); - - if (_dbus_message_loader_get_is_corrupted (loader)) - _dbus_assert_not_reached ("message loader corrupted"); - - message = _dbus_message_loader_pop_message (loader); - if (!message) - _dbus_assert_not_reached ("received a NULL message"); - - if (dbus_message_get_reply_serial (message) != 0x12345678) - _dbus_assert_not_reached ("reply serial fields differ"); - - message_iter_test (message); - - dbus_message_unref (message); - _dbus_message_loader_unref (loader); - - message = dbus_message_new_method_return (copy); - if (message == NULL) - _dbus_assert_not_reached ("out of memory\n"); - dbus_message_unref (copy); - - if (!dbus_message_append_args (message, - DBUS_TYPE_STRING, "hello", - DBUS_TYPE_INVALID)) - _dbus_assert_not_reached ("no memory"); - - if (!dbus_message_has_signature (message, "s")) - _dbus_assert_not_reached ("method return has wrong signature"); - - dbus_error_init (&error); - if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, - &t, DBUS_TYPE_INVALID)) - - { - _dbus_warn ("Failed to get expected string arg: %s\n", error.message); - exit (1); - } - dbus_free (t); - - dbus_message_unref (message); - - /* This ServiceAcquired message used to trigger a bug in - * setting header fields, adding to regression test. - */ - message = dbus_message_new_signal (DBUS_PATH_ORG_FREEDESKTOP_DBUS, - DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, - "ServiceAcquired"); - - if (message == NULL) - _dbus_assert_not_reached ("out of memory"); - - _dbus_verbose ("Bytes after creation\n"); - _dbus_verbose_bytes_of_string (&message->header, 0, - _dbus_string_get_length (&message->header)); - - if (!dbus_message_set_destination (message, ":1.0") || - !dbus_message_append_args (message, - DBUS_TYPE_STRING, ":1.0", - DBUS_TYPE_INVALID)) - _dbus_assert_not_reached ("out of memory"); - - _dbus_verbose ("Bytes after set_destination() and append_args()\n"); - _dbus_verbose_bytes_of_string (&message->header, 0, - _dbus_string_get_length (&message->header)); - - if (!dbus_message_set_sender (message, "org.freedesktop.DBus")) - _dbus_assert_not_reached ("out of memory"); - - _dbus_verbose ("Bytes after set_sender()\n"); - _dbus_verbose_bytes_of_string (&message->header, 0, - _dbus_string_get_length (&message->header)); - - /* When the bug happened the above set_destination() would - * corrupt the signature - */ - if (!dbus_message_has_signature (message, "s")) - { - _dbus_warn ("Signature should be 's' but is '%s'\n", - dbus_message_get_signature (message)); - _dbus_assert_not_reached ("signal has wrong signature"); - } - - /* have to set destination again to reproduce the bug */ - if (!dbus_message_set_destination (message, ":1.0")) - _dbus_assert_not_reached ("out of memory"); - - _dbus_verbose ("Bytes after set_destination()\n"); - _dbus_verbose_bytes_of_string (&message->header, 0, - _dbus_string_get_length (&message->header)); - - /* When the bug happened the above set_destination() would - * corrupt the signature - */ - if (!dbus_message_has_signature (message, "s")) - { - _dbus_warn ("Signature should be 's' but is '%s'\n", - dbus_message_get_signature (message)); - _dbus_assert_not_reached ("signal has wrong signature"); - } - - dbus_error_init (&error); - if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, - &t, DBUS_TYPE_INVALID)) - - { - _dbus_warn ("Failed to get expected string arg for signal: %s\n", error.message); - exit (1); - } - dbus_free (t); - - dbus_message_unref (message); - - /* Now load every message in test_data_dir if we have one */ - if (test_data_dir == NULL) - return TRUE; - - return dbus_internal_do_not_use_foreach_message_file (test_data_dir, - (DBusForeachMessageFileFunc) - dbus_internal_do_not_use_try_message_file, - NULL); - -#endif /* Commented out most tests for now */ - - return TRUE; -} -#endif /* DBUS_BUILD_TESTS */ +/* tests in dbus-message-util.c */ diff --git a/dbus/dbus-string-private.h b/dbus/dbus-string-private.h index a2fcd03b..a4b7e8c3 100644 --- a/dbus/dbus-string-private.h +++ b/dbus/dbus-string-private.h @@ -53,6 +53,62 @@ typedef struct unsigned int align_offset : 3; /**< str - align_offset is the actual malloc block */ } DBusRealString; + +/** + * @defgroup DBusStringInternals DBusString implementation details + * @ingroup DBusInternals + * @brief DBusString implementation details + * + * The guts of DBusString. + * + * @{ + */ + +/** + * This is the maximum max length (and thus also the maximum length) + * of a DBusString + */ +#define _DBUS_STRING_MAX_MAX_LENGTH (_DBUS_INT_MAX - _DBUS_STRING_ALLOCATION_PADDING) + +/** + * Checks a bunch of assertions about a string object + * + * @param real the DBusRealString + */ +#define DBUS_GENERIC_STRING_PREAMBLE(real) _dbus_assert ((real) != NULL); _dbus_assert (!(real)->invalid); _dbus_assert ((real)->len >= 0); _dbus_assert ((real)->allocated >= 0); _dbus_assert ((real)->max_length >= 0); _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); _dbus_assert ((real)->len <= (real)->max_length) + +/** + * Checks assertions about a string object that needs to be + * modifiable - may not be locked or const. Also declares + * the "real" variable pointing to DBusRealString. + * @param str the string + */ +#define DBUS_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \ + DBUS_GENERIC_STRING_PREAMBLE (real); \ + _dbus_assert (!(real)->constant); \ + _dbus_assert (!(real)->locked) + +/** + * Checks assertions about a string object that may be locked but + * can't be const. i.e. a string object that we can free. Also + * declares the "real" variable pointing to DBusRealString. + * + * @param str the string + */ +#define DBUS_LOCKED_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \ + DBUS_GENERIC_STRING_PREAMBLE (real); \ + _dbus_assert (!(real)->constant) + +/** + * Checks assertions about a string that may be const or locked. Also + * declares the "real" variable pointing to DBusRealString. + * @param str the string. + */ +#define DBUS_CONST_STRING_PREAMBLE(str) const DBusRealString *real = (DBusRealString*) str; \ + DBUS_GENERIC_STRING_PREAMBLE (real) + +/** @} */ + DBUS_END_DECLS #endif /* DBUS_STRING_PRIVATE_H */ diff --git a/dbus/dbus-string-util.c b/dbus/dbus-string-util.c new file mode 100644 index 00000000..1ff2ec67 --- /dev/null +++ b/dbus/dbus-string-util.c @@ -0,0 +1,727 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-string-util.c Would be in dbus-string.c, but not used in libdbus + * + * Copyright (C) 2002, 2003, 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-internals.h" +#include "dbus-string.h" +#define DBUS_CAN_USE_DBUS_STRING_PRIVATE 1 +#include "dbus-string-private.h" + +/** + * @addtogroup DBusString + * @{ + */ + +/** + * Copies the contents of a DBusString into a different + * buffer. The resulting buffer will be nul-terminated. + * + * @param str a string + * @param buffer a C buffer to copy data to + * @param avail_len maximum length of C buffer + */ +void +_dbus_string_copy_to_buffer (const DBusString *str, + char *buffer, + int avail_len) +{ + int copy_len; + DBUS_CONST_STRING_PREAMBLE (str); + + _dbus_assert (avail_len >= 0); + + copy_len = MIN (avail_len, real->len+1); + memcpy (buffer, real->str, copy_len); + if (avail_len > 0 && avail_len == copy_len) + buffer[avail_len-1] = '\0'; +} + +/** + * Returns whether a string ends with the given suffix + * + * @todo memcmp might make this faster. + * + * @param a the string + * @param c_str the C-style string + * @returns #TRUE if the string ends with the suffix + */ +dbus_bool_t +_dbus_string_ends_with_c_str (const DBusString *a, + const char *c_str) +{ + const unsigned char *ap; + const unsigned char *bp; + const unsigned char *a_end; + unsigned long c_str_len; + const DBusRealString *real_a = (const DBusRealString*) a; + DBUS_GENERIC_STRING_PREAMBLE (real_a); + _dbus_assert (c_str != NULL); + + c_str_len = strlen (c_str); + if (((unsigned long)real_a->len) < c_str_len) + return FALSE; + + ap = real_a->str + (real_a->len - c_str_len); + bp = (const unsigned char*) c_str; + a_end = real_a->str + real_a->len; + while (ap != a_end) + { + if (*ap != *bp) + return FALSE; + + ++ap; + ++bp; + } + + _dbus_assert (*ap == '\0'); + _dbus_assert (*bp == '\0'); + + return TRUE; +} + +/** + * Find the given byte scanning backward from the given start. + * Sets *found to -1 if the byte is not found. + * + * @param str the string + * @param start the place to start scanning (will not find the byte at this point) + * @param byte the byte to find + * @param found return location for where it was found + * @returns #TRUE if found + */ +dbus_bool_t +_dbus_string_find_byte_backward (const DBusString *str, + int start, + unsigned char byte, + int *found) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + _dbus_assert (found != NULL); + + i = start - 1; + while (i >= 0) + { + if (real->str[i] == byte) + break; + + --i; + } + + if (found) + *found = i; + + return i >= 0; +} + +/** + * Skips whitespace from start, storing the first non-whitespace in *end. + * (whitespace is space, tab, newline, CR). + * + * @param str the string + * @param start where to start + * @param end where to store the first non-whitespace byte index + */ +void +_dbus_string_skip_white (const DBusString *str, + int start, + int *end) +{ + int i; + DBUS_CONST_STRING_PREAMBLE (str); + _dbus_assert (start <= real->len); + _dbus_assert (start >= 0); + + i = start; + while (i < real->len) + { + if (!(real->str[i] == ' ' || + real->str[i] == '\n' || + real->str[i] == '\r' || + real->str[i] == '\t')) + break; + + ++i; + } + + _dbus_assert (i == real->len || !(real->str[i] == ' ' || + real->str[i] == '\t')); + + if (end) + *end = i; +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include + +static void +test_max_len (DBusString *str, + int max_len) +{ + if (max_len > 0) + { + if (!_dbus_string_set_length (str, max_len - 1)) + _dbus_assert_not_reached ("setting len to one less than max should have worked"); + } + + if (!_dbus_string_set_length (str, max_len)) + _dbus_assert_not_reached ("setting len to max len should have worked"); + + if (_dbus_string_set_length (str, max_len + 1)) + _dbus_assert_not_reached ("setting len to one more than max len should not have worked"); + + if (!_dbus_string_set_length (str, 0)) + _dbus_assert_not_reached ("setting len to zero should have worked"); +} + +static void +test_hex_roundtrip (const unsigned char *data, + int len) +{ + DBusString orig; + DBusString encoded; + DBusString decoded; + int end; + + if (len < 0) + len = strlen (data); + + if (!_dbus_string_init (&orig)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_init (&encoded)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_init (&decoded)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_append_len (&orig, data, len)) + _dbus_assert_not_reached ("couldn't append orig data"); + + if (!_dbus_string_hex_encode (&orig, 0, &encoded, 0)) + _dbus_assert_not_reached ("could not encode"); + + if (!_dbus_string_hex_decode (&encoded, 0, &end, &decoded, 0)) + _dbus_assert_not_reached ("could not decode"); + + _dbus_assert (_dbus_string_get_length (&encoded) == end); + + if (!_dbus_string_equal (&orig, &decoded)) + { + const char *s; + + printf ("Original string %d bytes encoded %d bytes decoded %d bytes\n", + _dbus_string_get_length (&orig), + _dbus_string_get_length (&encoded), + _dbus_string_get_length (&decoded)); + printf ("Original: %s\n", data); + s = _dbus_string_get_const_data (&decoded); + printf ("Decoded: %s\n", s); + _dbus_assert_not_reached ("original string not the same as string decoded from hex"); + } + + _dbus_string_free (&orig); + _dbus_string_free (&encoded); + _dbus_string_free (&decoded); +} + +typedef void (* TestRoundtripFunc) (const unsigned char *data, + int len); +static void +test_roundtrips (TestRoundtripFunc func) +{ + (* func) ("Hello this is a string\n", -1); + (* func) ("Hello this is a string\n1", -1); + (* func) ("Hello this is a string\n12", -1); + (* func) ("Hello this is a string\n123", -1); + (* func) ("Hello this is a string\n1234", -1); + (* func) ("Hello this is a string\n12345", -1); + (* func) ("", 0); + (* func) ("1", 1); + (* func) ("12", 2); + (* func) ("123", 3); + (* func) ("1234", 4); + (* func) ("12345", 5); + (* func) ("", 1); + (* func) ("1", 2); + (* func) ("12", 3); + (* func) ("123", 4); + (* func) ("1234", 5); + (* func) ("12345", 6); + { + unsigned char buf[512]; + int i; + + i = 0; + while (i < _DBUS_N_ELEMENTS (buf)) + { + buf[i] = i; + ++i; + } + i = 0; + while (i < _DBUS_N_ELEMENTS (buf)) + { + (* func) (buf, i); + ++i; + } + } +} + +#ifdef DBUS_BUILD_TESTS +/* The max length thing is sort of a historical artifact + * from a feature that turned out to be dumb; perhaps + * we should purge it entirely. The problem with + * the feature is that it looks like memory allocation + * failure, but is not a transient or resolvable failure. + */ +static void +set_max_length (DBusString *str, + int max_length) +{ + DBusRealString *real; + + real = (DBusRealString*) str; + + real->max_length = max_length; +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * @ingroup DBusStringInternals + * Unit test for DBusString. + * + * @todo Need to write tests for _dbus_string_copy() and + * _dbus_string_move() moving to/from each of start/middle/end of a + * string. Also need tests for _dbus_string_move_len () + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_string_test (void) +{ + DBusString str; + DBusString other; + int i, end; + long v; + double d; + int lens[] = { 0, 1, 2, 3, 4, 5, 10, 16, 17, 18, 25, 31, 32, 33, 34, 35, 63, 64, 65, 66, 67, 68, 69, 70, 71, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136 }; + char *s; + dbus_unichar_t ch; + + i = 0; + while (i < _DBUS_N_ELEMENTS (lens)) + { + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + set_max_length (&str, lens[i]); + + test_max_len (&str, lens[i]); + _dbus_string_free (&str); + + ++i; + } + + /* Test shortening and setting length */ + i = 0; + while (i < _DBUS_N_ELEMENTS (lens)) + { + int j; + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + set_max_length (&str, lens[i]); + + if (!_dbus_string_set_length (&str, lens[i])) + _dbus_assert_not_reached ("failed to set string length"); + + j = lens[i]; + while (j > 0) + { + _dbus_assert (_dbus_string_get_length (&str) == j); + if (j > 0) + { + _dbus_string_shorten (&str, 1); + _dbus_assert (_dbus_string_get_length (&str) == (j - 1)); + } + --j; + } + + _dbus_string_free (&str); + + ++i; + } + + /* Test appending data */ + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + i = 0; + while (i < 10) + { + if (!_dbus_string_append (&str, "a")) + _dbus_assert_not_reached ("failed to append string to string\n"); + + _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 1); + + if (!_dbus_string_append_byte (&str, 'b')) + _dbus_assert_not_reached ("failed to append byte to string\n"); + + _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 2); + + ++i; + } + + _dbus_string_free (&str); + + /* Check steal_data */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_steal_data (&str, &s)) + _dbus_assert_not_reached ("failed to steal data"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (((int)strlen (s)) == i); + + dbus_free (s); + + /* Check move */ + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_move (&str, 0, &other, 0)) + _dbus_assert_not_reached ("could not move"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (_dbus_string_get_length (&other) == i); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other))) + _dbus_assert_not_reached ("could not move"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (_dbus_string_get_length (&other) == i * 2); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other) / 2)) + _dbus_assert_not_reached ("could not move"); + + _dbus_assert (_dbus_string_get_length (&str) == 0); + _dbus_assert (_dbus_string_get_length (&other) == i * 3); + + _dbus_string_free (&other); + + /* Check copy */ + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_copy (&str, 0, &other, 0)) + _dbus_assert_not_reached ("could not copy"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i); + + if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other))) + _dbus_assert_not_reached ("could not copy"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 2); + _dbus_assert (_dbus_string_equal_c_str (&other, + "Hello WorldHello World")); + + if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other) / 2)) + _dbus_assert_not_reached ("could not copy"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 3); + _dbus_assert (_dbus_string_equal_c_str (&other, + "Hello WorldHello WorldHello World")); + + _dbus_string_free (&str); + _dbus_string_free (&other); + + /* Check replace */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello World")) + _dbus_assert_not_reached ("could not append to string"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str), + &other, 0, _dbus_string_get_length (&other))) + _dbus_assert_not_reached ("could not replace"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i); + _dbus_assert (_dbus_string_equal_c_str (&other, "Hello World")); + + if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str), + &other, 5, 1)) + _dbus_assert_not_reached ("could not replace center space"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1); + _dbus_assert (_dbus_string_equal_c_str (&other, + "HelloHello WorldWorld")); + + + if (!_dbus_string_replace_len (&str, 1, 1, + &other, + _dbus_string_get_length (&other) - 1, + 1)) + _dbus_assert_not_reached ("could not replace end character"); + + _dbus_assert (_dbus_string_get_length (&str) == i); + _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1); + _dbus_assert (_dbus_string_equal_c_str (&other, + "HelloHello WorldWorle")); + + _dbus_string_free (&str); + _dbus_string_free (&other); + + /* Check append/get unichar */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + ch = 0; + if (!_dbus_string_append_unichar (&str, 0xfffc)) + _dbus_assert_not_reached ("failed to append unichar"); + + _dbus_string_get_unichar (&str, 0, &ch, &i); + + _dbus_assert (ch == 0xfffc); + _dbus_assert (i == _dbus_string_get_length (&str)); + + _dbus_string_free (&str); + + /* Check insert/set/get byte */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello")) + _dbus_assert_not_reached ("failed to append Hello"); + + _dbus_assert (_dbus_string_get_byte (&str, 0) == 'H'); + _dbus_assert (_dbus_string_get_byte (&str, 1) == 'e'); + _dbus_assert (_dbus_string_get_byte (&str, 2) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 3) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 4) == 'o'); + + _dbus_string_set_byte (&str, 1, 'q'); + _dbus_assert (_dbus_string_get_byte (&str, 1) == 'q'); + + if (!_dbus_string_insert_bytes (&str, 0, 1, 255)) + _dbus_assert_not_reached ("can't insert byte"); + + if (!_dbus_string_insert_bytes (&str, 2, 4, 'Z')) + _dbus_assert_not_reached ("can't insert byte"); + + if (!_dbus_string_insert_bytes (&str, _dbus_string_get_length (&str), 1, 'W')) + _dbus_assert_not_reached ("can't insert byte"); + + _dbus_assert (_dbus_string_get_byte (&str, 0) == 255); + _dbus_assert (_dbus_string_get_byte (&str, 1) == 'H'); + _dbus_assert (_dbus_string_get_byte (&str, 2) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 3) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 4) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 5) == 'Z'); + _dbus_assert (_dbus_string_get_byte (&str, 6) == 'q'); + _dbus_assert (_dbus_string_get_byte (&str, 7) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 8) == 'l'); + _dbus_assert (_dbus_string_get_byte (&str, 9) == 'o'); + _dbus_assert (_dbus_string_get_byte (&str, 10) == 'W'); + + _dbus_string_free (&str); + + /* Check append/parse int/double */ + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append_int (&str, 27)) + _dbus_assert_not_reached ("failed to append int"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_parse_int (&str, 0, &v, &end)) + _dbus_assert_not_reached ("failed to parse int"); + + _dbus_assert (v == 27); + _dbus_assert (end == i); + + _dbus_string_free (&str); + + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append_double (&str, 50.3)) + _dbus_assert_not_reached ("failed to append float"); + + i = _dbus_string_get_length (&str); + + if (!_dbus_string_parse_double (&str, 0, &d, &end)) + _dbus_assert_not_reached ("failed to parse float"); + + _dbus_assert (d > (50.3 - 1e-6) && d < (50.3 + 1e-6)); + _dbus_assert (end == i); + + _dbus_string_free (&str); + + /* Test find */ + if (!_dbus_string_init (&str)) + _dbus_assert_not_reached ("failed to init string"); + + if (!_dbus_string_append (&str, "Hello")) + _dbus_assert_not_reached ("couldn't append to string"); + + if (!_dbus_string_find (&str, 0, "He", &i)) + _dbus_assert_not_reached ("didn't find 'He'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find (&str, 0, "Hello", &i)) + _dbus_assert_not_reached ("didn't find 'Hello'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find (&str, 0, "ello", &i)) + _dbus_assert_not_reached ("didn't find 'ello'"); + _dbus_assert (i == 1); + + if (!_dbus_string_find (&str, 0, "lo", &i)) + _dbus_assert_not_reached ("didn't find 'lo'"); + _dbus_assert (i == 3); + + if (!_dbus_string_find (&str, 2, "lo", &i)) + _dbus_assert_not_reached ("didn't find 'lo'"); + _dbus_assert (i == 3); + + if (_dbus_string_find (&str, 4, "lo", &i)) + _dbus_assert_not_reached ("did find 'lo'"); + + if (!_dbus_string_find (&str, 0, "l", &i)) + _dbus_assert_not_reached ("didn't find 'l'"); + _dbus_assert (i == 2); + + if (!_dbus_string_find (&str, 0, "H", &i)) + _dbus_assert_not_reached ("didn't find 'H'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find (&str, 0, "", &i)) + _dbus_assert_not_reached ("didn't find ''"); + _dbus_assert (i == 0); + + if (_dbus_string_find (&str, 0, "Hello!", NULL)) + _dbus_assert_not_reached ("Did find 'Hello!'"); + + if (_dbus_string_find (&str, 0, "Oh, Hello", NULL)) + _dbus_assert_not_reached ("Did find 'Oh, Hello'"); + + if (_dbus_string_find (&str, 0, "ill", NULL)) + _dbus_assert_not_reached ("Did find 'ill'"); + + if (_dbus_string_find (&str, 0, "q", NULL)) + _dbus_assert_not_reached ("Did find 'q'"); + + if (!_dbus_string_find_to (&str, 0, 2, "He", NULL)) + _dbus_assert_not_reached ("Didn't find 'He'"); + + if (_dbus_string_find_to (&str, 0, 2, "Hello", NULL)) + _dbus_assert_not_reached ("Did find 'Hello'"); + + if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'H', &i)) + _dbus_assert_not_reached ("Did not find 'H'"); + _dbus_assert (i == 0); + + if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'o', &i)) + _dbus_assert_not_reached ("Did not find 'o'"); + _dbus_assert (i == _dbus_string_get_length (&str) - 1); + + if (_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str) - 1, 'o', &i)) + _dbus_assert_not_reached ("Did find 'o'"); + _dbus_assert (i == -1); + + if (_dbus_string_find_byte_backward (&str, 1, 'e', &i)) + _dbus_assert_not_reached ("Did find 'e'"); + _dbus_assert (i == -1); + + if (!_dbus_string_find_byte_backward (&str, 2, 'e', &i)) + _dbus_assert_not_reached ("Didn't find 'e'"); + _dbus_assert (i == 1); + + _dbus_string_free (&str); + + /* Hex encoding */ + _dbus_string_init_const (&str, "cafebabe, this is a bogus hex string"); + if (!_dbus_string_init (&other)) + _dbus_assert_not_reached ("could not init string"); + + if (!_dbus_string_hex_decode (&str, 0, &end, &other, 0)) + _dbus_assert_not_reached ("deccoded bogus hex string with no error"); + + _dbus_assert (end == 8); + + _dbus_string_free (&other); + + test_roundtrips (test_hex_roundtrip); + + _dbus_string_free (&str); + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c index 52099d65..154193ce 100644 --- a/dbus/dbus-string.c +++ b/dbus/dbus-string.c @@ -68,61 +68,6 @@ * marshaling code which always does "inserts" now. */ -/** - * @defgroup DBusStringInternals DBusString implementation details - * @ingroup DBusInternals - * @brief DBusString implementation details - * - * The guts of DBusString. - * - * @{ - */ - -/** - * This is the maximum max length (and thus also the maximum length) - * of a DBusString - */ -#define MAX_MAX_LENGTH (_DBUS_INT_MAX - _DBUS_STRING_ALLOCATION_PADDING) - -/** - * Checks a bunch of assertions about a string object - * - * @param real the DBusRealString - */ -#define DBUS_GENERIC_STRING_PREAMBLE(real) _dbus_assert ((real) != NULL); _dbus_assert (!(real)->invalid); _dbus_assert ((real)->len >= 0); _dbus_assert ((real)->allocated >= 0); _dbus_assert ((real)->max_length >= 0); _dbus_assert ((real)->len <= ((real)->allocated - _DBUS_STRING_ALLOCATION_PADDING)); _dbus_assert ((real)->len <= (real)->max_length) - -/** - * Checks assertions about a string object that needs to be - * modifiable - may not be locked or const. Also declares - * the "real" variable pointing to DBusRealString. - * @param str the string - */ -#define DBUS_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \ - DBUS_GENERIC_STRING_PREAMBLE (real); \ - _dbus_assert (!(real)->constant); \ - _dbus_assert (!(real)->locked) - -/** - * Checks assertions about a string object that may be locked but - * can't be const. i.e. a string object that we can free. Also - * declares the "real" variable pointing to DBusRealString. - * - * @param str the string - */ -#define DBUS_LOCKED_STRING_PREAMBLE(str) DBusRealString *real = (DBusRealString*) str; \ - DBUS_GENERIC_STRING_PREAMBLE (real); \ - _dbus_assert (!(real)->constant) - -/** - * Checks assertions about a string that may be const or locked. Also - * declares the "real" variable pointing to DBusRealString. - * @param str the string. - */ -#define DBUS_CONST_STRING_PREAMBLE(str) const DBusRealString *real = (DBusRealString*) str; \ - DBUS_GENERIC_STRING_PREAMBLE (real) - -/** @} */ - /** * @addtogroup DBusString * @{ @@ -207,7 +152,7 @@ _dbus_string_init_preallocated (DBusString *str, real->len = 0; real->str[real->len] = '\0'; - real->max_length = MAX_MAX_LENGTH; + real->max_length = _DBUS_STRING_MAX_MAX_LENGTH; real->constant = FALSE; real->locked = FALSE; real->invalid = FALSE; @@ -231,6 +176,7 @@ _dbus_string_init (DBusString *str) return _dbus_string_init_preallocated (str, 0); } +#ifdef DBUS_BUILD_TESTS /* The max length thing is sort of a historical artifact * from a feature that turned out to be dumb; perhaps * we should purge it entirely. The problem with @@ -247,6 +193,7 @@ set_max_length (DBusString *str, real->max_length = max_length; } +#endif /* DBUS_BUILD_TESTS */ /** * Initializes a constant string. The value parameter is not copied @@ -286,7 +233,7 @@ _dbus_string_init_const_len (DBusString *str, _dbus_assert (str != NULL); _dbus_assert (value != NULL); - _dbus_assert (len <= MAX_MAX_LENGTH); + _dbus_assert (len <= _DBUS_STRING_MAX_MAX_LENGTH); _dbus_assert (len >= 0); real = (DBusRealString*) str; @@ -376,8 +323,8 @@ reallocate_for_length (DBusRealString *real, /* at least double our old allocation to avoid O(n), avoiding * overflow */ - if (real->allocated > (MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2) - new_allocated = MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING; + if (real->allocated > (_DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING) / 2) + new_allocated = _DBUS_STRING_MAX_MAX_LENGTH + _DBUS_STRING_ALLOCATION_PADDING; else new_allocated = real->allocated * 2; @@ -454,6 +401,7 @@ open_gap (int len, return TRUE; } +#ifndef _dbus_string_get_data /** * Gets the raw character buffer from the string. The returned buffer * will be nul-terminated, but note that strings may contain binary @@ -472,6 +420,7 @@ _dbus_string_get_data (DBusString *str) return real->str; } +#endif /* _dbus_string_get_data */ /* only do the function if we don't have the macro */ #ifndef _dbus_string_get_const_data @@ -683,6 +632,7 @@ _dbus_string_steal_data (DBusString *str, return TRUE; } +#ifdef DBUS_BUILD_TESTS /** * Like _dbus_string_get_data_len(), but removes the gotten data from * the original string. The caller must free the data returned. This @@ -733,7 +683,7 @@ _dbus_string_steal_data_len (DBusString *str, _dbus_string_free (&dest); return TRUE; } - +#endif /* DBUS_BUILD_TESTS */ /** * Copies the data from the string into a char* @@ -758,6 +708,7 @@ _dbus_string_copy_data (const DBusString *str, return TRUE; } +#ifdef DBUS_BUILD_TESTS /** * Copies a segment of the string into a char* * @@ -802,30 +753,7 @@ _dbus_string_copy_data_len (const DBusString *str, _dbus_string_free (&dest); return TRUE; } - -/** - * Copies the contents of a DBusString into a different - * buffer. The resulting buffer will be nul-terminated. - * - * @param str a string - * @param buffer a C buffer to copy data to - * @param avail_len maximum length of C buffer - */ -void -_dbus_string_copy_to_buffer (const DBusString *str, - char *buffer, - int avail_len) -{ - int copy_len; - DBUS_CONST_STRING_PREAMBLE (str); - - _dbus_assert (avail_len >= 0); - - copy_len = MIN (avail_len, real->len+1); - memcpy (buffer, real->str, copy_len); - if (avail_len > 0 && avail_len == copy_len) - buffer[avail_len-1] = '\0'; -} +#endif /* DBUS_BUILD_TESTS */ /* Only have the function if we don't have the macro */ #ifndef _dbus_string_get_length @@ -1046,6 +974,7 @@ _dbus_string_append (DBusString *str, return append (real, buffer, buffer_len); } + /** assign 4 bytes from one string to another */ #define ASSIGN_4_OCTETS(p, octets) \ *((dbus_uint32_t*)(p)) = *((dbus_uint32_t*)(octets)); @@ -1074,6 +1003,7 @@ do { \ } while (0) #endif /* DBUS_HAVE_INT64 */ +#ifdef DBUS_BUILD_TESTS /** * Appends 4 bytes aligned on a 4 byte boundary * with any alignment padding initialized to 0. @@ -1095,7 +1025,9 @@ _dbus_string_append_4_aligned (DBusString *str, return TRUE; } +#endif /* DBUS_BUILD_TESTS */ +#ifdef DBUS_BUILD_TESTS /** * Appends 8 bytes aligned on an 8 byte boundary * with any alignment padding initialized to 0. @@ -1117,6 +1049,7 @@ _dbus_string_append_8_aligned (DBusString *str, return TRUE; } +#endif /* DBUS_BUILD_TESTS */ /** * Inserts 4 bytes aligned on a 4 byte boundary @@ -1299,6 +1232,7 @@ _dbus_string_append_byte (DBusString *str, return TRUE; } +#ifdef DBUS_BUILD_TESTS /** * Appends a single Unicode character, encoding the character * in UTF-8 format. @@ -1369,6 +1303,7 @@ _dbus_string_append_unichar (DBusString *str, return TRUE; } +#endif /* DBUS_BUILD_TESTS */ static void delete (DBusRealString *real, @@ -1730,6 +1665,7 @@ _dbus_string_replace_len (const DBusString *source, ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ ((Char) & 0xFFFF) != 0xFFFF) +#ifdef DBUS_BUILD_TESTS /** * Gets a unicode character from a UTF-8 string. Does no validation; * you must verify that the string is valid UTF-8 in advance and must @@ -1776,6 +1712,7 @@ _dbus_string_get_unichar (const DBusString *str, if (end_return) *end_return = start + len; } +#endif /* DBUS_BUILD_TESTS */ /** * Finds the given substring in the string, @@ -1876,43 +1813,6 @@ _dbus_string_find_to (const DBusString *str, return FALSE; } -/** - * Find the given byte scanning backward from the given start. - * Sets *found to -1 if the byte is not found. - * - * @param str the string - * @param start the place to start scanning (will not find the byte at this point) - * @param byte the byte to find - * @param found return location for where it was found - * @returns #TRUE if found - */ -dbus_bool_t -_dbus_string_find_byte_backward (const DBusString *str, - int start, - unsigned char byte, - int *found) -{ - int i; - DBUS_CONST_STRING_PREAMBLE (str); - _dbus_assert (start <= real->len); - _dbus_assert (start >= 0); - _dbus_assert (found != NULL); - - i = start - 1; - while (i >= 0) - { - if (real->str[i] == byte) - break; - - --i; - } - - if (found) - *found = i; - - return i >= 0; -} - /** * Finds a blank (space or tab) in the string. Returns #TRUE * if found, #FALSE otherwise. If a blank is not found sets @@ -1988,43 +1888,6 @@ _dbus_string_skip_blank (const DBusString *str, *end = i; } -/** - * Skips whitespace from start, storing the first non-whitespace in *end. - * (whitespace is space, tab, newline, CR). - * - * @param str the string - * @param start where to start - * @param end where to store the first non-whitespace byte index - */ -void -_dbus_string_skip_white (const DBusString *str, - int start, - int *end) -{ - int i; - DBUS_CONST_STRING_PREAMBLE (str); - _dbus_assert (start <= real->len); - _dbus_assert (start >= 0); - - i = start; - while (i < real->len) - { - if (!(real->str[i] == ' ' || - real->str[i] == '\n' || - real->str[i] == '\r' || - real->str[i] == '\t')) - break; - - ++i; - } - - _dbus_assert (i == real->len || !(real->str[i] == ' ' || - real->str[i] == '\t')); - - if (end) - *end = i; -} - /** * Assigns a newline-terminated or \\r\\n-terminated line from the front * of the string to the given dest string. The dest string's previous @@ -2092,6 +1955,7 @@ _dbus_string_pop_line (DBusString *source, return TRUE; } +#ifdef DBUS_BUILD_TESTS /** * Deletes up to and including the first blank space * in the string. @@ -2108,7 +1972,9 @@ _dbus_string_delete_first_word (DBusString *str) _dbus_string_delete (str, 0, i); } +#endif +#ifdef DBUS_BUILD_TESTS /** * Deletes any leading blanks in the string * @@ -2124,6 +1990,7 @@ _dbus_string_delete_leading_blanks (DBusString *str) if (i > 0) _dbus_string_delete (str, 0, i); } +#endif /** * Tests two DBusString for equality. @@ -2164,6 +2031,7 @@ _dbus_string_equal (const DBusString *a, return TRUE; } +#ifdef DBUS_BUILD_TESTS /** * Tests two DBusString for equality up to the given length. * The strings may be shorter than the given length. @@ -2208,6 +2076,7 @@ _dbus_string_equal_len (const DBusString *a, return TRUE; } +#endif /* DBUS_BUILD_TESTS */ /** * Tests two sub-parts of two DBusString for equality. The specified @@ -2302,6 +2171,7 @@ _dbus_string_equal_c_str (const DBusString *a, return TRUE; } +#ifdef DBUS_BUILD_TESTS /** * Checks whether a string starts with the given C string. * @@ -2337,49 +2207,7 @@ _dbus_string_starts_with_c_str (const DBusString *a, else return FALSE; } - -/** - * Returns whether a string ends with the given suffix - * - * @todo memcmp might make this faster. - * - * @param a the string - * @param c_str the C-style string - * @returns #TRUE if the string ends with the suffix - */ -dbus_bool_t -_dbus_string_ends_with_c_str (const DBusString *a, - const char *c_str) -{ - const unsigned char *ap; - const unsigned char *bp; - const unsigned char *a_end; - unsigned long c_str_len; - const DBusRealString *real_a = (const DBusRealString*) a; - DBUS_GENERIC_STRING_PREAMBLE (real_a); - _dbus_assert (c_str != NULL); - - c_str_len = strlen (c_str); - if (((unsigned long)real_a->len) < c_str_len) - return FALSE; - - ap = real_a->str + (real_a->len - c_str_len); - bp = (const unsigned char*) c_str; - a_end = real_a->str + real_a->len; - while (ap != a_end) - { - if (*ap != *bp) - return FALSE; - - ++ap; - ++bp; - } - - _dbus_assert (*ap == '\0'); - _dbus_assert (*bp == '\0'); - - return TRUE; -} +#endif /* DBUS_BUILD_TESTS */ /** * Encodes a string in hex, the way MD5 and SHA-1 are usually @@ -2773,643 +2601,4 @@ _dbus_string_zero (DBusString *str) } /** @} */ -#ifdef DBUS_BUILD_TESTS -#include "dbus-test.h" -#include - -/** - * Parses a basic type defined by type contained in a DBusString. The - * end_return parameter may be #NULL if you aren't interested in it. The - * type is parsed and stored in value_return. Return parameters are not - * initialized if the function returns #FALSE. - * - * @param str the string - * @param type the type of the basic type - * @param start the byte index of the start of the type - * @param value_return return location of the value or #NULL - * @param end_return return location of the end of the type, or #NULL - * @returns #TRUE on success - */ -dbus_bool_t -_dbus_string_parse_basic_type (const DBusString *str, - char type, - int start, - void *value, - int *end_return) -{ - int end = start; - - switch (type) - { - case DBUS_TYPE_BOOLEAN: - { - int len = _dbus_string_get_length (str) - start; - if (len >= 5 && _dbus_string_find_to (str, start, start + 5, "false", NULL)) - { - end += 5; - *(unsigned char *) value = TRUE; - } - else if (len >= 4 && _dbus_string_find_to (str, start, start + 4, "true", NULL)) - { - end += 4; - *(unsigned char *) value = FALSE; - } - else - _dbus_warn ("could not parse BOOLEAN\n"); - break; - } - case DBUS_TYPE_BYTE: - { - long val = 0; - - if (_dbus_string_get_byte (str, start) == '\'' && - _dbus_string_get_length (str) >= start + 4 && - _dbus_string_get_byte (str, start + 1) == '\\' && - _dbus_string_get_byte (str, start + 2) == '\'' && - _dbus_string_get_byte (str, start + 3) == '\'') - { - val = '\''; - end += 4; - } - else if (_dbus_string_get_byte (str, start) == '\'' && - _dbus_string_get_length (str) >= start + 3 && - _dbus_string_get_byte (str, start + 2) == '\'') - { - val = _dbus_string_get_byte (str, start + 1); - end += 3; - } - else - { - if (!_dbus_string_parse_int (str, start, &val, &end)) - _dbus_warn ("Failed to parse integer for BYTE\n"); - } - - if (val > 255) - _dbus_warn ("A byte must be in range 0-255 not %ld\n", val); - - *(unsigned char *) value = val; - break; - } - case DBUS_TYPE_INT32: - { - long val; - if (_dbus_string_parse_int (str, start, &val, &end)) - *(dbus_int32_t *)value = val; - break; - } - case DBUS_TYPE_UINT32: - { - unsigned long val; - if (_dbus_string_parse_uint (str, start, &val, &end)) - *(dbus_uint32_t *)value = val; - break; - } -#ifdef DBUS_HAVE_INT64 - case DBUS_TYPE_INT64: - case DBUS_TYPE_UINT64: - /* use stroll oull */ - _dbus_assert_not_reached ("string -> [u]int64 not supported yet"); - break; -#endif /* DBUS_HAVE_INT64 */ - case DBUS_TYPE_DOUBLE: - _dbus_string_parse_double (str, start, value, &end); - break; - default: - _dbus_assert_not_reached ("not a basic type"); - break; - } - if (end_return) - *end_return = end; - - return end != start; -} - -static void -test_max_len (DBusString *str, - int max_len) -{ - if (max_len > 0) - { - if (!_dbus_string_set_length (str, max_len - 1)) - _dbus_assert_not_reached ("setting len to one less than max should have worked"); - } - - if (!_dbus_string_set_length (str, max_len)) - _dbus_assert_not_reached ("setting len to max len should have worked"); - - if (_dbus_string_set_length (str, max_len + 1)) - _dbus_assert_not_reached ("setting len to one more than max len should not have worked"); - - if (!_dbus_string_set_length (str, 0)) - _dbus_assert_not_reached ("setting len to zero should have worked"); -} - -static void -test_hex_roundtrip (const unsigned char *data, - int len) -{ - DBusString orig; - DBusString encoded; - DBusString decoded; - int end; - - if (len < 0) - len = strlen (data); - - if (!_dbus_string_init (&orig)) - _dbus_assert_not_reached ("could not init string"); - - if (!_dbus_string_init (&encoded)) - _dbus_assert_not_reached ("could not init string"); - - if (!_dbus_string_init (&decoded)) - _dbus_assert_not_reached ("could not init string"); - - if (!_dbus_string_append_len (&orig, data, len)) - _dbus_assert_not_reached ("couldn't append orig data"); - - if (!_dbus_string_hex_encode (&orig, 0, &encoded, 0)) - _dbus_assert_not_reached ("could not encode"); - - if (!_dbus_string_hex_decode (&encoded, 0, &end, &decoded, 0)) - _dbus_assert_not_reached ("could not decode"); - - _dbus_assert (_dbus_string_get_length (&encoded) == end); - - if (!_dbus_string_equal (&orig, &decoded)) - { - const char *s; - - printf ("Original string %d bytes encoded %d bytes decoded %d bytes\n", - _dbus_string_get_length (&orig), - _dbus_string_get_length (&encoded), - _dbus_string_get_length (&decoded)); - printf ("Original: %s\n", data); - s = _dbus_string_get_const_data (&decoded); - printf ("Decoded: %s\n", s); - _dbus_assert_not_reached ("original string not the same as string decoded from hex"); - } - - _dbus_string_free (&orig); - _dbus_string_free (&encoded); - _dbus_string_free (&decoded); -} - -typedef void (* TestRoundtripFunc) (const unsigned char *data, - int len); -static void -test_roundtrips (TestRoundtripFunc func) -{ - (* func) ("Hello this is a string\n", -1); - (* func) ("Hello this is a string\n1", -1); - (* func) ("Hello this is a string\n12", -1); - (* func) ("Hello this is a string\n123", -1); - (* func) ("Hello this is a string\n1234", -1); - (* func) ("Hello this is a string\n12345", -1); - (* func) ("", 0); - (* func) ("1", 1); - (* func) ("12", 2); - (* func) ("123", 3); - (* func) ("1234", 4); - (* func) ("12345", 5); - (* func) ("", 1); - (* func) ("1", 2); - (* func) ("12", 3); - (* func) ("123", 4); - (* func) ("1234", 5); - (* func) ("12345", 6); - { - unsigned char buf[512]; - int i; - - i = 0; - while (i < _DBUS_N_ELEMENTS (buf)) - { - buf[i] = i; - ++i; - } - i = 0; - while (i < _DBUS_N_ELEMENTS (buf)) - { - (* func) (buf, i); - ++i; - } - } -} - - -/** - * @ingroup DBusStringInternals - * Unit test for DBusString. - * - * @todo Need to write tests for _dbus_string_copy() and - * _dbus_string_move() moving to/from each of start/middle/end of a - * string. Also need tests for _dbus_string_move_len () - * - * @returns #TRUE on success. - */ -dbus_bool_t -_dbus_string_test (void) -{ - DBusString str; - DBusString other; - int i, end; - long v; - double d; - int lens[] = { 0, 1, 2, 3, 4, 5, 10, 16, 17, 18, 25, 31, 32, 33, 34, 35, 63, 64, 65, 66, 67, 68, 69, 70, 71, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136 }; - char *s; - dbus_unichar_t ch; - - i = 0; - while (i < _DBUS_N_ELEMENTS (lens)) - { - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - set_max_length (&str, lens[i]); - - test_max_len (&str, lens[i]); - _dbus_string_free (&str); - - ++i; - } - - /* Test shortening and setting length */ - i = 0; - while (i < _DBUS_N_ELEMENTS (lens)) - { - int j; - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - set_max_length (&str, lens[i]); - - if (!_dbus_string_set_length (&str, lens[i])) - _dbus_assert_not_reached ("failed to set string length"); - - j = lens[i]; - while (j > 0) - { - _dbus_assert (_dbus_string_get_length (&str) == j); - if (j > 0) - { - _dbus_string_shorten (&str, 1); - _dbus_assert (_dbus_string_get_length (&str) == (j - 1)); - } - --j; - } - - _dbus_string_free (&str); - - ++i; - } - - /* Test appending data */ - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - i = 0; - while (i < 10) - { - if (!_dbus_string_append (&str, "a")) - _dbus_assert_not_reached ("failed to append string to string\n"); - - _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 1); - - if (!_dbus_string_append_byte (&str, 'b')) - _dbus_assert_not_reached ("failed to append byte to string\n"); - - _dbus_assert (_dbus_string_get_length (&str) == i * 2 + 2); - - ++i; - } - - _dbus_string_free (&str); - - /* Check steal_data */ - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - if (!_dbus_string_append (&str, "Hello World")) - _dbus_assert_not_reached ("could not append to string"); - - i = _dbus_string_get_length (&str); - - if (!_dbus_string_steal_data (&str, &s)) - _dbus_assert_not_reached ("failed to steal data"); - - _dbus_assert (_dbus_string_get_length (&str) == 0); - _dbus_assert (((int)strlen (s)) == i); - - dbus_free (s); - - /* Check move */ - - if (!_dbus_string_append (&str, "Hello World")) - _dbus_assert_not_reached ("could not append to string"); - - i = _dbus_string_get_length (&str); - - if (!_dbus_string_init (&other)) - _dbus_assert_not_reached ("could not init string"); - - if (!_dbus_string_move (&str, 0, &other, 0)) - _dbus_assert_not_reached ("could not move"); - - _dbus_assert (_dbus_string_get_length (&str) == 0); - _dbus_assert (_dbus_string_get_length (&other) == i); - - if (!_dbus_string_append (&str, "Hello World")) - _dbus_assert_not_reached ("could not append to string"); - - if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other))) - _dbus_assert_not_reached ("could not move"); - - _dbus_assert (_dbus_string_get_length (&str) == 0); - _dbus_assert (_dbus_string_get_length (&other) == i * 2); - - if (!_dbus_string_append (&str, "Hello World")) - _dbus_assert_not_reached ("could not append to string"); - - if (!_dbus_string_move (&str, 0, &other, _dbus_string_get_length (&other) / 2)) - _dbus_assert_not_reached ("could not move"); - - _dbus_assert (_dbus_string_get_length (&str) == 0); - _dbus_assert (_dbus_string_get_length (&other) == i * 3); - - _dbus_string_free (&other); - - /* Check copy */ - - if (!_dbus_string_append (&str, "Hello World")) - _dbus_assert_not_reached ("could not append to string"); - - i = _dbus_string_get_length (&str); - - if (!_dbus_string_init (&other)) - _dbus_assert_not_reached ("could not init string"); - - if (!_dbus_string_copy (&str, 0, &other, 0)) - _dbus_assert_not_reached ("could not copy"); - - _dbus_assert (_dbus_string_get_length (&str) == i); - _dbus_assert (_dbus_string_get_length (&other) == i); - - if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other))) - _dbus_assert_not_reached ("could not copy"); - - _dbus_assert (_dbus_string_get_length (&str) == i); - _dbus_assert (_dbus_string_get_length (&other) == i * 2); - _dbus_assert (_dbus_string_equal_c_str (&other, - "Hello WorldHello World")); - - if (!_dbus_string_copy (&str, 0, &other, _dbus_string_get_length (&other) / 2)) - _dbus_assert_not_reached ("could not copy"); - - _dbus_assert (_dbus_string_get_length (&str) == i); - _dbus_assert (_dbus_string_get_length (&other) == i * 3); - _dbus_assert (_dbus_string_equal_c_str (&other, - "Hello WorldHello WorldHello World")); - - _dbus_string_free (&str); - _dbus_string_free (&other); - - /* Check replace */ - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - if (!_dbus_string_append (&str, "Hello World")) - _dbus_assert_not_reached ("could not append to string"); - - i = _dbus_string_get_length (&str); - - if (!_dbus_string_init (&other)) - _dbus_assert_not_reached ("could not init string"); - - if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str), - &other, 0, _dbus_string_get_length (&other))) - _dbus_assert_not_reached ("could not replace"); - - _dbus_assert (_dbus_string_get_length (&str) == i); - _dbus_assert (_dbus_string_get_length (&other) == i); - _dbus_assert (_dbus_string_equal_c_str (&other, "Hello World")); - - if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str), - &other, 5, 1)) - _dbus_assert_not_reached ("could not replace center space"); - - _dbus_assert (_dbus_string_get_length (&str) == i); - _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1); - _dbus_assert (_dbus_string_equal_c_str (&other, - "HelloHello WorldWorld")); - - - if (!_dbus_string_replace_len (&str, 1, 1, - &other, - _dbus_string_get_length (&other) - 1, - 1)) - _dbus_assert_not_reached ("could not replace end character"); - - _dbus_assert (_dbus_string_get_length (&str) == i); - _dbus_assert (_dbus_string_get_length (&other) == i * 2 - 1); - _dbus_assert (_dbus_string_equal_c_str (&other, - "HelloHello WorldWorle")); - - _dbus_string_free (&str); - _dbus_string_free (&other); - - /* Check append/get unichar */ - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - ch = 0; - if (!_dbus_string_append_unichar (&str, 0xfffc)) - _dbus_assert_not_reached ("failed to append unichar"); - - _dbus_string_get_unichar (&str, 0, &ch, &i); - - _dbus_assert (ch == 0xfffc); - _dbus_assert (i == _dbus_string_get_length (&str)); - - _dbus_string_free (&str); - - /* Check insert/set/get byte */ - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - if (!_dbus_string_append (&str, "Hello")) - _dbus_assert_not_reached ("failed to append Hello"); - - _dbus_assert (_dbus_string_get_byte (&str, 0) == 'H'); - _dbus_assert (_dbus_string_get_byte (&str, 1) == 'e'); - _dbus_assert (_dbus_string_get_byte (&str, 2) == 'l'); - _dbus_assert (_dbus_string_get_byte (&str, 3) == 'l'); - _dbus_assert (_dbus_string_get_byte (&str, 4) == 'o'); - - _dbus_string_set_byte (&str, 1, 'q'); - _dbus_assert (_dbus_string_get_byte (&str, 1) == 'q'); - - if (!_dbus_string_insert_bytes (&str, 0, 1, 255)) - _dbus_assert_not_reached ("can't insert byte"); - - if (!_dbus_string_insert_bytes (&str, 2, 4, 'Z')) - _dbus_assert_not_reached ("can't insert byte"); - - if (!_dbus_string_insert_bytes (&str, _dbus_string_get_length (&str), 1, 'W')) - _dbus_assert_not_reached ("can't insert byte"); - - _dbus_assert (_dbus_string_get_byte (&str, 0) == 255); - _dbus_assert (_dbus_string_get_byte (&str, 1) == 'H'); - _dbus_assert (_dbus_string_get_byte (&str, 2) == 'Z'); - _dbus_assert (_dbus_string_get_byte (&str, 3) == 'Z'); - _dbus_assert (_dbus_string_get_byte (&str, 4) == 'Z'); - _dbus_assert (_dbus_string_get_byte (&str, 5) == 'Z'); - _dbus_assert (_dbus_string_get_byte (&str, 6) == 'q'); - _dbus_assert (_dbus_string_get_byte (&str, 7) == 'l'); - _dbus_assert (_dbus_string_get_byte (&str, 8) == 'l'); - _dbus_assert (_dbus_string_get_byte (&str, 9) == 'o'); - _dbus_assert (_dbus_string_get_byte (&str, 10) == 'W'); - - _dbus_string_free (&str); - - /* Check append/parse int/double */ - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - if (!_dbus_string_append_int (&str, 27)) - _dbus_assert_not_reached ("failed to append int"); - - i = _dbus_string_get_length (&str); - - if (!_dbus_string_parse_int (&str, 0, &v, &end)) - _dbus_assert_not_reached ("failed to parse int"); - - _dbus_assert (v == 27); - _dbus_assert (end == i); - - _dbus_string_free (&str); - - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - if (!_dbus_string_append_double (&str, 50.3)) - _dbus_assert_not_reached ("failed to append float"); - - i = _dbus_string_get_length (&str); - - if (!_dbus_string_parse_double (&str, 0, &d, &end)) - _dbus_assert_not_reached ("failed to parse float"); - - _dbus_assert (d > (50.3 - 1e-6) && d < (50.3 + 1e-6)); - _dbus_assert (end == i); - - _dbus_string_free (&str); - - /* Test find */ - if (!_dbus_string_init (&str)) - _dbus_assert_not_reached ("failed to init string"); - - if (!_dbus_string_append (&str, "Hello")) - _dbus_assert_not_reached ("couldn't append to string"); - - if (!_dbus_string_find (&str, 0, "He", &i)) - _dbus_assert_not_reached ("didn't find 'He'"); - _dbus_assert (i == 0); - - if (!_dbus_string_find (&str, 0, "Hello", &i)) - _dbus_assert_not_reached ("didn't find 'Hello'"); - _dbus_assert (i == 0); - - if (!_dbus_string_find (&str, 0, "ello", &i)) - _dbus_assert_not_reached ("didn't find 'ello'"); - _dbus_assert (i == 1); - - if (!_dbus_string_find (&str, 0, "lo", &i)) - _dbus_assert_not_reached ("didn't find 'lo'"); - _dbus_assert (i == 3); - - if (!_dbus_string_find (&str, 2, "lo", &i)) - _dbus_assert_not_reached ("didn't find 'lo'"); - _dbus_assert (i == 3); - - if (_dbus_string_find (&str, 4, "lo", &i)) - _dbus_assert_not_reached ("did find 'lo'"); - - if (!_dbus_string_find (&str, 0, "l", &i)) - _dbus_assert_not_reached ("didn't find 'l'"); - _dbus_assert (i == 2); - - if (!_dbus_string_find (&str, 0, "H", &i)) - _dbus_assert_not_reached ("didn't find 'H'"); - _dbus_assert (i == 0); - - if (!_dbus_string_find (&str, 0, "", &i)) - _dbus_assert_not_reached ("didn't find ''"); - _dbus_assert (i == 0); - - if (_dbus_string_find (&str, 0, "Hello!", NULL)) - _dbus_assert_not_reached ("Did find 'Hello!'"); - - if (_dbus_string_find (&str, 0, "Oh, Hello", NULL)) - _dbus_assert_not_reached ("Did find 'Oh, Hello'"); - - if (_dbus_string_find (&str, 0, "ill", NULL)) - _dbus_assert_not_reached ("Did find 'ill'"); - - if (_dbus_string_find (&str, 0, "q", NULL)) - _dbus_assert_not_reached ("Did find 'q'"); - - if (!_dbus_string_find_to (&str, 0, 2, "He", NULL)) - _dbus_assert_not_reached ("Didn't find 'He'"); - - if (_dbus_string_find_to (&str, 0, 2, "Hello", NULL)) - _dbus_assert_not_reached ("Did find 'Hello'"); - - if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'H', &i)) - _dbus_assert_not_reached ("Did not find 'H'"); - _dbus_assert (i == 0); - - if (!_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str), 'o', &i)) - _dbus_assert_not_reached ("Did not find 'o'"); - _dbus_assert (i == _dbus_string_get_length (&str) - 1); - - if (_dbus_string_find_byte_backward (&str, _dbus_string_get_length (&str) - 1, 'o', &i)) - _dbus_assert_not_reached ("Did find 'o'"); - _dbus_assert (i == -1); - - if (_dbus_string_find_byte_backward (&str, 1, 'e', &i)) - _dbus_assert_not_reached ("Did find 'e'"); - _dbus_assert (i == -1); - - if (!_dbus_string_find_byte_backward (&str, 2, 'e', &i)) - _dbus_assert_not_reached ("Didn't find 'e'"); - _dbus_assert (i == 1); - - _dbus_string_free (&str); - - /* Hex encoding */ - _dbus_string_init_const (&str, "cafebabe, this is a bogus hex string"); - if (!_dbus_string_init (&other)) - _dbus_assert_not_reached ("could not init string"); - - if (!_dbus_string_hex_decode (&str, 0, &end, &other, 0)) - _dbus_assert_not_reached ("deccoded bogus hex string with no error"); - - _dbus_assert (end == 8); - - _dbus_string_free (&other); - - test_roundtrips (test_hex_roundtrip); - - _dbus_string_free (&str); - - return TRUE; -} - -#endif /* DBUS_BUILD_TESTS */ +/* tests are in dbus-string-util.c */ diff --git a/dbus/dbus-string.h b/dbus/dbus-string.h index df5f4232..52281210 100644 --- a/dbus/dbus-string.h +++ b/dbus/dbus-string.h @@ -54,6 +54,7 @@ struct DBusString * to inline non-exported symbols across files in the library. * Note that these break type safety (due to the casts) */ +#define _dbus_string_get_data(s) ((char*)(((DBusString*)(s))->dummy1)) #define _dbus_string_get_length(s) (((DBusString*)(s))->dummy2) #define _dbus_string_set_byte(s, i, b) ((((unsigned char*)(((DBusString*)(s))->dummy1))[(i)]) = (unsigned char) (b)) #define _dbus_string_get_byte(s, i) (((const unsigned char*)(((DBusString*)(s))->dummy1))[(i)]) @@ -71,7 +72,9 @@ dbus_bool_t _dbus_string_init_preallocated (DBusString *str, int allocate_size); void _dbus_string_free (DBusString *str); void _dbus_string_lock (DBusString *str); +#ifndef _dbus_string_get_data char* _dbus_string_get_data (DBusString *str); +#endif /* _dbus_string_get_data */ #ifndef _dbus_string_get_const_data const char* _dbus_string_get_const_data (const DBusString *str); #endif /* _dbus_string_get_const_data */ @@ -205,11 +208,6 @@ dbus_bool_t _dbus_string_parse_double (const DBusString *str, int start, double *value, int *end_return); -dbus_bool_t _dbus_string_parse_basic_type (const DBusString *str, - char type, - int start, - void *value, - int *end_return); dbus_bool_t _dbus_string_find (const DBusString *str, int start, const char *substr, diff --git a/dbus/dbus-sysdeps-util.c b/dbus/dbus-sysdeps-util.c new file mode 100644 index 00000000..52298f81 --- /dev/null +++ b/dbus/dbus-sysdeps-util.c @@ -0,0 +1,871 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-sysdeps-util.c Would be in dbus-sysdeps.c, but not used in libdbus + * + * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. + * Copyright (C) 2003 CodeFactory AB + * + * 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-sysdeps.h" +#include "dbus-internals.h" +#include "dbus-protocol.h" +#include "dbus-string.h" +#define DBUS_USERDB_INCLUDES_PRIVATE 1 +#include "dbus-userdb.h" +#include "dbus-test.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/** + * @addtogroup DBusInternalsUtils + * @{ + */ + +/** + * Does the chdir, fork, setsid, etc. to become a daemon process. + * + * @param pidfile #NULL, or pidfile to create + * @param print_pid_fd file descriptor to print pid to, or -1 for none + * @param error return location for errors + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_become_daemon (const DBusString *pidfile, + int print_pid_fd, + DBusError *error) +{ + const char *s; + pid_t child_pid; + int dev_null_fd; + + _dbus_verbose ("Becoming a daemon...\n"); + + _dbus_verbose ("chdir to /\n"); + if (chdir ("/") < 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Could not chdir() to root directory"); + return FALSE; + } + + _dbus_verbose ("forking...\n"); + switch ((child_pid = fork ())) + { + case -1: + _dbus_verbose ("fork failed\n"); + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to fork daemon: %s", _dbus_strerror (errno)); + return FALSE; + break; + + case 0: + _dbus_verbose ("in child, closing std file descriptors\n"); + + /* silently ignore failures here, if someone + * doesn't have /dev/null we may as well try + * to continue anyhow + */ + + dev_null_fd = open ("/dev/null", O_RDWR); + if (dev_null_fd >= 0) + { + dup2 (dev_null_fd, 0); + dup2 (dev_null_fd, 1); + + s = _dbus_getenv ("DBUS_DEBUG_OUTPUT"); + if (s == NULL || *s == '\0') + dup2 (dev_null_fd, 2); + else + _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n"); + } + + /* Get a predictable umask */ + _dbus_verbose ("setting umask\n"); + umask (022); + break; + + default: + if (pidfile) + { + _dbus_verbose ("parent writing pid file\n"); + if (!_dbus_write_pid_file (pidfile, + child_pid, + error)) + { + _dbus_verbose ("pid file write failed, killing child\n"); + kill (child_pid, SIGTERM); + return FALSE; + } + } + + /* Write PID if requested */ + if (print_pid_fd >= 0) + { + DBusString pid; + int bytes; + + if (!_dbus_string_init (&pid)) + { + _DBUS_SET_OOM (error); + kill (child_pid, SIGTERM); + return FALSE; + } + + if (!_dbus_string_append_int (&pid, _dbus_getpid ()) || + !_dbus_string_append (&pid, "\n")) + { + _dbus_string_free (&pid); + _DBUS_SET_OOM (error); + kill (child_pid, SIGTERM); + return FALSE; + } + + bytes = _dbus_string_get_length (&pid); + if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Printing message bus PID: %s\n", + _dbus_strerror (errno)); + _dbus_string_free (&pid); + kill (child_pid, SIGTERM); + return FALSE; + } + + _dbus_string_free (&pid); + } + _dbus_verbose ("parent exiting\n"); + _exit (0); + break; + } + + _dbus_verbose ("calling setsid()\n"); + if (setsid () == -1) + _dbus_assert_not_reached ("setsid() failed"); + + return TRUE; +} + + +/** + * Creates a file containing the process ID. + * + * @param filename the filename to write to + * @param pid our process ID + * @param error return location for errors + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_write_pid_file (const DBusString *filename, + unsigned long pid, + DBusError *error) +{ + const char *cfilename; + int fd; + FILE *f; + + cfilename = _dbus_string_get_const_data (filename); + + fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644); + + if (fd < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to open \"%s\": %s", cfilename, + _dbus_strerror (errno)); + return FALSE; + } + + if ((f = fdopen (fd, "w")) == NULL) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno)); + close (fd); + return FALSE; + } + + if (fprintf (f, "%lu\n", pid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to write to \"%s\": %s", cfilename, + _dbus_strerror (errno)); + return FALSE; + } + + if (fclose (f) == EOF) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to close \"%s\": %s", cfilename, + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + + +/** + * Changes the user and group the bus is running as. + * + * @param uid the new user ID + * @param gid the new group ID + * @param error return location for errors + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_change_identity (dbus_uid_t uid, + dbus_gid_t gid, + DBusError *error) +{ + /* setgroups() only works if we are a privileged process, + * so we don't return error on failure; the only possible + * failure is that we don't have perms to do it. + * FIXME not sure this is right, maybe if setuid() + * is going to work then setgroups() should also work. + */ + if (setgroups (0, NULL) < 0) + _dbus_warn ("Failed to drop supplementary groups: %s\n", + _dbus_strerror (errno)); + + /* Set GID first, or the setuid may remove our permission + * to change the GID + */ + if (setgid (gid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set GID to %lu: %s", gid, + _dbus_strerror (errno)); + return FALSE; + } + + if (setuid (uid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set UID to %lu: %s", uid, + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/** Installs a UNIX signal handler + * + * @param sig the signal to handle + * @param handler the handler + */ +void +_dbus_set_signal_handler (int sig, + DBusSignalHandler handler) +{ + struct sigaction act; + sigset_t empty_mask; + + sigemptyset (&empty_mask); + act.sa_handler = handler; + act.sa_mask = empty_mask; + act.sa_flags = 0; + sigaction (sig, &act, 0); +} + + +/** + * Removes a directory; Directory must be empty + * + * @param filename directory filename + * @param error initialized error object + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_delete_directory (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (rmdir (filename_c) != 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to remove directory %s: %s\n", + filename_c, _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/** Checks if a file exists +* +* @param file full path to the file +* @returns #TRUE if file exists +*/ +dbus_bool_t +_dbus_file_exists (const char *file) +{ + return (access (file, F_OK) == 0); +} + +/** Checks if user is at the console +* +* @param username user to check +* @param error return location for errors +* @returns #TRUE is the user is at the consolei and there are no errors +*/ +dbus_bool_t +_dbus_user_at_console (const char *username, + DBusError *error) +{ + + DBusString f; + dbus_bool_t result; + + result = FALSE; + if (!_dbus_string_init (&f)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_append (&f, DBUS_CONSOLE_DIR)) + { + _DBUS_SET_OOM (error); + goto out; + } + + + if (!_dbus_string_append (&f, username)) + { + _DBUS_SET_OOM (error); + goto out; + } + + result = _dbus_file_exists (_dbus_string_get_const_data (&f)); + + out: + _dbus_string_free (&f); + + return result; +} + + +/** + * Checks whether the filename is an absolute path + * + * @param filename the filename + * @returns #TRUE if an absolute path + */ +dbus_bool_t +_dbus_path_is_absolute (const DBusString *filename) +{ + if (_dbus_string_get_length (filename) > 0) + return _dbus_string_get_byte (filename, 0) == '/'; + else + return FALSE; +} + +/** + * stat() wrapper. + * + * @param filename the filename to stat + * @param statbuf the stat info to fill in + * @param error return location for error + * @returns #FALSE if error was set + */ +dbus_bool_t +_dbus_stat (const DBusString *filename, + DBusStat *statbuf, + DBusError *error) +{ + const char *filename_c; + struct stat sb; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (stat (filename_c, &sb) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "%s", _dbus_strerror (errno)); + return FALSE; + } + + statbuf->mode = sb.st_mode; + statbuf->nlink = sb.st_nlink; + statbuf->uid = sb.st_uid; + statbuf->gid = sb.st_gid; + statbuf->size = sb.st_size; + statbuf->atime = sb.st_atime; + statbuf->mtime = sb.st_mtime; + statbuf->ctime = sb.st_ctime; + + return TRUE; +} + + +/** + * Internals of directory iterator + */ +struct DBusDirIter +{ + DIR *d; /**< The DIR* from opendir() */ + +}; + +/** + * Open a directory to iterate over. + * + * @param filename the directory name + * @param error exception return object or #NULL + * @returns new iterator, or #NULL on error + */ +DBusDirIter* +_dbus_directory_open (const DBusString *filename, + DBusError *error) +{ + DIR *d; + DBusDirIter *iter; + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + d = opendir (filename_c); + if (d == NULL) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to read directory \"%s\": %s", + filename_c, + _dbus_strerror (errno)); + return NULL; + } + iter = dbus_new0 (DBusDirIter, 1); + if (iter == NULL) + { + closedir (d); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "Could not allocate memory for directory iterator"); + return NULL; + } + + iter->d = d; + + return iter; +} + +/** + * Get next file in the directory. Will not return "." or ".." on + * UNIX. If an error occurs, the contents of "filename" are + * undefined. The error is never set if the function succeeds. + * + * @todo for thread safety, I think we have to use + * readdir_r(). (GLib has the same issue, should file a bug.) + * + * @param iter the iterator + * @param filename string to be set to the next file in the dir + * @param error return location for error + * @returns #TRUE if filename was filled in with a new filename + */ +dbus_bool_t +_dbus_directory_get_next_file (DBusDirIter *iter, + DBusString *filename, + DBusError *error) +{ + struct dirent *ent; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + again: + errno = 0; + ent = readdir (iter->d); + if (ent == NULL) + { + if (errno != 0) + dbus_set_error (error, + _dbus_error_from_errno (errno), + "%s", _dbus_strerror (errno)); + return FALSE; + } + else if (ent->d_name[0] == '.' && + (ent->d_name[1] == '\0' || + (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) + goto again; + else + { + _dbus_string_set_length (filename, 0); + if (!_dbus_string_append (filename, ent->d_name)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "No memory to read directory entry"); + return FALSE; + } + else + return TRUE; + } +} + +/** + * Closes a directory iteration. + */ +void +_dbus_directory_close (DBusDirIter *iter) +{ + closedir (iter->d); + dbus_free (iter); +} + +static dbus_bool_t +fill_user_info_from_group (struct group *g, + DBusGroupInfo *info, + DBusError *error) +{ + _dbus_assert (g->gr_name != NULL); + + info->gid = g->gr_gid; + info->groupname = _dbus_strdup (g->gr_name); + + /* info->members = dbus_strdupv (g->gr_mem) */ + + if (info->groupname == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +fill_group_info (DBusGroupInfo *info, + dbus_gid_t gid, + const DBusString *groupname, + DBusError *error) +{ + const char *group_c_str; + + _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET); + _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET); + + if (groupname) + group_c_str = _dbus_string_get_const_data (groupname); + else + group_c_str = NULL; + + /* For now assuming that the getgrnam() and getgrgid() flavors + * always correspond to the pwnam flavors, if not we have + * to add more configure checks. + */ + +#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R) + { + struct group *g; + int result; + char buf[1024]; + struct group g_str; + + g = NULL; +#ifdef HAVE_POSIX_GETPWNAME_R + + if (group_c_str) + result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf), + &g); + else + result = getgrgid_r (gid, &g_str, buf, sizeof (buf), + &g); +#else + p = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf)); + result = 0; +#endif /* !HAVE_POSIX_GETPWNAME_R */ + if (result == 0 && g == &g_str) + { + return fill_user_info_from_group (g, info, error); + } + else + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Group %s unknown or failed to look it up\n", + group_c_str ? group_c_str : "???"); + return FALSE; + } + } +#else /* ! HAVE_GETPWNAM_R */ + { + /* I guess we're screwed on thread safety here */ + struct group *g; + + g = getgrnam (group_c_str); + + if (g != NULL) + { + return fill_user_info_from_group (g, info, error); + } + else + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Group %s unknown or failed to look it up\n", + group_c_str ? group_c_str : "???"); + return FALSE; + } + } +#endif /* ! HAVE_GETPWNAM_R */ +} + +/** + * Initializes the given DBusGroupInfo struct + * with information about the given group name. + * + * @param info the group info struct + * @param groupname name of group + * @param error the error return + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_group_info_fill (DBusGroupInfo *info, + const DBusString *groupname, + DBusError *error) +{ + return fill_group_info (info, DBUS_GID_UNSET, + groupname, error); + +} + +/** + * Initializes the given DBusGroupInfo struct + * with information about the given group ID. + * + * @param info the group info struct + * @param gid group ID + * @param error the error return + * @returns #FALSE if error is set + */ +dbus_bool_t +_dbus_group_info_fill_gid (DBusGroupInfo *info, + dbus_gid_t gid, + DBusError *error) +{ + return fill_group_info (info, gid, NULL, error); +} + +/** + * Frees the members of info (but not info itself). + * + * @param info the group info + */ +void +_dbus_group_info_free (DBusGroupInfo *info) +{ + dbus_free (info->groupname); +} + +/** @} */ /* End of DBusInternalsUtils functions */ + +/** + * @addtogroup DBusString + * + * @{ + */ +/** + * Get the directory name from a complete filename + * @param filename the filename + * @param dirname string to append directory name to + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_string_get_dirname (const DBusString *filename, + DBusString *dirname) +{ + int sep; + + _dbus_assert (filename != dirname); + _dbus_assert (filename != NULL); + _dbus_assert (dirname != NULL); + + /* Ignore any separators on the end */ + sep = _dbus_string_get_length (filename); + if (sep == 0) + return _dbus_string_append (dirname, "."); /* empty string passed in */ + + while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/') + --sep; + + _dbus_assert (sep >= 0); + + if (sep == 0) + return _dbus_string_append (dirname, "/"); + + /* Now find the previous separator */ + _dbus_string_find_byte_backward (filename, sep, '/', &sep); + if (sep < 0) + return _dbus_string_append (dirname, "."); + + /* skip multiple separators */ + while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/') + --sep; + + _dbus_assert (sep >= 0); + + if (sep == 0 && + _dbus_string_get_byte (filename, 0) == '/') + return _dbus_string_append (dirname, "/"); + else + return _dbus_string_copy_len (filename, 0, sep - 0, + dirname, _dbus_string_get_length (dirname)); +} +/** @} */ /* DBusString stuff */ + + +#ifdef DBUS_BUILD_TESTS +#include +static void +check_dirname (const char *filename, + const char *dirname) +{ + DBusString f, d; + + _dbus_string_init_const (&f, filename); + + if (!_dbus_string_init (&d)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_get_dirname (&f, &d)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_equal_c_str (&d, dirname)) + { + _dbus_warn ("For filename \"%s\" got dirname \"%s\" and expected \"%s\"\n", + filename, + _dbus_string_get_const_data (&d), + dirname); + exit (1); + } + + _dbus_string_free (&d); +} + +static void +check_path_absolute (const char *path, + dbus_bool_t expected) +{ + DBusString p; + + _dbus_string_init_const (&p, path); + + if (_dbus_path_is_absolute (&p) != expected) + { + _dbus_warn ("For path \"%s\" expected absolute = %d got %d\n", + path, expected, _dbus_path_is_absolute (&p)); + exit (1); + } +} + +/** + * Unit test for dbus-sysdeps.c. + * + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_sysdeps_test (void) +{ + DBusString str; + double val; + int pos; + + check_dirname ("foo", "."); + check_dirname ("foo/bar", "foo"); + check_dirname ("foo//bar", "foo"); + check_dirname ("foo///bar", "foo"); + check_dirname ("foo/bar/", "foo"); + check_dirname ("foo//bar/", "foo"); + check_dirname ("foo///bar/", "foo"); + check_dirname ("foo/bar//", "foo"); + check_dirname ("foo//bar////", "foo"); + check_dirname ("foo///bar///////", "foo"); + check_dirname ("/foo", "/"); + check_dirname ("////foo", "/"); + check_dirname ("/foo/bar", "/foo"); + check_dirname ("/foo//bar", "/foo"); + check_dirname ("/foo///bar", "/foo"); + check_dirname ("/", "/"); + check_dirname ("///", "/"); + check_dirname ("", "."); + + + _dbus_string_init_const (&str, "3.5"); + if (!_dbus_string_parse_double (&str, + 0, &val, &pos)) + { + _dbus_warn ("Failed to parse double"); + exit (1); + } + if (ABS(3.5 - val) > 1e-6) + { + _dbus_warn ("Failed to parse 3.5 correctly, got: %f", val); + exit (1); + } + if (pos != 3) + { + _dbus_warn ("_dbus_string_parse_double of \"3.5\" returned wrong position %d", pos); + exit (1); + } + + _dbus_string_init_const (&str, "0xff"); + if (!_dbus_string_parse_double (&str, + 0, &val, &pos)) + { + _dbus_warn ("Failed to parse double"); + exit (1); + } + if (ABS (0xff - val) > 1e-6) + { + _dbus_warn ("Failed to parse 0xff correctly, got: %f\n", val); + exit (1); + } + if (pos != 4) + { + _dbus_warn ("_dbus_string_parse_double of \"0xff\" returned wrong position %d", pos); + exit (1); + } + + check_path_absolute ("/", TRUE); + check_path_absolute ("/foo", TRUE); + check_path_absolute ("", FALSE); + check_path_absolute ("foo", FALSE); + check_path_absolute ("foo/bar", FALSE); + + return TRUE; +} +#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index 049c63ab..d951a8d6 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -1628,149 +1628,6 @@ _dbus_user_info_free (DBusUserInfo *info) dbus_free (info->homedir); } -static dbus_bool_t -fill_user_info_from_group (struct group *g, - DBusGroupInfo *info, - DBusError *error) -{ - _dbus_assert (g->gr_name != NULL); - - info->gid = g->gr_gid; - info->groupname = _dbus_strdup (g->gr_name); - - /* info->members = dbus_strdupv (g->gr_mem) */ - - if (info->groupname == NULL) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - return FALSE; - } - - return TRUE; -} - -static dbus_bool_t -fill_group_info (DBusGroupInfo *info, - dbus_gid_t gid, - const DBusString *groupname, - DBusError *error) -{ - const char *group_c_str; - - _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET); - _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET); - - if (groupname) - group_c_str = _dbus_string_get_const_data (groupname); - else - group_c_str = NULL; - - /* For now assuming that the getgrnam() and getgrgid() flavors - * always correspond to the pwnam flavors, if not we have - * to add more configure checks. - */ - -#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R) - { - struct group *g; - int result; - char buf[1024]; - struct group g_str; - - g = NULL; -#ifdef HAVE_POSIX_GETPWNAME_R - - if (group_c_str) - result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf), - &g); - else - result = getgrgid_r (gid, &g_str, buf, sizeof (buf), - &g); -#else - p = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf)); - result = 0; -#endif /* !HAVE_POSIX_GETPWNAME_R */ - if (result == 0 && g == &g_str) - { - return fill_user_info_from_group (g, info, error); - } - else - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Group %s unknown or failed to look it up\n", - group_c_str ? group_c_str : "???"); - return FALSE; - } - } -#else /* ! HAVE_GETPWNAM_R */ - { - /* I guess we're screwed on thread safety here */ - struct group *g; - - g = getgrnam (group_c_str); - - if (g != NULL) - { - return fill_user_info_from_group (g, info, error); - } - else - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Group %s unknown or failed to look it up\n", - group_c_str ? group_c_str : "???"); - return FALSE; - } - } -#endif /* ! HAVE_GETPWNAM_R */ -} - -/** - * Initializes the given DBusGroupInfo struct - * with information about the given group name. - * - * @param info the group info struct - * @param groupname name of group - * @param error the error return - * @returns #FALSE if error is set - */ -dbus_bool_t -_dbus_group_info_fill (DBusGroupInfo *info, - const DBusString *groupname, - DBusError *error) -{ - return fill_group_info (info, DBUS_GID_UNSET, - groupname, error); - -} - -/** - * Initializes the given DBusGroupInfo struct - * with information about the given group ID. - * - * @param info the group info struct - * @param gid group ID - * @param error the error return - * @returns #FALSE if error is set - */ -dbus_bool_t -_dbus_group_info_fill_gid (DBusGroupInfo *info, - dbus_gid_t gid, - DBusError *error) -{ - return fill_group_info (info, gid, NULL, error); -} - -/** - * Frees the members of info (but not info itself). - * - * @param info the group info - */ -void -_dbus_group_info_free (DBusGroupInfo *info) -{ - dbus_free (info->groupname); -} - /** * Sets fields in DBusCredentials to DBUS_PID_UNSET, * DBUS_UID_UNSET, DBUS_GID_UNSET. @@ -1849,6 +1706,7 @@ _dbus_getuid (void) return getuid (); } +#ifdef DBUS_BUILD_TESTS /** Gets our GID * @returns process GID */ @@ -1857,6 +1715,7 @@ _dbus_getgid (void) { return getgid (); } +#endif _DBUS_DEFINE_GLOBAL_LOCK (atomic); @@ -2467,118 +2326,6 @@ _dbus_concat_dir_and_file (DBusString *dir, _dbus_string_get_length (dir)); } -/** - * Internals of directory iterator - */ -struct DBusDirIter -{ - DIR *d; /**< The DIR* from opendir() */ - -}; - -/** - * Open a directory to iterate over. - * - * @param filename the directory name - * @param error exception return object or #NULL - * @returns new iterator, or #NULL on error - */ -DBusDirIter* -_dbus_directory_open (const DBusString *filename, - DBusError *error) -{ - DIR *d; - DBusDirIter *iter; - const char *filename_c; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - filename_c = _dbus_string_get_const_data (filename); - - d = opendir (filename_c); - if (d == NULL) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to read directory \"%s\": %s", - filename_c, - _dbus_strerror (errno)); - return NULL; - } - iter = dbus_new0 (DBusDirIter, 1); - if (iter == NULL) - { - closedir (d); - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, - "Could not allocate memory for directory iterator"); - return NULL; - } - - iter->d = d; - - return iter; -} - -/** - * Get next file in the directory. Will not return "." or ".." on - * UNIX. If an error occurs, the contents of "filename" are - * undefined. The error is never set if the function succeeds. - * - * @todo for thread safety, I think we have to use - * readdir_r(). (GLib has the same issue, should file a bug.) - * - * @param iter the iterator - * @param filename string to be set to the next file in the dir - * @param error return location for error - * @returns #TRUE if filename was filled in with a new filename - */ -dbus_bool_t -_dbus_directory_get_next_file (DBusDirIter *iter, - DBusString *filename, - DBusError *error) -{ - struct dirent *ent; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - again: - errno = 0; - ent = readdir (iter->d); - if (ent == NULL) - { - if (errno != 0) - dbus_set_error (error, - _dbus_error_from_errno (errno), - "%s", _dbus_strerror (errno)); - return FALSE; - } - else if (ent->d_name[0] == '.' && - (ent->d_name[1] == '\0' || - (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) - goto again; - else - { - _dbus_string_set_length (filename, 0); - if (!_dbus_string_append (filename, ent->d_name)) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, - "No memory to read directory entry"); - return FALSE; - } - else - return TRUE; - } -} - -/** - * Closes a directory iteration. - */ -void -_dbus_directory_close (DBusDirIter *iter) -{ - closedir (iter->d); - dbus_free (iter); -} - static dbus_bool_t pseudorandom_generate_random_bytes (DBusString *str, int n_bytes) @@ -2858,62 +2605,6 @@ _dbus_exit (int code) _exit (code); } -/** - * Creates a full-duplex pipe (as in socketpair()). - * Sets both ends of the pipe nonblocking. - * - * @param fd1 return location for one end - * @param fd2 return location for the other end - * @param blocking #TRUE if pipe should be blocking - * @param error error return - * @returns #FALSE on failure (if error is set) - */ -dbus_bool_t -_dbus_full_duplex_pipe (int *fd1, - int *fd2, - dbus_bool_t blocking, - DBusError *error) -{ -#ifdef HAVE_SOCKETPAIR - int fds[2]; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Could not create full-duplex pipe"); - return FALSE; - } - - if (!blocking && - (!_dbus_set_fd_nonblocking (fds[0], NULL) || - !_dbus_set_fd_nonblocking (fds[1], NULL))) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Could not set full-duplex pipe nonblocking"); - - close (fds[0]); - close (fds[1]); - - return FALSE; - } - - *fd1 = fds[0]; - *fd2 = fds[1]; - - _dbus_verbose ("full-duplex pipe %d <-> %d\n", - *fd1, *fd2); - - return TRUE; -#else - _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n"); - dbus_set_error (error, DBUS_ERROR_FAILED, - "_dbus_full_duplex_pipe() not implemented on this OS"); - return FALSE; -#endif -} - /** * Closes a file descriptor. * @@ -3055,6 +2746,67 @@ _dbus_parse_uid (const DBusString *uid_str, return TRUE; } +/** + * Creates a full-duplex pipe (as in socketpair()). + * Sets both ends of the pipe nonblocking. + * + * @todo libdbus only uses this for the debug-pipe server, so in + * principle it could be in dbus-sysdeps-util.c, except that + * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the + * debug-pipe server is used. + * + * @param fd1 return location for one end + * @param fd2 return location for the other end + * @param blocking #TRUE if pipe should be blocking + * @param error error return + * @returns #FALSE on failure (if error is set) + */ +dbus_bool_t +_dbus_full_duplex_pipe (int *fd1, + int *fd2, + dbus_bool_t blocking, + DBusError *error) +{ +#ifdef HAVE_SOCKETPAIR + int fds[2]; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not create full-duplex pipe"); + return FALSE; + } + + if (!blocking && + (!_dbus_set_fd_nonblocking (fds[0], NULL) || + !_dbus_set_fd_nonblocking (fds[1], NULL))) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not set full-duplex pipe nonblocking"); + + close (fds[0]); + close (fds[1]); + + return FALSE; + } + + *fd1 = fds[0]; + *fd2 = fds[1]; + + _dbus_verbose ("full-duplex pipe %d <-> %d\n", + *fd1, *fd2); + + return TRUE; +#else + _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n"); + dbus_set_error (error, DBUS_ERROR_FAILED, + "_dbus_full_duplex_pipe() not implemented on this OS"); + return FALSE; +#endif +} + /** @} end of sysdeps */ /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-userdb-util.c b/dbus/dbus-userdb-util.c index 20287f91..10226007 100644 --- a/dbus/dbus-userdb-util.c +++ b/dbus/dbus-userdb-util.c @@ -32,7 +32,6 @@ * @{ */ - /** * Checks to see if the UID sent in is the console user * diff --git a/dbus/dbus-userdb.c b/dbus/dbus-userdb.c index ff583204..85ee1b67 100644 --- a/dbus/dbus-userdb.c +++ b/dbus/dbus-userdb.c @@ -442,6 +442,7 @@ _dbus_user_database_new (void) return NULL; } +#ifdef DBUS_BUILD_TESTS /** * Increments refcount of user database. * @param db the database @@ -456,6 +457,7 @@ _dbus_user_database_ref (DBusUserDatabase *db) return db; } +#endif /* DBUS_BUILD_TESTS */ /** * Decrements refcount of user database. diff --git a/test/unused-code-gc.py b/test/unused-code-gc.py index 7bc1930b..a58597f7 100755 --- a/test/unused-code-gc.py +++ b/test/unused-code-gc.py @@ -230,6 +230,8 @@ trace 'reachable' through hardcoded function calls, if a function is called only through a vtable, it won't be marked reachable (and neither will its children in the call graph). +Also, the sizes mentioned are more or less completely bogus. + """ print "The following are hardcoded in as vtable roots: %s" % vtable_roots -- cgit