[glib/gvariant: 3/116] import GVariant



commit b77c1dfe85b1e418c6d5cc3477d8c99a041b0445
Author: Ryan Lortie <desrt desrt ca>
Date:   Sat Oct 31 19:13:00 2009 -0400

    import GVariant

 docs/reference/glib/Makefile.am       |    5 +-
 docs/reference/glib/glib-docs.sgml    |    5 +
 docs/reference/glib/glib-sections.txt |  188 +++
 glib/Makefile.am                      |   14 +
 glib/glib.h                           |    2 +
 glib/glib.symbols                     |  186 +++
 glib/gvariant-core.c                  | 1475 ++++++++++++++++++++++
 glib/gvariant-markup.c                | 1017 +++++++++++++++
 glib/gvariant-parser.c                | 2200 +++++++++++++++++++++++++++++++++
 glib/gvariant-printer.c               |  312 +++++
 glib/gvariant-private.h               |   43 +
 glib/gvariant-serialiser.c            | 1262 +++++++++++++++++++
 glib/gvariant-serialiser.h            |   56 +
 glib/gvariant-util.c                  | 2029 ++++++++++++++++++++++++++++++
 glib/gvariant-valist.c                | 1563 +++++++++++++++++++++++
 glib/gvariant.h                       |  259 ++++
 glib/gvarianttype.c                   | 1045 ++++++++++++++++
 glib/gvarianttype.h                   |  331 +++++
 glib/gvarianttypeinfo.c               |  469 +++++++
 glib/gvarianttypeinfo.h               |   63 +
 glib/tests/.gitignore                 |    9 +
 glib/tests/Makefile.am                |   27 +
 glib/tests/gvariant-basic.c           |  315 +++++
 glib/tests/gvariant-big.c             |  217 ++++
 glib/tests/gvariant-complex.c         |  175 +++
 glib/tests/gvariant-constructors.c    |  297 +++++
 glib/tests/gvariant-endian.c          |   32 +
 glib/tests/gvariant-fuzz.c            |  428 +++++++
 glib/tests/gvariant-markup.c          |  111 ++
 glib/tests/gvariant-printer-parser.c  |   75 ++
 glib/tests/gvariant-random.c          |  334 +++++
 glib/tests/gvariant-serialiser.c      |   90 ++
 glib/tests/gvariant-varargs-strings.c |   62 +
 glib/tests/gvariant-varargs.c         |  289 +++++
 34 files changed, 14984 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/glib/Makefile.am b/docs/reference/glib/Makefile.am
index c7b9feb..12dc63c 100644
--- a/docs/reference/glib/Makefile.am
+++ b/docs/reference/glib/Makefile.am
@@ -45,7 +45,10 @@ IGNORE_HFILES=			\
 	glib-mirroring-tab	\
 	gnulib			\
 	pcre			\
-	update-pcre
+	update-pcre		\
+	gvariant-serialiser.h	\
+	gvariant-private.h	\
+	gvarianttypeinfo.h
 
 # Images to copy into HTML directory
 HTML_IMAGES =  				\
diff --git a/docs/reference/glib/glib-docs.sgml b/docs/reference/glib/glib-docs.sgml
index b2741fd..2e84b9e 100644
--- a/docs/reference/glib/glib-docs.sgml
+++ b/docs/reference/glib/glib-docs.sgml
@@ -68,6 +68,9 @@
 <!ENTITY glib-Testing SYSTEM "xml/testing.xml">
 <!ENTITY glib-Hostutils SYSTEM "xml/ghostutils.xml">
 
+<!ENTITY glib-GVariantType SYSTEM "xml/gvarianttype.xml">
+<!ENTITY glib-GVariant SYSTEM "xml/gvariant.xml">
+
 <!ENTITY glib-Compiling SYSTEM "compiling.sgml">
 <!ENTITY glib-Building SYSTEM "building.sgml">
 <!ENTITY glib-Cross SYSTEM "cross.sgml">
@@ -198,6 +201,8 @@ synchronize their operation.
     &glib-Relations-and-Tuples;
     &glib-Caches;
     &glib-Memory-Allocators;
+    &glib-GVariantType;
+    &glib-GVariant;
   </chapter>
 
   <chapter id="tools">
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 9cb6c00..b6edbf3 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -2693,6 +2693,194 @@ g_test_log_buffer_pop
 g_test_log_msg_free
 </SECTION>
 
+<SECTION>
+<TITLE>GVariantType</TITLE>
+<FILE>gvarianttype</FILE>
+GVariantTypeClass
+g_variant_type_class_is_basic
+g_variant_type_class_is_container
+
+<SUBSECTION>
+GVariantType
+G_VARIANT_TYPE
+
+<SUBSECTION>
+G_VARIANT_TYPE_BOOLEAN
+G_VARIANT_TYPE_BYTE
+G_VARIANT_TYPE_INT16
+G_VARIANT_TYPE_UINT16
+G_VARIANT_TYPE_INT32
+G_VARIANT_TYPE_UINT32
+G_VARIANT_TYPE_INT64
+G_VARIANT_TYPE_UINT64
+G_VARIANT_TYPE_DOUBLE
+G_VARIANT_TYPE_STRING
+G_VARIANT_TYPE_OBJECT_PATH
+G_VARIANT_TYPE_SIGNATURE
+G_VARIANT_TYPE_VARIANT
+G_VARIANT_TYPE_ANY
+G_VARIANT_TYPE_ANY_BASIC
+G_VARIANT_TYPE_ANY_MAYBE
+G_VARIANT_TYPE_ANY_ARRAY
+G_VARIANT_TYPE_ANY_TUPLE
+G_VARIANT_TYPE_UNIT
+G_VARIANT_TYPE_ANY_DICT_ENTRY
+G_VARIANT_TYPE_ANY_DICTIONARY
+
+<SUBSECTION>
+g_variant_type_free
+g_variant_type_copy
+g_variant_type_new
+
+<SUBSECTION>
+g_variant_type_string_is_valid
+g_variant_type_string_scan
+g_variant_type_get_string_length
+g_variant_type_peek_string
+g_variant_type_dup_string
+
+<SUBSECTION>
+g_variant_type_get_class
+g_variant_type_is_in_class
+g_variant_type_is_basic
+g_variant_type_is_container
+g_variant_type_is_concrete
+
+<SUBSECTION>
+g_variant_type_hash
+g_variant_type_equal
+g_variant_type_matches
+
+<SUBSECTION>
+g_variant_type_new_maybe
+g_variant_type_new_array
+GVariantTypeGetter
+g_variant_type_new_tuple
+g_variant_type_new_dict_entry
+
+<SUBSECTION>
+g_variant_type_element
+g_variant_type_n_items
+g_variant_type_first
+g_variant_type_next
+g_variant_type_key
+g_variant_type_value
+</SECTION>
+
+<SECTION>
+<TITLE>GVariant</TITLE>
+<FILE>gvariant</FILE>
+GVariant
+g_variant_ref
+g_variant_ref_sink
+g_variant_unref
+g_variant_get_type
+g_variant_get_type_class
+g_variant_matches
+g_variant_get_type_string
+g_variant_is_basic
+g_variant_is_container
+
+<SUBSECTION>
+g_variant_new_boolean
+g_variant_new_byte
+g_variant_new_int16
+g_variant_new_uint16
+g_variant_new_int32
+g_variant_new_uint32
+g_variant_new_int64
+g_variant_new_uint64
+g_variant_new_double
+g_variant_new_string
+g_variant_new_object_path
+g_variant_is_object_path
+g_variant_new_signature
+g_variant_is_signature
+g_variant_new_variant
+g_variant_new_strv
+
+<SUBSECTION>
+g_variant_get_boolean
+g_variant_get_byte
+g_variant_get_int16
+g_variant_get_uint16
+g_variant_get_int32
+g_variant_get_uint32
+g_variant_get_int64
+g_variant_get_uint64
+g_variant_get_double
+g_variant_get_string
+g_variant_dup_string
+g_variant_get_variant
+g_variant_get_strv
+g_variant_dup_strv
+
+<SUBSECTION>
+g_variant_n_children
+g_variant_get_child_value
+g_variant_get_child
+g_variant_lookup_value
+g_variant_lookup
+g_variant_get_fixed
+g_variant_get_fixed_array
+
+<SUBSECTION>
+g_variant_get
+g_variant_get_va
+g_variant_new
+g_variant_new_va
+g_variant_format_string_scan
+
+<SUBSECTION>
+g_variant_print
+g_variant_print_string
+g_variant_parse
+g_variant_parse_full
+g_variant_new_parsed
+g_variant_new_parsed_va
+
+<SUBSECTION>
+GVariantIter
+g_variant_iter_cancel
+g_variant_iter_init
+g_variant_iter_next_value
+g_variant_iter_was_cancelled
+g_variant_iter_next
+
+<SUBSECTION>
+GVariantBuilder
+g_variant_builder_add_value
+g_variant_builder_cancel
+g_variant_builder_close
+g_variant_builder_end
+g_variant_builder_new
+g_variant_builder_open
+g_variant_builder_add
+G_VARIANT_BUILDER_ERROR
+GVariantBuilderError
+g_variant_builder_check_add
+g_variant_builder_check_end
+
+<SUBSECTION>
+g_variant_markup_parse
+g_variant_markup_parse_context_end
+g_variant_markup_parse_context_new
+g_variant_markup_print
+g_variant_markup_print_string
+g_variant_markup_subparser_end
+g_variant_markup_subparser_start
+
+<SUBSECTION>
+GVariantFlags
+g_variant_load
+g_variant_store
+g_variant_flatten
+g_variant_from_data
+g_variant_from_file
+g_variant_from_slice
+g_variant_get_data
+g_variant_get_size
+</SECTION>
 
 <SECTION>
 <FILE>ghostutils</FILE>
diff --git a/glib/Makefile.am b/glib/Makefile.am
index e2080fb..36a9b5f 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -165,6 +165,18 @@ libglib_2_0_la_SOURCES = 	\
 	gunicodeprivate.h	\
 	gurifuncs.c 		\
 	gutils.c		\
+	gvariant-private.h	\
+	gvariant-core.c		\
+	gvariant-parser.c	\
+	gvariant-printer.c	\
+	gvariant-markup.c	\
+	gvariant-serialiser.h	\
+	gvariant-serialiser.c	\
+	gvarianttype.c		\
+	gvarianttypeinfo.h	\
+	gvarianttypeinfo.c	\
+	gvariant-util.c		\
+	gvariant-valist.c	\
 	gdebug.h		\
 	gprintf.c		\
 	gprintfint.h
@@ -242,6 +254,8 @@ glibsubinclude_HEADERS =   \
 	gunicode.h	\
 	gurifuncs.h 		\
 	gutils.h	\
+	gvarianttype.h	\
+	gvariant.h	\
 	gwin32.h	\
 	gprintf.h
 
diff --git a/glib/glib.h b/glib/glib.h
index f8acdd2..dd0784d 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -85,6 +85,8 @@
 #include <glib/gunicode.h>
 #include <glib/gurifuncs.h>
 #include <glib/gutils.h>
+#include <glib/gvarianttype.h>
+#include <glib/gvariant.h>
 #ifdef G_PLATFORM_WIN32
 #include <glib/gwin32.h>
 #endif
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 4929ad6..88e715e 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1646,6 +1646,191 @@ g_match_info_fetch_all
 #endif
 #endif
 
+#if IN_HEADER(__G_VARIANT_H__)
+#if IN_FILE(__G_VARIANT_CORE_C__)
+g_variant_load
+g_variant_from_slice
+g_variant_from_data
+g_variant_store
+g_variant_get_data
+g_variant_get_size
+g_variant_ref
+g_variant_ref_sink
+g_variant_unref
+g_variant_get_fixed
+g_variant_get_fixed_array
+g_variant_get_child_value
+g_variant_n_children
+g_variant_get_type
+#endif
+
+#if IN_FILE(__G_VARIANT_UTIL_C__)
+g_variant_from_file
+g_variant_flatten
+g_variant_get_type_class
+g_variant_get_type_string
+g_variant_is_basic
+g_variant_is_container
+g_variant_matches
+g_variant_new_boolean
+g_variant_new_byte
+g_variant_new_uint16
+g_variant_new_int16
+g_variant_new_uint32
+g_variant_new_int32
+g_variant_new_uint64
+g_variant_new_int64
+g_variant_new_double
+g_variant_new_string
+g_variant_new_strv
+g_variant_new_object_path
+g_variant_is_object_path
+g_variant_new_signature
+g_variant_is_signature
+g_variant_new_variant
+g_variant_get_boolean
+g_variant_get_byte
+g_variant_get_uint16
+g_variant_get_int16
+g_variant_get_uint32
+g_variant_get_int32
+g_variant_get_uint64
+g_variant_get_int64
+g_variant_get_double
+g_variant_get_string
+g_variant_dup_string
+g_variant_get_variant
+g_variant_get_strv
+g_variant_dup_strv
+g_variant_iter_init
+g_variant_iter_next_value
+g_variant_iter_cancel
+g_variant_iter_was_cancelled
+g_variant_builder_add_value
+g_variant_builder_open
+g_variant_builder_close
+g_variant_builder_check_add
+g_variant_builder_check_end
+g_variant_builder_new
+g_variant_builder_end
+g_variant_builder_cancel
+g_variant_lookup_value
+#endif
+
+#if IN_FILE(__G_VARIANT_PARSER_C__)
+g_variant_parse
+g_variant_parse_full
+g_variant_new_parsed
+g_variant_new_parsed_va
+#endif
+
+#if IN_FILE(__G_VARIANT_PRINTER_C__)
+g_variant_print
+g_variant_print_string
+#endif
+
+#if IN_FILE(__G_VARIANT_MARKUP_C__)
+g_variant_markup_print
+g_variant_markup_print_string
+g_variant_markup_subparser_start
+g_variant_markup_subparser_end
+g_variant_markup_parse_context_new
+g_variant_markup_parse_context_end
+g_variant_markup_parse
+#endif
+
+#if IN_FILE(__G_VARIANT_VALIST_C__)
+g_variant_iter_next
+g_variant_builder_add
+g_variant_new
+g_variant_get
+g_variant_format_string_scan_type
+g_variant_format_string_scan
+g_variant_new_va
+g_variant_get_va
+g_variant_get_child
+g_variant_lookup
+#endif
+#endif
+
+#if IN_HEADER(__G_VARIANT_TYPE_H__)
+#if IN_FILE(__G_VARIANT_TYPE_C__)
+g_variant_type_string_is_valid
+g_variant_type_string_scan
+g_variant_type_free
+g_variant_type_copy
+g_variant_type_new
+g_variant_type_get_string_length
+g_variant_type_peek_string
+g_variant_type_dup_string
+g_variant_type_is_concrete
+g_variant_type_is_container
+g_variant_type_is_basic
+g_variant_type_hash
+g_variant_type_equal
+g_variant_type_matches
+g_variant_type_is_in_class
+g_variant_type_get_class
+g_variant_type_class_is_container
+g_variant_type_class_is_basic
+g_variant_type_element
+g_variant_type_first
+g_variant_type_next
+g_variant_type_n_items
+g_variant_type_key
+g_variant_type_value
+g_variant_type_new_array
+g_variant_type_new_maybe
+g_variant_type_new_tuple_
+g_variant_type_new_dict_entry
+g_variant_type_check_string_
+#endif
+#endif
+
+#if IN_HEADER(__G_VARIANT_TYPE_INFO_H__)
+#if IN_FILE(__G_VARIANT_TYPE_INFO_C__)
+g_variant_type_info_get_type
+g_variant_type_info_get_type_class
+g_variant_type_info_get_string
+g_variant_type_info_query
+g_variant_type_info_element
+g_variant_type_info_query_element
+g_variant_type_info_n_members
+g_variant_type_info_member_info
+g_variant_type_info_get
+g_variant_type_info_ref
+g_variant_type_info_unref
+#endif
+#endif
+
+#if IN_HEADER(__G_VARIANT_SERIALISER_H__)
+#if IN_FILE(__G_VARIANT_SERIALISER_C__)
+g_variant_serialised_n_children
+g_variant_serialised_get_child
+g_variant_serialiser_needed_size
+g_variant_serialiser_serialise
+g_variant_serialised_assert_invariant
+g_variant_serialised_is_normal
+g_variant_serialised_byteswap
+#endif
+#endif
+
+#if IN_HEADER(__G_VARIANT_PRIVATE_H__)
+#if IN_FILE(__G_VARIANT_CORE_C__)
+g_variant_new_tree
+g_variant_assert_invariant
+g_variant_is_trusted
+g_variant_load_fixed
+g_variant_is_normal_
+#endif
+
+#if IN_FILE(__G_VARIANT_UTIL_C__)
+g_variant_dump_data
+g_variant_iter_should_free
+g_variant_deep_copy
+#endif
+#endif
+
 #if IN_HEADER(__G_WIN32_H__)
 #if IN_FILE(__G_WIN32_H__)
 #ifdef G_OS_WIN32
@@ -1692,4 +1877,5 @@ glib_micro_version
 glib_minor_version
 glib_on_error_halt
 g_mem_gc_friendly
+g_variant_markup_parser
 #endif
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
new file mode 100644
index 0000000..2e6c82e
--- /dev/null
+++ b/glib/gvariant-core.c
@@ -0,0 +1,1475 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION: gvariant
+ * @title: GVariant
+ * @short_description: a general purpose variant datatype
+ * @see_also: GVariantType
+ *
+ * #GVariant is a variant datatype; it stores a value along with
+ * information about the type of that value.  The range of possible
+ * values is determined by the type.  The range of possible types is
+ * exactly those types that may be sent over DBus.
+ *
+ * #GVariant instances always have a type and a value (which are given
+ * at construction time).  The type and value of a #GVariant instance
+ * can never change other than by the #GVariant itself being
+ * destroyed.  A #GVariant can not contain a pointer.
+ *
+ * Facilities exist for serialising the value of a #GVariant into a
+ * byte sequence.  A #GVariant can be sent over the bus or be saved to
+ * disk.  Additionally, #GVariant is used as the basis of the
+ * #GSettings persistent storage system.
+ **/
+
+/*
+ * This file is organised into 6 sections
+ *
+ * SECTION 1: structure declaration, condition constants
+ * SECTION 2: allocation/free functions
+ * SECTION 3: condition enabling functions
+ * SECTION 4: condition machinery
+ * SECTION 5: other internal functions
+ * SECTION 6: user-visible functions
+ */
+
+#include "gvariant-serialiser.h"
+#include "gvariant-private.h"
+
+#include <string.h>
+#include <glib.h>
+
+#include "galias.h"
+
+/* == SECTION 1: structure declaration, condition constants ============== */
+/**
+ * GVariant:
+ *
+ * #GVariant is an opaque data structure and can only be accessed
+ * using the following functions.
+ **/
+struct OPAQUE_TYPE__GVariant
+{
+  union
+  {
+    struct
+    {
+      GVariant *source;
+      guint8 *data;
+    } serialised;
+
+    struct
+    {
+      GVariant **children;
+      gsize n_children;
+    } tree;
+
+    struct
+    {
+      GDestroyNotify callback;
+      gpointer user_data;
+    } notify;
+  } contents;
+
+  gsize size;
+  GVariantTypeInfo *type;
+  gboolean floating;
+  gint state;
+  gint ref_count;
+};
+
+#define CONDITION_NONE                           0
+#define CONDITION_SOURCE_NATIVE         0x00000001
+#define CONDITION_BECAME_NATIVE         0x00000002
+#define CONDITION_NATIVE                0x00000004
+
+#define CONDITION_SOURCE_TRUSTED        0x00000010
+#define CONDITION_BECAME_TRUSTED        0x00000020
+#define CONDITION_TRUSTED               0x00000040
+
+#define CONDITION_FIXED_SIZE            0x00000100
+#define CONDITION_SIZE_KNOWN            0x00000200
+#define CONDITION_SIZE_VALID            0x00000400
+
+#define CONDITION_SERIALISED            0x00001000
+#define CONDITION_INDEPENDENT           0x00002000
+#define CONDITION_RECONSTRUCTED         0x00004000
+
+#define CONDITION_NOTIFY                0x00010000
+#define CONDITION_LOCKED                0x80000000
+
+static const char * /* debugging only */
+g_variant_state_to_string (guint state)
+{
+  GString *string;
+
+  string = g_string_new (NULL);
+
+#define add(cond,name) if (state & cond) g_string_append (string, name ", ");
+  add (CONDITION_SOURCE_NATIVE,  "source native");
+  add (CONDITION_BECAME_NATIVE,  "became native");
+  add (CONDITION_NATIVE,         "native");
+  add (CONDITION_SOURCE_TRUSTED, "source trusted");
+  add (CONDITION_BECAME_TRUSTED, "became trusted");
+  add (CONDITION_TRUSTED,        "trusted");
+  add (CONDITION_FIXED_SIZE,     "fixed-size");
+  add (CONDITION_SIZE_KNOWN,     "size known");
+  add (CONDITION_SIZE_VALID,     "size valid");
+  add (CONDITION_INDEPENDENT,    "independent");
+  add (CONDITION_SERIALISED,     "serialised");
+  add (CONDITION_RECONSTRUCTED,  "reconstructed");
+  add (CONDITION_NOTIFY,         "notify");
+#undef add
+
+  g_string_truncate (string, string->len - 2);
+  return g_string_free (string, FALSE);
+}
+
+/* == SECTION 2: allocation/free functions =============================== */
+static GVariant *
+g_variant_alloc (GVariantTypeInfo *type,
+                 guint             initial_state)
+{
+  GVariant *new;
+
+  new = g_slice_new (GVariant);
+  new->ref_count = 1;
+  new->type = type;
+  new->floating = TRUE;
+  new->state = initial_state & ~CONDITION_LOCKED;
+
+  return new;
+}
+
+static void
+g_variant_free (GVariant *value)
+{
+  /* free the type info */
+  if (value->type)
+    g_variant_type_info_unref (value->type);
+
+  /* free the data */
+  if (value->state & CONDITION_NOTIFY)
+    value->contents.notify.callback (value->contents.notify.user_data);
+  else if (value->state & CONDITION_SERIALISED)
+    {
+      if (value->state & CONDITION_INDEPENDENT)
+        g_slice_free1 (value->size, value->contents.serialised.data);
+      else
+        g_variant_unref (value->contents.serialised.source);
+
+      if (value->state & CONDITION_RECONSTRUCTED)
+        g_variant_unref (value->contents.serialised.source);
+    }
+  else
+    {
+      GVariant **children;
+      gsize n_children;
+      gsize i;
+
+      children = value->contents.tree.children;
+      n_children = value->contents.tree.n_children;
+
+      for (i = 0; i < n_children; i++)
+        g_variant_unref (children[i]);
+
+      g_slice_free1 (sizeof (GVariant *) * n_children, children);
+    }
+
+  /* free the structure itself */
+  g_slice_free (GVariant, value);
+}
+
+static void
+g_variant_lock (GVariant *value)
+{
+  g_bit_lock (&value->state, 31);
+}
+
+static void
+g_variant_unlock (GVariant *value)
+{
+  g_bit_unlock (&value->state, 31);
+}
+
+/* == SECTION 3: condition enabling functions ============================ */
+static void g_variant_fill_gvs (GVariantSerialised *, gpointer);
+
+static gboolean
+g_variant_enable_size_known (GVariant *value)
+{
+  GVariant **children;
+  gsize n_children;
+
+  children = value->contents.tree.children;
+  n_children = value->contents.tree.n_children;
+  value->size = g_variant_serialiser_needed_size (value->type,
+                                                  &g_variant_fill_gvs,
+                                                  (gpointer *) children,
+                                                  n_children);
+
+  return TRUE;
+}
+
+static gboolean
+g_variant_enable_serialised (GVariant *value)
+{
+  GVariantSerialised gvs;
+  GVariant **children;
+  gsize n_children;
+  gsize i;
+
+  children = value->contents.tree.children;
+  n_children = value->contents.tree.n_children;
+
+  gvs.type = value->type;
+  gvs.size = value->size;
+  gvs.data = g_slice_alloc (gvs.size);
+
+  g_variant_serialiser_serialise (gvs, &g_variant_fill_gvs,
+                                  (gpointer *) children, n_children);
+
+  value->contents.serialised.source = NULL;
+  value->contents.serialised.data = gvs.data;
+
+  for (i = 0; i < n_children; i++)
+    g_variant_unref (children[i]);
+  g_slice_free1 (sizeof (GVariant *) * n_children, children);
+
+  return TRUE;
+}
+
+static gboolean
+g_variant_enable_source_native (GVariant *value)
+{
+  return (value->contents.serialised.source->state & CONDITION_NATIVE) != 0;
+}
+
+static gboolean
+g_variant_enable_became_native (GVariant *value)
+{
+  GVariantSerialised gvs;
+
+  gvs.type = value->type;
+  gvs.size = value->size;
+  gvs.data = value->contents.serialised.data;
+
+  g_variant_serialised_byteswap (gvs);
+
+  return TRUE;
+}
+
+static gboolean
+g_variant_enable_source_trusted (GVariant *value)
+{
+  return (value->contents.serialised.source->state & CONDITION_TRUSTED) != 0;
+}
+
+static gboolean
+g_variant_enable_became_trusted (GVariant *value)
+{
+  GVariantSerialised gvs;
+
+  gvs.type = value->type;
+  gvs.data = value->contents.serialised.data;
+  gvs.size = value->size;
+
+  return g_variant_serialised_is_normal (gvs);
+}
+
+static gboolean
+g_variant_enable_reconstructed (GVariant *value)
+{
+  GVariant *old, *new;
+
+  old = g_variant_alloc (g_variant_type_info_ref (value->type),
+                         value->state & ~CONDITION_LOCKED);
+  value->contents.serialised.source = g_variant_ref_sink (old);
+  old->contents.serialised.source = NULL;
+  old->contents.serialised.data = value->contents.serialised.data;
+  old->size = value->size;
+
+  new = g_variant_deep_copy (old);
+  g_variant_flatten (new);
+
+  /* steal the data from new.  this is very evil. */
+  new->state &= ~CONDITION_INDEPENDENT;
+  new->contents.serialised.source = value;
+  value->contents.serialised.data = new->contents.serialised.data;
+  value->size = new->size;
+
+  g_variant_unref (new);
+
+  return TRUE;
+}
+
+static gboolean
+g_variant_enable_independent (GVariant *value)
+{
+  GVariant *source;
+  gpointer  new;
+
+  source = value->contents.serialised.source;
+  g_assert (source->state & CONDITION_INDEPENDENT);
+
+  new = g_slice_alloc (value->size);
+  memcpy (new, value->contents.serialised.data, value->size);
+
+  /* barrier to ensure byteswap is not in progress */
+  g_variant_lock (source);
+  g_variant_unlock (source);
+
+  /* rare: check if the source became native while we were copying */
+  if (source->state & CONDITION_NATIVE)
+    {
+      /* our data is probably half-and-half */
+      g_slice_free1 (value->size, new);
+
+      return FALSE;
+    }
+
+  value->contents.serialised.source = NULL;
+  value->contents.serialised.data = new;
+  g_variant_unref (source);
+
+  return TRUE;
+}
+
+static gboolean
+g_variant_enable_fixed_size (GVariant *value)
+{
+  gsize fixed_size;
+
+  g_variant_type_info_query (value->type, NULL, &fixed_size);
+
+  return fixed_size != 0;
+}
+
+/* == SECTION 4: condition machinery ===================================== */
+struct precondition_clause
+{
+  guint required;
+  guint forbidden;
+};
+
+struct condition
+{
+  guint    condition;
+  guint    implies;
+  guint    forbids;
+  guint    absence_implies;
+  void     (*assert_invariant) (GVariant *);
+  gboolean (*enable) (GVariant *);
+  struct precondition_clause precondition[4];
+};
+
+struct condition condition_table[] =
+{
+  { CONDITION_SOURCE_NATIVE,
+    CONDITION_NATIVE | CONDITION_SERIALISED,
+    CONDITION_INDEPENDENT | CONDITION_BECAME_NATIVE |
+    CONDITION_RECONSTRUCTED,
+    CONDITION_NONE,
+    NULL, g_variant_enable_source_native,
+    { { CONDITION_NONE, CONDITION_NATIVE | CONDITION_INDEPENDENT    } } },
+
+  { CONDITION_BECAME_NATIVE,
+    CONDITION_NATIVE | CONDITION_INDEPENDENT | CONDITION_SERIALISED,
+    CONDITION_SOURCE_NATIVE | CONDITION_RECONSTRUCTED,
+    CONDITION_NONE,
+    NULL, g_variant_enable_became_native,
+    { { CONDITION_INDEPENDENT | CONDITION_SIZE_VALID,
+        CONDITION_NATIVE                                            } } },
+
+  { CONDITION_NATIVE,
+    CONDITION_NONE,
+    CONDITION_NONE,
+    CONDITION_SERIALISED,
+    NULL, NULL,
+    { { CONDITION_SOURCE_NATIVE                                     },
+      { CONDITION_BECAME_NATIVE                                     },
+      { CONDITION_RECONSTRUCTED                                     } } },
+
+  { CONDITION_SOURCE_TRUSTED,
+    CONDITION_TRUSTED | CONDITION_SERIALISED,
+    CONDITION_RECONSTRUCTED,
+    CONDITION_NONE,
+    NULL, g_variant_enable_source_trusted,
+    { { CONDITION_NONE, CONDITION_TRUSTED | CONDITION_INDEPENDENT   } } },
+
+  { CONDITION_BECAME_TRUSTED,
+    CONDITION_TRUSTED | CONDITION_SERIALISED,
+    CONDITION_SOURCE_TRUSTED | CONDITION_RECONSTRUCTED,
+    CONDITION_NONE,
+    NULL, g_variant_enable_became_trusted,
+    { { CONDITION_SERIALISED, CONDITION_TRUSTED                     } } },
+
+  { CONDITION_TRUSTED,
+    CONDITION_NONE,
+    CONDITION_NONE,
+    CONDITION_NONE,
+    NULL, NULL,
+    { { CONDITION_SOURCE_TRUSTED                                    },
+      { CONDITION_BECAME_TRUSTED                                    },
+      { CONDITION_RECONSTRUCTED                                     } } },
+
+  { CONDITION_SERIALISED,
+    CONDITION_SIZE_KNOWN,
+    CONDITION_NONE,
+    CONDITION_NATIVE | CONDITION_INDEPENDENT,
+    NULL, g_variant_enable_serialised,
+    { { CONDITION_SIZE_KNOWN                                        } } },
+
+  { CONDITION_SIZE_KNOWN,
+    CONDITION_NONE,
+    CONDITION_NOTIFY,
+    CONDITION_NONE,
+    NULL, g_variant_enable_size_known,
+    { { CONDITION_NONE, CONDITION_SIZE_KNOWN                        } } },
+
+  { CONDITION_SIZE_VALID,
+    CONDITION_SIZE_KNOWN,
+    CONDITION_NONE,
+    CONDITION_NONE,
+    NULL, NULL,
+    { { CONDITION_SIZE_KNOWN | CONDITION_FIXED_SIZE                 },
+      { CONDITION_SIZE_KNOWN | CONDITION_TRUSTED                    },
+      { CONDITION_SIZE_KNOWN | CONDITION_NATIVE                     } } },
+
+  { CONDITION_INDEPENDENT,
+    CONDITION_NONE,
+    CONDITION_NONE,
+    CONDITION_SERIALISED,
+    NULL, g_variant_enable_independent,
+    { { CONDITION_SERIALISED, CONDITION_NATIVE                      } } },
+
+  { CONDITION_RECONSTRUCTED,
+    CONDITION_TRUSTED | CONDITION_NATIVE | CONDITION_INDEPENDENT,
+    CONDITION_BECAME_NATIVE | CONDITION_BECAME_TRUSTED,
+    CONDITION_NONE,
+    NULL, g_variant_enable_reconstructed,
+    { { CONDITION_SERIALISED,
+        CONDITION_NATIVE | CONDITION_TRUSTED | CONDITION_FIXED_SIZE } } },
+
+  { CONDITION_FIXED_SIZE,
+    CONDITION_NONE,
+    CONDITION_NONE,
+    CONDITION_NONE,
+    NULL, g_variant_enable_fixed_size,
+    { { CONDITION_NONE, CONDITION_FIXED_SIZE                        } } },
+
+  { CONDITION_NOTIFY,
+    CONDITION_NONE,
+    CONDITION_NOTIFY - 1,
+    CONDITION_NONE,
+    NULL, NULL,
+    { } },
+
+  { }
+};
+
+static gboolean
+g_variant_state_is_valid (guint state)
+{
+  struct condition *c;
+
+  for (c = condition_table; c->condition; c++)
+    {
+      if (state & c->condition)
+        {
+          if (~state & c->implies)
+            return FALSE;
+
+          if (state & c->forbids)
+            return FALSE;
+        }
+      else
+        {
+          if (~state & c->absence_implies)
+            return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+/*
+ * g_variant_assert_invariant:
+ * @value: a #GVariant instance to check
+ *
+ * This function asserts the class invariant on a #GVariant instance.
+ * Any detected problems will result in an assertion failure.
+ *
+ * This function is potentially very slow.
+ *
+ * This function never fails.
+ */
+void
+g_variant_assert_invariant (GVariant *value)
+{
+  if (value->state & CONDITION_NOTIFY)
+    return;
+
+  g_variant_lock (value);
+
+  g_assert_cmpint (value->ref_count, >, 0);
+
+  if G_UNLIKELY (!g_variant_state_is_valid (value->state & ~CONDITION_LOCKED))
+    g_critical ("instance %p in invalid state: %s",
+                value, g_variant_state_to_string (value->state));
+
+  if (value->state & CONDITION_SERIALISED)
+    {
+      if (value->state & CONDITION_RECONSTRUCTED)
+        g_variant_assert_invariant (value->contents.serialised.source);
+
+      if (!(value->state & CONDITION_INDEPENDENT))
+        g_variant_assert_invariant (value->contents.serialised.source);
+    }
+  else
+    {
+      gsize i;
+
+      for (i = 0; i < value->contents.tree.n_children; i++)
+        {
+          g_assert_cmpint (value->state & CONDITION_TRUSTED, <=,
+                           value->contents.tree.children[i]->state &
+                           CONDITION_TRUSTED);
+
+          g_assert (!value->contents.tree.children[i]->floating);
+          g_variant_assert_invariant (value->contents.tree.children[i]);
+        }
+    }
+
+  g_variant_unlock (value);
+}
+
+/* how many bits are in 'reqd' but not 'have'?
+ */
+static int
+bits_missing (guint have,
+              guint reqd)
+{
+  guint count = 0;
+
+  reqd &= ~have;
+  while (reqd && ++count)
+    reqd &= reqd - 1;
+
+  return count;
+}
+
+static gboolean
+g_variant_try_unlocked (GVariant *value,
+                        guint     conditions)
+{
+  struct precondition_clause *p;
+  struct condition *c;
+  int max_missing;
+
+  /* attempt to enable each missing condition */
+  for (c = condition_table; c->condition; c++)
+    if ((value->state & c->condition) < (conditions & c->condition))
+      {
+        /* prefer preconditon clauses with the fewest false terms */
+        for (max_missing = 0; max_missing < 10; max_missing++)
+          for (p = c->precondition; p->required || p->forbidden; p++)
+            if (!(value->state & p->forbidden) &&
+                bits_missing (value->state, p->required) < max_missing &&
+                max_missing && g_variant_try_unlocked (value, p->required))
+              goto attempt_enable;
+
+        return FALSE;
+
+       attempt_enable:
+        if (c->enable && !c->enable (value))
+          return FALSE;
+
+        value->state |= c->condition;
+      }
+
+  if (~value->state & conditions)
+    g_error ("was %x %x", value->state, conditions);
+
+  g_assert (!(~value->state & conditions));
+
+  return TRUE;
+}
+
+static gboolean
+g_variant_try_enabling_conditions (GVariant *value,
+                                   guint     conditions)
+{
+  gboolean success;
+
+  g_variant_assert_invariant (value);
+
+  if ((value->state & conditions) == conditions)
+    return TRUE;
+
+  g_variant_lock (value);
+  success = g_variant_try_unlocked (value, conditions);
+  g_variant_unlock (value);
+  g_variant_assert_invariant (value);
+
+  return success;
+}
+
+static void
+g_variant_require_conditions (GVariant *value,
+                              guint     condition_set)
+{
+  if G_UNLIKELY (!g_variant_try_enabling_conditions (value, condition_set))
+    g_error ("instance %p unable to enable '%s' from '%s'\n",
+             value, g_variant_state_to_string (condition_set),
+             g_variant_state_to_string (value->state));
+}
+
+static gboolean
+g_variant_forbid_conditions (GVariant *value,
+                             guint     condition_set)
+{
+  g_variant_assert_invariant (value);
+
+  if (value->state & condition_set)
+    return FALSE;
+
+  g_variant_lock (value);
+
+  if (value->state & condition_set)
+    {
+      g_variant_unlock (value);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+GVariant *
+g_variant_load_fixed (const GVariantType *type,
+                      gconstpointer       data,
+                      gsize               n_items)
+{
+  GVariantTypeInfo *info;
+  gsize fixed_size;
+  GVariant *new;
+
+  info = g_variant_type_info_get (type);
+  if (g_variant_type_is_in_class (type, G_VARIANT_TYPE_CLASS_ARRAY))
+    {
+      g_variant_type_info_query_element (info, NULL, &fixed_size);
+      fixed_size *= n_items;
+    }
+  else
+    g_variant_type_info_query (info, NULL, &fixed_size);
+  g_assert (fixed_size);
+
+  /* TODO: can't be trusted yet since there may be non-zero
+   *       padding between the elements.  can fix this with
+   *       some sort of intelligent zero-inserting memdup.
+   */
+  new = g_variant_alloc (info,
+                         CONDITION_INDEPENDENT | CONDITION_NATIVE |
+                         CONDITION_SERIALISED | CONDITION_SIZE_KNOWN);
+  new->contents.serialised.source = NULL;
+  new->contents.serialised.data = g_slice_alloc (fixed_size);
+  memcpy (new->contents.serialised.data, data, fixed_size);
+  new->size = fixed_size;
+
+  g_variant_assert_invariant (new);
+
+  return new;
+}
+
+/* == SECTION 5: other internal functions ================================ */
+static GVariantSerialised
+g_variant_get_gvs (GVariant  *value,
+                   GVariant **source)
+{
+  GVariantSerialised gvs = { value->type };
+
+  g_assert (value->state & CONDITION_SERIALISED);
+  g_variant_require_conditions (value, CONDITION_SIZE_VALID);
+
+  if (g_variant_forbid_conditions (value, CONDITION_INDEPENDENT))
+    {
+      /* dependent */
+      gvs.data = value->contents.serialised.data;
+
+      if (source)
+        *source = g_variant_ref (value->contents.serialised.source);
+
+      g_variant_unlock (value);
+    }
+  else
+    {
+      /* independent */
+      gvs.data = value->contents.serialised.data;
+
+      if (source)
+        *source = g_variant_ref (value);
+
+      g_variant_unlock (value);
+    }
+
+  gvs.size = value->size;
+
+  return gvs;
+}
+
+/*
+ * g_variant_fill_gvs:
+ * @serialised: the #GVariantSerialised to fill
+ * @data: our #GVariant instance
+ *
+ * Utility function used as a callback from the serialiser to get
+ * information about a given #GVariant instance (in @data).
+ */
+static void
+g_variant_fill_gvs (GVariantSerialised *serialised,
+                    gpointer            data)
+{
+  GVariant *value = data;
+
+  g_variant_assert_invariant (value);
+
+  g_variant_require_conditions (value, CONDITION_SIZE_VALID);
+
+  if (serialised->type == NULL)
+    serialised->type = value->type;
+
+  if (serialised->size == 0)
+    serialised->size = value->size;
+
+  g_assert (serialised->type == value->type);
+  g_assert (serialised->size == value->size);
+
+  if (serialised->data && serialised->size)
+    g_variant_store (value, serialised->data);
+}
+
+/*
+ * g_variant_get_zeros:
+ * @size: a size, in bytes
+ * @returns: a new reference to a #GVariant
+ *
+ * Creates a #GVariant with no type that contains at least @size bytes
+ * worth of zeros.  The instance will live forever.
+ *
+ * This is required for the case where deserialisation of a fixed-size
+ * value from a non-fixed-sized container fails.  The fixed-sized
+ * value needs to have zero-filled data (in case this data is
+ * requested).  This data must also outlive the child since the parent
+ * from which that child was taken may have been flattened (in which
+ * case the user expects the child's data to live as long as the
+ * parent).
+ *
+ * There are less-permanent ways of doing this (ie: somehow
+ * associating the zeros with the parent) but this way is easy and it
+ * works fine for now.
+ */
+static GVariant *
+g_variant_get_zeros (gsize size)
+{
+  static GStaticMutex lock = G_STATIC_MUTEX_INIT;
+  static GSList *zeros;
+  GVariant *value;
+
+  /* there's actually no sense in storing the entire linked list since
+   * we will always use the first item in the list, but it prevents
+   * valgrind from complaining.
+   */
+
+  if (size < 4096)
+    size = 4096;
+
+  g_static_mutex_lock (&lock);
+  if (zeros)
+    {
+      value = zeros->data;
+
+      if (value->size < size)
+        value = NULL;
+    }
+  else
+    value = NULL;
+
+  if (value == NULL)
+    {
+      size--;
+      size |= size >> 1; size |= size >> 2; size |= size >> 4;
+      size |= size >> 8; size |= size >> 16;
+      size |= size >> 16; size |= size >> 16;
+      size++;
+
+      value = g_variant_alloc (NULL,
+                               CONDITION_SERIALISED | CONDITION_SIZE_KNOWN |
+                               CONDITION_NATIVE | CONDITION_TRUSTED |
+                               CONDITION_INDEPENDENT | CONDITION_SIZE_VALID);
+      value->contents.serialised.source = NULL;
+      value->contents.serialised.data = g_malloc0 (size);
+      value->size = size;
+
+      zeros = g_slist_prepend (zeros, g_variant_ref_sink (value));
+    }
+  g_static_mutex_unlock (&lock);
+
+  return g_variant_ref (value);
+}
+
+/*
+ * g_variant_apply_flags:
+ * @value: a fresh #GVariant instance
+ * @flags: various load flags
+ *
+ * This function is the common code used to apply flags (normalise,
+ * byteswap, etc) to fresh #GVariant instances created using one of
+ * the load functions.
+ */
+static GVariant *
+g_variant_apply_flags (GVariant      *value,
+                       GVariantFlags  flags)
+{
+  guint16 byte_order = flags;
+
+  if (byte_order == 0)
+    byte_order = G_BYTE_ORDER;
+
+  g_assert (byte_order == G_LITTLE_ENDIAN ||
+            byte_order == G_BIG_ENDIAN);
+
+  if (byte_order == G_BYTE_ORDER)
+    value->state |= CONDITION_NATIVE;
+
+  else if (!flags & G_VARIANT_LAZY_BYTESWAP)
+    g_variant_require_conditions (value, CONDITION_NATIVE);
+
+  g_variant_assert_invariant (value);
+
+  return value;
+}
+
+gboolean
+g_variant_is_trusted (GVariant *value)
+{
+  return !!(value->state & CONDITION_TRUSTED);
+}
+
+gboolean
+g_variant_is_normal_ (GVariant *value)
+{
+  return g_variant_serialised_is_normal (g_variant_get_gvs (value, NULL));
+}
+
+/* == SECTION 6: user-visibile functions ================================= */
+/**
+ * g_variant_get_type:
+ * @value: a #GVariant
+ * @returns: a #GVariantType
+ *
+ * Determines the type of @value.
+ *
+ * The return value is valid for the lifetime of @value and must not
+ * be freed.
+ */
+const GVariantType *
+g_variant_get_type (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, NULL);
+  g_variant_assert_invariant (value);
+
+  return g_variant_type_info_get_type (value->type);
+}
+
+/**
+ * g_variant_n_children:
+ * @value: a container #GVariant
+ * @returns: the number of children in the container
+ *
+ * Determines the number of children in a container #GVariant
+ * instance.  This includes variants, maybes, arrays, tuples and
+ * dictionary entries.  It is an error to call this function on any
+ * other type of #GVariant.
+ *
+ * For variants, the return value is always 1.  For maybes, it is
+ * always zero or one.  For arrays, it is the length of the array.
+ * For tuples it is the number of tuple items (which depends
+ * only on the type).  For dictionary entries, it is always 2.
+ *
+ * This function never fails.
+ * TS
+ **/
+gsize
+g_variant_n_children (GVariant *value)
+{
+  gsize n_children;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_variant_assert_invariant (value);
+
+  if (g_variant_forbid_conditions (value, CONDITION_SERIALISED))
+    {
+      n_children = value->contents.tree.n_children;
+      g_variant_unlock (value);
+    }
+  else
+    {
+      GVariantSerialised gvs;
+      GVariant *source;
+
+      gvs = g_variant_get_gvs (value, &source);
+      n_children = g_variant_serialised_n_children (gvs);
+      g_variant_unref (source);
+    }
+
+  return n_children;
+}
+
+/**
+ * g_variant_get_child_value:
+ * @value: a container #GVariant
+ * @index: the index of the child to fetch
+ * @returns: the child at the specified index
+ *
+ * Reads a child item out of a container #GVariant instance.  This
+ * includes variants, maybes, arrays, tuple and dictionary
+ * entries.  It is an error to call this function on any other type of
+ * #GVariant.
+ *
+ * It is an error if @index is greater than the number of child items
+ * in the container.  See g_variant_n_children().
+ *
+ * This function never fails.
+ **/
+GVariant *
+g_variant_get_child_value (GVariant *value,
+                           gsize     index)
+{
+  GVariant *child;
+
+  g_return_val_if_fail (value != NULL, NULL);
+  g_variant_assert_invariant (value);
+
+  if (g_variant_forbid_conditions (value, CONDITION_SERIALISED))
+    {
+      if G_UNLIKELY (index >= value->contents.tree.n_children)
+        g_error ("Attempt to access item %" G_GSIZE_FORMAT
+                 " in a container with only %" G_GSIZE_FORMAT
+                 " items", index, value->contents.tree.n_children);
+
+      child = g_variant_ref (value->contents.tree.children[index]);
+      g_variant_unlock (value);
+    }
+  else
+    {
+      GVariantSerialised gvs;
+      GVariant *source;
+
+      gvs = g_variant_get_gvs (value, &source);
+      gvs = g_variant_serialised_get_child (gvs, index);
+
+      child = g_variant_alloc (gvs.type, CONDITION_SERIALISED |
+                                         CONDITION_SIZE_KNOWN);
+      child->type = gvs.type;
+      child->contents.serialised.source = source;
+      child->contents.serialised.data = gvs.data;
+      child->size = gvs.size;
+      child->floating = FALSE;
+
+      if (gvs.data == NULL)
+        {
+          /* not actually using source data -- release it */
+          g_variant_unref (child->contents.serialised.source);
+          child->contents.serialised.source = NULL;
+
+          if (gvs.size)
+            {
+              GVariant *zeros;
+              gpointer data;
+
+              g_assert (!((source->state | value->state)
+                          & CONDITION_TRUSTED));
+
+              zeros = g_variant_get_zeros (gvs.size);
+              data = zeros->contents.serialised.data;
+
+              child->contents.serialised.source = zeros;
+              child->contents.serialised.data = data;
+
+              child->state |= CONDITION_FIXED_SIZE | CONDITION_TRUSTED;
+            }
+          else
+            child->state |= CONDITION_INDEPENDENT;
+
+          child->state |= CONDITION_NATIVE | CONDITION_SIZE_VALID;
+        }
+
+      /* inherit 'native' and 'trusted' attributes */
+      child->state |= (source->state | value->state) &
+                      (CONDITION_NATIVE | CONDITION_TRUSTED);
+    }
+
+  g_variant_assert_invariant (child);
+
+  return child;
+}
+
+/**
+ * g_variant_get_size:
+ * @value: a #GVariant instance
+ * @returns: the serialised size of @value
+ *
+ * Determines the number of bytes that would be required to store
+ * @value with g_variant_store().
+ *
+ * In the case that @value is already in serialised form or the size
+ * has already been calculated (ie: this function has been called
+ * before) then this function is O(1).  Otherwise, the size is
+ * calculated, an operation which is approximately O(n) in the number
+ * of values involved.
+ *
+ * This function never fails.
+ **/
+gsize
+g_variant_get_size (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, 0);
+
+  g_variant_require_conditions (value, CONDITION_SIZE_VALID);
+
+  return value->size;
+}
+
+/**
+ * g_variant_get_data:
+ * @value: a #GVariant instance
+ * @returns: the serialised form of @value
+ *
+ * Returns a pointer to the serialised form of a #GVariant instance.
+ * The returned data is in machine native byte order but may not be in
+ * fully-normalised form if read from an untrusted source.  The
+ * returned data must not be freed; it remains valid for as long as
+ * @value exists.
+ *
+ * In the case that @value is already in serialised form, this
+ * function is O(1).  If the value is not already in serialised form,
+ * serialisation occurs implicitly and is approximately O(n) in the
+ * size of the result.
+ *
+ * This function never fails.
+ **/
+gconstpointer
+g_variant_get_data (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, NULL);
+
+  g_variant_require_conditions (value, CONDITION_NATIVE | CONDITION_SERIALISED);
+
+  return value->contents.serialised.data;
+}
+
+/**
+ * g_variant_store:
+ * @value: the #GVariant to store
+ * @data: the location to store the serialised data at
+ *
+ * Stores the serialised form of @variant at @data.  @data should be
+ * serialised enough.  See g_variant_get_size().
+ *
+ * The stored data is in machine native byte order but may not be in
+ * fully-normalised form if read from an untrusted source.  See
+ * g_variant_normalise() for a solution.
+ *
+ * This function is approximately O(n) in the size of @data.
+ *
+ * This function never fails.
+ **/
+void
+g_variant_store (GVariant *value,
+                 gpointer  data)
+{
+  g_return_if_fail (value != NULL);
+
+  g_variant_assert_invariant (value);
+
+  g_variant_require_conditions (value, CONDITION_SIZE_VALID | CONDITION_NATIVE);
+
+  if (g_variant_forbid_conditions (value, CONDITION_SERIALISED))
+    {
+      GVariantSerialised gvs;
+      GVariant **children;
+      gsize n_children;
+
+      gvs.type = value->type;
+      gvs.data = data;
+      gvs.size = value->size;
+
+      children = value->contents.tree.children;
+      n_children = value->contents.tree.n_children;
+
+      /* XXX we hold the lock for an awful long time here... */
+      g_variant_serialiser_serialise (gvs,
+                                      &g_variant_fill_gvs,
+                                      (gpointer *) children,
+                                      n_children);
+      g_variant_unlock (value);
+    }
+  else
+    {
+      GVariantSerialised gvs;
+      GVariant *source;
+
+      gvs = g_variant_get_gvs (value, &source);
+      memcpy (data, gvs.data, gvs.size);
+      g_variant_unref (source);
+  }
+}
+
+/**
+ * g_variant_get_fixed:
+ * @value: a #GVariant
+ * @size: the size of @value
+ * @returns: a pointer to the fixed-sized data
+ *
+ * Gets a pointer to the data of a fixed sized #GVariant instance.
+ * This pointer can be treated as a pointer to the equivalent C
+ * stucture type and accessed directly.  The data is in machine byte
+ * order.
+ *
+ * @size must be equal to the fixed size of the type of @value.  It
+ * isn't used for anything, but serves as a sanity check to ensure the
+ * user of this function will be able to make sense of the data they
+ * receive a pointer to.
+ *
+ * This function may return %NULL if @size is zero.
+ **/
+gconstpointer
+g_variant_get_fixed (GVariant *value,
+                     gsize     size)
+{
+  gsize fixed_size;
+
+  g_return_val_if_fail (value != NULL, NULL);
+
+  g_variant_assert_invariant (value);
+
+  g_variant_type_info_query (value->type, NULL, &fixed_size);
+  g_assert (fixed_size);
+
+  g_assert_cmpint (size, ==, fixed_size);
+
+  return g_variant_get_data (value);
+}
+
+/**
+ * g_variant_get_fixed_array:
+ * @value: an array #GVariant
+ * @elem_size: the size of one array element
+ * @length: a pointer to the length of the array, or %NULL
+ * @returns: a pointer to the array data
+ *
+ * Gets a pointer to the data of an array of fixed sized #GVariant
+ * instances.  This pointer can be treated as a pointer to an array of
+ * the equivalent C type and accessed directly.  The data is
+ * in machine byte order.
+ *
+ * @elem_size must be equal to the fixed size of the element type of
+ * @value.  It isn't used for anything, but serves as a sanity check
+ * to ensure the user of this function will be able to make sense of
+ * the data they receive a pointer to.
+ *
+ * @length is set equal to the number of items in the array, so that
+ * the size of the memory region returned is @elem_size times @length.
+ *
+ * This function may return %NULL if either @elem_size or @length is
+ * zero.
+ */
+gconstpointer
+g_variant_get_fixed_array (GVariant *value,
+                           gsize     elem_size,
+                           gsize    *length)
+{
+  gsize fixed_elem_size;
+
+  g_return_val_if_fail (value != NULL, NULL);
+
+  g_variant_assert_invariant (value);
+
+  /* unsupported: maybes are treated as arrays of size zero or one */
+  g_variant_type_info_query_element (value->type, NULL, &fixed_elem_size);
+  g_assert (fixed_elem_size);
+
+  g_assert_cmpint (elem_size, ==, fixed_elem_size);
+
+  if (length != NULL)
+    *length = g_variant_n_children (value);
+
+  return g_variant_get_data (value);
+}
+
+/**
+ * g_variant_unref:
+ * @value: a #GVariant
+ *
+ * Decreases the reference count of @variant.  When its reference
+ * count drops to 0, the memory used by the variant is freed.
+ **/
+void
+g_variant_unref (GVariant *value)
+{
+  g_return_if_fail (value != NULL);
+
+  g_variant_assert_invariant (value);
+
+  if (g_atomic_int_dec_and_test (&value->ref_count))
+    g_variant_free (value);
+}
+
+/**
+ * g_variant_ref:
+ * @value: a #GVariant
+ * @returns: the same @variant
+ *
+ * Increases the reference count of @variant.
+ **/
+GVariant *
+g_variant_ref (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, NULL);
+
+  g_variant_assert_invariant (value);
+
+  g_atomic_int_inc (&value->ref_count);
+
+  return value;
+}
+
+/**
+ * g_variant_ref_sink:
+ * @value: a #GVariant
+ * @returns: the same @variant
+ *
+ * If @value is floating, mark it as no longer floating.  If it is not
+ * floating, increase its reference count.
+ **/
+GVariant *
+g_variant_ref_sink (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, NULL);
+
+  g_variant_assert_invariant (value);
+
+  g_variant_ref (value);
+  if (g_atomic_int_compare_and_exchange (&value->floating, 1, 0))
+    g_variant_unref (value);
+
+  return value;
+}
+
+/* private */
+GVariant *
+g_variant_new_tree (const GVariantType  *type,
+                    GVariant           **children,
+                    gsize                n_children,
+                    gboolean             trusted)
+{
+  GVariant *new;
+
+  new = g_variant_alloc (g_variant_type_info_get (type),
+                         CONDITION_INDEPENDENT | CONDITION_NATIVE);
+  new->contents.tree.children = children;
+  new->contents.tree.n_children = n_children;
+  new->size = 0;
+
+  if (trusted)
+    new->state |= CONDITION_TRUSTED;
+
+  g_variant_assert_invariant (new);
+
+  return new;
+}
+
+/**
+ * g_variant_from_slice:
+ * @type: the #GVariantType of the new variant
+ * @slice: a pointer to a GSlice-allocated region
+ * @size: the size of @slice
+ * @flags: zero or more #GVariantFlags
+ * @returns: a new #GVariant instance
+ *
+ * Creates a #GVariant instance from a memory slice.  Ownership of the
+ * memory slice is assumed.  This function allows efficiently creating
+ * #GVariant instances with data that is, for example, read over a
+ * socket.
+ *
+ * If @type is %NULL then @data is assumed to have the type
+ * %G_VARIANT_TYPE_VARIANT and the return value is the value extracted
+ * from that variant.
+ *
+ * This function never fails.
+ **/
+GVariant *
+g_variant_from_slice (const GVariantType *type,
+                      gpointer            slice,
+                      gsize               size,
+                      GVariantFlags       flags)
+{
+  GVariant *new;
+
+  g_return_val_if_fail (slice != NULL || size == 0, NULL);
+
+  if (type == NULL)
+    {
+      GVariant *variant;
+
+      variant = g_variant_from_slice (G_VARIANT_TYPE_VARIANT,
+                                      slice, size, flags);
+      new = g_variant_get_variant (variant);
+      g_variant_unref (variant);
+
+      return new;
+    }
+  else
+    {
+      new = g_variant_alloc (g_variant_type_info_get (type),
+                             CONDITION_SERIALISED | CONDITION_INDEPENDENT |
+                             CONDITION_SIZE_KNOWN);
+
+      new->contents.serialised.source = NULL;
+      new->contents.serialised.data = slice;
+      new->floating = FALSE;
+      new->size = size;
+
+      return g_variant_apply_flags (new, flags);
+    }
+}
+
+/**
+ * g_variant_from_data:
+ * @type: the #GVariantType of the new variant
+ * @data: a pointer to the serialised data
+ * @size: the size of @data
+ * @flags: zero or more #GVariantFlags
+ * @notify: a function to call when @data is no longer needed
+ * @user_data: a #gpointer to pass to @notify
+ * @returns: a new #GVariant instance
+ *
+ * Creates a #GVariant instance from serialised data.  The data is not
+ * copied.  When the data is no longer required (which may be before
+ * or after the return value is freed) @notify is called.  @notify may
+ * even be called before this function returns.
+ *
+ * If @type is %NULL then @data is assumed to have the type
+ * %G_VARIANT_TYPE_VARIANT and the return value is the value extracted
+ * from that variant.
+ *
+ * This function never fails.
+ **/
+GVariant *
+g_variant_from_data (const GVariantType *type,
+                          gconstpointer       data,
+                          gsize               size,
+                          GVariantFlags       flags,
+                          GDestroyNotify      notify,
+                          gpointer            user_data)
+{
+  GVariant *new;
+
+  g_return_val_if_fail (data != NULL || size == 0, NULL);
+
+  if (type == NULL)
+    {
+      GVariant *variant;
+
+      variant = g_variant_from_data (G_VARIANT_TYPE_VARIANT,
+                                     data, size, flags, notify, user_data);
+      new = g_variant_get_variant (variant);
+      g_variant_unref (variant);
+
+      return new;
+    }
+  else
+    {
+      GVariant *marker;
+
+      marker = g_variant_alloc (NULL, CONDITION_NOTIFY);
+      marker->contents.notify.callback = notify;
+      marker->contents.notify.user_data = user_data;
+
+      new = g_variant_alloc (g_variant_type_info_get (type),
+                             CONDITION_SERIALISED | CONDITION_SIZE_KNOWN);
+      new->contents.serialised.source = marker;
+      new->contents.serialised.data = (gpointer) data;
+      new->floating = FALSE;
+      new->size = size;
+
+      return g_variant_apply_flags (new, flags);
+    }
+}
+
+/**
+ * g_variant_load:
+ * @type: the #GVariantType of the new variant
+ * @data: the serialised #GVariant data to load
+ * @size: the size of @data
+ * @flags: zero or more #GVariantFlags
+ * @returns: a new #GVariant instance
+ *
+ * Creates a new #GVariant instance.  @data is copied.  For a more
+ * efficient way to create #GVariant instances, see
+ * g_variant_from_slice() or g_variant_from_data().
+ *
+ * This function is O(n) in the size of @data.
+ *
+ * This function never fails.
+ **/
+GVariant *
+g_variant_load (const GVariantType *type,
+                gconstpointer       data,
+                gsize               size,
+                GVariantFlags       flags)
+{
+  GVariant *new;
+
+  g_return_val_if_fail (data != NULL || size == 0, NULL);
+
+  if (type == NULL)
+    {
+      GVariant *variant;
+
+      variant = g_variant_load (G_VARIANT_TYPE_VARIANT, data, size, flags);
+      new = g_variant_get_variant (variant);
+      g_variant_unref (variant);
+
+      return new;
+    }
+  else
+    {
+      gpointer slice;
+
+      slice = g_slice_alloc (size);
+      memcpy (slice, data, size);
+
+      return g_variant_from_slice (type, slice, size, flags);
+    }
+}
+
+#define __G_VARIANT_CORE_C__
+#include "galiasdef.c"
diff --git a/glib/gvariant-markup.c b/glib/gvariant-markup.c
new file mode 100644
index 0000000..a1f7aa0
--- /dev/null
+++ b/glib/gvariant-markup.c
@@ -0,0 +1,1017 @@
+/*
+ * Copyright © 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gtestutils.h>
+#include <glib/gmessages.h>
+#include <glib/gvariant.h>
+
+#include <string.h>
+
+#include "galias.h"
+
+static void
+g_variant_markup_indent (GString *string,
+                         gint     indentation)
+{
+  gint i;
+
+  for (i = 0; i < indentation; i++)
+    g_string_append_c (string, ' ');
+}
+
+static void
+g_variant_markup_newline (GString  *string,
+                          gboolean  newlines)
+{
+  if (newlines)
+    g_string_append_c (string, '\n');
+}
+
+static gboolean
+has_no_evil_characters (const gchar *str)
+{
+  int i;
+
+  for (i = 0; str[i]; i++)
+    if ((0x01 <= str[i] && str[i] <= 0x08) ||
+        (0x0b <= str[i] && str[i] <= 0x0c) ||
+        (0x0e <= str[i] && str[i] <= 0x1f))
+      return FALSE;
+
+  return TRUE;
+}
+
+
+/**
+ * g_variant_markup_print:
+ * @value: a #GVariant
+ * @newlines: %TRUE if newlines should be printed
+ * @indentation: the current indentation level
+ * @tabstop: the number of spaces per indentation level
+ * @returns: a newly-allocated string containing the XML fragment
+ *
+ * Pretty-prints @value as an XML document fragment.
+ *
+ * The @newlines, @indentation and @tabstop parameters control the
+ * whitespace that is emitted as part of the document.
+ *
+ * If @newlines is %TRUE, then newline characters will be printed
+ * where appropriate.
+ *
+ * If @indentation is non-zero then this is the number of spaces that
+ * are printed before the first and last tag.  If @tabstop is non-zero
+ * then this is the number of additional spaces that are added for
+ * each level of nesting.
+ **/
+gchar *
+g_variant_markup_print (GVariant *value,
+                        gboolean  newlines,
+                        gint      indentation,
+                        gint      tabstop)
+{
+  GString *string = g_variant_markup_print_string (value, NULL, newlines,
+                                                   indentation, tabstop);
+  return g_string_free (string, FALSE);
+};
+
+/**
+ * g_variant_markup_print_string:
+ * @value: a #GVariant
+ * @string: a #GString, or %NULL
+ * @newlines: %TRUE if newlines should be printed
+ * @indentation: the current indentation level
+ * @tabstop: the number of spaces per indentation level
+ * @returns: a #GString containing the XML fragment
+ *
+ * Pretty-prints @value as an XML document fragment, behaving in the
+ * same way as g_variant_markup_print().
+ *
+ * If @string is non-%NULL then it is appended to and returned.  Else,
+ * a new empty #GString is allocated and it is returned.
+ **/
+GString *
+g_variant_markup_print_string (GVariant *value,
+                               GString  *string,
+                               gboolean  newlines,
+                               gint      indentation,
+                               gint      tabstop)
+{
+  const GVariantType *type;
+
+  type = g_variant_get_type (value);
+
+  if G_UNLIKELY (string == NULL)
+    string = g_string_new (NULL);
+
+  g_variant_markup_indent (string, indentation);
+  indentation += tabstop;
+
+  switch (g_variant_get_type_class (value))
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      {
+        GVariant *child;
+
+        g_string_append (string, "<variant>");
+        g_variant_markup_newline (string, newlines);
+
+        child = g_variant_get_variant (value);
+        g_variant_markup_print_string (child, string,
+                                       newlines, indentation, tabstop);
+        g_variant_unref (child);
+
+        g_variant_markup_indent (string, indentation - tabstop);
+        g_string_append (string, "</variant>");
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      {
+        if (g_variant_n_children (value))
+          {
+            GVariant *element;
+
+            g_string_append (string, "<maybe>");
+            g_variant_markup_newline (string, newlines);
+
+            element = g_variant_get_child_value (value, 0);
+            g_variant_markup_print_string (element, string,
+                                           newlines, indentation, tabstop);
+            g_variant_unref (element);
+
+            g_variant_markup_indent (string, indentation - tabstop);
+            g_string_append (string, "</maybe>");
+          }
+        else
+          g_string_append_printf (string, "<nothing type='%s'/>",
+                                  g_variant_get_type_string (value));
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      {
+        GVariantIter iter;
+
+        if (g_variant_iter_init (&iter, value))
+          {
+            GVariant *element;
+
+            g_string_append (string, "<array>");
+            g_variant_markup_newline (string, newlines);
+
+            while ((element = g_variant_iter_next_value (&iter)))
+              g_variant_markup_print_string (element, string,
+                                             newlines, indentation, tabstop);
+
+            g_variant_markup_indent (string, indentation - tabstop);
+            g_string_append (string, "</array>");
+          }
+        else
+          g_string_append_printf (string, "<array type='%s'/>",
+                                  g_variant_get_type_string (value));
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+      {
+        GVariantIter iter;
+
+        if (g_variant_iter_init (&iter, value))
+          {
+            GVariant *element;
+
+            g_string_append (string, "<struct>");
+            g_variant_markup_newline (string, newlines);
+
+            while ((element = g_variant_iter_next_value (&iter)))
+              g_variant_markup_print_string (element, string,
+                                             newlines, indentation, tabstop);
+
+            g_variant_markup_indent (string, indentation - tabstop);
+            g_string_append (string, "</struct>");
+          }
+        else
+          g_string_append (string, "<triv/>");
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      {
+        GVariantIter iter;
+        GVariant *element;
+
+        g_string_append (string, "<dictionary-entry>");
+        g_variant_markup_newline (string, newlines);
+
+        g_variant_iter_init (&iter, value);
+        while ((element = g_variant_iter_next_value (&iter)))
+          g_variant_markup_print_string (element, string,
+                                         newlines, indentation, tabstop);
+
+        g_variant_markup_indent (string, indentation - tabstop);
+        g_string_append (string, "</dictionary-entry>");
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_STRING:
+      {
+        const gchar *str;
+        char *escaped;
+
+        str = g_variant_get_string (value, NULL);
+
+        if (g_utf8_validate (str, -1, NULL) &&
+            has_no_evil_characters (str))
+          {
+            g_string_append (string, "<string>");
+            escaped = g_markup_escape_text (str, -1);
+            g_string_append (string, escaped);
+            g_string_append (string, "</string>");
+            g_free (escaped);
+          }
+        else
+          {
+            char *very_escaped;
+
+            g_string_append (string, "<escaped-string>");
+            escaped = g_strescape (str, NULL);
+            very_escaped = g_markup_escape_text (escaped, -1);
+            g_string_append (string, very_escaped);
+            g_string_append (string, "</escaped-string>");
+            g_free (very_escaped);
+            g_free (escaped);
+          }
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_BOOLEAN:
+      if (g_variant_get_boolean (value))
+        g_string_append (string, "<true/>");
+      else
+        g_string_append (string, "<false/>");
+      break;
+
+    case G_VARIANT_TYPE_CLASS_BYTE:
+      g_string_append_printf (string, "<byte>0x%02x</byte>",
+                              g_variant_get_byte (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_INT16:
+      g_string_append_printf (string, "<int16>%"G_GINT16_FORMAT"</int16>",
+                              g_variant_get_int16 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT16:
+      g_string_append_printf (string, "<uint16>%"G_GUINT16_FORMAT"</uint16>",
+                              g_variant_get_uint16 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_INT32:
+      g_string_append_printf (string, "<int32>%"G_GINT32_FORMAT"</int32>",
+                              g_variant_get_int32 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT32:
+      g_string_append_printf (string, "<uint32>%"G_GUINT32_FORMAT"</uint32>",
+                              g_variant_get_uint32 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_INT64:
+      g_string_append_printf (string, "<int64>%"G_GINT64_FORMAT"</int64>",
+                              g_variant_get_int64 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT64:
+      g_string_append_printf (string, "<uint64>%"G_GUINT64_FORMAT"</uint64>",
+                              g_variant_get_uint64 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_DOUBLE:
+      g_string_append_printf (string, "<double>%f</double>",
+                              g_variant_get_double (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+      g_string_append_printf (string, "<object-path>%s</object-path>",
+                              g_variant_get_string (value, NULL));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_SIGNATURE:
+      g_string_append_printf (string, "<signature>%s</signature>",
+                              g_variant_get_string (value, NULL));
+      break;
+
+    default:
+      g_error ("sorry... not handled yet: %s",
+               g_variant_get_type_string (value));
+  }
+
+  g_variant_markup_newline (string, newlines);
+
+  return string;
+}
+
+/* parser */
+typedef struct
+{
+  GVariantBuilder *builder;
+  gboolean terminal_value;
+  GString *string;
+} GVariantParseData;
+
+static GVariantParseData *
+g_variant_parse_data_new (const GVariantType *type)
+{
+  GVariantParseData *data;
+
+  data = g_slice_new (GVariantParseData);
+  data->builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_VARIANT, type);
+  data->terminal_value = FALSE;
+  data->string = NULL;
+
+  return data;
+}
+
+static void
+g_variant_parse_data_free (gpointer _data)
+{
+  GVariantParseData *data = _data;
+
+  if (data->builder)
+    g_variant_builder_cancel (data->builder);
+
+  if (data->string)
+    g_string_free (data->string, TRUE);
+
+  g_slice_free (GVariantParseData, data);
+}
+
+static GVariant *
+g_variant_parse_data_end (GVariantParseData  *data,
+                          gboolean            and_free,
+                          GError            **error)
+{
+  GVariant *variant, *value;
+
+  g_assert (data != NULL);
+  g_assert (data->builder != NULL);
+  g_assert (data->string == NULL);
+
+  /* we're sure that the tag stack is balanced since
+   * GMarkup wouldn't let us end otherwise...
+   */
+  if (!g_variant_builder_check_end (data->builder, error))
+    return NULL;
+
+  variant = g_variant_builder_end (data->builder);
+  data->builder = NULL;
+
+  value = g_variant_get_child_value (variant, 0);
+  g_variant_unref (variant);
+
+  if (and_free)
+    g_variant_parse_data_free (data);
+
+  return value;
+}
+
+static GVariantTypeClass
+type_class_from_keyword (const char *keyword)
+{
+  struct keyword_mapping
+  {
+    GVariantTypeClass class;
+    const char *keyword;
+  } list[] = {
+    { G_VARIANT_TYPE_CLASS_BOOLEAN,          "boolean" },
+    { G_VARIANT_TYPE_CLASS_BYTE,             "byte" },
+    { G_VARIANT_TYPE_CLASS_INT16,            "int16" },
+    { G_VARIANT_TYPE_CLASS_UINT16,           "uint16" },
+    { G_VARIANT_TYPE_CLASS_INT32,            "int32" },
+    { G_VARIANT_TYPE_CLASS_UINT32,           "uint32" },
+    { G_VARIANT_TYPE_CLASS_INT64,            "int64" },
+    { G_VARIANT_TYPE_CLASS_UINT64,           "uint64" },
+    { G_VARIANT_TYPE_CLASS_DOUBLE,           "double" },
+    { G_VARIANT_TYPE_CLASS_STRING,           "escaped-string" },
+    { G_VARIANT_TYPE_CLASS_STRING,           "string" },
+    { G_VARIANT_TYPE_CLASS_OBJECT_PATH,      "object-path" },
+    { G_VARIANT_TYPE_CLASS_SIGNATURE,        "signature" },
+    { G_VARIANT_TYPE_CLASS_VARIANT,          "variant" },
+    { G_VARIANT_TYPE_CLASS_MAYBE,            "maybe" },
+    { G_VARIANT_TYPE_CLASS_MAYBE,            "nothing" },
+    { G_VARIANT_TYPE_CLASS_ARRAY,            "array" },
+    { G_VARIANT_TYPE_CLASS_TUPLE,           "struct" },
+    { G_VARIANT_TYPE_CLASS_DICT_ENTRY,       "dictionary-entry" }
+  };
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (list); i++)
+    if (!strcmp (keyword, list[i].keyword))
+      return list[i].class;
+
+  return G_VARIANT_TYPE_CLASS_INVALID;
+}
+
+static GVariant *
+value_from_keyword (const char *keyword)
+{
+
+  if (!strcmp (keyword, "true"))
+    return g_variant_new_boolean (TRUE);
+
+  else if (!strcmp (keyword, "false"))
+    return g_variant_new_boolean (FALSE);
+
+  else if (!strcmp (keyword, "triv"))
+    return g_variant_new ("()");
+
+  return NULL;
+}
+
+static void
+g_variant_markup_parser_start_element (GMarkupParseContext  *context,
+                                       const char           *element_name,
+                                       const char          **attribute_names,
+                                       const char          **attribute_values,
+                                       gpointer              user_data,
+                                       GError              **error)
+{
+  GVariantParseData *data = user_data;
+  const gchar *type_string;
+  const GVariantType *type;
+  GVariantTypeClass class;
+  GVariant *value;
+
+  if (data->string != NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_INVALID_CONTENT,
+                   "only character data may appear here (not <%s>)",
+                   element_name);
+      return;
+    }
+
+  if (data->terminal_value)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_INVALID_CONTENT,
+                   "nothing may appear here except </%s>",
+                   (const gchar *)
+                   g_markup_parse_context_get_element_stack (context)
+                     ->next->data);
+      return;
+    }
+
+  if ((value = value_from_keyword (element_name)))
+    {
+      if (!g_variant_builder_check_add (data->builder,
+                                        g_variant_get_type_class (value),
+                                        g_variant_get_type (value), error))
+        return;
+
+      g_variant_builder_add_value (data->builder, value);
+      data->terminal_value = TRUE;
+
+      return;
+    }
+
+  class = type_class_from_keyword (element_name);
+
+  if (class == G_VARIANT_TYPE_CLASS_INVALID)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                   "the <%s> tag is unrecognised", element_name);
+      return;
+    }
+
+  if (!g_markup_collect_attributes (element_name,
+                                    attribute_names, attribute_values,
+                                    error,
+                                    G_MARKUP_COLLECT_OPTIONAL |
+                                      G_MARKUP_COLLECT_STRING,
+                                    "type", &type_string,
+                                    G_MARKUP_COLLECT_INVALID))
+    return;
+
+  if (type_string && !g_variant_type_string_is_valid (type_string))
+    {
+      g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                   G_VARIANT_BUILDER_ERROR_TYPE,
+                   "'%s' is not a valid type string", type_string);
+      return;
+    }
+
+  type = type_string ? G_VARIANT_TYPE (type_string) : NULL;
+
+  if (!g_variant_builder_check_add (data->builder, class, type, error))
+    return;
+
+  if (g_variant_type_class_is_basic (class))
+    data->string = g_string_new (NULL);
+  else
+    data->builder = g_variant_builder_open (data->builder, class, type);
+
+  /* special case: <nothing/> may contain no children */
+  if (strcmp (element_name, "nothing") == 0)
+    {
+      data->builder = g_variant_builder_close (data->builder);
+      data->terminal_value = TRUE;
+    }
+}
+
+static gboolean
+parse_bool (char *str, char **end)
+{
+  if (!strncmp (str, "true", 4))
+    {
+      *end = str + 4;
+      return TRUE;
+    }
+  else if (!strncmp (str, "false", 4))
+    {
+      *end = str + 5;
+      return FALSE;
+    }
+  else
+    {
+      *end = str;
+      return FALSE;
+    }
+}
+
+static void
+g_variant_markup_parser_end_element (GMarkupParseContext  *context,
+                                     const char           *element_name,
+                                     gpointer              user_data,
+                                     GError              **error)
+{
+  GVariantParseData *data = user_data;
+  GVariantTypeClass class;
+
+  if (data->terminal_value)
+    {
+      data->terminal_value = FALSE;
+      return;
+    }
+
+  class = type_class_from_keyword (element_name);
+
+  if (g_variant_type_class_is_basic (class))
+    {
+      GVariant *value;
+      char *string;
+      char *end;
+      int i;
+
+      g_assert (data->string);
+
+      /* ensure at least one non-whitespace character */
+      for (i = 0; i < data->string->len; i++)
+        if (!g_ascii_isspace (data->string->str[i]))
+          break;
+
+      if G_UNLIKELY (i == data->string->len &&
+                     class != G_VARIANT_TYPE_CLASS_STRING &&
+                     class != G_VARIANT_TYPE_CLASS_SIGNATURE)
+        {
+          g_set_error (error, G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_INVALID_CONTENT,
+                       "character data expected before </%s>",
+                       element_name);
+          return;
+        }
+
+      string = &data->string->str[i];
+
+      switch (class)
+      {
+        case G_VARIANT_TYPE_CLASS_BOOLEAN:
+          value = g_variant_new_boolean (parse_bool (string, &end));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_BYTE:
+          value = g_variant_new_byte (g_ascii_strtoll (string, &end, 0));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_INT16:
+          value = g_variant_new_int16 (g_ascii_strtoll (string, &end, 0));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_UINT16:
+          value = g_variant_new_uint16 (g_ascii_strtoull (string, &end, 0));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_INT32:
+          value = g_variant_new_int32 (g_ascii_strtoll (string, &end, 0));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_UINT32:
+          value = g_variant_new_uint32 (g_ascii_strtoull (string, &end, 0));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_INT64:
+          value = g_variant_new_int64 (g_ascii_strtoll (string, &end, 0));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_UINT64:
+          value = g_variant_new_uint64 (g_ascii_strtoull (string, &end, 0));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_DOUBLE:
+          value = g_variant_new_double (g_ascii_strtod (string, &end));
+          break;
+
+        case G_VARIANT_TYPE_CLASS_STRING:
+          if (strcmp (element_name, "string") == 0)
+            {
+              value = g_variant_new_string (data->string->str);
+            }
+          else if (strcmp (element_name, "escaped-string") == 0)
+            {
+              char *unescaped;
+
+              unescaped = g_strcompress (data->string->str);
+              value = g_variant_new_string (unescaped);
+              g_free (unescaped);
+            }
+
+          else
+            g_assert_not_reached ();
+
+          end = NULL;
+          break;
+
+        case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+          if (!g_variant_is_object_path (data->string->str))
+            {
+              g_set_error (error, G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_INVALID_CONTENT,
+                           "invalid object path: '%s'", data->string->str);
+              return;
+            }
+          value = g_variant_new_object_path (data->string->str);
+          end = NULL;
+          break;
+
+        case G_VARIANT_TYPE_CLASS_SIGNATURE:
+          if (!g_variant_is_signature (data->string->str))
+            {
+              g_set_error (error, G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_INVALID_CONTENT,
+                           "invalid DBus signature: '%s'", data->string->str);
+              return;
+            }
+          value = g_variant_new_signature (data->string->str);
+          end = NULL;
+          break;
+
+        default:
+          g_assert_not_reached ();
+      }
+
+      /* ensure only trailing whitespace */
+      for (i = 0; end && end[i]; i++)
+        if G_UNLIKELY (!g_ascii_isspace (end[i]))
+          {
+            g_set_error (error, G_MARKUP_ERROR,
+                         G_MARKUP_ERROR_INVALID_CONTENT,
+                         "cannot interpret character data");
+            g_variant_unref (value);
+            return;
+          }
+
+      g_variant_builder_add_value (data->builder, value);
+      g_string_free (data->string, TRUE);
+      data->string = NULL;
+    }
+  else
+    {
+      if (!g_variant_builder_check_end (data->builder, error))
+        return;
+
+      data->builder = g_variant_builder_close (data->builder);
+    }
+}
+
+static void
+g_variant_markup_parser_text (GMarkupParseContext  *context,
+                              const char           *text,
+                              gsize                 text_len,
+                              gpointer              user_data,
+                              GError              **error)
+{
+  GVariantParseData *data = user_data;
+
+  if (data->string == NULL)
+    {
+      int i;
+
+      for (i = 0; i < text_len; i++)
+        if G_UNLIKELY (!g_ascii_isspace (text[i]))
+          {
+            g_set_error (error, G_MARKUP_ERROR,
+                         G_MARKUP_ERROR_INVALID_CONTENT,
+                         "character data ('%c') is invalid here", text[i]);
+            break;
+          }
+
+      return;
+    }
+  else
+    g_string_append_len (data->string, text, text_len);
+}
+
+static void
+g_variant_markup_parser_error (GMarkupParseContext *context,
+                               GError              *error,
+                               gpointer             user_data)
+{
+  GVariantParseData *data = user_data;
+
+  g_variant_parse_data_free (data);
+}
+
+GMarkupParser g_variant_markup_parser =
+{
+  g_variant_markup_parser_start_element,
+  g_variant_markup_parser_end_element,
+  g_variant_markup_parser_text,
+  NULL,
+  g_variant_markup_parser_error
+};
+
+
+/**
+ * g_variant_markup_subparser_start:
+ * @context: a #GMarkupParseContext
+ * @type: a #GVariantType constraining the type of the root element
+ *
+ * One of the three interfaces to the #GVariant markup parser.  For
+ * information about the others, see g_variant_markup_parse() and
+ * g_variant_markup_parse_context_new().
+ *
+ * You should use this interface if you are parsing an XML document
+ * using #GMarkupParser and that document contains an embedded
+ * #GVariant among the markup.
+ *
+ * You should call this function from the start_element handler of
+ * your parser for the element containing the markup for the
+ * #GVariant and then return immediately.  The next call to your
+ * parser will either be an error condition or a call to the
+ * end_element handler for the tag matching the start tag.  From here,
+ * you should call g_variant_markup_parser_pop to collect the result.
+ *
+ * For example, if your document contained sections like this:
+ *
+ * <programlisting>
+ *   &lt;my-value&gt;
+ *     &lt;int32&gt;42&lt;/int32&gt;
+ *   &lt;/my-value&gt;
+ * </programlisting>
+ *
+ * Then your handlers might contain code like:
+ *
+ * <programlisting>
+ * start_element()
+ * {
+ *   if (strcmp (element_name, "my-value") == 0)
+ *     g_variant_markup_subparser_start (context, NULL);
+ *   else
+ *     {
+ *       ...
+ *     }
+ *
+ * }
+ *
+ * end_element()
+ * {
+ *   if (strcmp (element_name, "my-value") == 0)
+ *     {
+ *       GVariant *value;
+ *       
+ *       if (!(value = g_variant_markup_subparser_pop (context, error)))
+ *         return;
+ *
+ *       ...
+ *     }
+ *   else
+ *     {
+ *       ...
+ *     }
+ * }
+ * </programlisting>
+ *
+ * If @type is non-%NULL then it constrains the permissible types that
+ * the root element may have.  It also serves to hint the parser about
+ * the type of this element (and may, for example, resolve errors
+ * caused by the inability to infer the type).
+ *
+ * This call never fails, but it is possible that the call to
+ * g_variant_markup_subparser_end() will.
+ **/
+void
+g_variant_markup_subparser_start (GMarkupParseContext *context,
+                                  const GVariantType  *type)
+{
+  g_markup_parse_context_push (context, &g_variant_markup_parser,
+                               g_variant_parse_data_new (type));
+}
+
+/**
+ * g_variant_markup_subparser_end:
+ * @context: a #GMarkupParseContext
+ * @error: the end_element handler @error, passed through
+ * @returns: a #GVariant or %NULL.
+ *
+ * Ends the subparser started by g_variant_markup_subparser_start()
+ * and collects the results.
+ *
+ * You must call this function from the end_element handler invocation
+ * corresponding to the start_element handler invocation from which
+ * g_variant_markup_subparser_start() was called.  This will be the
+ * first end_handler invocation that is received after calling
+ * g_variant_markup_subparser_start().
+ *
+ * If an error occured while processing tags in the subparser then
+ * your end_element handler will not be invoked at all and you should
+ * not call this function.
+ *
+ * The only time this function will fail is if no value was contained
+ * between the start and ending tags.
+ **/
+GVariant *
+g_variant_markup_subparser_end (GMarkupParseContext  *context,
+                                GError              **error)
+{
+  return g_variant_parse_data_end (g_markup_parse_context_pop (context),
+                                   TRUE, error);
+}
+
+/**
+ * g_variant_markup_parse:
+ * @text: the self-contained document to parse
+ * @text_len: the length of @text, or -1
+ * @type: a #GVariantType constraining the type of the root element
+ * @error: a #GError
+ * @returns: a new #GVariant, or %NULL in case of an error
+ *
+ * One of the three interfaces to the #GVariant markup parser.  For
+ * information about the others, see
+ * g_variant_markup_subparser_start() and
+ * g_variant_markup_parse_context_new().
+ *
+ * You should use this interface if you have an XML document
+ * representing a #GVariant value entirely contained within a single
+ * string.
+ *
+ * @text should be the full text of the document.  If @text_len is not
+ * -1 then it gives the length of @text (similar to
+ *  g_markup_parse_context_parse()).
+ *
+ * If @type is non-%NULL then it constrains the permissible types that
+ * the root element may have.  It also serves to hint the parser about
+ * the type of this element (and may, for example, resolve errors
+ * caused by the inability to infer the type).
+ *
+ * In the case of an error then %NULL is returned and @error is set to
+ * a description of the error condition.  This function is robust
+ * against arbitrary input; all error conditions are reported via
+ * @error -- your program will never abort.
+ **/
+GVariant *
+g_variant_markup_parse (const gchar         *text,
+                        gssize               text_len,
+                        const GVariantType  *type,
+                        GError             **error)
+{
+  GMarkupParseContext *context;
+  GVariantParseData *data;
+  GVariant *value;
+  GVariant *child;
+
+  data = g_slice_new (GVariantParseData);
+  data->builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_VARIANT, type);
+  data->terminal_value = FALSE;
+  data->string = NULL;
+
+  context = g_markup_parse_context_new (&g_variant_markup_parser,
+                                        0, data, NULL);
+
+  if (!g_markup_parse_context_parse (context, text, text_len, error))
+    return FALSE;
+
+  if (!g_markup_parse_context_end_parse (context, error))
+    return FALSE;
+
+  g_markup_parse_context_free (context);
+
+  g_assert (data != NULL);
+  g_assert (data->builder != NULL);
+
+  if (!g_variant_builder_check_end (data->builder, error))
+    return NULL;
+
+  g_assert (data->string == NULL);
+  value = g_variant_builder_end (data->builder);
+  g_slice_free (GVariantParseData, data);
+
+  child = g_variant_get_child_value (value, 0);
+  g_variant_unref (value);
+
+  return child;
+}
+
+/**
+ * g_variant_markup_parse_context_new:
+ * @flags: #GMarkupParseFlags
+ * @type: a #GVariantType constraining the type of the root element
+ * @returns: a new #GMarkupParseContext
+ *
+ * One of the three interfaces to the #GVariant markup parser.  For
+ * information about the others, see g_variant_markup_parse() and
+ * g_variant_markup_subparser_start().
+ *
+ * You should use this interface if you have an XML document that you
+ * want to feed to the parser in chunks.
+ *
+ * This call creates a #GMarkupParseContext setup for parsing a
+ * #GVariant XML document.  You feed the document to the parser one
+ * chunk at a time using the normal g_markup_parse_context_parse()
+ * call.  After the entire document is fed, you call
+ * g_variant_markup_parse_context_end() to free the context and
+ * retreive the value.
+ *
+ * If @type is non-%NULL then it constrains the permissible types that
+ * the root element may have.  It also serves to hint the parser about
+ * the type of this element (and may, for example, resolve errors
+ * caused by the inability to infer the type).
+ *
+ * If you want to abort parsing, you should free the context using
+ * g_markup_parse_context_free().
+ **/
+GMarkupParseContext *
+g_variant_markup_parse_context_new (GMarkupParseFlags   flags,
+                                    const GVariantType *type)
+{
+  return g_markup_parse_context_new (&g_variant_markup_parser,
+                                     flags, g_variant_parse_data_new (type),
+                                     &g_variant_parse_data_free);
+}
+
+/**
+ * g_variant_markup_parse_context_end:
+ * @context: a #GMarkupParseContext
+ * @error: a #GError
+ * @returns: a #GVariant, or %NULL
+ *
+ * Ends the parsing started with g_variant_markup_parse_context_new().
+ *
+ * @context must have been the result of a previous call to
+ * g_variant_markup_parse_context_new().
+ *
+ * This function calls g_markup_parse_context_end_parse() and
+ * g_markup_parse_context_free() for you.
+ *
+ * If the parsing was successful, a #GVariant is returned.  Otherwise,
+ * %NULL is returned and @error is set accordingly.
+ **/
+GVariant *
+g_variant_markup_parse_context_end (GMarkupParseContext  *context,
+                                    GError              **error)
+{
+  GVariant *value = NULL;
+
+  if (g_markup_parse_context_end_parse (context, error))
+    value = g_variant_parse_data_end (
+              g_markup_parse_context_get_user_data (context), FALSE, error);
+
+  g_markup_parse_context_free (context);
+
+  return value;
+}
+
+#define __G_VARIANT_MARKUP_C__
+#include "galiasdef.c"
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
new file mode 100644
index 0000000..f4a0331
--- /dev/null
+++ b/glib/gvariant-parser.c
@@ -0,0 +1,2200 @@
+/*
+ * Copyright © 2009, Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "galias.h"
+
+/*
+ * two-pass algorithm
+ * designed by ryan lortie and william hua
+ * designed in itb-229 and at ghazi's, 2009.
+ */
+
+typedef struct
+{
+  GVariantParseError error;
+
+  const gchar *stream;
+  const gchar *end;
+
+  const gchar *this;
+} TokenStream;
+
+static void
+set_error (GVariantParseError *error,
+           const gchar        *start,
+           const gchar        *end,
+           const gchar        *message)
+{
+  g_assert (error->error == NULL);
+
+  error->error = g_strdup (message);
+  error->start = start;
+  error->end = end;
+}
+
+static void
+token_stream_prepare (TokenStream *stream)
+{
+  gint brackets = 0;
+  const gchar *end;
+
+  if (stream->this != NULL)
+    return;
+
+  while (stream->stream != stream->end && g_ascii_isspace (*stream->stream))
+    stream->stream++;
+
+  if (stream->stream == stream->end || *stream->stream == '\0')
+    {
+      stream->this = stream->stream;
+      return;
+    }
+
+  switch (stream->stream[0])
+    {
+    case '-': case '+': case '.': case '0': case '1': case '2':
+    case '3': case '4': case '5': case '6': case '7': case '8':
+    case '9':
+      for (end = stream->stream; end != stream->end; end++)
+        if (!g_ascii_isalnum (*end) &&
+            *end != '-' && *end != '+' && *end != '.')
+          break;
+      break;
+
+    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+    case 'y': case 'z':
+      for (end = stream->stream; end != stream->end; end++)
+        if (!g_ascii_isalnum (*end))
+          break;
+      break;
+
+    case '@': case '%':
+      /* stop at the first space or unmatched bracket.
+       * deals nicely with cases like (%i, %i).
+       */
+      for (end = stream->stream + 1;
+           end != stream->end && !g_ascii_isspace (*end);
+           end++)
+
+        if (*end == '(' || *end == '{')
+          brackets++;
+
+        else if ((*end == ')' || *end == '}') && !brackets--)
+          break;
+
+      break;
+
+    case '\'': case '"':
+      for (end = stream->stream + 1; end != stream->end; end++)
+        if (*end == stream->stream[0] || *end == '\0' ||
+            (*end == '\\' && (++end == stream->end || *end == '\0')))
+          break;
+
+      if (end != stream->end && *end)
+        end++;
+      break;
+
+    default:
+      end = stream->stream + 1;
+      break;
+    }
+
+  stream->this = stream->stream;
+  stream->stream = end;
+}
+
+static void
+token_stream_next (TokenStream *stream)
+{
+  stream->this = NULL;
+}
+
+static gboolean
+token_stream_peek (TokenStream *stream,
+                   gchar        first_char)
+{
+  token_stream_prepare (stream);
+
+  return stream->this[0] == first_char;
+}
+
+static gboolean
+token_stream_is_keyword (TokenStream *stream)
+{
+  token_stream_prepare (stream);
+
+  return g_ascii_isalpha (stream->this[0]);
+}
+
+static gboolean
+token_stream_is_numeric (TokenStream *stream)
+{
+  token_stream_prepare (stream);
+
+  return (g_ascii_isdigit (stream->this[0]) ||
+          stream->this[0] == '-' ||
+          stream->this[0] == '+' ||
+          stream->this[0] == '.');
+}
+
+static gboolean
+token_stream_consume (TokenStream *stream,
+                      const gchar *token)
+{
+  gint length = strlen (token);
+
+  token_stream_prepare (stream);
+
+  if (stream->stream - stream->this == length &&
+      memcmp (stream->this, token, length) == 0)
+    {
+      token_stream_next (stream);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+token_stream_require (TokenStream *stream,
+                      const gchar *token,
+                      const gchar *purpose)
+{
+
+  if (!token_stream_consume (stream, token))
+    {
+      gchar *message;
+
+      message = g_strdup_printf ("expected `%s'%s", token, purpose);
+      set_error (&stream->error, stream->this, stream->this, message);
+      g_free (message);
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+token_stream_assert (TokenStream *stream,
+                     const gchar *token)
+{
+  gboolean correct_token;
+
+  correct_token = token_stream_consume (stream, token);
+  g_assert (correct_token);
+}
+
+static gchar *
+token_stream_get (TokenStream *stream)
+{
+  gchar *result;
+
+  token_stream_prepare (stream);
+
+  result = g_strndup (stream->this, stream->stream - stream->this);
+
+  return result;
+}
+
+typedef enum
+{
+  NO_CONSTRAINTS,
+  CONSTRAINT_IS_NUMBER       = 0x010000,
+  CONSTRAINT_IS_STRING       = 0x020000,
+  CONSTRAINT_HAS_POINT       = 0x080000,
+  CONSTRAINT_IS_BOOLEAN      = 0x100000
+} Constraints;
+
+static char
+type_from_constraints (Constraints constraints)
+{
+  if (constraints & CONSTRAINT_IS_STRING)
+    {
+      if (constraints & (CONSTRAINT_IS_NUMBER | CONSTRAINT_IS_BOOLEAN))
+        return 0;
+
+      return 's';
+    }
+
+  if (constraints & CONSTRAINT_IS_BOOLEAN)
+    {
+      if (constraints & CONSTRAINT_IS_NUMBER)
+        return 0;
+
+      return 'b';
+    }
+  
+  if (constraints & CONSTRAINT_HAS_POINT)
+    return 'd';
+
+  return 'i';
+}
+
+typedef struct
+{
+  gchar *type_string;
+
+  Constraints *constraints;
+  gint n_constraints;
+} Pattern;
+
+static void
+pattern_free (Pattern *pattern)
+{
+  g_free (pattern->constraints);
+  g_free (pattern->type_string);
+  g_slice_free (Pattern, pattern);
+}
+
+static Pattern *
+pattern_concat (Pattern **items,
+                gint      n_items)
+{
+  Pattern *result;
+  gint i, j, c;
+
+  result = g_slice_new (Pattern);
+
+  result->n_constraints = 0;
+  for (i = 0; i < n_items; i++)
+    result->n_constraints += items[i]->n_constraints;
+
+  result->type_string = g_new (gchar, result->n_constraints + 1);
+  result->constraints = g_new (Constraints, result->n_constraints);
+
+  c = 0;
+  for (i = 0; i < n_items; i++)
+    {
+      const Pattern *item = items[i];
+
+      for (j = 0; j < item->n_constraints; j++, c++)
+        {
+          result->type_string[c] = item->type_string[j];
+          result->constraints[c] = item->constraints[j];
+        }
+
+      g_assert (item->type_string[j] == '\0');
+    }
+  g_assert (c == result->n_constraints);
+  result->type_string[c] = '\0';
+
+  return result;
+}
+
+static gboolean
+pattern_coalesce (Pattern       *dest,
+                  const Pattern *src)
+{
+  gint i;
+
+  if (dest->n_constraints != src->n_constraints)
+    return FALSE;
+
+  for (i = 0; i < dest->n_constraints; i++)
+    {
+      if (src->type_string[i] == dest->type_string[i])
+        dest->constraints[i] |= src->constraints[i];
+
+      else if (dest->type_string[i] == '?' &&
+               strchr ("bynqiuxtdsog", src->type_string[i]))
+        dest->type_string[i] = src->type_string[i];
+
+      else if (src->type_string[i] != '?' ||
+               !strchr ("bynqiuxtdsog", dest->type_string[i]))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static Pattern *
+pattern_from_class (GVariantTypeClass class)
+{
+  Pattern *pattern;
+
+  pattern = g_slice_new (Pattern);
+  pattern->n_constraints = 1;
+  pattern->type_string = g_new (gchar, 2);
+  pattern->type_string[0] = class;
+  pattern->type_string[1] = '\0';
+  pattern->constraints = g_new (Constraints, 1); /* value will not be used */
+
+  return pattern;
+}
+
+static Pattern *
+pattern_from_type (const GVariantType *type)
+{
+  Pattern *pattern;
+
+  pattern = g_slice_new (Pattern);
+  pattern->n_constraints = g_variant_type_get_string_length (type);
+  pattern->type_string = g_variant_type_dup_string (type);
+  pattern->constraints = g_new (Constraints, pattern->n_constraints);
+
+  return pattern;
+}
+
+static Pattern *
+pattern_from_constraints (Constraints constraints)
+{
+  Pattern *pattern;
+
+  pattern = g_slice_new (Pattern);
+  pattern->n_constraints = 1;
+  pattern->type_string = g_strdup ("?");
+  pattern->constraints = g_new (Constraints, 1);
+  pattern->constraints[0] = constraints;
+
+  return pattern;
+}
+
+typedef struct _AST AST;
+typedef Pattern *  (*get_pattern_func)  (AST                 *ast,
+                                         GVariantParseError  *error);
+typedef GVariant * (*get_value_func)    (AST                 *ast,
+                                         const GVariantType  *type,
+                                         GVariantParseError  *error);
+typedef GSList *   (*get_examples_func) (AST                 *ast,
+                                         gint                 index,
+                                         Constraints         *constraints);
+typedef void       (*free_func)         (AST                 *ast);
+ 
+typedef struct
+{
+  get_pattern_func   get_pattern;
+  get_value_func     get_value;
+  get_examples_func  get_examples;
+  free_func          free;
+} ASTClass;
+
+struct _AST
+{
+  const ASTClass *class;
+
+  const gchar *start;
+  const gchar *end;
+};
+
+static Pattern *
+ast_get_pattern (AST                *ast,
+                 GVariantParseError *error)
+{
+  return ast->class->get_pattern (ast, error);
+}
+
+static gint
+ast_get_pattern_size (AST *ast)
+{
+  Pattern *pattern;
+  gint size;
+
+  pattern = ast_get_pattern (ast, NULL);
+  g_assert (pattern != NULL);
+
+  size = pattern->n_constraints;
+  pattern_free (pattern);
+
+  return size;
+}
+
+static GVariant *
+ast_get_value (AST                *ast,
+               const GVariantType *type,
+               GVariantParseError *error)
+{
+  return ast->class->get_value (ast, type, error);
+}
+
+static GSList *
+ast_get_examples (AST         *ast,
+                  gint         index,
+                  Constraints *constraints)
+{
+  return ast->class->get_examples (ast, index, constraints);
+}
+
+static GVariant *
+ast_resolve (AST                *ast,
+             GVariantParseError *error)
+{
+  Pattern *pattern;
+  GVariant *value;
+  gint i;
+
+  pattern = ast_get_pattern (ast, error);
+
+  if (pattern == NULL)
+    return NULL;
+
+  for (i = 0; i < pattern->n_constraints; i++)
+    if (pattern->type_string[i] == '?')
+      {
+        char new;
+
+        new = type_from_constraints (pattern->constraints[i]);
+
+        if (!new)
+          {
+            Constraints constraints = pattern->constraints[i];
+            GSList *examples;
+
+            examples = ast_get_examples (ast, i, &constraints);
+            g_assert_cmpint (g_slist_length (examples), ==, 2);
+
+            set_error (error,
+                       "Unable to infer a type to contain both "
+                       "'%s' and '%s'",
+                       (gchar *) examples->data,
+                       (gchar *) examples->next->data);
+
+            g_slist_free (examples);
+            pattern_free (pattern);
+
+            return NULL;
+          }
+
+        pattern->type_string[i] = new;
+      }
+  g_assert (pattern->type_string[i] == '\0');
+
+  value = ast_get_value (ast, G_VARIANT_TYPE (pattern->type_string), error);
+  pattern_free (pattern);
+
+  return value;
+}
+
+static void
+ast_free (AST *ast)
+{
+  ast->class->free (ast);
+}
+
+static AST *parse (TokenStream *stream,
+                   va_list     *app);
+
+static void
+ast_array_append (AST  ***array,
+                  gint   *n_items,
+                  AST    *ast)
+{
+  if ((*n_items & (*n_items - 1)) == 0)
+    *array = g_renew (AST *, *array, *n_items ? 2 ** n_items : 1);
+
+  (*array)[(*n_items)++] = ast;
+}
+
+static void
+ast_array_free (AST  **array,
+                gint   n_items)
+{
+  gint i;
+
+  for (i = 0; i < n_items; i++)
+    ast_free (array[i]);
+  g_free (array);
+}
+
+static void
+ast_set_error (AST                *ast,
+               GVariantParseError *error,
+               const gchar        *format,
+               ...)
+{
+  gchar *message;
+  va_list ap;
+
+  va_start (ap, format);
+  message = g_strdup_vprintf (format, ap);
+  va_end (ap);
+
+  set_error (error, ast->start, ast->end, message);
+}
+
+typedef struct
+{
+  AST ast;
+
+  AST **children;
+  gint n_children;
+} Array;
+
+static Pattern *
+array_get_pattern (AST                *ast,
+                   GVariantParseError *error)
+{
+  Array *array = (Array *) ast;
+  Pattern *result = NULL;
+  Pattern *child_type;
+  gint i;
+
+  if (array->n_children == 0)
+    {
+      ast_set_error (ast, error, "unable to infer type of empty array");
+      return NULL;
+    }
+
+  child_type = ast_get_pattern (array->children[0], error);
+
+  if (child_type == NULL)
+    return NULL;
+
+  for (i = 1; i < array->n_children; i++)
+    {
+      Pattern *other_child_type;
+
+      other_child_type = ast_get_pattern (array->children[i], error);
+
+      if (other_child_type == NULL)
+        break;
+
+      if (!pattern_coalesce (child_type, other_child_type))
+        {
+          ast_set_error (ast, error,
+                         "unable to coalesce types '%s' and '%s'",
+                         child_type->type_string,
+                         other_child_type->type_string);
+
+          pattern_free (other_child_type);
+
+          break;
+        }
+
+      pattern_free (other_child_type);
+    }
+
+  if (i == array->n_children)
+    {
+      Constraints undefined;
+      Pattern array_prefix = { "a", &undefined, 1 };
+      Pattern *parts[] = { &array_prefix, child_type };
+
+      result = pattern_concat (parts, 2);
+    }
+
+  pattern_free (child_type);
+
+  return result;
+}
+
+static GVariant *
+array_get_value (AST                *ast,
+                 const GVariantType *type,
+                 GVariantParseError *error)
+{
+  Array *array = (Array *) ast;
+  const GVariantType *childtype;
+  GVariantBuilder *builder;
+  gint i;
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY, type);
+  childtype = g_variant_type_element (type);
+
+  for (i = 0; i < array->n_children; i++)
+    {
+      GVariant *child;
+
+      child = ast_get_value (array->children[i], childtype, error);
+
+      if (child == NULL)
+        {
+          g_variant_builder_cancel (builder);
+          return NULL;
+        }
+
+      g_variant_builder_add_value (builder, child);
+    }
+
+  return g_variant_builder_end (builder);
+}
+
+static GSList *
+array_get_examples (AST         *ast,
+                    gint         index,
+                    Constraints *constraints)
+{
+  Array *array = (Array *) ast;
+  GSList *result = NULL;
+  gint i;
+
+  /* remove the 'a' */
+  index--;
+
+  for (i = 0; i < array->n_children && *constraints; i++)
+    {
+      GSList *examples;
+
+      examples = ast_get_examples (array->children[i], index, constraints);
+      result = g_slist_concat (result, examples);
+    }
+
+  return result;
+}
+
+static void
+array_free (AST *ast)
+{
+  Array *array = (Array *) ast;
+
+  ast_array_free (array->children, array->n_children);
+  g_slice_free (Array, array);
+}
+
+static AST *
+array_parse (TokenStream *stream,
+             va_list     *app)
+{
+  static const ASTClass array_class = {
+    array_get_pattern,
+    array_get_value,
+    array_get_examples,
+    array_free
+  };
+  gboolean need_comma = FALSE;
+  Array *array;
+
+  array = g_slice_new (Array);
+  array->ast.class = &array_class;
+  array->children = NULL;
+  array->n_children = 0;
+
+  token_stream_assert (stream, "[");
+  while (!token_stream_consume (stream, "]"))
+    {
+      AST *child;
+
+      if (need_comma &&
+          !token_stream_require (stream, ",",
+                                 " or ']' to follow array element"))
+        goto error;
+
+      child = parse (stream, app);
+
+      if (!child)
+        goto error;
+        
+      ast_array_append (&array->children, &array->n_children, child);
+      need_comma = TRUE;
+    }
+
+  return (AST *) array;
+
+ error:
+  ast_array_free (array->children, array->n_children);
+  g_slice_free (Array, array);
+
+  return NULL;
+}
+
+typedef struct
+{
+  AST ast;
+
+  AST **children;
+  gint n_children;
+} Tuple;
+
+static Pattern *
+tuple_get_pattern (AST                *ast,
+                   GVariantParseError *error)
+{
+  Tuple *tuple = (Tuple *) ast;
+  Pattern *result = NULL;
+  Pattern **parts;
+  gint i;
+
+  parts = g_new (Pattern *, tuple->n_children + 2);
+  for (i = 0; i < tuple->n_children; i++)
+    {
+      Pattern *child;
+
+      child = ast_get_pattern (tuple->children[i], error);
+
+      if (child == NULL)
+        break;
+
+      parts[i + 1] = child;
+    }
+
+  if (i == tuple->n_children)
+    {
+      Constraints undefined;
+      Pattern open = { "(", &undefined, 1 };
+      Pattern close = { ")", &undefined, 1 };
+      parts[0] = &open;
+      parts[i + 1] = &close;
+
+      result = pattern_concat (parts, i + 2);
+    }
+
+  while (i)
+    pattern_free (parts[i--]);
+  g_free (parts);
+
+  return result;
+}
+
+static GVariant *
+tuple_get_value (AST                *ast,
+                 const GVariantType *type,
+                 GVariantParseError *error)
+{
+  Tuple *tuple = (Tuple *) ast;
+  const GVariantType *childtype;
+  GVariantBuilder *builder;
+  gint i;
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_TUPLE, type);
+  childtype = g_variant_type_first (type);
+
+  for (i = 0; i < tuple->n_children; i++)
+    {
+      GVariant *child;
+
+      child = ast_get_value (tuple->children[i], childtype, error);
+
+      if (child == NULL)
+        {
+          g_variant_builder_cancel (builder);
+          return NULL;
+        }
+
+      g_variant_builder_add_value (builder, child);
+      childtype = g_variant_type_next (childtype);
+    }
+
+  return g_variant_builder_end (builder);
+}
+
+static GSList *
+tuple_get_examples (AST         *ast,
+                    gint         index,
+                    Constraints *constraints)
+{
+  Tuple *tuple = (Tuple *) ast;
+  gint i;
+
+  /* remove the '(' */
+  index--;
+
+  for (i = 0; i < tuple->n_children; i++)
+    {
+      gint size;
+
+      size = ast_get_pattern_size (tuple->children[i]);
+
+      if (index < size)
+        return ast_get_examples (tuple->children[i], index, constraints);
+
+      index -= size;
+    }
+
+  g_assert_not_reached ();
+}
+
+static void
+tuple_free (AST *ast)
+{
+  Tuple *tuple = (Tuple *) ast;
+
+  ast_array_free (tuple->children, tuple->n_children);
+  g_slice_free (Tuple, tuple);
+}
+
+static AST *
+tuple_parse (TokenStream *stream,
+             va_list     *app)
+{
+  static const ASTClass tuple_class = {
+    tuple_get_pattern,
+    tuple_get_value,
+    tuple_get_examples,
+    tuple_free
+  };
+  gboolean need_comma = FALSE;
+  Tuple *tuple;
+
+  tuple = g_slice_new (Tuple);
+  tuple->ast.class = &tuple_class;
+  tuple->children = NULL;
+  tuple->n_children = 0;
+
+  token_stream_assert (stream, "(");
+  while (!token_stream_consume (stream, ")"))
+    {
+      AST *child;
+
+      if (need_comma &&
+          !token_stream_require (stream, ",",
+                                 " or ')' to follow tuple element"))
+        goto error;
+
+      child = parse (stream, app);
+
+      if (!child)
+        goto error;
+        
+      ast_array_append (&tuple->children, &tuple->n_children, child);
+      need_comma = TRUE;
+    }
+
+  return (AST *) tuple;
+
+ error:
+  ast_array_free (tuple->children, tuple->n_children);
+  g_slice_free (Tuple, tuple);
+
+  return NULL;
+}
+
+typedef struct
+{
+  AST ast;
+
+  AST *value;
+} Variant;
+
+static Pattern *
+variant_get_pattern (AST                *ast,
+                     GVariantParseError *error)
+{
+  return pattern_from_class (G_VARIANT_TYPE_CLASS_VARIANT);
+}
+
+static GVariant *
+variant_get_value (AST                *ast,
+                   const GVariantType *type,
+                   GVariantParseError *error)
+{
+  Variant *variant = (Variant *) ast;
+
+  g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT));
+  return g_variant_new_variant (ast_resolve (variant->value, error));
+}
+
+static GSList *
+variant_get_examples (AST         *ast,
+                      gint         index,
+                      Constraints *constraints)
+{
+  g_assert_not_reached ();
+}
+
+static void
+variant_free (AST *ast)
+{
+  Variant *variant = (Variant *) ast;
+
+  ast_free (variant->value);
+  g_slice_free (Variant, variant);
+}
+
+static AST *
+variant_parse (TokenStream *stream,
+               va_list     *app)
+{
+  static const ASTClass variant_class = {
+    variant_get_pattern,
+    variant_get_value,
+    variant_get_examples,
+    variant_free
+  };
+  Variant *variant;
+  AST *value;
+
+  token_stream_assert (stream, "<");
+  value = parse (stream, app);
+
+  if (!value)
+    return NULL;
+
+  if (!token_stream_require (stream, ">", " to follow variant value"))
+    {
+      ast_free (value);
+      return NULL;
+    }
+
+  variant = g_slice_new (Variant);
+  variant->ast.class = &variant_class;
+  variant->value = value;
+
+  return (AST *) variant;
+}
+
+typedef struct
+{
+  AST ast;
+
+  AST **keys;
+  AST **values;
+  gint n_children;
+} Dictionary;
+
+static Pattern *
+dictionary_get_pattern (AST                *ast,
+                        GVariantParseError *error)
+{
+  Dictionary *dict = (Dictionary *) ast;
+  Pattern *result = NULL;
+  Pattern *value_pattern;
+  Pattern *key_pattern;
+  gint i;
+
+  if (dict->n_children == 0)
+    {
+      ast_set_error (ast, error,
+                     "unable to infer the type of an empty dictionary");
+      return NULL;
+    }
+
+  key_pattern = ast_get_pattern (dict->keys[0], error);
+
+  if (key_pattern == NULL)
+    return NULL;
+
+  value_pattern = ast_get_pattern (dict->values[0], error);
+
+  if (value_pattern == NULL)
+    {
+      pattern_free (key_pattern);
+      return NULL;
+    }
+
+  for (i = 1; i < dict->n_children; i++)
+    {
+      Pattern *other_pattern;
+
+      /* key */
+      other_pattern = ast_get_pattern (dict->keys[i], error);
+
+      if (other_pattern == NULL)
+        break;
+
+      if (!strchr ("bynqiuxtdsog?", other_pattern->type_string[0]))
+        {
+          ast_set_error (ast, error, "dictionary keys must have basic types");
+          pattern_free (other_pattern);
+          break;
+        }
+
+      if (!pattern_coalesce (key_pattern, other_pattern))
+        {
+          ast_set_error (ast, error,
+                         "unable to coalesce types '%s' and '%s'",
+                         key_pattern->type_string,
+                         other_pattern->type_string);
+
+          pattern_free (other_pattern);
+
+          break;
+        }
+
+      pattern_free (other_pattern);
+
+      /* value */
+      other_pattern = ast_get_pattern (dict->values[i], error);
+
+      if (other_pattern == NULL)
+        break;
+
+      if (!pattern_coalesce (value_pattern, other_pattern))
+        {
+          ast_set_error (ast, error,
+                         "unable to coalesce types '%s' and '%s'",
+                         key_pattern->type_string,
+                         other_pattern->type_string);
+
+          pattern_free (other_pattern);
+
+          break;
+        }
+
+      pattern_free (other_pattern);
+    }
+
+  if (i >= dict->n_children)
+    {
+      Constraints undefined;
+      Pattern a = { "a", &undefined, 1 };
+      Pattern open = { "{", &undefined, 1 };
+      Pattern close = { "}", &undefined, 1 };
+      Pattern *parts[] = { &a, &open, key_pattern, value_pattern, &close };
+
+      if (dict->n_children == -1)
+        result = pattern_concat (parts + 1, 4);
+      else
+        result = pattern_concat (parts, 5);
+    }
+
+  pattern_free (key_pattern);
+  pattern_free (value_pattern);
+
+  return result;
+}
+
+static GVariant *
+dictionary_get_value (AST                *ast,
+                      const GVariantType *type,
+                      GVariantParseError *error)
+{
+  Dictionary *dict = (Dictionary *) ast;
+
+  if (dict->n_children == -1)
+    {
+      const GVariantType *subtype;
+      GVariantBuilder *builder;
+      GVariant *subvalue;
+
+      builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_DICT_ENTRY, type);
+
+      subtype = g_variant_type_key (type);
+      if (!(subvalue = ast_get_value (dict->keys[0], subtype, error)))
+        {
+          g_variant_builder_cancel (builder);
+          return NULL;
+        }
+      g_variant_builder_add_value (builder, subvalue);
+
+      subtype = g_variant_type_value (type);
+      if (!(subvalue = ast_get_value (dict->values[0], subtype, error)))
+        {
+          g_variant_builder_cancel (builder);
+          return NULL;
+        }
+      g_variant_builder_add_value (builder, subvalue);
+
+      return g_variant_builder_end (builder);
+    }
+  else
+    {
+      const GVariantType *entry, *key, *value;
+      GVariantBuilder *builder;
+      gint i;
+
+      entry = g_variant_type_element (type);
+      key = g_variant_type_key (entry);
+      value = g_variant_type_value (entry);
+
+      builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY, type);
+
+      for (i = 0; i < dict->n_children; i++)
+        {
+          GVariantBuilder *item;
+          GVariant *subvalue;
+
+          item = g_variant_builder_open (builder,
+                                         G_VARIANT_TYPE_CLASS_DICT_ENTRY,
+                                         entry);
+
+          if (!(subvalue = ast_get_value (dict->keys[i], key, error)))
+            {
+              g_variant_builder_cancel (builder);
+              return NULL;
+            }
+          g_variant_builder_add_value (item, subvalue);
+
+          if (!(subvalue = ast_get_value (dict->values[i], value, error)))
+            {
+              g_variant_builder_cancel (builder);
+              return NULL;
+            }
+          g_variant_builder_add_value (item, subvalue);
+          g_variant_builder_close (item);
+        }
+
+      return g_variant_builder_end (builder);
+    }
+}
+
+static GSList *
+dictionary_get_examples (AST         *ast,
+                         gint         index,
+                         Constraints *constraints)
+{
+  Dictionary *dict = (Dictionary *) ast;
+  GSList *result = NULL;
+  AST **list;
+  gint i;
+
+  if (dict->n_children == -1)
+    /* remove the '{' */
+    index--;
+  else
+    /* remove the 'a{' */
+    index -= 2;
+
+  if (index > 0)
+    {
+      /* we know keys have pattern size of 1,
+       * so if index > 0 then it must be inside the value.
+       * subtract one from the index and scan the values.
+       */
+      list = dict->values;
+      index--;
+    }
+  else
+    list = dict->keys;
+
+  for (i = 0; i < dict->n_children && *constraints; i++)
+    {
+      GSList *examples;
+
+      examples = ast_get_examples (list[i], index, constraints);
+      result = g_slist_concat (result, examples);
+    }
+
+  return result;
+}
+
+static void
+dictionary_free (AST *ast)
+{
+  Dictionary *dict = (Dictionary *) ast;
+  gint n_children;
+
+  if (dict->n_children > -1)
+    n_children = dict->n_children;
+  else
+    n_children = 1;
+
+  ast_array_free (dict->keys, n_children);
+  ast_array_free (dict->values, n_children);
+  g_slice_free (Dictionary, dict);
+}
+
+static AST *
+dictionary_parse (TokenStream *stream,
+                  va_list     *app)
+{
+  static const ASTClass dictionary_class = {
+    dictionary_get_pattern,
+    dictionary_get_value,
+    dictionary_get_examples,
+    dictionary_free
+  };
+  gint n_keys, n_values;
+  gboolean only_one;
+  Dictionary *dict;
+  AST *first;
+
+  dict = g_slice_new (Dictionary);
+  dict->ast.class = &dictionary_class;
+  dict->keys = NULL;
+  dict->values = NULL;
+  n_keys = n_values = 0;
+
+  token_stream_assert (stream, "{");
+
+  if ((first = parse (stream, app)) == NULL)
+    goto error;
+
+  ast_array_append (&dict->keys, &n_keys, first);
+
+  only_one = token_stream_consume (stream, ",");
+  if (!only_one &&
+      !token_stream_require (stream, ":",
+                             " or ':' to follow dictionary entry key"))
+    goto error;
+
+  if ((first = parse (stream, app)) == NULL)
+    goto error;
+
+  ast_array_append (&dict->values, &n_values, first);
+
+  if (only_one)
+    {
+      if (!token_stream_require (stream, "}", " at end of dictionary entry"))
+        goto error;
+
+      g_assert (n_keys == 1 && n_values == 1);
+      dict->n_children = -1;
+
+      return (AST *) dict;
+    }
+
+  while (!token_stream_consume (stream, "}"))
+    {
+      AST *child;
+
+      if (!token_stream_require (stream, ",",
+                                 " or '}' to follow dictionary entry"))
+        goto error;
+
+      child = parse (stream, app);
+
+      if (!child)
+        goto error;
+        
+      ast_array_append (&dict->keys, &n_keys, child);
+
+      if (!token_stream_require (stream, ":",
+                                 " to follow dictionary entry key"))
+        goto error;
+
+      child = parse (stream, app);
+
+      if (!child)
+        goto error;
+
+      ast_array_append (&dict->values, &n_values, child);
+    }
+
+  g_assert (n_keys == n_values);
+  dict->n_children = n_keys;
+
+  return (AST *) dict;
+
+ error:
+  ast_array_free (dict->keys, n_keys);
+  ast_array_free (dict->values, n_values);
+  g_slice_free (Dictionary, dict);
+
+  return NULL;
+}
+
+typedef struct
+{
+  AST ast;
+
+  gchar *string;
+  gchar *token;
+} Terminal;
+
+static Constraints
+constraints_from_string (const gchar *token)
+{
+  if (token[0] == '\'' || token[0] == '\"')
+    return CONSTRAINT_IS_STRING;
+
+  if (strchr (token, '.') || strchr (token, 'e'))
+    return CONSTRAINT_HAS_POINT | CONSTRAINT_IS_NUMBER;
+
+  return CONSTRAINT_IS_NUMBER;
+}
+
+static Pattern *
+terminal_get_pattern (AST                *ast,
+                      GVariantParseError *error)
+{
+  Terminal *terminal = (Terminal *) ast;
+  Constraints constraints;
+
+  constraints = constraints_from_string (terminal->token);
+
+  return pattern_from_constraints (constraints);
+}
+
+static gboolean
+parse_string (const gchar         *token,
+              gchar              **result,
+              const gchar         *start,
+              GVariantParseError  *error)
+{
+  gint length;
+  gchar *tmp;
+
+  length = strlen (token);
+
+  if (length < 2 || token[0] != token[length - 1])
+    {
+      set_error (error, start, start + strlen (token),
+                 "unterminated string constant");
+      return FALSE;
+    }
+
+  tmp = g_strndup (token + 1, length - 2);
+  *result = g_strcompress (tmp);
+  g_free (tmp);
+
+  return TRUE;
+}
+
+static gboolean
+parse_signed (const gchar        *token,
+              gint64              min,
+              gint64              max,
+              gint64             *result,
+              AST                *ast,
+              GVariantParseError *error)
+{
+  gint64 value;
+  gchar *end;
+
+  errno = 0;
+  value = g_ascii_strtoll (token, &end, 0);
+
+  if (errno == ERANGE || value < min || value > max)
+    {
+      ast_set_error (ast, error,
+                     "Signed integer '%s' is too large for type", token);
+      return FALSE;
+    }
+
+  if (*end)
+    {
+      ast_set_error (ast, error,
+                     "Unable to parse signed integer '%s'", token);
+      return FALSE;
+    }
+
+  *result = value;
+
+  return TRUE;
+}
+
+static gboolean
+parse_unsigned (const gchar        *token,
+                guint64             max,
+                guint64            *result,
+                AST                *ast,
+                GVariantParseError *error)
+{
+  guint64 value;
+  gchar *end;
+
+  /* g_ascii_strtoull handles '-' very poorly, so do it ourselves */
+  if (token[0] == '-')
+    goto error;
+
+  errno = 0;
+  value = g_ascii_strtoull (token, &end, 0);
+
+  if (errno == ERANGE || value > max)
+    {
+      ast_set_error (ast, error,
+                     "Unsigned integer '%s' is too large for type", token);
+      return FALSE;
+    }
+
+  if (*end)
+    {
+ error:
+      ast_set_error (ast, error,
+                     "Unable to parse unsigned integer '%s'", token);
+      return FALSE;
+    }
+
+  *result = value;
+
+  return TRUE;
+}
+
+static gboolean
+parse_floating (const gchar        *token,
+                gdouble            *result,
+                AST                *ast,
+                GVariantParseError *error)
+{
+  gdouble value;
+  gchar *end;
+
+  errno = 0;
+  value = g_ascii_strtod (token, &end);
+
+  if (errno == ERANGE)
+    {
+      ast_set_error (ast, error,
+                     "Floating point value '%s' is too large", token);
+      return FALSE;
+    }
+
+  if (*end)
+    {
+      ast_set_error (ast, error,
+                     "Unable to parse floating point value '%s'", token);
+      return FALSE;
+    }
+
+  *result = value;
+
+  return TRUE;
+}
+
+static GVariant *
+terminal_get_value (AST                *ast,
+                    const GVariantType *type,
+                    GVariantParseError *error)
+{
+  Terminal *terminal = (Terminal *) ast;
+
+  switch (g_variant_type_get_class (type))
+    {
+    case G_VARIANT_TYPE_CLASS_BOOLEAN:
+      {
+        ast_set_error (ast, error,
+                       "Unable to parse as boolean: %s", terminal->token);
+        return NULL;
+      }
+
+    case G_VARIANT_TYPE_CLASS_BYTE:
+      if (terminal->string != NULL)
+        {
+          if (strlen (terminal->string) != 1)
+            {
+              ast_set_error (ast, error,
+                             "Character constants must be one character");
+              return NULL;
+            }
+
+          return g_variant_new_byte (terminal->string[0]);
+        }
+      else
+        {
+          guint64 value;
+
+          if (!parse_unsigned (terminal->token, 255, &value, ast, error))
+            return NULL;
+
+          return g_variant_new_byte (value);
+        }
+
+    case G_VARIANT_TYPE_CLASS_INT16:
+      {
+        gint64 value;
+
+        if (!parse_signed (terminal->token,
+                           G_MININT16, G_MAXINT16,
+                           &value, ast, error))
+          return NULL;
+
+        return g_variant_new_int16 (value);
+      }
+
+    case G_VARIANT_TYPE_CLASS_UINT16:
+      {
+        guint64 value;
+
+        if (!parse_unsigned (terminal->token,
+                             G_MAXUINT16,
+                             &value, ast, error))
+          return NULL;
+
+        return g_variant_new_uint16 (value);
+      }
+
+    case G_VARIANT_TYPE_CLASS_INT32:
+      {
+        gint64 value;
+
+        if (!parse_signed (terminal->token,
+                           G_MININT32, G_MAXINT32,
+                           &value, ast, error))
+          return NULL;
+
+        return g_variant_new_int32 (value);
+      }
+
+    case G_VARIANT_TYPE_CLASS_UINT32:
+      {
+        guint64 value;
+
+        if (!parse_unsigned (terminal->token,
+                             G_MAXUINT32,
+                             &value, ast, error))
+          return NULL;
+
+        return g_variant_new_uint32 (value);
+      }
+
+    case G_VARIANT_TYPE_CLASS_INT64:
+      {
+        gint64 value;
+
+        if (!parse_signed (terminal->token,
+                           G_MININT64, G_MAXINT64,
+                           &value, ast, error))
+          return NULL;
+
+        return g_variant_new_int64 (value);
+      }
+
+    case G_VARIANT_TYPE_CLASS_UINT64:
+      {
+        guint64 value;
+
+        if (!parse_unsigned (terminal->token,
+                             G_MAXUINT64,
+                             &value, ast, error))
+          return NULL;
+
+        return g_variant_new_uint64 (value);
+      }
+
+    case G_VARIANT_TYPE_CLASS_DOUBLE:
+      {
+        gdouble floating;
+
+        if (!parse_floating (terminal->token, &floating, ast, error))
+          return NULL;
+
+        return g_variant_new_double (floating);
+      }
+
+    case G_VARIANT_TYPE_CLASS_STRING:
+      {
+        if (terminal->string == NULL)
+          {
+            ast_set_error (ast, error, "unable to parse as string");
+            return NULL;
+          }
+
+        return g_variant_new_string (terminal->string);
+      }
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static GSList *
+terminal_get_examples (AST         *ast,
+                       gint         index,
+                       Constraints *constraints)
+{
+  Terminal *terminal = (Terminal *) ast;
+  Constraints my_constraints;
+
+  g_assert_cmpint (index, ==, 0);
+
+  my_constraints = constraints_from_string (terminal->token);
+
+  if ((*constraints & my_constraints) == 0)
+    return NULL;
+
+  *constraints &= ~my_constraints;
+
+  return g_slist_prepend (NULL, terminal->token);
+}
+
+static void
+terminal_free (AST *ast)
+{
+  Terminal *terminal = (Terminal *) ast;
+
+  g_free (terminal->token);
+  g_slice_free (Terminal, terminal);
+}
+
+static AST *
+terminal_parse (TokenStream *stream,
+                va_list     *app)
+{
+  static const ASTClass terminal_class = {
+    terminal_get_pattern,
+    terminal_get_value,
+    terminal_get_examples,
+    terminal_free
+  };
+  Terminal *terminal;
+  gchar *string;
+  gchar *token;
+
+  token = token_stream_get (stream);
+
+  if (token[0] == '"' || token[0] == '\'')
+    {
+      if (!parse_string (token, &string, stream->this, &stream->error))
+        {
+          g_free (token);
+          return NULL;
+        }
+    }
+  else
+    string = NULL;
+
+  terminal = g_slice_new (Terminal);
+  terminal->ast.class = &terminal_class;
+  terminal->token = token;
+  terminal->string = string;
+
+  token_stream_next (stream);
+
+  return (AST *) terminal;
+}
+
+typedef struct
+{
+  AST ast;
+  gboolean value;
+} Boolean;
+
+static Pattern *
+boolean_get_pattern (AST                *ast,
+                     GVariantParseError *error)
+{
+  /* do this as a constraint so that
+   * we get more consistent errors
+   */
+  return pattern_from_constraints (CONSTRAINT_IS_BOOLEAN);
+}
+
+static GVariant *
+boolean_get_value (AST                *ast,
+                   const GVariantType *type,
+                   GVariantParseError *error)
+{
+  Boolean *boolean = (Boolean *) ast;
+
+  return g_variant_new_boolean (boolean->value);
+}
+
+static GSList *
+boolean_get_examples (AST         *ast,
+                      gint         index,
+                      Constraints *constraints)
+{
+  Boolean *boolean = (Boolean *) ast;
+  GSList *examples = NULL;
+
+  g_assert_cmpint (index, ==, 0);
+
+  if (*constraints & CONSTRAINT_IS_BOOLEAN)
+    {
+      *constraints &= ~CONSTRAINT_IS_BOOLEAN;
+
+      examples = g_slist_prepend (examples,
+                                  g_strdup (boolean->value ?
+                                            "true" : "false"));
+    }
+
+  return examples;
+}
+
+static void
+boolean_free (AST *ast)
+{
+  Boolean *boolean = (Boolean *) ast;
+
+  g_slice_free (Boolean, boolean);
+}
+
+static AST *
+boolean_new (gboolean value)
+{
+  static const ASTClass boolean_class = {
+    boolean_get_pattern,
+    boolean_get_value,
+    boolean_get_examples,
+    boolean_free
+  };
+  Boolean *boolean;
+
+  boolean = g_slice_new (Boolean);
+  boolean->ast.class = &boolean_class;
+  boolean->value = value;
+
+  return (AST *) boolean;
+}
+
+typedef struct
+{
+  AST ast;
+
+  GVariant *value;
+} Positional;
+
+static Pattern *
+positional_get_pattern (AST                *ast,
+                        GVariantParseError *error)
+{
+  Positional *positional = (Positional *) ast;
+
+  return pattern_from_type (g_variant_get_type (positional->value));
+}
+
+static GVariant *
+positional_get_value (AST                *ast,
+                      const GVariantType *type,
+                      GVariantParseError *error)
+{
+  Positional *positional = (Positional *) ast;
+  GVariant *tmp;
+
+  g_assert (positional->value != NULL);
+  g_assert (g_variant_matches (positional->value, type));
+
+  /* XXX if _get is called more than once then
+   * things get messed up with respect to floating refs.
+   */
+  tmp = positional->value;
+  positional->value = NULL;
+
+  return tmp;
+}
+
+static GSList *
+positional_get_examples (AST         *ast,
+                         gint         index,
+                         Constraints *constraints)
+{
+  g_assert_not_reached ();
+}
+
+static void
+positional_free (AST *ast)
+{
+  Positional *positional = (Positional *) ast;
+
+  if (positional->value)
+    g_variant_unref (g_variant_ref_sink (positional->value));
+  g_slice_free (Positional, positional);
+}
+
+static AST *
+positional_parse (TokenStream *stream,
+                  va_list     *app)
+{
+  static const ASTClass positional_class = {
+    positional_get_pattern,
+    positional_get_value,
+    positional_get_examples,
+    positional_free
+  };
+  Positional *positional;
+  const gchar *endptr;
+  gchar *token;
+
+  token = token_stream_get (stream);
+  g_assert (token[0] == '%');
+
+  positional = g_slice_new (Positional);
+  positional->ast.class = &positional_class;
+  positional->value = g_variant_new_va (NULL, token + 1, &endptr, app);
+
+  if (*endptr || positional->value == NULL)
+    {
+      set_error (&stream->error, stream->stream, stream->stream,
+                 "invalid GVariant format string");
+
+      if (positional->value != NULL)
+        g_variant_unref (g_variant_ref_sink (positional->value));
+
+      g_slice_free (Positional, positional);
+      g_free (token);
+
+      return NULL;
+    }
+
+  token_stream_next (stream);
+  g_free (token);
+
+  return (AST *) positional;
+}
+
+typedef struct
+{
+  AST ast;
+
+  GVariantType *type;
+  AST *child;
+} TypeDecl;
+
+static Pattern *
+typedecl_get_pattern (AST                *ast,
+                      GVariantParseError *error)
+{
+  TypeDecl *decl = (TypeDecl *) ast;
+
+  return pattern_from_type (decl->type);
+}
+
+static GVariant *
+typedecl_get_value (AST                *ast,
+                    const GVariantType *type,
+                    GVariantParseError *error)
+{
+  TypeDecl *decl = (TypeDecl *) ast;
+
+  return ast_get_value (decl->child, type, error);
+}
+
+static GSList *
+typedecl_get_examples (AST         *ast,
+                       gint         index,
+                       Constraints *constraints)
+{
+  g_assert_not_reached ();
+}
+
+static void
+typedecl_free (AST *ast)
+{
+  TypeDecl *decl = (TypeDecl *) ast;
+
+  ast_free (decl->child);
+  g_variant_type_free (decl->type);
+  g_slice_free (TypeDecl, decl);
+}
+
+static AST *
+typedecl_parse (TokenStream *stream,
+                va_list     *app)
+{
+  static const ASTClass typedecl_class = {
+    typedecl_get_pattern,
+    typedecl_get_value,
+    typedecl_get_examples,
+    typedecl_free
+  };
+  GVariantType *type;
+  TypeDecl *decl;
+  AST *child;
+
+  if (token_stream_peek (stream, '@'))
+    {
+      gchar *token;
+
+      token = token_stream_get (stream);
+
+      if (!g_variant_type_string_is_valid (token + 1))
+        {
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "Invalid type declaration");
+          g_free (token);
+
+          return NULL;
+        }
+
+      type = g_variant_type_new (token + 1);
+
+      if (!g_variant_type_is_concrete (type))
+        {
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "Type declarations must be concrete");
+          g_variant_type_free (type);
+          g_free (token);
+
+          return NULL;
+        }
+
+      token_stream_next (stream);
+      g_free (token);
+    }
+  else
+    {
+      if (token_stream_consume (stream, "boolean"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
+
+      else if (token_stream_consume (stream, "byte"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_BYTE);
+
+      else if (token_stream_consume (stream, "int16"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_INT16);
+
+      else if (token_stream_consume (stream, "uint16"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_UINT16);
+
+      else if (token_stream_consume (stream, "int32"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_INT32);
+
+      else if (token_stream_consume (stream, "uint32"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_UINT32);
+
+      else if (token_stream_consume (stream, "int64"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_INT64);
+
+      else if (token_stream_consume (stream, "uint64"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
+
+      else if (token_stream_consume (stream, "double"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
+
+      else if (token_stream_consume (stream, "string"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_STRING);
+
+      else if (token_stream_consume (stream, "objectpath"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_OBJECT_PATH);
+
+      else if (token_stream_consume (stream, "signature"))
+        type = g_variant_type_copy (G_VARIANT_TYPE_SIGNATURE);
+
+      else
+        {
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "unknown keyword");
+
+          return NULL;
+        }
+    }
+
+  if ((child = parse (stream, app)) == NULL)
+    {
+      g_variant_type_free (type);
+      return NULL;
+    }
+
+  decl = g_slice_new (TypeDecl);
+  decl->ast.class = &typedecl_class;
+  decl->type = type;
+  decl->child = child;
+
+  return (AST *) decl;
+}
+
+static AST *
+parse (TokenStream *stream,
+       va_list     *app)
+{
+  const gchar *start;
+  AST *result;
+
+  token_stream_prepare (stream);
+  start = stream->this;
+
+  if (token_stream_peek (stream, '['))
+    result = array_parse (stream, app);
+
+  else if (token_stream_peek (stream, '('))
+    result = tuple_parse (stream, app);
+
+  else if (token_stream_peek (stream, '<'))
+    result = variant_parse (stream, app);
+
+  else if (token_stream_peek (stream, '{'))
+    result = dictionary_parse (stream, app);
+
+  else if (app && token_stream_peek (stream, '%'))
+    result = positional_parse (stream, app);
+
+  else if (token_stream_consume (stream, "true"))
+    result = boolean_new (TRUE);
+
+  else if (token_stream_consume (stream, "false"))
+    result = boolean_new (FALSE);
+
+  else if (token_stream_peek (stream, '@') || token_stream_is_keyword (stream))
+    result = typedecl_parse (stream, app);
+
+  else if (token_stream_is_numeric (stream) ||
+           token_stream_peek (stream, '\'') ||
+           token_stream_peek (stream, '"'))
+    result = terminal_parse (stream, app);
+
+  else
+    {
+      gchar *token = token_stream_get (stream);
+
+      if (token[0])
+        {
+          gchar *escaped = g_strescape (token, 0);
+          set_error (&stream->error, stream->this, stream->stream,
+                     "stray symbol");
+          g_free (escaped);
+        }
+      else
+        {
+          set_error (&stream->error, stream->stream, stream->stream,
+                     "expected value");
+        }
+
+      g_free (token);
+
+      return NULL;
+    }
+
+  if (result != NULL)
+    {
+      result->start = start;
+      result->end = stream->stream;
+    }
+
+  return result;
+}
+
+/**
+ * g_variant_parse_full:
+ * @text: a string containing a GVariant in text form
+ * @limit: a pointer to the end of @text, or %NULL
+ * @endptr: a location to store the end pointer, or %NULL
+ * @type: a #GVariantType, or %NULL
+ * @error: an #GVariantParseError, or %NULL
+ * @Returns: a reference to a #GVariant, or %NULL
+ *
+ * Parses a #GVariant from a text representation.  This is the
+ * generalised form of g_variant_parse().  See that call for a simpler
+ * interface.
+ *
+ * A single #GVariant is parsed from the content of @text.
+ *
+ * The memory at @limit will never be accessed and the parser behaves as
+ * if the character at @limit is the nul terminator.  This has the
+ * effect of bounding @text.
+ *
+ * If @endptr is non-%NULL then @text is permitted to contain data
+ * following the value that this function parses and @endptr will be
+ * updated to point to the first character past the end of the text
+ * parsed by this function.  If @endptr is %NULL and there is extra data
+ * then an error is returned.
+ *
+ * If @type is non-%NULL then the value will be parsed to have that
+ * type.  This may result in additional parse errors (in the case that
+ * the parsed value doesn't fit the type) but may also result in fewer
+ * errors (in the case that the type would have been ambiguous, such as
+ * with empty arrays).
+ *
+ * In the event that the parsing is successful, the resulting #GVariant
+ * is returned.
+ *
+ * In case of any error, %NULL will be returned.  If @error is non-%NULL
+ * then it will be set to reflect the error that occured.
+ **/
+GVariant *
+g_variant_parse_full (const gchar         *text,
+                      const gchar         *limit,
+                      const gchar        **endptr,
+                      const GVariantType  *type,
+                      GVariantParseError  *error)
+{
+  TokenStream stream = {  };
+  GVariant *result = NULL;
+  AST *ast;
+
+  g_return_val_if_fail (text != NULL, NULL);
+  g_return_val_if_fail (text == limit || text != NULL, NULL);
+
+  stream.stream = text;
+  stream.end = limit;
+
+  if ((ast = parse (&stream, NULL)))
+    {
+      if (type == NULL)
+        result = ast_resolve (ast, &stream.error);
+      else
+        result = ast_get_value (ast, type, &stream.error);
+
+      if (result != NULL)
+        {
+          if (endptr == NULL)
+            {
+              while (stream.stream != limit &&
+                     g_ascii_isspace (*stream.stream))
+                stream.stream++;
+
+              if (stream.stream != limit && *stream.stream != '\0')
+                {
+                  set_error (&stream.error, text, text,
+                             "trailing text after value");
+                  g_variant_ref_sink (result);
+                  g_variant_unref (result);
+
+                  result = NULL;
+                }
+            }
+          else
+            *endptr = stream.stream;
+        }
+
+      ast_free (ast);
+    }
+
+  if (error != NULL)
+    *error = stream.error;
+
+  return result;
+}
+
+GVariant *
+g_variant_new_parsed_va (const gchar *format,
+                         va_list     *app)
+{
+  TokenStream stream = {  };
+  GVariant *result = NULL;
+  AST *ast;
+
+  g_return_val_if_fail (format != NULL, NULL);
+  g_return_val_if_fail (app != NULL, NULL);
+
+  stream.stream = format;
+  stream.end = NULL;
+
+  if ((ast = parse (&stream, app)))
+    {
+      result = ast_resolve (ast, &stream.error);
+      ast_free (ast);
+    }
+
+  if (result != NULL)
+    {
+      while (g_ascii_isspace (*stream.stream))
+        stream.stream++;
+
+      if (*stream.stream)
+        g_critical ("g_variant_new_parsed: trailing text after value");
+    }
+  else
+    {
+      g_critical ("g_variant_new_parsed: %s", stream.error.error);
+      g_free (stream.error.error);
+    }
+
+  return result;
+}
+
+GVariant *
+g_variant_new_parsed (const gchar *format,
+                      ...)
+{
+  GVariant *result;
+  va_list ap;
+
+  va_start (ap, format);
+  result = g_variant_new_parsed_va (format, &ap);
+  va_end (ap);
+
+  return result;
+}
+
+GVariant *
+g_variant_parse (const gchar         *text,
+                 gint                 text_length,
+                 const GVariantType  *type,
+                 GError             **error)
+{
+  GVariantParseError parse_error;
+  GVariant *result;
+  const gchar *end;
+
+  g_return_val_if_fail (text != NULL || text_length == 0, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  if (text_length >= 0)
+    end = text + text_length;
+  else
+    end = NULL;
+
+  if (!(result = g_variant_parse_full (text, end, NULL, type, &parse_error)))
+    {
+      g_set_error (error, 0, 0, "%s", parse_error.error);
+      g_free (parse_error.error);
+    }
+
+  return result;
+}
+
+#define __G_VARIANT_PARSER_C__
+#include "galiasdef.c"
diff --git a/glib/gvariant-printer.c b/glib/gvariant-printer.c
new file mode 100644
index 0000000..63d6967
--- /dev/null
+++ b/glib/gvariant-printer.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright © 2009, Sam Thursfield
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "galias.h"
+
+/**
+ * g_variant_print:
+ * @value: a #GVariant
+ * @type_annotate: %TRUE if type information should be included in
+ *                 the output
+ * @returns: a newly-allocated string holding the result.
+ * 
+ * Pretty-prints @value in the format understood by g_variant_parse().
+ *
+ * If @type_annotate is %TRUE, then type information is included in
+ * the output.
+ */
+gchar *
+g_variant_print (GVariant *value,
+                 gboolean type_annotate)
+{
+  return g_string_free (g_variant_print_string (value, NULL, type_annotate),
+                        FALSE);
+};
+
+/**
+ * g_variant_print_string:
+ * @value: a #GVariant
+ * @string: a #GString, or %NULL
+ * @type_annotate: %TRUE if type information should be included in
+ *                 the output
+ * @returns: a #GString containing the string
+ *
+ * Behaves as g_variant_print(), but operates on a #GString.
+ *
+ * If @string is non-%NULL then it is appended to and returned.  Else,
+ * a new empty #GString is allocated and it is returned.
+ **/
+GString *
+g_variant_print_string (GVariant *value,
+                        GString *string,
+                        gboolean type_annotate)
+{
+  const GVariantType *type;
+
+  type = g_variant_get_type (value);
+
+  if G_UNLIKELY (string == NULL)
+      string = g_string_new (NULL);
+
+  switch (g_variant_get_type_class (value))
+  {
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      {
+        GVariantIter iter;
+
+        if (g_variant_iter_init (&iter, value))
+          {
+            GVariant *element;
+
+            g_string_append_c (string, '[');
+
+            /* only type annotate the first element (if requested) */
+            while ((element = g_variant_iter_next_value (&iter)))
+              {
+                g_variant_print_string (element, string, type_annotate);
+                g_string_append (string, ", ");
+                type_annotate = FALSE;
+              }
+            g_string_truncate (string, string->len - 2);
+
+            g_string_append_c (string, ']');
+          }
+        else
+          {
+            /* if there are no elements then we must type
+             * annotate the array itself (if requested)
+             */
+            if (type_annotate)
+              g_string_append_printf (string, "@%s ",
+                                      g_variant_get_type_string (value));
+            g_string_append (string, "[]");
+          }
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      {
+        GVariant *child = g_variant_get_variant (value);
+
+        /* Always annotate types in nested variants, because they are
+         * (by nature) of variable type. 
+         */
+        g_string_append_c (string, '<');
+        g_variant_print_string (child, string, TRUE);
+        g_string_append_c (string, '>');
+
+        g_variant_unref (child);
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+      {
+        GVariantIter iter;
+
+        g_string_append_c (string, '(');
+        if (g_variant_iter_init (&iter, value))
+          {
+            GVariant *element;
+
+            while ((element = g_variant_iter_next_value (&iter)))
+              {
+                g_variant_print_string (element, string, type_annotate);
+                g_string_append (string, ", ");
+              }
+            g_string_truncate (string, string->len - 2);
+          }
+        g_string_append_c (string, ')');
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      {
+        GVariantIter iter;
+        GVariant *element;
+
+        g_string_append_c (string, '{');
+
+        if (g_variant_iter_init (&iter, value))
+          {
+            while ((element = g_variant_iter_next_value (&iter)))
+              {
+                g_variant_print_string (element, string, type_annotate);
+
+                g_string_append_c (string, ':');
+
+                element = g_variant_iter_next_value (&iter);
+                g_variant_print_string (element, string, type_annotate);
+                g_string_append (string, ", ");
+              }
+            g_string_truncate (string, string->len - 2);
+
+            g_string_append_c (string, '}');
+          }
+
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_BOOLEAN:
+      if (g_variant_get_boolean (value))
+        g_string_append (string, "true");
+      else
+        g_string_append (string, "false");
+      break;
+
+    /*case G_VARIANT_TYPE_CLASS_MAYBE:
+      {
+        if (g_variant_n_children (value))
+          {
+            GVariant *element;
+
+            g_string_append (string, "<maybe>");
+            g_variant_markup_newline (string, newlines);
+
+            element = g_variant_get_child_value (value, 0);
+            g_variant_markup_print (element, string,
+                                    newlines, indentation, tabstop);
+            g_variant_unref (element);
+
+            g_variant_markup_indent (string, indentation - tabstop);
+            g_string_append (string, "</maybe>");
+          }
+        else
+          g_string_append_printf (string, "<nothing type='%s'/>",
+                                  g_variant_get_type_string (value));
+
+        break;
+      }*/
+
+    case G_VARIANT_TYPE_CLASS_STRING:
+      {
+        const gchar *str = g_variant_get_string (value, NULL);
+        gchar *escaped = g_strescape (str, NULL);
+
+        g_string_append_printf (string, "\"%s\"", escaped);
+
+        g_free (escaped);
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_BYTE:
+      if (type_annotate)
+        g_string_append (string, "byte ");
+      g_string_append_printf (string, "0x%02x",
+                              g_variant_get_byte (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_INT16:
+      if (type_annotate)
+        g_string_append (string, "int16 ");
+      g_string_append_printf (string, "%"G_GINT16_FORMAT,
+                              g_variant_get_int16 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT16:
+      if (type_annotate)
+        g_string_append (string, "uint16 ");
+      g_string_append_printf (string, "%"G_GUINT16_FORMAT,
+                              g_variant_get_uint16 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_INT32:
+      /* Never annotate this type because it is the default for numbers
+       * (and this is a *pretty* printer)
+       */
+      g_string_append_printf (string, "%"G_GINT32_FORMAT,
+                              g_variant_get_int32 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT32:
+      if (type_annotate)
+        g_string_append (string, "uint32 ");
+      g_string_append_printf (string, "%"G_GUINT32_FORMAT,
+                              g_variant_get_uint32 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_INT64:
+      if (type_annotate)
+        g_string_append (string, "int64 ");
+      g_string_append_printf (string, "%"G_GINT64_FORMAT,
+                              g_variant_get_int64 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT64:
+      if (type_annotate)
+        g_string_append (string, "uint64 ");
+      g_string_append_printf (string, "%"G_GUINT64_FORMAT,
+                              g_variant_get_uint64 (value));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_DOUBLE:
+      {
+        gchar buffer[100];
+        gint i;
+
+        g_ascii_dtostr (buffer, sizeof buffer, g_variant_get_double (value));
+
+        for (i = 0; buffer[i]; i++)
+          if (buffer[i] == '.' || buffer[i] == 'e' ||
+              buffer[i] == 'n' || buffer[i] == 'N')
+            break;
+
+        /* if there is no '.' or 'e' in the float then add one */
+        if (buffer[i] == '\0')
+          {
+            buffer[i++] = '.';
+            buffer[i++] = '0';
+            buffer[i++] = '\0';
+          }
+
+        g_string_append (string, buffer);
+        break;
+      }
+
+    case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+      if (type_annotate)
+        g_string_append (string, "objectpath ");
+      g_string_append_printf (string, "\"%s\"",
+                              g_variant_get_string (value, NULL));
+      break;
+
+    case G_VARIANT_TYPE_CLASS_SIGNATURE:
+      if (type_annotate)
+        g_string_append (string, "signature");
+      g_string_append_printf (string, "\"%s\"",
+                              g_variant_get_string (value, NULL));
+      break;
+
+    default:
+      g_error ("g_variant_print: sorry... not handled yet: %s",
+               g_variant_get_type_string (value));
+  }
+
+  return string;
+};
+
+#define __G_VARIANT_PRINTER_C__
+#include "galiasdef.c"
diff --git a/glib/gvariant-private.h b/glib/gvariant-private.h
new file mode 100644
index 0000000..dd38c6d
--- /dev/null
+++ b/glib/gvariant-private.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_VARIANT_PRIVATE_H__
+#define __G_VARIANT_PRIVATE_H__
+
+#include "gvarianttypeinfo.h"
+#include "gvariant.h"
+
+/* gvariant-core.c */
+GVariant *                      g_variant_new_tree                      (const GVariantType  *type,
+                                                                         GVariant           **children,
+                                                                         gsize                n_children,
+                                                                         gboolean             trusted);
+void                            g_variant_assert_invariant              (GVariant            *value);
+gboolean                        g_variant_is_trusted                    (GVariant            *value);
+void                            g_variant_dump_data                     (GVariant            *value);
+GVariant *                      g_variant_load_fixed                    (const GVariantType  *type,
+                                                                         gconstpointer        data,
+                                                                         gsize                n_items);
+gboolean                        g_variant_iter_should_free              (GVariantIter        *iter);
+GVariant *                      g_variant_deep_copy                     (GVariant            *value);
+
+/* do not use -- only for test cases */
+gboolean                        g_variant_is_normal_                    (GVariant            *value);
+
+#endif /* __G_VARIANT_PRIVATE_H__ */
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
new file mode 100644
index 0000000..82f394b
--- /dev/null
+++ b/glib/gvariant-serialiser.c
@@ -0,0 +1,1262 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gvariant-serialiser.h"
+
+#include <glib/gtestutils.h>
+
+#include <string.h>
+
+#include "galias.h"
+
+#ifndef GSIZE_TO_LE
+#if GLIB_SIZEOF_SIZE_T == 4
+# define GSIZE_TO_LE GUINT32_TO_LE
+#else
+# define GSIZE_TO_LE GUINT64_TO_LE
+#endif
+#define GSIZE_FROM_LE GSIZE_TO_LE
+#endif
+
+static gsize
+g_variant_serialiser_determine_size (gsize    content_end,
+                                     gsize    offsets,
+                                     gboolean non_zero)
+{
+  if (!non_zero && content_end == 0)
+    return 0;
+
+  if (content_end + offsets <= G_MAXUINT8)
+    return content_end + offsets;
+
+  if (content_end + offsets * 2 <= G_MAXUINT16)
+    return content_end + offsets * 2;
+
+  if (content_end + offsets * 4 <= G_MAXUINT32)
+    return content_end + offsets * 4;
+
+  return content_end + offsets * 8;
+}
+
+static guint
+g_variant_serialiser_offset_size (GVariantSerialised container)
+{
+  if (container.size == 0)
+    return 0;
+
+  if (container.size <= G_MAXUINT8)
+    return 1;
+
+  if (container.size <= G_MAXUINT16)
+    return 2;
+
+  if (container.size <= G_MAXUINT32)
+    return 4;
+
+  return 8;
+}
+
+#define do_case(_ofs_size, body) \
+  {                                                             \
+    guint offset_size = (_ofs_size);                            \
+    body                                                        \
+  }
+
+#define check_case(max, _ofs_size, body) \
+  if (container.size <= max)                                    \
+    do_case (_ofs_size, body)
+
+#define check_cases(init_val, zero_case, else_case, use_val) \
+  G_STMT_START {                                                \
+    guchar *bytes;                                              \
+    union                                                       \
+    {                                                           \
+      guchar bytes[GLIB_SIZEOF_SIZE_T];                         \
+      gsize integer;                                            \
+    } tmpvalue;                                                 \
+                                                                \
+    bytes = container.data + container.size;                    \
+    tmpvalue.integer = init_val;                                \
+                                                                \
+    if (container.size == 0) { zero_case }                      \
+    else check_case (G_MAXUINT8, 1, else_case)                  \
+    else check_case (G_MAXUINT16, 2, else_case)                 \
+    else check_case (G_MAXUINT32, 4, else_case)                 \
+    else do_case (8, else_case)                                 \
+    use_val;                                                    \
+  } G_STMT_END
+
+static gboolean
+g_variant_serialiser_dereference (GVariantSerialised container,
+                                  gsize              index,
+                                  gsize             *result)
+{
+  check_cases (0,,
+               {
+                 if (index >= container.size / offset_size)
+                   return FALSE;
+
+                 bytes -= (index + 1) * offset_size;
+                 memcpy (tmpvalue.bytes, bytes, offset_size);
+               },
+               *result = GSIZE_TO_LE (tmpvalue.integer));
+
+  return G_LIKELY (*result <= container.size);
+}
+
+static gboolean
+g_variant_serialiser_array_length (GVariantSerialised  container,
+                                   gsize              *length_ret)
+{
+  gsize divider;
+
+  check_cases (0, g_assert_not_reached ();,
+               {
+                 bytes -= offset_size;
+                 memcpy (tmpvalue.bytes, bytes, offset_size);
+                 divider = offset_size;
+               },
+               {
+                 gsize length;
+
+                 length = GSIZE_TO_LE (tmpvalue.integer);
+
+                 if (length > container.size)
+                   return FALSE;
+
+                 length = container.size - length;
+                 if G_UNLIKELY (length % divider != 0)
+                   return FALSE;
+
+                 *length_ret = length / divider;
+               });
+
+  return TRUE;
+}
+
+
+gsize
+g_variant_serialised_n_children (GVariantSerialised container)
+{
+  g_variant_serialised_assert_invariant (container);
+
+  switch (g_variant_type_info_get_type_class (container.type))
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      return 1;
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+      return g_variant_type_info_n_members (container.type);
+
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      return 2;
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      {
+        gsize size;
+
+        if (container.size == 0)
+          return 0;
+
+        g_variant_type_info_query_element (container.type, NULL, &size);
+
+        if (size && size != container.size)
+          break;
+
+        return 1;
+      }
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      {
+        gsize fixed_size;
+
+        /* an array with a length of zero always has a size of zero.
+         * an array with a size of zero always has a length of zero.
+         */
+        if (container.size == 0)
+          return 0;
+
+        g_variant_type_info_query_element (container.type, NULL, &fixed_size);
+
+        if (!fixed_size)
+          /* case where array contains variable-sized elements
+           * or fixed-size elements of size 0 (treated as variable)
+           */
+          {
+            gsize length;
+
+            if G_UNLIKELY (!g_variant_serialiser_array_length (container, &length))
+              break;
+
+            return length;
+         }
+        else
+          /* case where array contains fixed-sized elements */
+          {
+            if G_UNLIKELY (container.size % fixed_size > 0)
+              break;
+
+            return container.size / fixed_size;
+          }
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+
+  return 0;
+}
+
+/*
+ * g_variant_serialised_get_child:
+ * @container: a #GVariantSerialised
+ * @index: the index of the child to fetch
+ * @returns: a #GVariantSerialised for the child
+ *
+ * Extracts a child from a serialised container.
+ *
+ * It is an error to call this function with an index out of bounds.
+ *
+ * If the result .data == %NULL and .size > 0 then there has been an
+ * error extracting the requested fixed-sized value.  This number of
+ * zero bytes needs to be allocated instead.
+ *
+ * This function will never return .size == 0 and .data != NULL.
+ */
+GVariantSerialised
+g_variant_serialised_get_child (GVariantSerialised container,
+                                gsize              index)
+{
+  g_variant_serialised_assert_invariant (container);
+
+  switch (g_variant_type_info_get_type_class (container.type))
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      {
+        GVariantSerialised child = { NULL, container.data, container.size };
+
+        if G_UNLIKELY (index > 0)
+          break;
+
+        /* find '\0' character */
+        while (child.size && container.data[--child.size]);
+
+        /* the loop can finish for two reasons.
+         * 1) we found a '\0'.   ((good.))
+         * 2) we hit the start.  ((only good if there's a '\0' there))
+         */
+        if (container.size && container.data[child.size] == '\0')
+          {
+            gchar *str = (gchar *) container.data + child.size + 1;
+
+            /* in the case that we're accessing a shared memory buffer,
+             * someone could change the  string under us and cause us
+             * to access out-of-bounds memory.
+             *
+             * if we carefully make our own copy then we avoid that.
+             */
+            str = g_strndup (str, container.size - child.size - 1);
+            if (g_variant_type_string_is_valid (str))
+              {
+                const GVariantType *type = G_VARIANT_TYPE (str);
+
+                if (g_variant_type_is_concrete (type))
+                  child.type = g_variant_type_info_get (type);
+              }
+
+            g_free (str);
+          }
+
+        if G_UNLIKELY (child.type == NULL)
+          {
+            child.type = g_variant_type_info_get (G_VARIANT_TYPE_UNIT);
+            child.data = NULL;
+            child.size = 1;
+
+            return child;
+          }
+
+        {
+          gsize fixed_size;
+
+          g_variant_type_info_query (child.type, NULL, &fixed_size);
+
+          if G_UNLIKELY (fixed_size && child.size != fixed_size)
+            {
+              child.size = fixed_size;
+              child.data = NULL;
+            }
+        }
+
+        if (child.size == 0)
+          child.data = NULL;
+
+        return child;
+      }
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      {
+        GVariantSerialised child;
+        gsize fixed_size;
+
+        child.type = g_variant_type_info_element (container.type);
+        g_variant_type_info_ref (child.type);
+        child.data = container.data;
+
+        if G_UNLIKELY (container.size == 0 || index > 0)
+          break;
+
+        g_variant_type_info_query (child.type, NULL, &fixed_size);
+
+        if (fixed_size)
+          {
+            /* if this doesn't match then we are considered
+             * 'Nothing', so there is no child to get...
+             */
+            if G_UNLIKELY (container.size != fixed_size)
+              break;
+
+            /* fixed size: child fills entire container */
+            child.size = container.size;
+          }
+        else
+          /* variable size: remove trailing '\0' marker */
+          child.size = container.size - 1;
+
+        return child;
+      }
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      {
+        GVariantSerialised child;
+        gsize fixed_size;
+        guint alignment;
+
+        child.type = g_variant_type_info_element (container.type);
+        g_variant_type_info_ref (child.type);
+
+        g_variant_type_info_query (child.type, &alignment, &fixed_size);
+
+        if (fixed_size)
+          {
+            if G_UNLIKELY (container.size % fixed_size != 0 ||
+                           fixed_size * (index + 1) > container.size)
+              break;
+           
+            child.data = container.data + fixed_size * index;
+            child.size = fixed_size; 
+
+            return child;
+          }
+
+        else
+          {
+            gsize boundary = 0, start = 0, end = 0;
+            guint offset_size;
+
+            offset_size = g_variant_serialiser_offset_size (container);
+            memcpy (&boundary,
+                    container.data + container.size - offset_size,
+                    offset_size);
+            boundary = GSIZE_FROM_LE (boundary);
+            
+            if G_UNLIKELY (boundary > container.size ||
+                          (container.size - boundary) % offset_size ||
+                          boundary + index * offset_size >= container.size)
+              break;
+
+            if (index)
+              {
+                memcpy (&start,
+                        container.data + boundary + (index - 1) * offset_size,
+                        offset_size);
+                start = GSIZE_FROM_LE (start);
+              }
+
+            memcpy (&end,
+                    container.data + boundary + index * offset_size,
+                    offset_size);
+            end = GSIZE_FROM_LE (end);
+
+            start += (-start) & alignment;
+
+            if (start < end && end <= container.size)
+              {
+                child.data = container.data + start;
+                child.size = end - start;
+              }
+            else
+              {
+                child.data = NULL;
+                child.size = 0;
+              }
+          }
+
+        return child;
+      }
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      {
+        const GVariantMemberInfo *info;
+        GVariantSerialised child;
+        gsize start = 0, end = 0;
+        gsize n_children;
+        guint offset_size;
+
+        offset_size = g_variant_serialiser_offset_size (container);
+        n_children = g_variant_type_info_n_members (container.type);
+        info = g_variant_type_info_member_info (container.type, index);
+        if (info == NULL)
+          break;
+
+        child.type = g_variant_type_info_ref (info->type);
+        g_variant_type_info_query (info->type, NULL, &child.size);
+        child.data = NULL;
+
+        if (info->i != -1l)
+          {
+            if (offset_size * (info->i + 1) > container.size)
+              return child;
+
+            memcpy (&start,
+                    container.data + container.size - offset_size * (info->i + 1),
+                    offset_size);
+            start = GSIZE_FROM_LE (start);
+          }
+
+        start += info->a;
+        start &= info->b;
+        start |= info->c;
+
+        if (child.size)
+          {
+            if (start + child.size <= container.size)
+              child.data = container.data + start;
+
+            return child;
+          }
+
+        if (index == n_children - 1)
+          end = container.size - offset_size * (info->i + 1);
+        else
+          {
+            if (offset_size * (info->i + 2) > container.size)
+              return child;
+
+            memcpy (&end,
+                    container.data + container.size - offset_size * (info->i + 2),
+                    offset_size);
+            end = GSIZE_FROM_LE (end);
+          }
+
+        if (start < end && end <= container.size)
+          {
+            child.data = container.data + start;
+            child.size = end - start;
+          }
+
+        return child;
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+
+  g_error ("Attempt to access item %"G_GSIZE_FORMAT
+           " in a container with only %"G_GSIZE_FORMAT" items",
+           index, g_variant_serialised_n_children (container));
+}
+
+void
+g_variant_serialiser_serialise (GVariantSerialised        container,
+                                GVariantSerialisedFiller  gvs_filler,
+                                const gpointer           *children,
+                                gsize                     n_children)
+{
+  switch (g_variant_type_info_get_type_class (container.type))
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      {
+        GVariantSerialised child = { NULL, container.data, 0 };
+        const gchar *type_string;
+        gsize type_length;
+
+        g_assert_cmpint (n_children, ==, 1);
+
+        /* write the data for the child.
+         * side effect: child.type and child.size are set
+         */
+        gvs_filler (&child, children[0]);
+
+        type_string = g_variant_type_info_get_string (child.type);
+        type_length = strlen (type_string);
+
+        /* write the separator byte */
+        container.data[child.size] = '\0';
+
+        /* and the type string */
+        memcpy (container.data + child.size + 1, type_string, type_length);
+
+        /* make sure that it all adds up */
+        g_assert_cmpint (child.size + 1 + type_length, ==, container.size);
+
+        return;
+      }
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      {
+        g_assert_cmpint (n_children, ==, (container.size > 0));
+
+        if (n_children)
+          {
+            GVariantSerialised child = { NULL, container.data, 0 };
+            gsize fixed_size;
+
+            child.type = g_variant_type_info_element (container.type);
+            g_variant_type_info_query (child.type, NULL, &fixed_size);
+
+            /* write the data for the child.
+             * side effect: asserts child.type matches
+             * also: sets child.size
+             */
+            gvs_filler (&child, children[0]);
+
+            if (fixed_size)
+              /* for all fixed sized children, double check */
+              g_assert_cmpint (fixed_size, ==, child.size);
+            else
+              /* for variable-width children, add a pad byte */
+              container.data[child.size++] = '\0';
+
+            g_assert_cmpint (child.size, ==, container.size);
+          }
+
+        return;
+      }
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      {
+        g_assert_cmpint ((n_children > 0), ==, (container.size > 0));
+
+        if (n_children)
+          {
+            GVariantSerialised child = { NULL, container.data, 0 };
+            gsize offset, offsets_boundary, offset_ptr;
+            guint offset_size, align;
+            gsize fixed_size;
+
+            child.type = g_variant_type_info_element (container.type);
+            g_variant_type_info_query (child.type, &align, &fixed_size);
+            offset_size = g_variant_serialiser_offset_size (container);
+
+            if (!fixed_size)
+              offsets_boundary = container.size - offset_size * n_children;
+            else
+              offsets_boundary = container.size;
+            offset_ptr = offsets_boundary;
+
+            offset = 0;
+            while (n_children--)
+              {
+                child.size = 0;
+                child.data = container.data + offset + ((-offset) & align);
+                gvs_filler (&child, *children++);
+                while (child.size &&& container.data[offset] != child.data)
+                  container.data[offset++] = '\0';
+                offset += child.size;
+
+                if (!fixed_size)
+                  {
+                    gsize le_offset = GSIZE_TO_LE (offset);
+
+                    memcpy (&container.data[offset_ptr],
+                            &le_offset, offset_size);
+                    offset_ptr += offset_size;
+                  }
+              }
+
+            g_assert (offset_ptr == container.size &&
+                      offset == offsets_boundary);
+          }
+
+        return;
+      }
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      {
+        if (n_children)
+          {
+            gsize offset, offsets_boundary, offset_ptr;
+            const GVariantMemberInfo *info;
+            guint offset_size, align;
+            gsize fixed_size;
+            gboolean fixed;
+
+            info = g_variant_type_info_member_info (container.type, 0);
+            offset_size = g_variant_serialiser_offset_size (container);
+
+            offset = 0;
+            fixed = TRUE;
+            offsets_boundary = offset_ptr = container.size;
+            offsets_boundary -= (info[n_children - 1].i + 1) * offset_size;
+
+            while (n_children--)
+              {
+                GVariantSerialised child = { info++->type };
+
+                g_variant_type_info_query (child.type, &align, &fixed_size);
+                child.data = container.data + offset + ((-offset) & align);
+                gvs_filler (&child, *children++);
+
+                while (child.size &&& container.data[offset] != child.data)
+                  container.data[offset++] = '\0';
+                offset += child.size;
+
+                if (!fixed_size)
+                  {
+                    fixed = FALSE;
+
+                    if (n_children)
+                      {
+                        gsize le_offset = GSIZE_TO_LE (offset);
+                        
+                        offset_ptr -= offset_size;
+                        memcpy (&container.data[offset_ptr],
+                                &le_offset, offset_size);
+                      }
+                  }
+              }
+
+            g_variant_type_info_query (container.type, &align, &fixed_size);
+
+            if (fixed)
+              {
+                while (offset & align)
+                  container.data[offset++] = '\0';
+
+                g_assert_cmpint (offsets_boundary, ==, container.size);
+                g_assert_cmpint (offset, ==, fixed_size);
+              }
+            else
+              g_assert_cmpint (fixed_size, ==, 0);
+
+            g_assert_cmpint (offset, ==, offsets_boundary);
+            g_assert_cmpint (offset_ptr, ==, offsets_boundary);
+          }
+        else
+          {
+            /* () */
+            g_assert_cmpint (container.size, ==, 1);
+            container.data[0] = '\0';
+          }
+
+        return;
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+gsize
+g_variant_serialiser_needed_size (GVariantTypeInfo         *type,
+                                  GVariantSerialisedFiller  gvs_filler,
+                                  const gpointer           *children,
+                                  gsize                     n_children)
+{
+  switch (g_variant_type_info_get_type_class (type))
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      {
+        GVariantSerialised child = {};
+        const gchar *type_string;
+
+        g_assert_cmpint (n_children, ==, 1);
+        gvs_filler (&child, children[0]);
+
+        type_string = g_variant_type_info_get_string (child.type);
+
+        return child.size + 1 + strlen (type_string);
+      }
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      {
+        GVariantSerialised child = { g_variant_type_info_element (type) };
+        gsize fixed_size;
+
+        g_assert_cmpint (n_children, <=, 1);
+
+        if (n_children == 0)
+          return 0;
+
+        gvs_filler (&child, children[0]);
+        g_variant_type_info_query (child.type, NULL, &fixed_size);
+
+        if (fixed_size)
+          /* double check */
+          g_assert (child.size == fixed_size);
+        else
+          /* room for the padding byte */
+          child.size++;
+
+        return child.size;
+      }
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      {
+        GVariantTypeInfo *elem_type;
+        gsize fixed_size;
+        guint alignment;
+
+        if (n_children == 0)
+          return 0;
+
+        elem_type = g_variant_type_info_element (type);
+        g_variant_type_info_query (elem_type, &alignment, &fixed_size);
+
+        if (!fixed_size)
+          {
+            gsize offset;
+            gsize i;
+
+            offset = 0;
+
+            for (i = 0; i < n_children; i++)
+              {
+                GVariantSerialised child = {};
+
+                gvs_filler (&child, children[i]);
+                g_assert (child.type == elem_type);
+
+                if (child.size)
+                  offset += (-offset) & alignment;
+                offset += child.size;
+              }
+
+            return g_variant_serialiser_determine_size (offset,
+                                                        n_children,
+                                                        TRUE);
+          }
+        else
+          return fixed_size * n_children;
+      }
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      {
+        gsize n_offsets;
+        gsize offset;
+        gsize i;
+
+        g_assert_cmpint (g_variant_type_info_n_members (type), ==, n_children);
+
+        {
+          gsize fixed_size;
+
+          g_variant_type_info_query (type, NULL, &fixed_size);
+
+          if (fixed_size)
+            return fixed_size;
+        }
+
+        n_offsets = 0;
+        offset = 0;
+
+        for (i = 0; i < n_children; i++)
+          {
+            GVariantSerialised child = {};
+            const GVariantMemberInfo *info;
+            guint alignment;
+            gsize fixed_size;
+
+            info = g_variant_type_info_member_info (type, i);
+            gvs_filler (&child, children[i]);
+            g_assert (child.type == info->type);
+
+            g_variant_type_info_query (info->type, &alignment, &fixed_size);
+
+            if (!fixed_size)
+              {
+                if (child.size)
+                  {
+                    offset += (-offset) & alignment;
+                    offset += child.size;
+                  }
+
+                if (i != n_children - 1)
+                  n_offsets++;
+              }
+            else
+              {
+                offset += (-offset) & alignment;
+                offset += fixed_size;
+              }
+          }
+
+          /* no need to pad fixed-sized tuples here because
+           * fixed sized tuples are taken care of directly.
+           */
+
+        return g_variant_serialiser_determine_size (offset, n_offsets, FALSE);
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+void
+g_variant_serialised_byteswap (GVariantSerialised value)
+{
+  gsize fixed_size;
+  guint alignment;
+
+  if (!value.data)
+    return;
+
+  /* the types we potentially need to byteswap are
+   * exactly those with alignment requirements.
+   */
+  g_variant_type_info_query (value.type, &alignment, &fixed_size);
+  if (!alignment)
+    return;
+
+  /* if fixed size and alignment are equal then we are down
+   * to the base integer type and we should swap it.  the
+   * only exception to this is if we have a tuple with a
+   * single item, and then swapping it will be OK anyway.
+   */
+  if (alignment + 1 == fixed_size)
+    {
+      switch (fixed_size)
+      {
+        case 2:
+          {
+            guint16 *ptr = (guint16 *) value.data;
+
+            g_assert_cmpint (value.size, ==, 2);
+            *ptr = GUINT16_SWAP_LE_BE (*ptr);
+          }
+          return;
+
+        case 4:
+          {
+            guint32 *ptr = (guint32 *) value.data;
+
+            g_assert_cmpint (value.size, ==, 4);
+            *ptr = GUINT32_SWAP_LE_BE (*ptr);
+          }
+          return;
+
+        case 8:
+          {
+            guint64 *ptr = (guint64 *) value.data;
+
+            g_assert_cmpint (value.size, ==, 8);
+            *ptr = GUINT64_SWAP_LE_BE (*ptr);
+          }
+          return;
+
+        default:
+          g_assert_not_reached ();
+      }
+    }
+
+  /* else, we have a container that potentially contains
+   * some children that need to be byteswapped.
+   */
+  else
+    {
+      gsize children, i;
+      
+      children = g_variant_serialised_n_children (value);
+      for (i = 0; i < children; i++)
+        {
+          GVariantSerialised child;
+
+          child = g_variant_serialised_get_child (value, i);
+          g_variant_serialised_byteswap (child);
+        }
+    }
+}
+
+void
+g_variant_serialised_assert_invariant (GVariantSerialised value)
+{
+  gsize fixed_size;
+  guint alignment;
+
+  g_assert (value.type != NULL);
+  g_assert_cmpint ((value.data == NULL), <=, (value.size == 0));
+
+  g_variant_type_info_query (value.type, &alignment, &fixed_size);
+
+  g_assert_cmpint (((gsize) value.data) & alignment, ==, 0);
+  if (fixed_size)
+    g_assert_cmpint (value.size, ==, fixed_size);
+}
+
+gboolean
+g_variant_serialised_is_normal (GVariantSerialised value)
+{
+#define fail return FALSE
+//#define fail g_assert_not_reached ()
+  switch (g_variant_type_info_get_type_class (value.type))
+  {
+    case G_VARIANT_TYPE_CLASS_BYTE:
+    case G_VARIANT_TYPE_CLASS_INT16:
+    case G_VARIANT_TYPE_CLASS_UINT16:
+    case G_VARIANT_TYPE_CLASS_INT32:
+    case G_VARIANT_TYPE_CLASS_UINT32:
+    case G_VARIANT_TYPE_CLASS_INT64:
+    case G_VARIANT_TYPE_CLASS_UINT64:
+    case G_VARIANT_TYPE_CLASS_DOUBLE:
+      return TRUE;
+
+    case G_VARIANT_TYPE_CLASS_BOOLEAN:
+      if (value.data[0] > 1)
+        fail;
+
+      return TRUE;
+
+    case G_VARIANT_TYPE_CLASS_STRING:
+      /* XXX this is not safe if the data changes... */
+      if (value.size == 0 || value.data[value.size - 1] != '\0' ||
+          strlen ((const char *) value.data) + 1 != value.size)
+        fail;
+
+      return TRUE;
+
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      {
+        GVariantSerialised child = { NULL, value.data, value.size };
+
+        /* find '\0' character */
+        while (child.size && value.data[--child.size]);
+
+        /* the loop can finish for two reasons.
+         * 1) we found a '\0'.   ((good.))
+         * 2) we hit the start.  ((only good if there's a '\0' there))
+         */
+        if (value.size && value.data[child.size] == '\0')
+          {
+            gchar *str = (gchar *) value.data + child.size + 1;
+
+            /* in the case that we're accessing a shared memory buffer,
+             * someone could change the  string under us and cause us
+             * to access out-of-bounds memory.
+             *
+             * if we carefully make our own copy then we avoid that.
+             */
+            str = g_strndup (str, value.size - child.size - 1);
+
+            if (g_variant_type_string_is_valid (str))
+              {
+                const GVariantType *type = G_VARIANT_TYPE (str);
+
+                if (g_variant_type_is_concrete (type))
+                  child.type = g_variant_type_info_get (type);
+              }
+
+            g_free (str);
+          }
+
+        if G_UNLIKELY (child.type == NULL)
+          fail;
+
+        {
+          gsize fixed_size;
+
+          g_variant_type_info_query (child.type, NULL, &fixed_size);
+
+          if G_UNLIKELY (fixed_size && child.size != fixed_size)
+            {
+              g_variant_type_info_unref (child.type);
+              fail;
+            }
+        }
+
+        if (child.size == 0)
+          child.data = NULL;
+
+        return g_variant_serialised_is_normal (child);
+      }
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      {
+        gsize fixed_size;
+
+        /* Nothing case */
+        if (value.size == 0)
+          return TRUE;
+
+        /* Just case */
+        value.type = g_variant_type_info_element (value.type);
+        g_variant_type_info_query (value.type, NULL, &fixed_size);
+
+        if (fixed_size)
+          {
+            /* if element is fixed size, Just must be the same */
+            if (value.size != fixed_size)
+              fail;
+          }
+        else
+          {
+            /* if element is variable size, we have a zero pad */
+            if (value.data[--value.size] != '\0')
+              fail;
+
+            if (value.size == 0)
+              value.data = NULL;
+          }
+
+        /* check the element itself */
+        return g_variant_serialised_is_normal (value);
+      }
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      {
+        GVariantSerialised child = { g_variant_type_info_element (value.type),
+                                     value.data,
+                                     0 };
+        guint alignment;
+
+        if (value.size == 0)
+          return TRUE;
+
+        g_variant_type_info_query (child.type, &alignment, &child.size);
+
+        if (child.size)
+          {
+            gsize i;
+
+            if (value.size % child.size)
+              fail;
+
+            for (i = 0; i < value.size / child.size; i++)
+              {
+                if (!g_variant_serialised_is_normal (child))
+                  fail;
+
+                child.data += child.size;
+              }
+
+            g_assert (child.data == value.data + value.size);
+
+            return TRUE;
+          }
+        else
+          {
+            gsize n_children, offsets_boundary, offset_ptr;
+            guint offset_size;
+            gsize i_am_here;
+
+            /* we will need this to check on some things manually */
+            offset_size = g_variant_serialiser_offset_size (value);
+            g_assert (offset_size > 0);
+
+            /* make sure the end offset is in-bounds */
+            if (!g_variant_serialiser_dereference (value, 0, &offsets_boundary))
+              fail;
+
+            /* make sure we have an integer number of offsets */
+            if ((value.size - offsets_boundary) % offset_size)
+              fail;
+
+            /* number of offsets = length of the array */
+            n_children = (value.size - offsets_boundary) / offset_size;
+
+            /* ensure that the smallest possible offset size was chosen */
+            if (value.size !=
+                g_variant_serialiser_determine_size (offsets_boundary, n_children, TRUE))
+              fail;
+
+            offset_ptr = offsets_boundary;
+            i_am_here = 0;
+            while (n_children--)
+              {
+                gsize child_end = 0;
+
+                memcpy (&child_end, &value.data[offset_ptr], offset_size);
+                child_end = GSIZE_FROM_LE (child_end);
+                offset_ptr += offset_size;
+
+                if (child_end < i_am_here || child_end > offsets_boundary)
+                  fail;
+
+                if (child_end > i_am_here)
+                  {
+                    /* >= is correct.  zero side-children have no padding */
+                    if (i_am_here + ((-i_am_here) & alignment) >= child_end)
+                      fail;
+
+                    while (i_am_here & alignment)
+                      if (value.data[i_am_here++] != '\0')
+                        fail;
+                  }
+
+                child.data = value.data + i_am_here;
+                child.size = child_end - i_am_here;
+                i_am_here = child_end;
+
+                if (!g_variant_serialised_is_normal (child))
+                  return FALSE;
+              }
+
+            if (i_am_here != offsets_boundary)
+              return FALSE;
+
+            g_assert (offset_ptr == value.size);
+          }
+
+        return TRUE;
+      }
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      {
+        const GVariantMemberInfo *info;
+        guint offset_size;
+        gsize offset_ptr;
+        gsize offsets_boundary;
+        gsize i_am_here;
+        gsize n_children;
+
+        info = g_variant_type_info_member_info (value.type, 0);
+        n_children = g_variant_type_info_n_members (value.type);
+
+        if (n_children == 0)
+          {
+            if (value.size != 1 || value.data[0] != '\0')
+              fail;
+
+            return TRUE;
+          }
+
+        offset_size = g_variant_serialiser_offset_size (value);
+
+        if (offset_size * (info[n_children - 1].i + 1) > value.size)
+          fail;
+
+        offset_ptr = offsets_boundary = value.size;
+        offsets_boundary -= offset_size * (info[n_children - 1].i + 1);
+
+        i_am_here = 0;
+        while (n_children--)
+          {
+            GVariantSerialised child = { info->type, NULL, 0 };
+            guint alignment;
+
+            g_variant_type_info_query (info->type, &alignment, &child.size);
+
+            if (child.size)
+              {
+                if (i_am_here + ((-i_am_here) & alignment) > offsets_boundary)
+                  fail;
+
+                while (i_am_here & alignment)
+                  if (value.data[i_am_here++] != '\0')
+                    fail;
+
+                if (i_am_here + child.size > offsets_boundary)
+                  fail;
+              }
+            else
+              {
+                gsize child_end = 0;
+
+                if (offset_ptr > offsets_boundary)
+                  {
+                    offset_ptr -= offset_size;
+                    memcpy (&child_end, &value.data[offset_ptr], offset_size);
+                    child_end = GSIZE_FROM_LE (child_end);
+                  }
+                else
+                  child_end = offsets_boundary;
+
+                if (child_end < i_am_here || child_end > offsets_boundary)
+                  fail;
+
+                if (child_end > i_am_here)
+                  {
+                    /* >= is correct.  zero side-children have no padding */
+                    if (i_am_here + ((-i_am_here) & alignment) >= child_end)
+                      fail;
+
+                    while (i_am_here & alignment)
+                      if (value.data[i_am_here++] != '\0')
+                        fail;
+                  }
+
+                child.size = child_end - i_am_here;
+              }
+
+            child.data = value.data + i_am_here;
+            i_am_here += child.size;
+
+            if (child.size == 0)
+              child.data = NULL;
+
+            if (!g_variant_serialised_is_normal (child))
+              return FALSE;
+
+            info++;
+          }
+
+        {
+          gsize fixed_size;
+          guint alignment;
+
+          g_variant_type_info_query (value.type, &alignment, &fixed_size);
+
+          if (fixed_size)
+            {
+              if (i_am_here + ((-i_am_here) & alignment) != offsets_boundary)
+                fail;
+
+              while (i_am_here & alignment)
+                if (value.data[i_am_here++] != '\0')
+                  fail;
+
+              g_assert (i_am_here == offsets_boundary);
+            }
+        }
+
+        g_assert (i_am_here <= offsets_boundary);
+        if (i_am_here != offsets_boundary)
+          fail;
+
+        g_assert (offsets_boundary == offset_ptr);
+
+        return TRUE;
+      }
+
+    case G_VARIANT_TYPE_CLASS_SIGNATURE:
+    case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+      return TRUE; /* XXX */
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+#define __G_VARIANT_SERIALISER_C__
+#include "galiasdef.c"
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
new file mode 100644
index 0000000..3201da1
--- /dev/null
+++ b/glib/gvariant-serialiser.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_VARIANT_SERIALISER_H__
+#define __G_VARIANT_SERIALISER_H__
+
+#include "gvarianttypeinfo.h"
+
+typedef struct
+{
+  GVariantTypeInfo *type;
+  guchar           *data;
+  gsize             size;
+} GVariantSerialised;
+
+/* deserialisation */
+gsize                           g_variant_serialised_n_children         (GVariantSerialised        container);
+GVariantSerialised              g_variant_serialised_get_child          (GVariantSerialised        container,
+                                                                         gsize                     index);
+
+/* serialisation */
+typedef void                  (*GVariantSerialisedFiller)               (GVariantSerialised       *serialised,
+                                                                         gpointer                  data);
+
+gsize                           g_variant_serialiser_needed_size        (GVariantTypeInfo         *info,
+                                                                         GVariantSerialisedFiller  gsv_filler,
+                                                                         const gpointer           *children,
+                                                                         gsize                     n_children);
+
+void                            g_variant_serialiser_serialise          (GVariantSerialised        container,
+                                                                         GVariantSerialisedFiller  gsv_filler,
+                                                                         const gpointer           *children,
+                                                                         gsize                     n_children);
+
+/* misc */
+void                            g_variant_serialised_assert_invariant   (GVariantSerialised        value);
+gboolean                        g_variant_serialised_is_normal          (GVariantSerialised        value);
+void                            g_variant_serialised_byteswap           (GVariantSerialised        value);
+
+#endif /* __G_VARIANT_SERIALISER_H__ */
diff --git a/glib/gvariant-util.c b/glib/gvariant-util.c
new file mode 100644
index 0000000..c963c93
--- /dev/null
+++ b/glib/gvariant-util.c
@@ -0,0 +1,2029 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "gvariant-private.h"
+
+#include "galias.h"
+
+/**
+ * GVariantIter:
+ *
+ * An opaque structure type used to iterate over a container #GVariant
+ * instance.
+ *
+ * The iter must be initialised with a call to g_variant_iter_init()
+ * before using it.  After that, g_variant_iter_next() will return the
+ * child values, in order.
+ *
+ * The iter may maintain a reference to the container #GVariant until
+ * g_variant_iter_next() returns %NULL.  For this reason, it is
+ * essential that you call g_variant_iter_next() until %NULL is
+ * returned.  If you want to abort iterating part way through then use
+ * g_variant_iter_cancel().
+ */
+typedef struct
+{
+  GVariant *value;
+  GVariant *child;
+  gsize length;
+  gsize offset;
+  gboolean cancelled;
+} GVariantIterReal;
+
+/**
+ * g_variant_iter_init:
+ * @iter: a #GVariantIter
+ * @value: a container #GVariant instance
+ * @returns: the number of items in the container
+ *
+ * Initialises the fields of a #GVariantIter and perpare to iterate
+ * over the contents of @value.
+ *
+ * @iter is allowed to be completely uninitialised prior to this call;
+ * it does not, for example, have to be cleared to zeros.  For this
+ * reason, if @iter was already being used, you should first cancel it
+ * with g_variant_iter_cancel().
+ *
+ * After this call, @iter holds a reference to @value.  The reference
+ * will be automatically dropped once all values have been iterated
+ * over or manually by calling g_variant_iter_cancel().
+ *
+ * This function returns the number of times that
+ * g_variant_iter_next() will return non-%NULL.  You're not expected to
+ * use this value, but it's there incase you wanted to know.
+ **/
+gsize
+g_variant_iter_init (GVariantIter *iter,
+                     GVariant     *value)
+{
+  GVariantIterReal *real = (GVariantIterReal *) iter;
+
+  g_return_val_if_fail (iter != NULL, 0);
+  g_return_val_if_fail (value != NULL, 0);
+
+  g_assert (sizeof (GVariantIterReal) <= sizeof (GVariantIter));
+
+  real->cancelled = FALSE;
+  real->length = g_variant_n_children (value);
+  real->offset = 0;
+  real->child = NULL;
+
+  if (real->length)
+    real->value = g_variant_ref (value);
+  else
+    real->value = NULL;
+
+  return real->length;
+}
+
+/**
+ * g_variant_iter_next_value:
+ * @iter: a #GVariantIter
+ * @returns: a #GVariant for the next child
+ *
+ * Retreives the next child value from @iter.  In the event that no
+ * more child values exist, %NULL is returned and @iter drops its
+ * reference to the value that it was created with.
+ *
+ * The return value of this function is internally cached by the
+ * @iter, so you don't have to unref it when you're done.  For this
+ * reason, thought, it is important to ensure that you call
+ * g_variant_iter_next() one last time, even if you know the number of
+ * items in the container.
+ *
+ * It is permissable to call this function on a cancelled iter, in
+ * which case %NULL is returned and nothing else happens.
+ **/
+GVariant *
+g_variant_iter_next_value (GVariantIter *iter)
+{
+  GVariantIterReal *real = (GVariantIterReal *) iter;
+
+  g_return_val_if_fail (iter != NULL, NULL);
+
+  if (real->child)
+    {
+      g_variant_unref (real->child);
+      real->child = NULL;
+    }
+
+  if (real->value == NULL)
+    return NULL;
+
+  real->child = g_variant_get_child_value (real->value, real->offset++);
+
+  if (real->offset == real->length)
+    {
+      g_variant_unref (real->value);
+      real->value = NULL;
+    }
+
+  return real->child;
+}
+
+/**
+ * g_variant_iter_cancel:
+ * @iter: a #GVariantIter
+ *
+ * Causes @iter to drop its reference to the container that it was
+ * created with.  You need to call this on an iter if, for some
+ * reason, you stopped iterating before reading the end.
+ *
+ * You do not need to call this in the normal case of visiting all of
+ * the elements.  In this case, the reference will be automatically
+ * dropped by g_variant_iter_next() just before it returns %NULL.
+ *
+ * It is permissable to call this function more than once on the same
+ * iter.  It is permissable to call this function after the last
+ * value.
+ **/
+void
+g_variant_iter_cancel (GVariantIter *iter)
+{
+  GVariantIterReal *real = (GVariantIterReal *) iter;
+
+  g_return_if_fail (iter != NULL);
+
+  real->cancelled = TRUE;
+
+  if (real->value)
+    {
+      g_variant_unref (real->value);
+      real->value = NULL;
+    }
+
+  if (real->child)
+    {
+      g_variant_unref (real->child);
+      real->child = NULL;
+    }
+}
+
+/**
+ * g_variant_iter_was_cancelled:
+ * @iter: a #GVariantIter
+ * @returns: %TRUE if g_variant_iter_cancel() was called
+ *
+ * Determines if g_variant_iter_cancel() was called on @iter.
+ **/
+gboolean
+g_variant_iter_was_cancelled (GVariantIter *iter)
+{
+  GVariantIterReal *real = (GVariantIterReal *) iter;
+
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  return real->cancelled;
+}
+
+/* private */
+gboolean
+g_variant_iter_should_free (GVariantIter *iter)
+{
+  GVariantIterReal *real = (GVariantIterReal *) iter;
+
+  return real->child != NULL;
+}
+
+/**
+ * g_variant_matches:
+ * @value: a #GVariant instance
+ * @pattern: a #GVariantType
+ * @returns: %TRUE if the type of @value matches @pattern
+ *
+ * Checks if a value has a type matching the provided pattern.  This
+ * call is equivalent to calling g_variant_get_type() then
+ * g_variant_type_matches().
+ **/
+gboolean
+g_variant_matches (GVariant           *value,
+                   const GVariantType *pattern)
+{
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (value != NULL, FALSE);
+
+  return g_variant_type_matches (g_variant_get_type (value), pattern);
+}
+
+/**
+ * g_variant_new_boolean:
+ * @boolean: a #gboolean value
+ * @returns: a new boolean #GVariant instance
+ *
+ * Creates a new boolean #GVariant instance -- either %TRUE or %FALSE.
+ **/
+GVariant *
+g_variant_new_boolean (gboolean boolean)
+{
+  guint8 byte = !!boolean;
+
+  return g_variant_load (G_VARIANT_TYPE_BOOLEAN,
+                         &byte, 1, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_byte:
+ * @byte: a #guint8 value
+ * @returns: a new byte #GVariant instance
+ *
+ * Creates a new byte #GVariant instance.
+ **/
+GVariant *
+g_variant_new_byte (guint8 byte)
+{
+  return g_variant_load (G_VARIANT_TYPE_BYTE,
+                         &byte, 1, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_int16:
+ * @int16: a #gint16 value
+ * @returns: a new byte #GVariant instance
+ *
+ * Creates a new int16 #GVariant instance.
+ **/
+GVariant *
+g_variant_new_int16 (gint16 int16)
+{
+  return g_variant_load (G_VARIANT_TYPE_INT16,
+                         &int16, 2, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_uint16:
+ * @uint16: a #guint16 value
+ * @returns: a new byte #GVariant instance
+ *
+ * Creates a new uint16 #GVariant instance.
+ **/
+GVariant *
+g_variant_new_uint16 (guint16 uint16)
+{
+  return g_variant_load (G_VARIANT_TYPE_UINT16,
+                         &uint16, 2, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_int32:
+ * @int32: a #gint32 value
+ * @returns: a new byte #GVariant instance
+ *
+ * Creates a new int32 #GVariant instance.
+ **/
+GVariant *
+g_variant_new_int32 (gint32 int32)
+{
+  return g_variant_load (G_VARIANT_TYPE_INT32,
+                         &int32, 4, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_uint32:
+ * @uint32: a #guint32 value
+ * @returns: a new uint32 #GVariant instance
+ *
+ * Creates a new uint32 #GVariant instance.
+ **/
+GVariant *
+g_variant_new_uint32 (guint32 uint32)
+{
+  return g_variant_load (G_VARIANT_TYPE_UINT32,
+                         &uint32, 4, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_int64:
+ * @int64: a #gint64 value
+ * @returns: a new byte #GVariant instance
+ *
+ * Creates a new int64 #GVariant instance.
+ **/
+GVariant *
+g_variant_new_int64 (gint64 int64)
+{
+  return g_variant_load (G_VARIANT_TYPE_INT64,
+                         &int64, 8, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_uint64:
+ * @uint64: a #guint64 value
+ * @returns: a new uint64 #GVariant instance
+ *
+ * Creates a new uint64 #GVariant instance.
+ **/
+GVariant *
+g_variant_new_uint64 (guint64 uint64)
+{
+  return g_variant_load (G_VARIANT_TYPE_UINT64,
+                         &uint64, 8, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_double:
+ * @floating: a #gdouble floating point value
+ * @returns: a new double #GVariant instance
+ *
+ * Creates a new double #GVariant instance.
+ **/
+GVariant *
+g_variant_new_double (gdouble floating)
+{
+  return g_variant_load (G_VARIANT_TYPE_DOUBLE,
+                         &floating, 8, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_string:
+ * @string: a normal C nul-terminated string
+ * @returns: a new string #GVariant instance
+ *
+ * Creates a string #GVariant with the contents of @string.
+ **/
+GVariant *
+g_variant_new_string (const gchar *string)
+{
+  return g_variant_load (G_VARIANT_TYPE_STRING,
+                         string, strlen (string) + 1, G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_new_object_path:
+ * @string: a normal C nul-terminated string
+ * @returns: a new object path #GVariant instance
+ *
+ * Creates a DBus object path #GVariant with the contents of @string.
+ * @string must be a valid DBus object path.  Use
+ * g_variant_is_object_path() if you're not sure.
+ **/
+GVariant *
+g_variant_new_object_path (const gchar *string)
+{
+  g_return_val_if_fail (g_variant_is_object_path (string), NULL);
+
+  return g_variant_load (G_VARIANT_TYPE_OBJECT_PATH,
+                         string, strlen (string) + 1,
+                         G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_is_object_path:
+ * @string: a normal C nul-terminated string
+ * @returns: %TRUE if @string is a DBus object path
+ *
+ * Determines if a given string is a valid DBus object path.  You
+ * should ensure that a string is a valid DBus object path before
+ * passing it to g_variant_new_object_path().
+ *
+ * A valid object path starts with '/' followed by zero or more
+ * sequences of characters separated by '/' characters.  Each sequence
+ * must contain only the characters "[A-Z][a-z][0-9]_".  No sequence
+ * (including the one following the final '/' character) may be empty.
+ **/
+gboolean
+g_variant_is_object_path (const gchar *string)
+{
+  /* according to DBus specification */
+  gsize i;
+
+  g_return_val_if_fail (string != NULL, FALSE);
+
+  /* The path must begin with an ASCII '/' (integer 47) character */
+  if (string[0] != '/')
+    return FALSE;
+
+  for (i = 1; string[i]; i++)
+    /* Each element must only contain the ASCII characters
+     * "[A-Z][a-z][0-9]_" 
+     */
+    if (g_ascii_isalnum (string[i]) || string[i] == '_')
+      ;
+
+    /* must consist of elements separated by slash characters. */
+    else if (string[i] == '/')
+      {
+        /* No element may be the empty string. */
+        /* Multiple '/' characters cannot occur in sequence. */
+        if (string[i - 1] == '/')
+          return FALSE;
+      }
+
+    else
+      return FALSE;
+
+  /* A trailing '/' character is not allowed unless the path is the
+   * root path (a single '/' character).
+   */
+  if (i > 1 && string[i - 1] == '/')
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * g_variant_new_signature:
+ * @string: a normal C nul-terminated string
+ * @returns: a new signature #GVariant instance
+ *
+ * Creates a DBus type signature #GVariant with the contents of
+ * @string.  @string must be a valid DBus type signature.  Use
+ * g_variant_is_signature() if you're not sure.
+ **/
+GVariant *
+g_variant_new_signature (const gchar *string)
+{
+  g_return_val_if_fail (g_variant_is_signature (string), NULL);
+
+  return g_variant_load (G_VARIANT_TYPE_SIGNATURE,
+                         string, strlen (string) + 1,
+                         G_VARIANT_TRUSTED);
+}
+
+/**
+ * g_variant_is_signature:
+ * @string: a normal C nul-terminated string
+ * @returns: %TRUE if @string is a DBus type signature
+ *
+ * Determines if a given string is a valid DBus type signature.  You
+ * should ensure that a string is a valid DBus object path before
+ * passing it to g_variant_new_signature().
+ *
+ * DBus type signatures consist of zero or more concrete #GVariantType
+ * strings in sequence.
+ **/
+gboolean
+g_variant_is_signature (const gchar *string)
+{
+  gsize first_invalid;
+
+  g_return_val_if_fail (string != NULL, FALSE);
+
+  /* make sure no non-concrete characters appear */
+  first_invalid = strspn (string, "ybnqiuxtdvmasog(){}");
+  if (string[first_invalid])
+    return FALSE;
+
+  /* make sure each type string is well-formed */
+  while (*string)
+    if (!g_variant_type_string_scan (string, NULL, &string))
+      return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * g_variant_new_variant:
+ * @value: a #GVariance instance
+ * @returns: a new variant #GVariant instance
+ *
+ * Boxes @value.  The result is a #GVariant instance representing a
+ * variant containing the original value.
+ **/
+GVariant *
+g_variant_new_variant (GVariant *value)
+{
+  GVariant **children;
+
+  g_return_val_if_fail (value != NULL, NULL);
+
+  children = g_slice_new (GVariant *);
+  children[0] = g_variant_ref_sink (value);
+
+  return g_variant_new_tree (G_VARIANT_TYPE_VARIANT,
+                             children, 1,
+                             g_variant_is_trusted (value));
+}
+
+/**
+ * g_variant_get_boolean:
+ * @value: a boolean #GVariant instance
+ * @returns: %TRUE or %FALSE
+ *
+ * Returns the boolean value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_BOOLEAN.
+ **/
+gboolean
+g_variant_get_boolean (GVariant *value)
+{
+  guint8 byte;
+
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_BOOLEAN));
+  g_variant_store (value, &byte);
+
+  return byte;
+}
+
+/**
+ * g_variant_get_byte:
+ * @value: a byte #GVariant instance
+ * @returns: a #guchar
+ *
+ * Returns the byte value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_BYTE.
+ **/
+guint8
+g_variant_get_byte (GVariant *value)
+{
+  guint8 byte;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_BYTE));
+  g_variant_store (value, &byte);
+
+  return byte;
+}
+
+/**
+ * g_variant_get_int16:
+ * @value: a int16 #GVariant instance
+ * @returns: a #gint16
+ *
+ * Returns the 16-bit signed integer value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_INT16.
+ **/
+gint16
+g_variant_get_int16 (GVariant *value)
+{
+  gint16 int16;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_INT16));
+  g_variant_store (value, &int16);
+
+  return int16;
+}
+
+/**
+ * g_variant_get_uint16:
+ * @value: a uint16 #GVariant instance
+ * @returns: a #guint16
+ *
+ * Returns the 16-bit unsigned integer value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_UINT16.
+ **/
+guint16
+g_variant_get_uint16 (GVariant *value)
+{
+  guint16 uint16;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_UINT16));
+  g_variant_store (value, &uint16);
+
+  return uint16;
+}
+
+/**
+ * g_variant_get_int32:
+ * @value: a int32 #GVariant instance
+ * @returns: a #gint32
+ *
+ * Returns the 32-bit signed integer value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_INT32.
+ **/
+gint32
+g_variant_get_int32 (GVariant *value)
+{
+  gint32 int32;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_INT32));
+  g_variant_store (value, &int32);
+
+  return int32;
+}
+
+/**
+ * g_variant_get_uint32:
+ * @value: a uint32 #GVariant instance
+ * @returns: a #guint32
+ *
+ * Returns the 32-bit unsigned integer value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_UINT32.
+ **/
+guint32
+g_variant_get_uint32 (GVariant *value)
+{
+  guint32 uint32;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_UINT32));
+  g_variant_store (value, &uint32);
+
+  return uint32;
+}
+
+/**
+ * g_variant_get_int64:
+ * @value: a int64 #GVariant instance
+ * @returns: a #gint64
+ *
+ * Returns the 64-bit signed integer value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_INT64.
+ **/
+gint64
+g_variant_get_int64 (GVariant *value)
+{
+  gint64 int64;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_INT64));
+  g_variant_store (value, &int64);
+
+  return int64;
+}
+
+/**
+ * g_variant_get_uint64:
+ * @value: a uint64 #GVariant instance
+ * @returns: a #guint64
+ *
+ * Returns the 64-bit unsigned integer value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_UINT64.
+ **/
+guint64
+g_variant_get_uint64 (GVariant *value)
+{
+  guint64 uint64;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_UINT64));
+  g_variant_store (value, &uint64);
+
+  return uint64;
+}
+
+/**
+ * g_variant_get_double:
+ * @value: a double #GVariant instance
+ * @returns: a #gdouble
+ *
+ * Returns the double precision floating point value of @value.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than %G_VARIANT_TYPE_DOUBLE.
+ **/
+gdouble
+g_variant_get_double (GVariant *value)
+{
+  gdouble floating;
+
+  g_return_val_if_fail (value != NULL, 0);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_DOUBLE));
+  g_variant_store (value, &floating);
+
+  return floating;
+}
+
+/**
+ * g_variant_get_string:
+ * @value: a string #GVariant instance
+ * @length: a pointer to a #gsize, to store the length
+ * @returns: the constant string
+ *
+ * Returns the string value of a #GVariant instance with a string
+ * type.  This includes the types %G_VARIANT_TYPE_STRING,
+ * %G_VARIANT_TYPE_OBJECT_PATH and %G_VARIANT_TYPE_SIGNATURE.
+ *
+ * If @length is non-%NULL then the length of the string (in bytes) is
+ * returned there.  For trusted values, this information is already
+ * known.  For untrusted values, a strlen() will be performed.
+ *
+ * It is an error to call this function with a @value of any type
+ * other than those three.
+ *
+ * The return value remains valid as long as @value exists.
+ **/
+const gchar *
+g_variant_get_string (GVariant *value,
+                      gsize    *length)
+{
+  GVariantTypeClass class;
+  gboolean valid_string;
+  const gchar *string;
+  gssize size;
+
+  g_return_val_if_fail (value != NULL, NULL);
+  class = g_variant_get_type_class (value);
+  g_return_val_if_fail (class == G_VARIANT_TYPE_CLASS_STRING ||
+                        class == G_VARIANT_TYPE_CLASS_OBJECT_PATH ||
+                        class == G_VARIANT_TYPE_CLASS_SIGNATURE, NULL);
+
+  string = g_variant_get_data (value);
+  size = g_variant_get_size (value);
+
+  if (g_variant_is_trusted (value))
+    {
+      if (length)
+        *length = size - 1;
+
+      return string;
+    }
+
+  valid_string = string != NULL && size > 0 && string[size - 1] == '\0';
+
+  switch (class)
+  {
+    case G_VARIANT_TYPE_CLASS_STRING:
+      if (valid_string)
+        break;
+
+      if (length)
+        *length = 0;
+
+      return "";
+
+    case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+      if (valid_string && g_variant_is_object_path (string))
+        break;
+
+      if (length)
+        *length = 1;
+
+      return "/";
+
+    case G_VARIANT_TYPE_CLASS_SIGNATURE:
+      if (valid_string && g_variant_is_signature (string))
+        break;
+
+      if (length)
+        *length = 0;
+
+      return "";
+
+    default:
+      g_assert_not_reached ();
+  }
+
+  if (length)
+    *length = strlen (string);
+
+  return string;
+}
+
+/**
+ * g_variant_dup_string:
+ * @value: a string #GVariant instance
+ * @length: a pointer to a #gsize, to store the length
+ * @returns: a newly allocated string
+ *
+ * Similar to g_variant_get_string() except that instead of returning
+ * a constant string, the string is duplicated.
+ *
+ * The return value must be freed using g_free().
+ **/
+gchar *
+g_variant_dup_string (GVariant *value,
+                      gsize    *length)
+{
+  GVariantTypeClass class;
+  int size;
+
+  g_return_val_if_fail (value != NULL, NULL);
+  class = g_variant_get_type_class (value);
+  g_return_val_if_fail (class == G_VARIANT_TYPE_CLASS_STRING ||
+                        class == G_VARIANT_TYPE_CLASS_OBJECT_PATH ||
+                        class == G_VARIANT_TYPE_CLASS_SIGNATURE, NULL);
+
+  size = g_variant_get_size (value);
+
+  if (length)
+    *length = size - 1;
+
+  return g_memdup (g_variant_get_data (value), size);
+}
+
+/**
+ * g_variant_get_variant:
+ * @value: a variant #GVariance instance
+ * @returns: the item contained in the variant
+ *
+ * Unboxes @value.  The result is the #GVariant instance that was
+ * contained in @value.
+ **/
+GVariant *
+g_variant_get_variant (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, NULL);
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_VARIANT));
+
+  return g_variant_get_child_value (value, 0);
+}
+
+/**
+ * GVariantBuilder:
+ *
+ * An opaque type used to build container #GVariant instances one
+ * child value at a time.
+ */
+struct OPAQUE_TYPE__GVariantBuilder
+{
+  GVariantBuilder *parent;
+
+  GVariantTypeClass class;
+  GVariantType *type;
+  const GVariantType *expected;
+  /* expected2 is always concrete.  it is set if adding a second
+   * element to an array (or open sub-builder thereof).  it is
+   * required to ensure the following works correctly:
+   *
+   *   b = new(array, "a**");
+   *   sb = open(b, array, "ai");
+   *   b = close (sb);
+   *
+   *   b = open (b, array, "a*");    <-- valid
+   *   add (sb, "u", 1234);          <-- must fail here
+   *
+   * the 'valid' call is valid since the "a*" is no more general than
+   * the element type of "aa*" (in fact, it is exactly equal) but the
+   * 'must fail' call still needs to know that it requires element
+   * type 'i'.  this requires keeping track of the two types
+   * separately.
+   */
+  const GVariantType *expected2;
+
+  GVariant **children;
+  gsize children_allocated;
+  gsize offset;
+  int has_child : 1;
+  int trusted : 1;
+};
+
+/**
+ * G_VARIANT_BUILDER_ERROR:
+ *
+ * Error domain for #GVariantBuilder. Errors in this domain will be
+ * from the #GVariantBuilderError enumeration.  See #GError for
+ * information on error domains. 
+ **/
+/**
+ * GVariantBuilderError:
+ * @G_VARIANT_BUILDER_ERROR_TOO_MANY: too many items have been added
+ * (returned by g_variant_builder_check_add())
+ * @G_VARIANT_BUILDER_ERROR_TOO_FEW: too few items have been added
+ * (returned by g_variant_builder_check_end())
+ * @G_VARIANT_BUILDER_ERROR_INFER: unable to infer the type of an
+ * array or maybe (returned by g_variant_builder_check_end())
+ * @G_VARIANT_BUILDER_ERROR_TYPE: the value is of the incorrect type
+ * (returned by g_variant_builder_check_add())
+ *
+ * Errors codes returned by g_variant_builder_check_add() and
+ * g_variant_builder_check_end().
+ */
+
+static void
+g_variant_builder_resize (GVariantBuilder *builder,
+                          int              new_allocated)
+{
+  GVariant **new_children;
+  int i;
+
+  g_assert_cmpint (builder->offset, <=, new_allocated);
+
+  new_children = g_slice_alloc (sizeof (GVariant *) * new_allocated);
+
+  for (i = 0; i < builder->offset; i++)
+    new_children[i] = builder->children[i];
+
+  g_slice_free1 (sizeof (GVariant **) * builder->children_allocated,
+                 builder->children);
+  builder->children = new_children;
+  builder->children_allocated = new_allocated;
+}
+
+static gboolean
+g_variant_builder_check_add_value (GVariantBuilder  *builder,
+                                   GVariant         *value,
+                                   GError          **error)
+{
+  const GVariantType *type;
+  GVariantTypeClass class;
+
+  type = g_variant_get_type (value);
+  class = g_variant_type_get_class (type);
+
+  return g_variant_builder_check_add (builder, class, type, error);
+}
+
+/**
+ * g_variant_builder_add_value:
+ * @builder: a #GVariantBuilder
+ * @value: a #GVariant
+ *
+ * Adds @value to @builder.
+ *
+ * It is an error to call this function if @builder has an outstanding
+ * child.  It is an error to call this function in any case that
+ * g_variant_builder_check_add() would return %FALSE.
+ **/
+void
+g_variant_builder_add_value (GVariantBuilder *builder,
+                             GVariant        *value)
+{
+  GError *error = NULL;
+
+  g_return_if_fail (builder != NULL);
+  g_return_if_fail (value != NULL);
+
+  if G_UNLIKELY (!g_variant_builder_check_add_value (builder, value, &error))
+    g_error ("g_variant_builder_add_value: %s", error->message);
+
+  builder->trusted &= g_variant_is_trusted (value);
+
+  if (builder->expected &&
+      (builder->class == G_VARIANT_TYPE_CLASS_TUPLE ||
+       builder->class == G_VARIANT_TYPE_CLASS_DICT_ENTRY))
+    builder->expected = g_variant_type_next (builder->expected);
+
+  if (builder->expected2 &&
+      (builder->class == G_VARIANT_TYPE_CLASS_TUPLE ||
+       builder->class == G_VARIANT_TYPE_CLASS_DICT_ENTRY))
+    builder->expected2 = g_variant_type_next (builder->expected2);
+
+  if (builder->class == G_VARIANT_TYPE_CLASS_ARRAY &&
+      builder->expected2 == NULL)
+    builder->expected2 = g_variant_get_type (value);
+
+  if (builder->offset == builder->children_allocated)
+    g_variant_builder_resize (builder, builder->children_allocated * 2);
+
+  builder->children[builder->offset++] = g_variant_ref_sink (value);
+}
+
+/**
+ * g_variant_builder_open:
+ * @parent: a #GVariantBuilder
+ * @tclass: a #GVariantTypeClass
+ * @type: a #GVariantType, or %NULL
+ * @returns: a new (child) #GVariantBuilder
+ *
+ * Opens a subcontainer inside the given @parent.
+ *
+ * It is not permissible to use any other builder calls with @parent
+ * until @g_variant_builder_close() is called on the return value of
+ * this function.
+ *
+ * It is an error to call this function if @parent has an outstanding
+ * child.  It is an error to call this function in any case that
+ * g_variant_builder_check_add() would return %FALSE.  It is an error
+ * to call this function in any case that it's an error to call
+ * g_variant_builder_new().
+ *
+ * If @type is %NULL and @parent was given type information, that
+ * information is passed down to the subcontainer and constrains what
+ * values may be added to it.
+ **/
+GVariantBuilder *
+g_variant_builder_open (GVariantBuilder    *parent,
+                        GVariantTypeClass   tclass,
+                        const GVariantType *type)
+{
+  GVariantBuilder *child;
+  GError *error = NULL;
+
+  g_return_val_if_fail (parent != NULL, NULL);
+
+  if G_UNLIKELY (!g_variant_builder_check_add (parent, tclass, type, &error))
+    g_error ("g_variant_builder_open: %s", error->message);
+
+  if G_UNLIKELY (parent->has_child)
+    g_error ("GVariantBuilder already has open child");
+
+  child = g_variant_builder_new (tclass, type);
+
+  if (parent->expected2)
+    {
+      if (tclass == G_VARIANT_TYPE_CLASS_MAYBE ||
+          tclass == G_VARIANT_TYPE_CLASS_ARRAY)
+        child->expected2 = g_variant_type_element (parent->expected2);
+      
+      if (tclass == G_VARIANT_TYPE_CLASS_TUPLE ||
+          tclass == G_VARIANT_TYPE_CLASS_DICT_ENTRY)
+        child->expected2 = g_variant_type_first (parent->expected2);
+
+      /* in variant case, we don't want to propagate the type */
+    }
+
+  parent->has_child = TRUE;
+  child->parent = parent;
+
+  return child;
+}
+
+/**
+ * g_variant_builder_close:
+ * @child: a #GVariantBuilder
+ * @returns: the original parent of @child
+ *
+ * This function closes a builder that was created with a call to
+ * g_variant_builder_open().
+ *
+ * It is an error to call this function on a builder that was created
+ * using g_variant_builder_new().  It is an error to call this
+ * function if @child has an outstanding child.  It is an error to
+ * call this function in any case that g_variant_builder_check_end()
+ * would return %FALSE.
+ **/
+GVariantBuilder *
+g_variant_builder_close (GVariantBuilder *child)
+{
+  GVariantBuilder *parent;
+  GVariant *value;
+
+  g_return_val_if_fail (child != NULL, NULL);
+
+  g_assert (child->has_child == FALSE);
+  g_assert (child != NULL);
+  g_assert (child->parent != NULL);
+
+  parent = child->parent;
+  parent->has_child = FALSE;
+  parent = child->parent;
+  child->parent = NULL;
+
+  value = g_variant_builder_end (child);
+  g_variant_builder_add_value (parent, value);
+
+  return parent;
+}
+
+/**
+ * g_variant_builder_new:
+ * @tclass: a container #GVariantTypeClass
+ * @type: a type contained in @tclass, or %NULL
+ * @returns: a #GVariantBuilder
+ *
+ * Creates a new #GVariantBuilder.
+ *
+ * @tclass must be specified and must be a container type.
+ *
+ * If @type is given, it constrains the child values that it is
+ * permissible to add.  If @tclass is not %G_VARIANT_TYPE_CLASS_VARIANT
+ * then @type must be contained in @tclass and will match the type of
+ * the final value.  If @tclass is %G_VARIANT_TYPE_CLASS_VARIANT then
+ * @type must match the value that must be added to the variant.
+ *
+ * After the builder is created, values are added using
+ * g_variant_builder_add_value().
+ *
+ * After all the child values are added, g_variant_builder_end() ends
+ * the process. 
+ **/
+GVariantBuilder *
+g_variant_builder_new (GVariantTypeClass   tclass,
+                       const GVariantType *type)
+{
+  GVariantBuilder *builder;
+
+  g_assert (g_variant_type_class_is_container (tclass));
+  g_assert (tclass == G_VARIANT_TYPE_CLASS_VARIANT ||
+            type == NULL || g_variant_type_get_class (type) == tclass);
+
+  builder = g_slice_new (GVariantBuilder);
+  builder->parent = NULL;
+  builder->offset = 0;
+  builder->has_child = FALSE;
+  builder->class = tclass;
+  builder->type = type ? g_variant_type_copy (type) : NULL;
+  builder->expected = NULL;
+  builder->trusted = TRUE;
+  builder->expected2 = FALSE;
+
+  switch (tclass)
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      builder->children_allocated = 1;
+      builder->expected = builder->type;
+      break;
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      builder->children_allocated = 8;
+      if (builder->type)
+        builder->expected = g_variant_type_element (builder->type);
+      break;
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      builder->children_allocated = 1;
+      if (builder->type)
+        builder->expected = g_variant_type_element (builder->type);
+      break;
+
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      builder->children_allocated = 2;
+      if (builder->type)
+        builder->expected = g_variant_type_key (builder->type);
+      break;
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+      builder->children_allocated = 8;
+
+      if (builder->type &&
+          g_variant_type_equal (builder->type, G_VARIANT_TYPE_ANY_TUPLE))
+        /* this is useless information.  they should have just said NULL */
+        {
+          g_variant_type_free (builder->type);
+          builder->type = NULL;
+        }
+
+      if (builder->type)
+        builder->expected = g_variant_type_first (builder->type);
+
+      break;
+
+    default:
+      g_error ("g_variant_builder_new() works only with container types");
+   }
+
+  builder->children = g_slice_alloc (sizeof (GVariant *) *
+                                     builder->children_allocated);
+
+  return builder;
+}
+
+/**
+ * g_variant_builder_end:
+ * @builder: a #GVariantBuilder
+ * @returns: a new, floating, #GVariant
+ *
+ * Ends the builder process and returns the constructed value.
+ *
+ * It is an error to call this function on a #GVariantBuilder created
+ * by a call to g_variant_builder_open().  It is an error to call this
+ * function if @builder has an outstanding child.  It is an error to
+ * call this function in any case that g_variant_builder_check_end()
+ * would return %FALSE.
+ **/
+GVariant *
+g_variant_builder_end (GVariantBuilder *builder)
+{
+  GVariantType *my_type;
+  GError *error = NULL;
+  GVariant *value;
+
+  g_return_val_if_fail (builder, NULL);
+  g_return_val_if_fail (builder->parent == NULL, NULL);
+
+  if G_UNLIKELY (!g_variant_builder_check_end (builder, &error))
+    g_error ("g_variant_builder_end: %s", error->message);
+
+  g_variant_builder_resize (builder, builder->offset);
+
+  if (builder->class == G_VARIANT_TYPE_CLASS_VARIANT)
+    {
+      my_type = g_variant_type_copy (G_VARIANT_TYPE_VARIANT);
+      if (builder->type)
+        g_variant_type_free (builder->type);
+    }
+  else if (builder->type != NULL)
+    {
+      if (!g_variant_type_is_concrete (builder->type))
+        {
+          g_variant_type_free (builder->type);
+          builder->type = NULL;
+        }
+
+      my_type = builder->type;
+    }
+  else
+    my_type = NULL;
+
+
+  if (my_type == NULL)
+    switch (builder->class)
+    {
+      case G_VARIANT_TYPE_CLASS_ARRAY:
+        my_type = g_variant_type_new_array (
+                    g_variant_get_type (builder->children[0]));
+        break;
+
+      case G_VARIANT_TYPE_CLASS_MAYBE:
+        my_type = g_variant_type_new_maybe (
+                    g_variant_get_type (builder->children[0]));
+        break;
+
+      case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+        my_type = g_variant_type_new_dict_entry (
+                    g_variant_get_type (builder->children[0]),
+                    g_variant_get_type (builder->children[1]));
+        break;
+
+      case G_VARIANT_TYPE_CLASS_TUPLE:
+        my_type = g_variant_type_new_tuple (builder->children,
+                                            g_variant_get_type,
+                                            builder->offset);
+        break;
+
+      default:
+        g_assert_not_reached ();
+    }
+
+  value = g_variant_new_tree (my_type, builder->children,
+                              builder->offset, builder->trusted);
+
+  g_slice_free (GVariantBuilder, builder);
+  g_variant_type_free (my_type);
+
+  return value;
+}
+
+/**
+ * g_variant_builder_check_end:
+ * @builder: a #GVariantBuilder
+ * @error: a #GError
+ * @returns: %TRUE if ending is safe
+ *
+ * Checks if a call to g_variant_builder_end() or
+ * g_variant_builder_close() would succeed.
+ *
+ * It is an error to call this function if @builder has a child (ie:
+ * g_variant_builder_open() has been used on @builder and
+ * g_variant_builder_close() has not yet been called).
+ *
+ * This function checks that a sufficient number of items have been
+ * added to the builder.  For dictionary entries, for example, it
+ * ensures that 2 items were added.
+ *
+ * This function also checks that array and maybe builders that were
+ * created without concrete type information contain at least one item
+ * (without which it would be impossible to infer the concrete type).
+ *
+ * If some sort of error (either too few items were added or type
+ * inference is not possible) prevents the builder from being ended
+ * then %FALSE is returned and @error is set.
+ **/
+gboolean
+g_variant_builder_check_end (GVariantBuilder  *builder,
+                             GError          **error)
+{
+  g_return_val_if_fail (builder != NULL, FALSE);
+  g_return_val_if_fail (builder->has_child == FALSE, FALSE);
+
+  switch (builder->class)
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      if (builder->offset < 1)
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TOO_FEW,
+                       "a variant must contain exactly one value");
+          return FALSE;
+        }
+      break;
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      if (builder->offset == 0 &&
+          (builder->type == NULL ||
+           !g_variant_type_is_concrete (builder->type)))
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_INFER,
+                       "unable to infer type for empty array");
+          return FALSE;
+        }
+      break;
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      if (builder->offset == 0 &&
+          (builder->type == NULL ||
+           !g_variant_type_is_concrete (builder->type)))
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_INFER,
+                       "unable to infer type for maybe with no value");
+          return FALSE;
+        }
+      break;
+
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      if (builder->offset < 2)
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TOO_FEW,
+                       "a dictionary entry must have a key and a value");
+          return FALSE;
+        }
+      break;
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+      if (builder->expected)
+        {
+          gchar *type_string;
+
+          type_string = g_variant_type_dup_string (builder->type);
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TOO_FEW,
+                       "a tuple of type %s must contain %" G_GSIZE_FORMAT
+                       " children but only %" G_GSIZE_FORMAT
+                       " have been given", type_string,
+                       g_variant_type_n_items (builder->type),
+                       builder->offset);
+          g_free (type_string);
+
+          return FALSE;
+        }
+      break;
+
+    default:
+      g_assert_not_reached ();
+  }
+
+  return TRUE;
+}
+
+/**
+ * g_variant_builder_check_add:
+ * @builder: a #GVariantBuilder
+ * @tclass: a #GVariantTypeClass
+ * @type: a #GVariantType, or %NULL
+ * @error: a #GError
+ * @returns: %TRUE if adding is safe
+ *
+ * Does all sorts of checks to ensure that it is safe to call
+ * g_variant_builder_add() or g_variant_builder_open().
+ *
+ * It is an error to call this function if @builder has a child (ie:
+ * g_variant_builder_open() has been used on @builder and
+ * g_variant_builder_close() has not yet been called).
+ *
+ * It is an error to call this function with an invalid @tclass
+ * (including %G_VARIANT_TYPE_CLASS_INVALID) or a class that's not the
+ * smallest class for some concrete type (for example,
+ * %G_VARIANT_TYPE_CLASS_ALL).
+ *
+ * If @type is non-%NULL this function first checks that it is a
+ * member of @tclass (except, as with g_variant_builder_new(), if
+ * @tclass is %G_VARIANT_TYPE_CLASS_VARIANT then any @type is OK).
+ *
+ * The function then checks if any child of class @tclass (and type
+ * @type, if given) would be suitable for adding to the builder.  If
+ * @type is non-%NULL and is non-concrete then all concrete types
+ * matching @type must be suitable for adding (ie: @type must be equal
+ * to or less general than the type expected by the builder).
+ *
+ * In the case of an array that already has at least one item in it,
+ * this function performs an additional check to ensure that @tclass
+ * and @type match the items already in the array.  @type, if given,
+ * need not be concrete in order for this check to pass.
+ *
+ * Errors are flagged in the event that the builder contains too many
+ * items or the addition would cause a type error.
+ *
+ * If @tclass is specified and is a container type and @type is not
+ * given then there is no guarantee that adding a value of that class
+ * would not fail.  Calling g_variant_builder_open() with that @tclass
+ * (and @type as %NULL) would succeed, though.
+ *
+ * In the case that any error is detected @error is set and %FALSE is
+ * returned.
+ **/
+gboolean
+g_variant_builder_check_add (GVariantBuilder     *builder,
+                             GVariantTypeClass    tclass,
+                             const GVariantType  *type,
+                             GError             **error)
+{
+  g_return_val_if_fail (builder != NULL, FALSE);
+  g_return_val_if_fail (builder->has_child == FALSE, FALSE);
+  g_return_val_if_fail (tclass != G_VARIANT_TYPE_CLASS_INVALID &&
+                        tclass != G_VARIANT_TYPE_CLASS_ALL &&
+                        tclass != G_VARIANT_TYPE_CLASS_BASIC, FALSE);
+
+  if (tclass == G_VARIANT_TYPE_CLASS_VARIANT)
+    type = NULL;
+
+  /* basically, we need to check the following:
+   *
+   * expected2 <= type <= tclass <= expected
+   *
+   * but since any combination of the types may be %NULL, we need
+   * explicit checks:
+   *
+   *   type <= class
+   *   tclass <= expected
+   *   type <= expected
+   *   expected2 <= tclass
+   *   expected2 <= type
+   *
+   * (we already know expected2 <= expected)
+   */
+
+  /* type <= tclass */
+  if (type && g_variant_type_get_class (type) != tclass)
+    {
+      gchar *type_str;
+
+      type_str = g_variant_type_dup_string (type);
+      g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                   G_VARIANT_BUILDER_ERROR_TYPE,
+                   "type '%s' is not in the correct class, expected '%c'",
+                   type_str, tclass);
+      g_free (type_str);
+      return FALSE;
+    }
+
+  /* tclass <= expected */
+  if (builder->expected)
+    {
+      GVariantTypeClass expected_class;
+     
+      expected_class = g_variant_type_get_class (builder->expected);
+
+      if (expected_class != G_VARIANT_TYPE_CLASS_ALL &&
+          (expected_class != G_VARIANT_TYPE_CLASS_BASIC ||
+           !g_variant_type_class_is_basic (tclass)) &&
+          expected_class != tclass)
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TYPE,
+                       "expecting value of class '%c', not '%c'",
+                       expected_class, tclass);
+          return FALSE;
+        }
+    }
+
+  /* type <= expected */
+  if (builder->expected && type &&
+      !g_variant_type_matches (type, builder->expected))
+    {
+      gchar *expected_str, *type_str;
+
+      expected_str = g_variant_type_dup_string (builder->expected);
+      type_str = g_variant_type_dup_string (type);
+      g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                   G_VARIANT_BUILDER_ERROR_TYPE,
+                   "type '%s' does not match expected type '%s'",
+                   type_str, expected_str);
+      g_free (expected_str);
+      g_free (type_str);
+      return FALSE;
+    }
+
+  /* expected2 <= tclass && expected2 <= type */
+  if (builder->expected2 &&
+      ((!g_variant_type_is_in_class (builder->expected2, tclass)) ||
+       (type && !g_variant_type_matches (builder->expected2, type))))
+    {
+      g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                   G_VARIANT_BUILDER_ERROR_TYPE,
+                   "all elements in an array must have the same type");
+      return FALSE;
+    }
+
+  switch (builder->class)
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      if (builder->offset)
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TOO_MANY,
+                       "a variant cannot contain more than one value");
+          return FALSE;
+        }
+      break;
+
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+      break;
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+      if (builder->offset)
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TOO_MANY,
+                       "a maybe cannot contain more than one value");
+          return FALSE;
+        }
+      break;
+
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      if (builder->offset > 1)
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TOO_MANY,
+                       "a dictionary entry may have only a key and a value");
+          return FALSE;
+        }
+      else if (builder->offset == 0)
+        {
+          if (!g_variant_type_class_is_basic (tclass))
+            {
+              g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                           G_VARIANT_BUILDER_ERROR_TYPE,
+                           "dictionary entry key must be a basic type");
+              return FALSE;
+            }
+        }
+      break;
+
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+      if (builder->type && builder->expected == NULL)
+        {
+          gchar *type_str;
+
+          type_str = g_variant_type_dup_string (builder->type);
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TOO_MANY,
+                       "too many items (%" G_GSIZE_FORMAT 
+                       ") for this tuple type '%s'",
+                       builder->offset + 1, type_str);
+          g_free (type_str);
+
+          return FALSE;
+        }
+      break;
+
+    default:
+      g_assert_not_reached ();
+  }
+
+  return TRUE;
+}
+
+/**
+ * g_variant_builder_cancel:
+ * @builder: a #GVariantBuilder
+ *
+ * Cancels the build process.  All memory associated with @builder is
+ * freed.  If the builder was created with g_variant_builder_open()
+ * then all ancestors are also freed.
+ **/
+void
+g_variant_builder_cancel (GVariantBuilder *builder)
+{
+  GVariantBuilder *parent;
+
+  g_return_if_fail (builder != NULL);
+
+  do
+    {
+      gsize i;
+
+      for (i = 0; i < builder->offset; i++)
+        g_variant_unref (builder->children[i]);
+
+      g_slice_free1 (sizeof (GVariant *) * builder->children_allocated,
+                     builder->children);
+
+      if (builder->type)
+        g_variant_type_free (builder->type);
+
+      parent = builder->parent;
+      g_slice_free (GVariantBuilder, builder);
+    }
+  while ((builder = parent));
+}
+
+/**
+ * g_variant_flatten:
+ * @value: a #GVariant instance
+ *
+ * Flattens @value.
+ *
+ * This is a strange function with no direct effects but some
+ * noteworthy side-effects.  Essentially, it ensures that @value is in
+ * its most favourable form.  This involves ensuring that the value is
+ * serialised and in machine byte order.  The investment of time now
+ * can pay off by allowing shorter access times for future calls and
+ * typically results in a reduction of memory consumption.
+ *
+ * A value received over the network or read from the disk in machine
+ * byte order is already flattened.
+ *
+ * Some of the effects of this call are that any future accesses to
+ * the data of @value (or children taken from it after flattening)
+ * will occur in O(1) time.  Also, any data accessed from such a child
+ * value will continue to be valid even after the child has been
+ * destroyed, as long as @value still exists (since the contents of
+ * the children are now serialised as part of the parent).
+ **/
+void
+g_variant_flatten (GVariant *value)
+{
+  g_return_if_fail (value != NULL);
+  g_variant_get_data (value);
+}
+
+/**
+ * g_variant_get_type_string:
+ * @value: a #GVariant
+ * @returns: the type string for the type of @value
+ *
+ * Returns the type string of @value.  Unlike the result of calling
+ * g_variant_type_peek_string(), this string is nul-terminated.  This
+ * string belongs to #GVariant and must not be freed.
+ **/
+const gchar *
+g_variant_get_type_string (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, NULL);
+  return (const gchar *) g_variant_get_type (value);
+}
+
+/**
+ * g_variant_get_type_class:
+ * @value: a #GVariant
+ * @returns: the #GVariantTypeClass of @value
+ *
+ * Returns the type class of @value.  This function is equivalent to
+ * calling g_variant_get_type() followed by
+ * g_variant_type_get_class().
+ **/
+GVariantTypeClass
+g_variant_get_type_class (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, 0);
+  return g_variant_type_get_class (g_variant_get_type (value));
+}
+
+/**
+ * g_variant_is_basic:
+ * @value: a #GVariant
+ * @returns: %TRUE if @value has a basic type
+ *
+ * Determines if @value has a basic type.  Values with basic types may
+ * be used as the keys of dictionary entries.
+ *
+ * This function is the exact opposite of g_variant_is_container().
+ **/
+gboolean
+g_variant_is_basic (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, FALSE);
+  return g_variant_type_class_is_basic (g_variant_get_type_class (value));
+}
+
+/**
+ * g_variant_is_container:
+ * @value: a #GVariant
+ * @returns: %TRUE if @value has a basic type
+ *
+ * Determines if @value has a container type.  Values with container
+ * types may be used with the functions g_variant_n_children() and
+ * g_variant_get_child().
+ *
+ * This function is the exact opposite of g_variant_is_basic().
+ **/
+gboolean
+g_variant_is_container (GVariant *value)
+{
+  g_return_val_if_fail (value != NULL, FALSE);
+  return g_variant_type_class_is_container (g_variant_get_type_class (value));
+}
+
+#include <stdio.h>
+void
+g_variant_dump_data (GVariant *value)
+{
+  const guchar *data;
+  const guchar *end;
+  char row[3*16+2];
+  gsize data_size;
+  gsize i;
+
+  g_return_if_fail (value != NULL);
+  data_size = g_variant_get_size (value);
+
+  g_debug ("GVariant at %p (type '%s', %" G_GSIZE_FORMAT " bytes):",
+           value, g_variant_get_type_string (value), data_size);
+
+  data = g_variant_get_data (value);
+  end = data + data_size;
+
+  i = 0;
+  row[3*16+1] = '\0';
+  while (data < end || (i & 15))
+    {
+      if ((i & 15) == (((gsize) data) & 15) && data < end)
+        sprintf (&row[3 * (i & 15) + (i & 8)/8], "%02x  ", *data++);
+      else
+        sprintf (&row[3 * (i & 15) + (i & 8)/8], "    ");
+
+      if ((++i & 15) == 0)
+        {
+          g_debug ("   %s", row);
+          memset (row, 'q', 3 * 16 + 1);
+        }
+    }
+
+  g_debug ("==");
+}
+
+GVariant *
+g_variant_deep_copy (GVariant *value)
+{
+  switch (g_variant_get_type_class (value))
+  {
+    case G_VARIANT_TYPE_CLASS_BOOLEAN:
+      return g_variant_new_boolean (g_variant_get_boolean (value));
+
+    case G_VARIANT_TYPE_CLASS_BYTE:
+      return g_variant_new_byte (g_variant_get_byte (value));
+
+    case G_VARIANT_TYPE_CLASS_INT16:
+      return g_variant_new_int16 (g_variant_get_int16 (value));
+
+    case G_VARIANT_TYPE_CLASS_UINT16:
+      return g_variant_new_uint16 (g_variant_get_uint16 (value));
+
+    case G_VARIANT_TYPE_CLASS_INT32:
+      return g_variant_new_int32 (g_variant_get_int32 (value));
+
+    case G_VARIANT_TYPE_CLASS_UINT32:
+      return g_variant_new_uint32 (g_variant_get_uint32 (value));
+
+    case G_VARIANT_TYPE_CLASS_INT64:
+      return g_variant_new_int64 (g_variant_get_int64 (value));
+
+    case G_VARIANT_TYPE_CLASS_UINT64:
+      return g_variant_new_uint64 (g_variant_get_uint64 (value));
+
+    case G_VARIANT_TYPE_CLASS_DOUBLE:
+      return g_variant_new_double (g_variant_get_double (value));
+      
+    case G_VARIANT_TYPE_CLASS_STRING:
+      return g_variant_new_string (g_variant_get_string (value, NULL));
+
+    case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+      return g_variant_new_object_path (g_variant_get_string (value, NULL));
+
+    case G_VARIANT_TYPE_CLASS_SIGNATURE:
+      return g_variant_new_signature (g_variant_get_string (value, NULL));
+
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      {
+        GVariant *inside, *new;
+       
+        inside = g_variant_get_variant (value);
+        new = g_variant_new_variant (g_variant_deep_copy (inside));
+        g_variant_unref (inside);
+
+        return new;
+      }
+
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      {
+        GVariantBuilder *builder;
+        GVariantIter iter;
+        GVariant *child;
+
+        builder = g_variant_builder_new (g_variant_get_type_class (value),
+                                         g_variant_get_type (value));
+        g_variant_iter_init (&iter, value);
+
+        while ((child = g_variant_iter_next_value (&iter)))
+          g_variant_builder_add_value (builder, g_variant_deep_copy (child));
+
+        return g_variant_builder_end (builder);
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+/**
+ * g_variant_new_strv:
+ * @strv: an array of strings
+ * @length: the length of @strv, or -1
+ * @returns: a new floating #GVariant instance
+ *
+ * Constructs an array of strings #GVariant from the given array of
+ * strings.
+ *
+ * If @length is not -1 then it gives the maximum length of @strv.  In
+ * any case, a %NULL pointer in @strv is taken as a terminator.
+ **/
+GVariant *
+g_variant_new_strv (const gchar * const *strv,
+                    gint                 length)
+{
+  GVariantBuilder *builder;
+
+  g_return_val_if_fail (strv != NULL || length == 0, NULL);
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                   G_VARIANT_TYPE ("as"));
+  while (length-- && *strv)
+    g_variant_builder_add (builder, "s", *strv++);
+
+  return g_variant_builder_end (builder);
+}
+
+/**
+ * g_variant_get_strv:
+ * @value: an array of strings #GVariant
+ * @length: the length of the result, or %NULL
+ * @returns: an array of constant strings
+ *
+ * Gets the contents of an array of strings #GVariant.  This call
+ * makes a shallow copy; the return result should be released with
+ * g_free(), but the individual strings must not be modified.
+ *
+ * If @length is non-%NULL then the number of elements in the result
+ * is stored there.  In any case, the resulting array will be
+ * %NULL-terminated.
+ *
+ * For an empty array, @length will be set to 0 and a pointer to a
+ * %NULL pointer will be returned.
+ **/
+const gchar **
+g_variant_get_strv (GVariant *value,
+                    gint     *length)
+{
+  const gchar **result;
+  gint my_length;
+  gint i;
+
+  g_return_val_if_fail (value != NULL, NULL);
+  g_return_val_if_fail (g_variant_matches (value, G_VARIANT_TYPE ("as")),
+                        NULL);
+
+  g_variant_flatten (value);
+
+  my_length = g_variant_n_children (value);
+  result = g_new (const gchar *, my_length + 1);
+
+  if (length)
+    *length = my_length;
+
+  for (i = 0; i < my_length; i++)
+    {
+      GVariant *child = g_variant_get_child_value (value, i);
+      result[i] = g_variant_get_string (child, NULL);
+      g_variant_unref (child);
+    }
+  result[i] = NULL;
+
+  return result;
+}
+
+/**
+ * g_variant_dup_strv:
+ * @value: an array of strings #GVariant
+ * @length: the length of the result, or %NULL
+ * @returns: an array of constant strings
+ *
+ * Gets the contents of an array of strings #GVariant.  This call
+ * makes a deep copy; the return result should be released with
+ * g_strfreev().
+ *
+ * If @length is non-%NULL then the number of elements in the result
+ * is stored there.  In any case, the resulting array will be
+ * %NULL-terminated.
+ *
+ * For an empty array, @length will be set to 0 and a pointer to a
+ * %NULL pointer will be returned.
+ **/
+gchar **
+g_variant_dup_strv (GVariant *value,
+                    gint     *length)
+{
+  gchar **result;
+  gint my_length;
+  gint i;
+
+  g_return_val_if_fail (value != NULL, NULL);
+  g_return_val_if_fail (g_variant_matches (value, G_VARIANT_TYPE ("as")),
+                        NULL);
+
+  g_variant_flatten (value);
+
+  my_length = g_variant_n_children (value);
+  result = g_new (gchar *, my_length + 1);
+
+  if (length)
+    *length = my_length;
+
+  for (i = 0; i < my_length; i++)
+    {
+      GVariant *child = g_variant_get_child_value (value, i);
+      result[i] = g_variant_dup_string (child, NULL);
+      g_variant_unref (child);
+    }
+  result[i] = NULL;
+
+  return result;
+}
+
+/**
+ * g_variant_lookup_value:
+ * @dictionary: a #GVariant dictionary, keyed by strings
+ * @key: a string to lookup in the dictionary
+ * @returns: the value corresponding to @key, or %NULL
+
+ * Looks up @key in @dictionary.  This is essentially a convenience
+ * function for dealing with the extremely common case of a dictionary
+ * keyed by strings.
+ *
+ * In the case that the key is found, the corresponding value is
+ * returned; not the dictionary entry.  If the key is not found then
+ * this function returns %NULL.
+ **/
+GVariant *
+g_variant_lookup_value (GVariant    *dictionary,
+                        const gchar *key)
+{
+  GVariantIter iter;
+  const gchar *_key;
+  GVariant *value;
+  GVariant *result = NULL;
+
+  g_return_val_if_fail (dictionary != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
+  g_variant_iter_init (&iter, dictionary);
+  while (g_variant_iter_next (&iter, "{&s*}", &_key, &value))
+    if (strcmp (_key, key) == 0)
+      {
+        result = g_variant_ref (value);
+        g_variant_iter_cancel (&iter);
+      }
+
+  return result;
+}
+
+/**
+ * g_variant_from_file:
+ * @type: the #GVariantType of the new variant
+ * @filename: the filename to load from
+ * @flags: zero or more #GVariantFlags
+ * @error: a pointer to a #GError, or %NULL
+ * @returns: a new #GVariant instance, or %NULL
+ *
+ * Utility function to load a #GVariant from the contents of a file,
+ * using a #GMappedFile.
+ *
+ * This function attempts to open @filename using #GMappedFile and then
+ * calls g_variant_from_data() on the result.  As with that function,
+ * @type may be %NULL.
+ **/
+GVariant *
+g_variant_from_file (const GVariantType *type,
+                     const gchar        *filename,
+                     GVariantFlags       flags,
+                     GError             **error)
+{
+  GMappedFile *mapped;
+  gconstpointer data;
+  gsize size;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  mapped = g_mapped_file_new (filename, FALSE, error);
+
+  if (mapped == NULL)
+    return NULL;
+
+  data = g_mapped_file_get_contents (mapped);
+  size = g_mapped_file_get_length (mapped);
+
+  if (size == 0)
+  /* #595535 */
+    data = NULL;
+
+  return g_variant_from_data (type, data, size, flags,
+                              (GDestroyNotify) g_mapped_file_unref,
+                              mapped);
+}
+
+#define __G_VARIANT_UTIL_C__
+#include "galiasdef.c"
diff --git a/glib/gvariant-valist.c b/glib/gvariant-valist.c
new file mode 100644
index 0000000..0e302ab
--- /dev/null
+++ b/glib/gvariant-valist.c
@@ -0,0 +1,1563 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gvariant-private.h"
+
+#include <glib/gtestutils.h>
+#include <glib/gmessages.h>
+#include <string.h>
+
+#include "galias.h"
+
+/**
+ * g_variant_format_string_scan:
+ * @string: a string that may be prefixed with a format string
+ * @limit: a pointer to the end of @string
+ * @endptr: location to store the end pointer, or %NULL
+ * @returns: %TRUE if there was a valid format string
+ *
+ * Checks the string pointed to by @string for starting with a properly
+ * formed #GVariant varargs format string.  If no valid format string is
+ * found then %FALSE is returned.
+ *
+ * If @string does start with a valid format string and @endptr is
+ * non-%NULL then it is updated to point to the first character after
+ * the format string.  If @endptr is %NULL and there is any character
+ * following the format string then this call returns %FALSE.  Another
+ * way of saying this is that if @endptr is %NULL then @string is
+ * checked to contain exactly one valid format string and nothing else.
+ *
+ * If @limit is non-%NULL then @limit (and any charater after it) will
+ * not be accessed and the effect is otherwise equivalent to if the
+ * character at @limit were nul.
+ *
+ * All valid #GVariantType strings are also valid format strings.  See
+ * g_variant_type_string_is_valid().
+ *
+ * Additionally, any type string contained in the format string may be
+ * prefixed with a '@' character.  Nested '@' characters may not
+ * appear.
+ *
+ * Additionally, any fixed-width type may be prefixed with a '&'
+ * character.  No wildcard type is a fixed-width type.  Like '@', '&'
+ * characters may not be nested.
+ *
+ * Additionally, any array of fixed-width types may be prefixed with a
+ * '&'.
+ *
+ * Additionally, '&s', '^as' and '^a&s' may appear.
+ *
+ * No '@', '&' or '^' character, however, may appear as part of an array
+ * type.
+ *
+ * Currently, there are no other permissible format strings.  Others
+ * may be added in the future.
+ *
+ * For an explanation of what these strings mean, see g_variant_new()
+ * and g_variant_get().
+ **/
+gboolean
+g_variant_format_string_scan (const gchar  *string,
+                              const gchar  *limit,
+                              const gchar **endptr)
+{
+  const gchar *start;
+
+  if (string == limit || *string == '\0')
+    return FALSE;
+
+  switch (*string++)
+    {
+    case '(':
+      while (string == limit || *string != ')')
+        if (!g_variant_format_string_scan (string, limit, &string))
+          return FALSE;
+
+      string++;
+      break;
+
+    case '{':
+      if (string != limit && (*string == '@' || *string == '&'))
+        string++;
+
+      if (string == limit || *string == '\0' ||                    /* { */
+          !strchr ("bynqiuxtdsog?", *string++) ||                  /* key */
+          !g_variant_format_string_scan (string, limit, &string) ||/* value */
+          string == limit || *string++ != '}')                     /* } */
+        return FALSE;
+
+      break;
+
+    case 'm':
+      return g_variant_format_string_scan (string, limit, endptr); /* tcall */
+
+    case 'a':
+    case '@':
+      return g_variant_type_string_scan (string, limit, endptr);   /* tcall */
+
+    case '&':
+      start = string;
+
+      if (!g_variant_type_string_scan (string, limit, &string))
+        return FALSE;
+
+      if (start + 1 == string && *start == 's')
+        break;
+
+      if (*start == 'a')
+        start++;
+
+      while (start != string)
+        if (!strchr ("bynqiuxtd(){}", *start++))
+          return FALSE;
+
+      break;
+
+    case '^':
+      if (string == limit || *string++ != 'a')
+        return FALSE;
+
+      if (string != limit && *string == '&')
+        string++;
+
+      if (string == limit || *string++ != 's')
+        return FALSE;
+
+      break;
+
+    case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
+    case 'x': case 't': case 'd': case 's': case 'o': case 'g':
+    case 'v': case '*': case '?': case 'r':
+      break;
+
+    default:
+      return FALSE;
+  }
+
+  if (endptr != NULL)
+    *endptr = string;
+  else
+    {
+      if (string != limit && *string != '\0')
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * g_variant_format_string_scan_type:
+ * @string: a string that may be prefixed with a format string
+ * @limit: a pointer to the end of @string
+ * @endptr: location to store the end pointer, or %NULL
+ * @returns: a #GVariantType if there was a valid format string
+ *
+ * If @string starts with a valid format string then this function will
+ * return the type that the format string corresponds to.  Otherwise
+ * this function returns %NULL.
+ *
+ * The returned type must be freed by the caller.
+ *
+ * This function is otherwise exactly like
+ * g_variant_format_string_scan().
+ **/
+GVariantType *
+g_variant_format_string_scan_type (const gchar  *string,
+                                   const gchar  *limit,
+                                   const gchar **endptr)
+{
+  const gchar *my_end;
+  gchar *dest;
+  gchar *new;
+
+  if (endptr == NULL)
+    endptr = &my_end;
+
+  if (!g_variant_format_string_scan (string, limit, endptr))
+    return NULL;
+
+  dest = new = g_malloc (*endptr - string + 1);
+  while (string != *endptr)
+    {
+      if (*string != '@' && *string != '&' && *string != '^')
+        *dest++ = *string;
+      string++;
+    }
+  *dest = '\0';
+
+  return (GVariantType *) G_VARIANT_TYPE (new);
+}
+
+static GVariant *
+g_variant_valist_new (const gchar **format_string,
+                      va_list      *app)
+{
+  switch (**format_string)
+  {
+    case 'b':
+      (*format_string)++;
+      return g_variant_new_boolean (va_arg (*app, gboolean));
+
+    case 'y':
+      (*format_string)++;
+      return g_variant_new_byte (va_arg (*app, guint));
+
+    case 'n':
+      (*format_string)++;
+      return g_variant_new_int16 (va_arg (*app, gint));
+
+    case 'q':
+      (*format_string)++;
+      return g_variant_new_uint16 (va_arg (*app, guint));
+
+    case 'i':
+      (*format_string)++;
+      return g_variant_new_int32 (va_arg (*app, gint));
+
+    case 'u':
+      (*format_string)++;
+      return g_variant_new_uint32 (va_arg (*app, guint));
+
+    case 'x':
+      (*format_string)++;
+      return g_variant_new_int64 (va_arg (*app, gint64));
+
+    case 't':
+      (*format_string)++;
+      return g_variant_new_uint64 (va_arg (*app, guint64));
+
+    case 'd':
+      (*format_string)++;
+      return g_variant_new_double (va_arg (*app, gdouble));
+
+    case 's':
+      (*format_string)++;
+      return g_variant_new_string (va_arg (*app, const gchar *));
+
+    case 'o':
+      (*format_string)++;
+      return g_variant_new_object_path (va_arg (*app, const gchar *));
+
+    case 'g':
+      (*format_string)++;
+      return g_variant_new_signature (va_arg (*app, const gchar *));
+
+    case 'v':
+      (*format_string)++;
+      return g_variant_new_variant (va_arg (*app, GVariant *));
+
+    case '@':
+    case '*':
+    case '?':
+    case 'r':
+      g_variant_format_string_scan (*format_string, NULL, format_string);
+      return va_arg (*app, GVariant *);
+
+    case '^':
+      {
+        const gchar *string = (*format_string + 1);
+
+        if (string[0] == 'a' &&
+            (string[1] == 's' ||
+             (string[1] == '&' && string[2] == 's')))
+          {
+            gconstpointer ptr;
+            gint n_items;
+
+            *format_string += 3 + (string[1] == '&');
+            ptr = va_arg (*app, gconstpointer);
+            n_items = va_arg (*app, gint);
+
+            return g_variant_new_strv ((const gchar **) ptr, n_items);
+          }
+
+        g_error ("Currently, only ^as and ^a&s are supported");
+      }
+
+    case '&':
+      {
+        const gchar *string = (*format_string + 1);
+        gconstpointer ptr;
+        gint n_items = 0;
+
+        ptr = va_arg (*app, gconstpointer);
+
+        switch (*string++)
+          {
+          case 's': /* '&s' */
+            /* for building, just the same as normal 's' */
+            *format_string += 2;
+            return g_variant_new_string (ptr);
+
+          case 'a':
+            n_items = va_arg (*app, gint);
+
+            if (n_items < 0)
+              g_error ("size of -1 can only be specified for string arrays");
+
+            /* fall through */
+
+          default:
+            {
+              GVariantType *type;
+              GVariant *value;
+
+              type = g_variant_format_string_scan_type (*format_string,
+                                                        NULL, format_string);
+              g_assert (g_variant_type_is_concrete (type));
+
+              value = g_variant_load_fixed (type, ptr, n_items);
+              g_variant_type_free (type);
+
+              return value;
+            }
+          }
+      }
+
+    case 'a':
+      g_variant_format_string_scan (*format_string, NULL, format_string);
+      return g_variant_builder_end (va_arg (*app, GVariantBuilder *));
+
+    case 'm':
+      {
+        GVariantBuilder *builder;
+        const gchar *string;
+        GVariantType *type;
+        GVariant *value;
+
+        string = (*format_string) + 1;
+        type = g_variant_format_string_scan_type (*format_string,
+                                                  NULL, format_string);
+        builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_MAYBE, type);
+        g_variant_type_free (type);
+
+        switch (*string)
+        {
+          case 's':
+            if ((string = va_arg (*app, const gchar *)))
+              g_variant_builder_add_value (builder,
+                                           g_variant_new_string (string));
+            break;
+
+          case 'o':
+            if ((string = va_arg (*app, const gchar *)))
+              g_variant_builder_add_value (builder,
+                                           g_variant_new_object_path (string));
+            break;
+
+          case 'g':
+            if ((string = va_arg (*app, const gchar *)))
+              g_variant_builder_add_value (builder,
+                                           g_variant_new_signature (string));
+            break;
+
+          case '@':
+          case '*':
+          case '?':
+          case 'r':
+            if ((value = va_arg (*app, GVariant *)))
+              g_variant_builder_add_value (builder, value);
+            break;
+
+          case 'v':
+            if ((value = va_arg (*app, GVariant *)))
+              g_variant_builder_add_value (builder,
+                                           g_variant_new_variant (value));
+            break;
+
+          case 'a':
+            {
+              GVariantBuilder *array;
+
+              if ((array = va_arg (*app, GVariantBuilder *)))
+                g_variant_builder_add_value (builder,
+                                             g_variant_builder_end (array));
+              break;
+            }
+
+          case '&':
+            {
+              gconstpointer ptr;
+
+              if ((ptr = va_arg (*app, gconstpointer)))
+                {
+                  GVariantType *type;
+                  gsize n_items;
+
+                  type = g_variant_format_string_scan_type (string,
+                                                            NULL, NULL);
+                  g_assert (g_variant_type_is_concrete (type));
+
+                  if (g_variant_type_is_in_class (type,
+                                                  G_VARIANT_TYPE_CLASS_ARRAY))
+                    n_items = va_arg (*app, gsize);
+                  else
+                    n_items = 0;
+
+                  g_variant_builder_add_value (builder,
+                                               g_variant_load_fixed (type,
+                                                                     ptr,
+                                                                     n_items));
+                  g_variant_type_free (type);
+                }
+              break;
+            }
+            
+          default:
+            {
+              gboolean just;
+
+              just = va_arg (*app, gboolean);
+
+              if (just)
+                {
+                  value = g_variant_valist_new (&string, app);
+                  g_variant_builder_add_value (builder, value);
+                  g_assert (string == *format_string);
+                }
+            }
+        }
+
+        return g_variant_builder_end (builder);
+      }
+
+    case '(':
+    case '{':
+      {
+        GVariantBuilder *builder;
+
+        if (**format_string == '(')
+          builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_TUPLE,
+                                           NULL);
+        else
+          builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_DICT_ENTRY,
+                                           NULL);
+
+        (*format_string)++;                                          /* '(' */
+        while (**format_string != ')' && **format_string != '}')
+          {
+            GVariant *value;
+
+            value = g_variant_valist_new (format_string, app);
+            g_variant_builder_add_value (builder, value);
+          }
+        (*format_string)++;                                          /* ')' */
+
+        return g_variant_builder_end (builder);
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+static void
+g_variant_valist_get (GVariant     *value,
+                      gboolean      free,
+                      const gchar **format_string,
+                      va_list      *app)
+{
+  switch ((*format_string)[0])
+  {
+    case 'b':
+      {
+        gboolean *ptr = va_arg (*app, gboolean *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_boolean (value);
+            else
+              *ptr = FALSE;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'y':
+      {
+        guchar *ptr = va_arg (*app, guchar *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_byte (value);
+            else
+              *ptr = '\0';
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'n':
+      {
+        gint16 *ptr = va_arg (*app, gint16 *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_int16 (value);
+            else
+              *ptr = 0;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'q':
+      {
+        guint16 *ptr = va_arg (*app, guint16 *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_uint16 (value);
+            else
+              *ptr = 0;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'i':
+      {
+        gint32 *ptr = va_arg (*app, gint32 *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_int32 (value);
+            else
+              *ptr = 0;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'u':
+      {
+        guint32 *ptr = va_arg (*app, guint32 *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_uint32 (value);
+            else
+              *ptr = 0;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'x':
+      {
+        gint64 *ptr = va_arg (*app, gint64 *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_int64 (value);
+            else
+              *ptr = 0;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 't':
+      {
+        guint64 *ptr = va_arg (*app, guint64 *);
+        
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_uint64 (value);
+            else
+              *ptr = 0;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'd':
+      {
+        gdouble *ptr = va_arg (*app, gdouble *);
+
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_double (value);
+            else
+              *ptr = 0.;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 's':
+    case 'o':
+    case 'g':
+      {
+        gchar **ptr = va_arg (*app, gchar **);
+
+        if (ptr)
+          {
+            if (free && *ptr)
+              g_free (*ptr);
+
+            if (value)
+              *ptr = g_variant_dup_string (value, NULL);
+            else
+              *ptr = NULL;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case 'v':
+      {
+        GVariant **ptr = va_arg (*app, GVariant **);
+
+        if (ptr)
+          {
+            if (free && *ptr)
+              g_variant_unref (*ptr);
+
+            if (value)
+              *ptr = g_variant_get_variant (value);
+            else
+              *ptr = NULL;
+          }
+
+        (*format_string)++;
+        return;
+      }
+
+    case '@':
+    case '*':
+    case '?':
+    case 'r':
+      {
+        GVariant **ptr = va_arg (*app, GVariant **);
+
+        if (ptr)
+          {
+            if (free && *ptr)
+              g_variant_unref (*ptr);
+
+            if (value)
+              *ptr = g_variant_ref (value);
+            else
+              *ptr = NULL;
+          }
+
+        g_variant_format_string_scan (*format_string, NULL, format_string);
+        return;
+      }
+
+    case 'a':
+      {
+        GVariantIter *ptr = va_arg (*app, GVariantIter *);
+
+        if (ptr)
+          {
+            if (free)
+              g_variant_iter_cancel (ptr);
+
+            if (value)
+              g_variant_iter_init (ptr, value);
+            else
+              {
+                memset (ptr, 0, sizeof (GVariantIter));
+                g_variant_iter_cancel (ptr);
+              }
+          }
+
+        g_variant_format_string_scan (*format_string, NULL, format_string);
+        return;
+      }
+
+    case '^':
+      {
+        const gchar *string = (*format_string + 1);
+
+        if (string[0] == 'a')
+          {
+            gpointer *ptr;
+            gint *n_items;
+
+            ptr = va_arg (*app, gpointer *);
+            n_items = va_arg (*app, gint *);
+
+            if (string[1] == 's')
+              {
+                *format_string += 3;
+
+                if (free && *ptr)
+                  g_strfreev (*ptr);
+
+                if (value)
+                  *ptr = g_variant_dup_strv (value, n_items);
+                else
+                  *ptr = NULL;
+
+                return;
+              }
+
+            if (string[1] == '&' && string[2] == 's')
+              {
+                *format_string += 4;
+
+                if (free && *ptr)
+                  g_free (*ptr);
+
+                if (value)
+                  *ptr = g_variant_get_strv (value, n_items);
+                else
+                  *ptr = NULL;
+
+                return;
+              }
+          }
+
+        g_error ("Currently, only ^as and ^a&s are supported");
+      }
+
+
+    case '&':
+      {
+        gconstpointer *ptr;
+
+        ptr = va_arg (*app, gconstpointer *);
+
+        if ((*format_string)[1] == 's') /* '&s' */
+          {
+            /* cannot just get_data() like below.
+             * might not be nul-terminated */
+            if (ptr)
+              {
+                if (value)
+                  *ptr = g_variant_get_string (value, NULL);
+                else
+                  *ptr = NULL;
+              }
+
+            (*format_string) += 2;
+            return;
+          }
+
+        if ((*format_string)[1] == 'a') /* '&a..' */
+          {
+            gint *n_items = va_arg (*app, gint *);
+
+            if (n_items)
+              {
+                if (value)
+                  *n_items = g_variant_n_children (value);
+                else
+                  *n_items = 0;
+              }
+          }
+
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_data (value);
+            else
+              *ptr = NULL;
+          }
+
+        g_variant_format_string_scan (*format_string, NULL, format_string);
+        return;
+      }
+
+    case 'm':
+      {
+        GVariant *just;
+
+        if (value && g_variant_n_children (value))
+          just = g_variant_get_child_value (value, 0);
+        else
+          just = NULL;
+
+        /* skip the 'm', check the next character */
+        if (!strchr ("sogv *?ra&", *++(*format_string)))
+          {
+            gboolean *ptr = va_arg (*app, gboolean *);
+
+            if (ptr)
+              {
+                g_variant_valist_get (just, free && *ptr,
+                                      format_string, app);
+                *ptr = just != NULL;
+              }
+            else
+              g_variant_format_string_scan (*format_string,
+                                            NULL, format_string);
+          }
+        else
+          g_variant_valist_get (just, free, format_string, app);
+
+        if (just)
+          g_variant_unref (just);
+        return;
+      }
+
+    case '(':
+    case '{':
+      {
+        GVariantIter iter;
+        GVariant *child;
+        char end_char;
+
+        if (**format_string == '(')
+          end_char = ')';
+        else
+          end_char = '}';
+
+        if (value)
+          g_variant_iter_init (&iter, value);
+
+        (*format_string)++;                                          /* '(' */
+        while (**format_string != ')' && **format_string != '}')
+          {
+            if (value)
+              {
+                child = g_variant_iter_next_value (&iter);
+                g_assert (child != NULL);
+              }
+            else
+              child = NULL;
+
+            g_variant_valist_get (child, free, format_string, app);
+          }
+        (*format_string)++;                                          /* ')' */
+
+        if (value)
+          {
+            child = g_variant_iter_next_value (&iter);
+            g_assert (child == NULL);
+          }
+
+        return;
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+/**
+ * g_variant_new:
+ * @format_string: a #GVariant format string
+ * @...: arguments, as per @format_string
+ * @returns: a new floating #GVariant instance
+ *
+ * Creates a new #GVariant instance.
+ *
+ * Think of this function as an analogue to g_strdup_printf().
+ *
+ * The type of the created instance and the arguments that are
+ * expected by this function are determined by @format_string.  In the
+ * most simple case, @format_string is exactly equal to a concrete
+ * #GVariantType type string and the result is of that type.  All
+ * exceptions to this case are explicitly mentioned below.
+ *
+ * The arguments that this function collects are determined by
+ * scanning @format_string from start to end.  Brackets do not impact
+ * the collection of arguments (except when following 'a', 'm' or '@';
+ * see below).  Each other character that is encountered will result
+ * in an argument being collected.
+ *
+ * Arguments for the base types are expected as follows:
+ * <informaltable>
+ *   <tgroup cols='3'>
+ *     <colspec align='center'/>
+ *     <colspec align='center'/>
+ *     <colspec align='left'/>
+ *     <thead>
+ *       <row>
+ *       <entry>Character</entry>
+ *       <entry>Argument Type</entry>
+ *       <entry>Notes</entry>
+ *       </row>
+ *     </thead>
+ *     <tbody>
+ *       <row>
+ *         <entry><literal>b</literal></entry>
+ *         <entry>#gboolean</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>y</literal></entry>
+ *         <entry>#guchar</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>n</literal></entry>
+ *         <entry>#gint16</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>q</literal></entry>
+ *         <entry>#guint16</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>i</literal></entry>
+ *         <entry>#gint32</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>u</literal></entry>
+ *         <entry>#guint32</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>x</literal></entry>
+ *           <entry>#gint64</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>t</literal></entry>
+ *         <entry>#guint64</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>d</literal></entry>
+ *         <entry>#gdouble</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>s</literal></entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>must be non-%NULL</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>o</literal></entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>must be non-%NULL and a valid DBus object path</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>g</literal></entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>must be non-%NULL and a valid DBus signature string</entry>
+ *       </row>
+ *     </tbody>
+ *   </tgroup>
+ * </informaltable>
+ *
+ * If a 'v' character is encountered in @format_string then a
+ * (#GVariant *) is collected which must be non-%NULL and must point
+ * to a valid #GVariant instance.
+ *
+ * If an array type is encountered in @format_string, a
+ * (#GVariantBuilder *) is collected and has g_variant_builder_end()
+ * called on it.  The type of the array has no impact on argument
+ * collection but is checked against the type of the array and can be
+ * used to infer the type of an empty array.  As a special exception,
+ * if %NULL is given then an empty array of the given type will be
+ * used.
+ *
+ * If a maybe type is encountered in @format_string, then the expected
+ * arguments vary depending on the type.
+ *
+ * <informaltable>
+ *   <tgroup cols='3'>
+ *     <colspec align='center'/>
+ *     <colspec align='center'/>
+ *     <colspec align='left'/>
+ *     <thead>
+ *       <row>
+ *       <entry>Format string</entry>
+ *       <entry>Argument Type</entry>
+ *       <entry>Notes</entry>
+ *       </row>
+ *     </thead>
+ *     <tbody>
+ *       <row>
+ *         <entry>ms</entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>use %NULL for Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>mo</entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>use %NULL for Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>mg</entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>use %NULL for Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>mv</entry>
+ *         <entry>#GVariant *</entry>
+ *         <entry>use %NULL for Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>m *</entry>
+ *         <entry>#GVariant *</entry>
+ *         <entry>use %NULL for Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>otherwise</entry>
+ *         <entry>#gboolean</entry>
+ *         <entry>
+ *           use %FALSE for Nothing.  If %TRUE is given then the
+ *           arguments will be collected, after the #gboolean, exactly
+ *           as they would be if there were no 'm'.
+ *         </entry>
+ *       </row>
+ *     </tbody>
+ *   </tgroup>
+ * </informaltable>
+ *
+ * If a '@' character is encountered in @format_string, followed by a
+ * type string, then a (#GVariant *) is collected which must be
+ * non-%NULL and must point to a valid #GVariant instance.  This
+ * #GVariant is inserted directly at the given position.  The given
+ * #GVariant must match the provided format string.
+ *
+ * '*' means exactly the same as '@*'. 'r' means exactly the same as
+ * '@r'.  '?' means exactly the same as '@?'.
+ *
+ * The first character of the format string must not be '*' '?' '@' or
+ * 'r'; in essence, a new #GVariant must always be constructed by this
+ * function (and not merely passed through it unmodified).
+ *
+ * Please note that the syntax of the format string is very likely to
+ * be extended in the future.
+ **/
+GVariant *
+g_variant_new (const gchar *format_string,
+               ...)
+{
+  GVariant *value;
+  va_list ap;
+
+  g_assert (format_string != NULL);
+  g_assert (strchr ("*? r", format_string[0]) == NULL);
+
+  va_start (ap, format_string);
+  value = g_variant_new_va (NULL, format_string, NULL, &ap);
+  va_end (ap);
+
+  return value;
+}
+
+/**
+ * g_variant_new_va:
+ * @must_be_null: %NULL (for future expansion)
+ * @format_string: a string that is prefixed with a format string
+ * @endptr: location to store the end pointer, or %NULL
+ * @app: a pointer to a #va_list
+ * @returns: a new, usually floating, #GVariant
+ *
+ * This function is intended to be used by libraries based on
+ * #GVariant that want to provide g_variant_new()-like functionality
+ * to their users.
+ *
+ * The API is more general than g_variant_new() to allow a wider range
+ * of possible uses.
+
+ * @format_string must still point to a valid format string, but it only
+ * need to be nul-terminated if @endptr is %NULL.  If @endptr is
+ * non-%NULL then it is updated to point to the first character past the
+ * end of the format string.
+ *
+ * @app is a pointer to a #va_list.  The arguments, according to
+ * @format_string, are collected from this #va_list and the list is left
+ * pointing to the argument following the last.
+ *
+ * These two generalisations allow mixing of multiple calls to
+ * g_variant_new_va() and g_variant_get_va() within a single actual
+ * varargs call by the user.
+ *
+ * The return value will be floating if it was a newly created GVariant
+ * instance (for example, if the format string was "(ii)").  In the case
+ * that the format_string was '*', '?', 'r', or a format starting with
+ * '@' then the collected #GVariant pointer will be returned unmodified,
+ * without adding any additional references.
+ *
+ * In order to behave correctly in all cases it is necessary for the
+ * calling function to g_variant_ref_sink() the return result before
+ * returning control to the user that originally provided the pointer.
+ * At this point, the caller will have their own full reference to the
+ * result.  This can also be done by adding the result to a container,
+ * or by passing it to another g_variant_new() call.
+ **/
+GVariant *
+g_variant_new_va (gpointer      must_be_null,
+                  const gchar  *format_string,
+                  const gchar **endptr,
+                  va_list      *app)
+{
+  GVariant *value;
+
+  g_return_val_if_fail (format_string != NULL, NULL);
+  g_return_val_if_fail (must_be_null == NULL, NULL);
+  g_return_val_if_fail (app != NULL, NULL);
+
+  value = g_variant_valist_new (&format_string, app);
+
+  if (endptr != NULL)
+    *endptr = format_string;
+  else
+    g_assert (*format_string == '\0');
+
+  g_variant_flatten (value);
+
+  return value;
+}
+
+/**
+ * g_variant_get:
+ * @value: a #GVariant instance
+ * @format_string: a #GVariant format string
+ * @...: arguments, as per @format_string
+ *
+ * Deconstructs a #GVariant instance.
+ *
+ * Think of this function as an analogue to scanf().
+ *
+ * The arguments that are expected by this function are entirely
+ * determined by @format_string.  @format_string also restricts the
+ * permissible types of @value.  It is an error to give a value with
+ * an incompatible type.
+ *
+ * In the most simple case, @format_string is exactly equal to a
+ * concrete #GVariantType type string and @value must have that type.
+ * All exceptions to this case are explicitly mentioned below.
+ *
+ * The arguments that this function collects are determined by
+ * scanning @format_string from start to end.  Brackets do not impact
+ * the collection of arguments (except when following 'a', 'm' or '@';
+ * see below).  Each other character that is encountered will result
+ * in an argument being collected.
+ *
+ * All arguments are pointer types.  Any pointer may be %NULL and in
+ * that case, collection will be supressed for that position (ie: you
+ * can use a %NULL pointer if you don't care about the value of a
+ * particular child of a complex value).
+ *
+ * Arguments for the base types are expected as follows:
+ * <informaltable>
+ *   <tgroup cols='2'>
+ *     <colspec align='center'/>
+ *     <colspec align='center'/>
+ *     <thead>
+ *       <row>
+ *         <entry>Character</entry>
+ *         <entry>Argument Type</entry>
+ *       </row>
+ *     </thead>
+ *     <tbody>
+ *       <row>
+ *         <entry><literal>b</literal></entry>
+ *         <entry>#gboolean*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>y</literal></entry>
+ *         <entry>#guchar*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>n</literal></entry>
+ *         <entry>#gint16*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>q</literal></entry>
+ *         <entry>#guint16*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>i</literal></entry>
+ *         <entry>#gint32*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>u</literal></entry>
+ *         <entry>#guint32*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>x</literal></entry>
+ *         <entry>#gint64*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>t</literal></entry>
+ *         <entry>#guint64*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>d</literal></entry>
+ *         <entry>#gdouble*</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>s</literal></entry>
+ *         <entry>const #gchar *</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>o</literal></entry>
+ *         <entry>const #gchar *</entry>
+ *       </row>
+ *       <row>
+ *         <entry><literal>g</literal></entry>
+ *         <entry>const #gchar *</entry>
+ *       </row>
+ *     </tbody>
+ *   </tgroup>
+ * </informaltable>
+ *
+ * If a 'v' character is encountered in @format_string then a
+ * (#GVariant **) is collected.  If this is non-%NULL then it is set
+ * to a new reference to a #GVariant.  You must call g_variant_unref()
+ * on it later.
+ *
+ * If an array type is encountered in @format_string, a
+ * (#GVariantIter *) is collected.  If this is non-%NULL then the
+ * #GVariantIter at which it points will be initialised for iterating
+ * over the array.  The array must be iterated (or the iter cancelled)
+ * in order to avoid leaking memory.
+ *
+ * If a maybe type is encountered in @format_string, then the expected
+ * arguments vary depending on the type.
+ *
+ * <informaltable>
+ *   <tgroup cols='3'>
+ *     <colspec align='center'/>
+ *     <colspec align='center'/>
+ *     <colspec align='left'/>
+ *     <thead>
+ *       <row>
+ *       <entry>Format string</entry>
+ *       <entry>Argument Type</entry>
+ *       <entry>Notes</entry>
+ *       </row>
+ *     </thead>
+ *     <tbody>
+ *       <row>
+ *         <entry>ms</entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>set to %NULL in case of Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>mo</entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>set to %NULL in case of Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>mg</entry>
+ *         <entry>const #gchar *</entry>
+ *         <entry>set to %NULL in case of Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>mv</entry>
+ *         <entry>#GVariant *</entry>
+ *         <entry>set to %NULL in case of Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>m *</entry>
+ *         <entry>#GVariant *</entry>
+ *         <entry>set to %NULL in case of Nothing</entry>
+ *       </row>
+ *       <row>
+ *         <entry>otherwise</entry>
+ *         <entry>#gboolean*</entry>
+ *         <entry>
+ *           If %NULL is given then the entire value is ignored.
+ *           Else, the #gboolean is set to %TRUE to indicate that the
+ *           maybe is non-Nothing or %FALSE to indicate Nothing.  In
+ *           the %TRUE case, the normal arguments (as per the format
+ *           string) are collected and handled normally.  In the
+ *           %FALSE case, the normal arguments are collected and
+ *           ignored.  This allows the same set of arguments to be
+ *           given without knowing the content of the value ahead of
+ *           time.
+ *         </entry>
+ *       </row>
+ *     </tbody>
+ *   </tgroup>
+ * </informaltable>
+ *
+ * If a '@' character is encountered in @format_string, followed by a
+ * type string, then a (#GVariant **) is collected.  If this is
+ * non-%NULL then it is set to a new reference to a #GVariant the
+ * corresponds to the value at the current position.  You must call
+ * g_variant_unref() on it later.  The returned value will match the
+ * type string provided.
+ *
+ * '*' means exactly the same as '@*'. 'r' means exactly the same as
+ * '@r'.  '?' means exactly the same as '@?'.
+ *
+ * Please note that the syntax of the format string is very likely to
+ * be extended in the future.
+ **/
+void
+g_variant_get (GVariant    *value,
+               const gchar *format_string,
+               ...)
+{
+  va_list ap;
+
+  va_start (ap, format_string);
+  g_variant_get_va (value, NULL, format_string, NULL, &ap);
+  va_end (ap);
+}
+
+/**
+ * g_variant_get_va:
+ * @value: a #GVariant
+ * @must_be_null: %NULL (for future expansion)
+ * @format_string: a string that is prefixed with a format string
+ * @endptr: location to store the end pointer, or %NULL
+ * @app: a pointer to a #va_list
+ *
+ * This function is intended to be used by libraries based on #GVariant
+ * that want to provide g_variant_get()-like functionality to their
+ * users.
+ *
+ * The API is more general than g_variant_get() to allow a wider range
+ * of possible uses.
+ *
+ * @format_string must still point to a valid format string, but it only
+ * need to be nul-terminated if @endptr is %NULL.  If @endptr is
+ * non-%NULL then it is updated to point to the first character past the
+ * end of the format string.
+ *
+ * @app is a pointer to a #va_list.  The arguments, according to
+ * @format_string, are collected from this #va_list and the list is left
+ * pointing to the argument following the last.
+ *
+ * These two generalisations allow mixing of multiple calls to
+ * g_variant_new_va() and g_variant_get_va() within a single actual
+ * varargs call by the user.
+ **/
+void
+g_variant_get_va (GVariant     *value,
+                  gpointer      must_be_null,
+                  const gchar  *format_string,
+                  const gchar **endptr,
+                  va_list      *app)
+{
+  g_return_if_fail (format_string != NULL);
+  g_return_if_fail (must_be_null == NULL);
+  g_return_if_fail (value != NULL);
+  g_return_if_fail (app != NULL);
+
+  {
+    GVariantType *type;
+
+    type = g_variant_format_string_scan_type (format_string, NULL, endptr);
+    g_return_if_fail (type != NULL);
+    g_return_if_fail (g_variant_matches (value, type));
+    g_variant_type_free (type);
+  }
+
+  g_variant_flatten (value);
+  g_variant_valist_get (value, FALSE, &format_string, app);
+}
+
+/**
+ * g_variant_iter_next:
+ * @iter: a #GVariantIter
+ * @format_string: a format string
+ * @...: arguments, as per @format_string
+ * @returns: %TRUE if a child was fetched or %FALSE if not
+ *
+ * Retreives the next child value from @iter and deconstructs it
+ * according to @format_string.  This call is sort of like calling
+ * g_variant_iter_next() and g_variant_get().
+ *
+ * This function does something else, though: on all but the first
+ * call (including on the last call, which returns %FALSE) the values
+ * allocated by the previous call will be freed.  This allows you to
+ * iterate without ever freeing anything yourself.  In the case of
+ * #GVariant * arguments, they are unref'd and in the case of
+ * #GVariantIter arguments, they are cancelled.
+ *
+ * Note that strings are not freed since (as with g_variant_get())
+ * they are constant pointers to internal #GVariant data.
+ *
+ * This function might be used as follows:
+ *
+ * <programlisting>
+ * {
+ *   const gchar *key, *value;
+ *   GVariantIter iter;
+ *   ...
+ *
+ *   while (g_variant_iter_next (iter, "{ss}", &key, &value))
+ *     printf ("dict['%s'] = '%s'\n", key, value);
+ * }
+ * </programlisting>
+ **/
+gboolean
+g_variant_iter_next (GVariantIter *iter,
+                     const gchar  *format_string,
+                     ...)
+{
+  gboolean free_args;
+  GVariant *next;
+  va_list ap;
+
+  free_args = g_variant_iter_should_free (iter);
+  next = g_variant_iter_next_value (iter);
+  /* g_assert (free_args || next != NULL);
+   * XXX this fails on empty iters */
+
+  if (next)
+    g_variant_flatten (next);
+
+  va_start (ap, format_string);
+  g_variant_valist_get (next, free_args, &format_string, &ap);
+  g_assert (*format_string == '\0');
+  va_end (ap);
+
+  return next != NULL;
+}
+
+/**
+ * g_variant_builder_add:
+ * @builder: a #GVariantBuilder
+ * @format_string: a #GVariant varargs format string
+ * @...: arguments, as per @format_string
+ *
+ * Adds to a #GVariantBuilder.
+ *
+ * This call is a convenience wrapper that is exactly equivalent to
+ * calling g_variant_new() followed by g_variant_builder_add_value().
+ * 
+ * This function might be used as follows:
+ *
+ * <programlisting>
+ * GVariant *
+ * make_pointless_dictionary (void)
+ * {
+ *   GVariantBuilder *builder;
+ *   int i;
+ *
+ *   builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+ *                                    NULL);
+ *   for (i = 0; i < 16; i++)
+ *     {
+ *       char buf[3];
+ *
+ *       sprintf (buf, "%d", i);
+ *       g_variant_builder_add (builder, "{is}", i, buf);
+ *     }
+ *
+ *   return g_variant_builder_end (builder);
+ * }
+ * </programlisting>
+ **/
+void
+g_variant_builder_add (GVariantBuilder *builder,
+                       const gchar     *format_string,
+                       ...)
+{
+  GVariant *variant;
+  va_list ap;
+
+  va_start (ap, format_string);
+  variant = g_variant_new_va (NULL, format_string, NULL, &ap);
+  va_end (ap);
+
+  g_variant_builder_add_value (builder, variant);
+}
+
+/**
+ * g_variant_get_child:
+ * @value: a container #GVariant
+ * @index: the index of the child to deconstruct
+ * @format_string: a #GVariant format string
+ * @...: arguments, as per @format_string
+ *
+ * Reads a child item out of a container #GVariant instance and
+ * deconstructs it according to @format_string.  This call is
+ * essentially a combination of g_variant_get_child_value() and
+ * g_variant_get().
+ **/
+void
+g_variant_get_child (GVariant    *value,
+                     gint         index,
+                     const gchar *format_string,
+                     ...)
+{
+  GVariant *child;
+  va_list ap;
+
+  g_variant_flatten (value);
+  child = g_variant_get_child_value (value, index);
+
+  va_start (ap, format_string);
+  g_variant_get_va (child, NULL, format_string, NULL, &ap);
+  va_end (ap);
+
+  g_variant_unref (child);
+}
+
+/**
+ * g_variant_lookup:
+ * @dictionary: a #GVariant dictionary, keyed by strings
+ * @key: a string to lookup in the dictionary
+ * @format_string: a #GVariant format string, or %NULL
+ * @...: arguments, as per @format_string
+ * @returns: %TRUE if @key was found, else %FALSE
+ *
+ * Looks up @key in @dictionary and deconstructs it according to
+ * @format_string.  This call is essentially a combination of
+ * g_variant_lookup_value() and g_variant_get().
+ *
+ * If @key is not found, then no deconstruction occurs (ie: the argument
+ * list is left untouched) and %FALSE is returned.  If @key is found
+ * then %TRUE is returned.
+ *
+ * As a special case, if @format_string is %NULL then the lookup occurs
+ * but no deconstruction is preformed.  This is useful for checking (via
+ * the return value) if a key is in the dictionary or not.
+ **/
+gboolean
+g_variant_lookup (GVariant    *dictionary,
+                  const gchar *key,
+                  const gchar *format_string,
+                  ...)
+{
+  GVariant *child;
+  va_list ap;
+
+  if (format_string)
+    g_variant_flatten (dictionary);
+
+  child = g_variant_lookup_value (dictionary, key);
+
+  if (child == NULL)
+    return FALSE;
+
+  if (format_string)
+    {
+      va_start (ap, format_string);
+      g_variant_get_va (child, NULL, format_string, NULL, &ap);
+      va_end (ap);
+    }
+
+  g_variant_unref (child);
+
+  return TRUE;
+}
+
+#define __G_VARIANT_VALIST_C__
+#include "galiasdef.c"
diff --git a/glib/gvariant.h b/glib/gvariant.h
new file mode 100644
index 0000000..01f3cca
--- /dev/null
+++ b/glib/gvariant.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ * Copyright © 2009 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_VARIANT_H__
+#define __G_VARIANT_H__
+
+#include <glib/gvarianttype.h>
+#include <glib/gstring.h>
+#include <glib/gmarkup.h>
+#include <glib/gerror.h>
+#include <stdarg.h>
+
+typedef struct                  OPAQUE_TYPE__GVariant                   GVariant;
+typedef struct                  OPAQUE_TYPE__GVariantIter               GVariantIter;
+typedef struct                  OPAQUE_TYPE__GVariantBuilder            GVariantBuilder;
+
+struct OPAQUE_TYPE__GVariantIter
+{
+  gpointer priv[8];
+};
+
+G_BEGIN_DECLS
+
+GVariant *                      g_variant_ref                           (GVariant             *value);
+GVariant *                      g_variant_ref_sink                      (GVariant             *value);
+void                            g_variant_unref                         (GVariant             *value);
+void                            g_variant_flatten                       (GVariant             *value);
+
+GVariantTypeClass               g_variant_get_type_class                (GVariant             *value);
+const GVariantType *            g_variant_get_type                      (GVariant             *value);
+const gchar *                   g_variant_get_type_string               (GVariant             *value);
+gboolean                        g_variant_is_basic                      (GVariant             *value);
+gboolean                        g_variant_is_container                  (GVariant             *value);
+gboolean                        g_variant_matches                       (GVariant             *value,
+                                                                         const GVariantType   *pattern);
+
+/* varargs construct/deconstruct */
+GVariant *                      g_variant_new                           (const gchar          *format_string,
+                                                                         ...);
+void                            g_variant_get                           (GVariant             *value,
+                                                                         const gchar          *format_string,
+                                                                         ...);
+
+gboolean                        g_variant_format_string_scan            (const gchar          *string,
+                                                                         const gchar          *limit,
+                                                                         const gchar         **endptr);
+GVariantType *                  g_variant_format_string_scan_type       (const gchar          *string,
+                                                                         const gchar          *limit,
+                                                                         const gchar         **endptr);
+GVariant *                      g_variant_new_va                        (gpointer              must_be_null,
+                                                                         const gchar          *format_string,
+                                                                         const gchar         **endptr,
+                                                                         va_list              *app);
+void                            g_variant_get_va                        (GVariant             *value,
+                                                                         gpointer              must_be_null,
+                                                                         const gchar          *format_string,
+                                                                         const gchar         **endptr,
+                                                                         va_list              *app);
+
+/* constructors */
+GVariant *                      g_variant_new_boolean                   (gboolean              boolean);
+GVariant *                      g_variant_new_byte                      (guint8                byte);
+GVariant *                      g_variant_new_uint16                    (guint16               uint16);
+GVariant *                      g_variant_new_int16                     (gint16                int16);
+GVariant *                      g_variant_new_uint32                    (guint32               uint32);
+GVariant *                      g_variant_new_int32                     (gint32                int32);
+GVariant *                      g_variant_new_uint64                    (guint64               uint64);
+GVariant *                      g_variant_new_int64                     (gint64                int64);
+GVariant *                      g_variant_new_double                    (gdouble               floating);
+GVariant *                      g_variant_new_string                    (const gchar          *string);
+GVariant *                      g_variant_new_object_path               (const gchar          *string);
+gboolean                        g_variant_is_object_path                (const gchar          *string);
+GVariant *                      g_variant_new_signature                 (const gchar          *string);
+gboolean                        g_variant_is_signature                  (const gchar          *string);
+GVariant *                      g_variant_new_variant                   (GVariant             *value);
+GVariant *                      g_variant_new_strv                      (const gchar * const  *strv,
+                                                                         gint                  length);
+
+/* deconstructors */
+gboolean                        g_variant_get_boolean                   (GVariant             *value);
+guint8                          g_variant_get_byte                      (GVariant             *value);
+guint16                         g_variant_get_uint16                    (GVariant             *value);
+gint16                          g_variant_get_int16                     (GVariant             *value);
+guint32                         g_variant_get_uint32                    (GVariant             *value);
+gint32                          g_variant_get_int32                     (GVariant             *value);
+guint64                         g_variant_get_uint64                    (GVariant             *value);
+gint64                          g_variant_get_int64                     (GVariant             *value);
+gdouble                         g_variant_get_double                    (GVariant             *value);
+const gchar *                   g_variant_get_string                    (GVariant             *value,
+                                                                         gsize                *length);
+gchar *                         g_variant_dup_string                    (GVariant             *value,
+                                                                         gsize                *length);
+const gchar **                  g_variant_get_strv                      (GVariant             *value,
+                                                                         gint                 *length);
+gchar **                        g_variant_dup_strv                      (GVariant             *value,
+                                                                         gint                 *length);
+GVariant *                      g_variant_get_variant                   (GVariant             *value);
+gconstpointer                   g_variant_get_fixed                     (GVariant             *value,
+                                                                         gsize                 size);
+gconstpointer                   g_variant_get_fixed_array               (GVariant             *value,
+                                                                         gsize                 elem_size,
+                                                                         gsize                *length);
+gsize                           g_variant_n_children                    (GVariant             *value);
+GVariant *                      g_variant_get_child_value               (GVariant             *value,
+                                                                         gsize                 index);
+void                            g_variant_get_child                     (GVariant             *value,
+                                                                         gint                  index,
+                                                                         const gchar          *format_string,
+                                                                         ...);
+GVariant *                      g_variant_lookup_value                  (GVariant             *dictionary,
+                                                                         const gchar          *key);
+gboolean                        g_variant_lookup                        (GVariant             *dictionary,
+                                                                         const gchar          *key,
+                                                                         const gchar          *format_string,
+                                                                         ...);
+
+/* GVariantIter */
+gsize                           g_variant_iter_init                     (GVariantIter         *iter,
+                                                                         GVariant             *value);
+GVariant *                      g_variant_iter_next_value               (GVariantIter         *iter);
+void                            g_variant_iter_cancel                   (GVariantIter         *iter);
+gboolean                        g_variant_iter_was_cancelled            (GVariantIter         *iter);
+gboolean                        g_variant_iter_next                     (GVariantIter         *iter,
+                                                                         const gchar          *format_string,
+                                                                         ...);
+
+/* GVariantBuilder */
+void                            g_variant_builder_add_value             (GVariantBuilder      *builder,
+                                                                         GVariant             *value);
+void                            g_variant_builder_add                   (GVariantBuilder      *builder,
+                                                                         const gchar          *format_string,
+                                                                         ...);
+GVariantBuilder *               g_variant_builder_open                  (GVariantBuilder      *parent,
+                                                                         GVariantTypeClass     tclass,
+                                                                         const GVariantType   *type);
+GVariantBuilder *               g_variant_builder_close                 (GVariantBuilder      *child);
+gboolean                        g_variant_builder_check_add             (GVariantBuilder      *builder,
+                                                                         GVariantTypeClass     tclass,
+                                                                         const GVariantType   *type,
+                                                                         GError              **error);
+gboolean                        g_variant_builder_check_end             (GVariantBuilder      *builder,
+                                                                         GError              **error);
+GVariantBuilder *               g_variant_builder_new                   (GVariantTypeClass     tclass,
+                                                                         const GVariantType   *type);
+GVariant *                      g_variant_builder_end                   (GVariantBuilder      *builder);
+void                            g_variant_builder_cancel                (GVariantBuilder      *builder);
+
+#define G_VARIANT_BUILDER_ERROR \
+    (g_quark_from_static_string ("g-variant-builder-error-quark"))
+
+typedef enum
+{
+  G_VARIANT_BUILDER_ERROR_TOO_MANY,
+  G_VARIANT_BUILDER_ERROR_TOO_FEW,
+  G_VARIANT_BUILDER_ERROR_INFER,
+  G_VARIANT_BUILDER_ERROR_TYPE
+} GVariantBuilderError;
+
+/* text printing/parsing */
+typedef struct
+{
+  const gchar *start;
+  const gchar *end;
+  gchar *error;
+} GVariantParseError;
+
+gchar *                         g_variant_print                         (GVariant             *value,
+                                                                         gboolean              type_annotate);
+GString *                       g_variant_print_string                  (GVariant             *value,
+                                                                         GString              *string,
+                                                                         gboolean              type_annotate);
+GVariant *                      g_variant_parse                         (const gchar          *text,
+                                                                         gint                  text_length,
+                                                                         const GVariantType   *type,
+                                                                         GError              **error);
+GVariant *                      g_variant_parse_full                    (const gchar          *text,
+                                                                         const gchar          *limit,
+                                                                         const gchar         **endptr,
+                                                                         const GVariantType   *type,
+                                                                         GVariantParseError   *error);
+GVariant *                      g_variant_new_parsed                    (const gchar          *format,
+                                                                         ...);
+GVariant *                      g_variant_new_parsed_va                 (const gchar          *format,
+                                                                         va_list              *app);
+
+/* markup printing/parsing */
+gchar *                         g_variant_markup_print                  (GVariant             *value,
+                                                                         gboolean              newlines,
+                                                                         gint                  indentation,
+                                                                         gint                  tabstop);
+GString *                       g_variant_markup_print_string           (GVariant             *value,
+                                                                         GString              *string,
+                                                                         gboolean              newlines,
+                                                                         gint                  indentation,
+                                                                         gint                  tabstop);
+void                            g_variant_markup_subparser_start        (GMarkupParseContext  *context,
+                                                                         const GVariantType   *type);
+GVariant *                      g_variant_markup_subparser_end          (GMarkupParseContext  *context,
+                                                                         GError              **error);
+GMarkupParseContext *           g_variant_markup_parse_context_new      (GMarkupParseFlags     flags,
+                                                                         const GVariantType   *type);
+GVariant *                      g_variant_markup_parse_context_end      (GMarkupParseContext  *context,
+                                                                         GError              **error);
+GVariant *                      g_variant_markup_parse                  (const gchar          *text,
+                                                                         gssize                text_len,
+                                                                         const GVariantType   *type,
+                                                                         GError              **error);
+
+/* load/store serialised format */
+typedef enum
+{
+  G_VARIANT_TRUSTED             = 0x00010000,
+  G_VARIANT_LAZY_BYTESWAP       = 0x00020000,
+} GVariantFlags;
+
+GVariant *                      g_variant_load                          (const GVariantType  *type,
+                                                                         gconstpointer        data,
+                                                                         gsize                size,
+                                                                         GVariantFlags        flags);
+GVariant *                      g_variant_from_slice                    (const GVariantType  *type,
+                                                                         gpointer             slice,
+                                                                         gsize                size,
+                                                                         GVariantFlags        flags);
+GVariant *                      g_variant_from_data                     (const GVariantType  *type,
+                                                                         gconstpointer        data,
+                                                                         gsize                size,
+                                                                         GVariantFlags        flags,
+                                                                         GDestroyNotify       notify,
+                                                                         gpointer             user_data);
+GVariant *                      g_variant_from_file                     (const GVariantType  *type,
+                                                                         const gchar         *filename,
+                                                                         GVariantFlags        flags,
+                                                                         GError              **error);
+
+void                            g_variant_store                         (GVariant            *value,
+                                                                         gpointer             data);
+gconstpointer                   g_variant_get_data                      (GVariant            *value);
+gsize                           g_variant_get_size                      (GVariant            *value);
+
+G_END_DECLS
+
+#endif /* __G_VARIANT_H__ */
diff --git a/glib/gvarianttype.c b/glib/gvarianttype.c
new file mode 100644
index 0000000..4651647
--- /dev/null
+++ b/glib/gvarianttype.c
@@ -0,0 +1,1045 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gvarianttype.h"
+
+#include <glib/gtestutils.h>
+
+#include <string.h>
+
+#include "galias.h"
+
+/**
+ * SECTION: gvarianttype
+ * @title: GVariantType
+ * @short_description: a variant type system
+ * @see_also: GVariantType
+ *
+ * A #GVariantType represents the type of a #GVariant instance.
+ **/
+
+/**
+ * GVariantType:
+ *
+ * An opaque type representing either the type of a #GVariant instance
+ * or a pattern that could match other types.
+ *
+ * Each #GVariantType has a corresponding type string.  The grammar
+ * generating all valid type strings is:
+ *
+ * <programlisting>
+ * type = base | 'a' type | 'm' type | 'v' | '*'
+ *      | 'r' | '{' + base + type + '}' | '(' + types + ')'
+ *
+ * base = 'b' | 'y' | 'n' | 'q' | 'i' | 'u' | 'x'
+ *      | 't' | 'd' | 's' | 'o' | 'g' | '?'
+ *
+ * types = '' | type + types
+ * </programlisting>
+ *
+ * The types that have single character type strings are all defined
+ * with their own constants (for example, %G_VARIANT_TYPE_BOOLEAN).
+ *
+ * The types that have type strings starting with 'a' are array types,
+ * where the characters after the 'a' are the type string of the array
+ * element type.
+ *
+ * The types that have type strings starting with 'm' are maybe types,
+ * where the characters after the 'm' are the type string of the maybe
+ * element type.
+ *
+ * The types that start with '{' and end with '}' are dictionary
+ * entry types, where the first contained type string is the one
+ * corresponding to the type of the key, and the second is the one
+ * corresponding to the type of the value.
+ *
+ * The types that start with '(' and end with ')' are tuple types,
+ * where each type string contained between the brackets corresponds
+ * to an item type of that tuple type.
+ *
+ * Any type that has a type string that can be generated from 'base'
+ * is in the #GVariantTypeClass %G_VARIANT_TYPE_CLASS_BASIC.
+ *
+ * Any type that has a type string that can be generated from 'type'
+ * is in the class %G_VARIANT_TYPE_CLASS_ALL.  This is all types.
+ *
+ * Each type is a member of at least one other #GVariantTypeClass.
+ *
+ * Note that, in reality, a #GVariantType is just a string pointer
+ * cast to an opaque type.  It is only valid to have a pointer of
+ * this type, however, if you are sure that it is a valid type string.
+ * Functions that take #GVariantType as parameters assume that the
+ * string is well-formed.  Also note that a #GVariantType is not
+ * necessarily nul-terminated.
+ **/
+
+/**
+ * G_VARIANT_TYPE:
+ * @type_string: a well-formed #GVariantType type string
+ *
+ * Converts a string to a const #GVariantType.  Depending on the
+ * current debugging level, this function may perform a runtime check
+ * to ensure that @string is valid.
+ *
+ * It is always a programmer error to use this macro with an invalid
+ * type string.
+ **/
+
+#ifdef GVARIANT_DEBUG
+static gboolean
+g_variant_type_check (const GVariantType *type)
+{
+  const gchar *type_string = (const gchar *) type;
+
+  return g_variant_type_string_scan (type_string, NULL, &type_string);
+}
+#endif
+
+/**
+ * g_variant_type_string_scan:
+ * @string: a pointer to any string
+ * @limit: the end of @string, or %NULL
+ * @endptr: location to store the end pointer, or %NULL
+ * @returns: %TRUE if a valid type string was found
+ *
+ * Scan for a single complete and valid #GVariantType type string in
+ * @string.  The memory pointed to by @limit (or bytes beyond it)
+ * is never accessed.
+ *
+ * If a valid type string is found, @endptr is updated to point
+ * to the first character past the end of the string that was found
+ * and %TRUE is returned.
+ *
+ * If there is no valid type string starting at @string, or if
+ * the type string does not end before @limit then %FALSE is returned.
+ *
+ * For the simple case of checking if a string is a valid type string,
+ * see g_variant_type_string_is_valid().
+ **/
+gboolean
+g_variant_type_string_scan (const gchar  *string,
+                            const gchar  *limit,
+                            const gchar **endptr)
+{
+  if (string == limit || *string == '\0')
+    return FALSE;
+
+  switch (*string++)
+    {
+    case '(':
+      while (string == limit || *string != ')')
+        if (!g_variant_type_string_scan (string, limit, &string))
+          return FALSE;
+
+      string++;
+      break;
+
+    case '{':
+      if (string == limit || *string == '\0' ||                    /* { */
+          !strchr ("bynqiuxtdsog?", *string++) ||                  /* key */
+          !g_variant_type_string_scan (string, limit, &string) ||  /* value */
+          string == limit || *string++ != '}')                     /* } */
+        return FALSE;
+
+      break;
+
+    case 'm': case 'a':
+      return g_variant_type_string_scan (string, limit, endptr);   /* tcall */
+
+    case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
+    case 'x': case 't': case 'd': case 's': case 'o': case 'g':
+    case 'v': case 'r': case '*': case '?':
+      break;
+
+    default:
+      return FALSE;
+    }
+
+  if (endptr != NULL)
+    *endptr = string;
+  else
+    {
+      if (string != limit && *string != '\0')
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+/**
+ * g_variant_type_string_is_valid:
+ * @type_string: a pointer to any string
+ * @returns: %TRUE if @type_string is exactly one valid type string
+ *
+ * Checks if @type_string is a valid #GVariantType type string.  This
+ * call is equivalent to calling g_variant_type_string_scan() and
+ * confirming that the following character is a nul terminator.
+ **/
+gboolean
+g_variant_type_string_is_valid (const gchar *type_string)
+{
+  if (!g_variant_type_string_scan (type_string, NULL, NULL))
+    return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * g_variant_type_free:
+ * @type: a #GVariantType
+ *
+ * Frees a #GVariantType that was allocated with
+ * g_variant_type_copy(), g_variant_type_new() or one of the container
+ * type constructor functions.
+ **/
+void
+g_variant_type_free (GVariantType *type)
+{
+  g_free (type);
+}
+
+/**
+ * g_variant_type_copy:
+ * @type: a #GVariantType
+ * @returns: a new #GVariantType
+ *
+ * Makes a copy of a #GVariantType.  This copy must be freed using
+ * g_variant_type_free().
+ **/
+GVariantType *
+g_variant_type_copy (const GVariantType *type)
+{
+  gsize length;
+  gchar *new;
+
+  length = g_variant_type_get_string_length (type);
+  new = g_malloc (length + 1);
+
+  memcpy (new, type, length);
+  new[length] = '\0';
+
+  return (GVariantType *) new;
+}
+
+/**
+ * g_variant_type_new:
+ * @type_string: a valid #GVariantType type string
+ * @returns: a new #GVariantType
+ *
+ * Creates a new #GVariantType corresponding to the type string given
+ * by @type_string.  This new type must be freed using
+ * g_variant_type_free().
+ *
+ * It is an error to call this function with an invalid type string.
+ */
+GVariantType *
+g_variant_type_new (const gchar *type_string)
+{
+  return g_variant_type_copy (G_VARIANT_TYPE (type_string));
+}
+
+/**
+ * g_variant_type_get_string_length:
+ * @type: a #GVariantType
+ * @returns: the length of the corresponding type string
+ *
+ * Returns the length of the type string corresponding to the given
+ * @type.  This function must be used to determine the valid extent of
+ * the memory region returned by g_variant_type_peek_string().
+ **/
+gsize
+g_variant_type_get_string_length (const GVariantType *type)
+{
+  const gchar *type_string = (const gchar *) type;
+  gint brackets = 0;
+  gsize index = 0;
+
+  do
+    {
+      while (type_string[index] == 'a' || type_string[index] == 'm')
+        index++;
+
+      if (type_string[index]== '(' || type_string[index] == '{')
+        brackets++;
+
+      else if (type_string[index] == ')' || type_string[index] == '}')
+        brackets--;
+
+      index++;
+    }
+  while (brackets);
+
+  return index;
+}
+
+/**
+ * g_variant_type_peek_string:
+ * @type: a #GVariantType
+ * @returns: the corresponding type string (non-terminated)
+ *
+ * Returns the type string corresponding to the given @type.  The
+ * result is not nul-terminated; in order to determine its length you
+ * must call g_variant_type_get_string_length().
+ *
+ * To get a nul-terminated string, see g_variant_type_dup_string().
+ **/
+const gchar *
+g_variant_type_peek_string (const GVariantType *type)
+{
+  return (const gchar *) type;
+}
+
+/**
+ * g_variant_type_dup_string:
+ * @type: a #GVariantType
+ * @returns: the corresponding type string (must be freed)
+ *
+ * Returns a newly-allocated copy of the type string corresponding to
+ * @type.  The return result is nul-terminated and must be freed using
+ * g_free().
+ **/
+gchar *
+g_variant_type_dup_string (const GVariantType *type)
+{
+  return g_strndup (g_variant_type_peek_string (type),
+                    g_variant_type_get_string_length (type));
+}
+
+const GVariantType *
+g_variant_type_check_string_ (const gchar *type_string)
+{
+  g_assert (g_variant_type_string_is_valid (type_string));
+
+  return (GVariantType *) type_string;
+}
+
+/**
+ * g_variant_type_is_concrete:
+ * @type: a #GVariantType
+ * @returns: %TRUE if @type is concrete
+ *
+ * Determines if the given @type is a concrete (ie: non-wildcard)
+ * type.  A #GVariant instance may only have a concrete type.
+ *
+ * A type is concrete if its type string does not contain any wildcard
+ * characters ('*', '?' or 'r').
+ **/
+gboolean
+g_variant_type_is_concrete (const GVariantType *type)
+{
+  const gchar *type_string = g_variant_type_peek_string (type);
+  gsize type_length = g_variant_type_get_string_length (type);
+  gsize i;
+
+  for (i = 0; i < type_length; i++)
+    if (strchr ("*?r", type_string[i]))
+      return FALSE;
+
+  return TRUE;
+}
+
+/**
+ * g_variant_type_is_container:
+ * @type: a #GVariantType
+ * @returns: %TRUE if @type is a container type
+ *
+ * Determines if the given @type is a container type.
+ *
+ * Container types are any array, maybe, tuple, or dictionary
+ * entry types plus the variant type.
+ *
+ * This function returns %TRUE for any wildcard type for which every
+ * matching concrete type is a container.  This does not include
+ * %G_VARIANT_TYPE_ANY.
+ **/
+gboolean
+g_variant_type_is_container (const GVariantType *type)
+{
+  gchar first_char = g_variant_type_peek_string (type)[0];
+
+  switch (first_char)
+  {
+    case 'a':
+    case 'm':
+    case 'r':
+    case '(':
+    case '{':
+    case 'v':
+      return TRUE;
+
+    default:
+      return FALSE;
+  }
+}
+
+/**
+ * g_variant_type_is_basic:
+ * @type: a #GVariantType
+ * @returns: %TRUE if @type is a basic type
+ *
+ * Determines if the given @type is a basic type.
+ *
+ * Basic types are booleans, bytes, integers, doubles, strings, object
+ * paths and signatures.
+ *
+ * Only a basic type may be used as the key of a dictionary entry.
+ *
+ * This function returns %FALSE for all wildcard types except
+ * %G_VARIANT_TYPE_ANY_BASIC.
+ **/
+gboolean
+g_variant_type_is_basic (const GVariantType *type)
+{
+  gchar first_char = g_variant_type_peek_string (type)[0];
+
+  switch (first_char)
+  {
+    case 'b':
+    case 'y':
+    case 'n':
+    case 'q':
+    case 'i':
+    case 'u':
+    case 't':
+    case 'x':
+    case 'd':
+    case 's':
+    case 'o':
+    case 'g':
+    case '?':
+      return TRUE;
+
+    default:
+      return FALSE;
+  }
+}
+
+/**
+ * g_variant_type_hash:
+ * @type: a #GVariantType
+ * @returns: the hash value
+ *
+ * Hashes @type.
+ *
+ * The argument type of @type is only #gconstpointer to allow use with
+ * #GHashTable without function pointer casting.  A valid
+ * #GVariantType must be provided.
+ **/
+guint
+g_variant_type_hash (gconstpointer type)
+{
+  const gchar *type_string = g_variant_type_peek_string (type);
+  gsize length = g_variant_type_get_string_length (type);
+  guint value = 0;
+  gsize i;
+
+  for (i = 0; i < length; i++)
+    value = (value << 5) - value + type_string[i];
+
+  return value;
+}
+
+/**
+ * g_variant_type_equal:
+ * @type1: a #GVariantType
+ * @type2: a #GVariantType
+ * @returns: %TRUE if @type1 and @type2 are exactly equal
+ *
+ * Compares @type1 and @type2 for equality.
+ *
+ * Only returns %TRUE if the types are exactly equal.  Even if one
+ * type is a wildcard type and the other matches it, false will be
+ * returned if they are not exactly equal.  If you want to check for
+ * matching, use g_variant_type_matches().
+ *
+ * The argument types of @type1 and @type2 are only #gconstpointer to
+ * allow use with #GHashTable without function pointer casting.  For
+ * both arguments, a valid #GVariantType must be provided.
+ **/
+gboolean
+g_variant_type_equal (gconstpointer type1,
+                      gconstpointer type2)
+{
+  const gchar *string1, *string2;
+  gsize size1, size2;
+
+  if (type1 == type2)
+    return TRUE;
+
+  size1 = g_variant_type_get_string_length (type1);
+  size2 = g_variant_type_get_string_length (type2);
+
+  if (size1 != size2)
+    return FALSE;
+
+  string1 = g_variant_type_peek_string (type1);
+  string2 = g_variant_type_peek_string (type2);
+
+  return memcmp (string1, string2, size1) == 0;
+}
+
+/**
+ * g_variant_type_matches:
+ * @type: a #GVariantType
+ * @pattern: a #GVariantType
+ * @returns: %TRUE if @type matches @pattern
+ *
+ * Performs a pattern match between @type and @pattern.
+ *
+ * This function returns %TRUE if @type can be reached by making
+ * @pattern less general (ie: by replacing zero or more wildcard
+ * characters in the type string of @pattern with matching type
+ * strings that possibly contain wildcards themselves).
+ *
+ * This function defines a bounded join-semilattice over #GVariantType
+ * for which %G_VARIANT_TYPE_ANY is top.
+ **/
+gboolean
+g_variant_type_matches (const GVariantType *type,
+                        const GVariantType *pattern)
+{
+  const gchar *pattern_string;
+  const gchar *pattern_end;
+  const gchar *type_string;
+
+  pattern_string = g_variant_type_peek_string (pattern);
+  type_string = g_variant_type_peek_string (type);
+
+  pattern_end = pattern_string + g_variant_type_get_string_length (pattern);
+
+  /* we know that type and pattern are both well-formed, so it's
+   * safe to treat this merely as a text processing problem.
+   */
+  while (pattern_string < pattern_end)
+    {
+      char pattern_char = *pattern_string++;
+
+      if (pattern_char == *type_string)
+        type_string++;
+
+      else if (*type_string == ')')
+        return FALSE;
+
+      else
+        {
+          const GVariantType *target_type = (GVariantType *) type_string;
+
+          if (!g_variant_type_is_in_class (target_type, pattern_char))
+            return FALSE;
+
+          type_string += g_variant_type_get_string_length (target_type);
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * g_variant_type_is_in_class:
+ * @type: a #GVariantType
+ * @tclass: a #GVariantTypeClass
+ * @returns: %TRUE if @type is in the given @class
+ *
+ * Determines if @type is contained within @class.
+ *
+ * Note that the class %G_VARIANT_TYPE_CLASS_ALL contains every type
+ * and the class %G_VARIANT_TYPE_CLASS_BASIC contains every basic
+ * type.
+ **/
+gboolean
+g_variant_type_is_in_class (const GVariantType *type,
+                            GVariantTypeClass   tclass)
+{
+  char first_char = *(const gchar *) type;
+
+  switch (tclass)
+  {
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+      return first_char == '(' || first_char == 'r';
+
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      return first_char == '{';
+
+    case G_VARIANT_TYPE_CLASS_BASIC:
+      return g_variant_type_is_basic (type);
+
+    case G_VARIANT_TYPE_CLASS_ALL:
+      return TRUE;
+
+    default:
+      return tclass == first_char;
+  }
+}
+
+/**
+ * g_variant_type_get_class:
+ * @type: a #GVariantType
+ * @returns: the smallest class containing @type
+ *
+ * Determines the smallest type class containing @type.
+ *
+ * For example, this function would return
+ * %G_VARIANT_TYPE_CLASS_BOOLEAN for %G_VARIANT_TYPE_BOOLEAN and
+ * %G_VARIANT_TYPE_CLASS_ARRAY for the type corresponding to the type
+ * string "ai".  %G_VARIANT_TYPE_CLASS_ALL is returned for
+ * %G_VARIANT_TYPE_ANY and for no other types (because although it
+ * contains all types, it is not the smallest containing class for any
+ * other type).
+ **/
+GVariantTypeClass
+g_variant_type_get_class (const GVariantType *type)
+{
+  char first_char = *(const gchar *) type;
+
+  switch (first_char)
+  {
+    case '(':
+      return G_VARIANT_TYPE_CLASS_TUPLE;
+
+    case '{':
+      return G_VARIANT_TYPE_CLASS_DICT_ENTRY;
+
+    default:
+      return first_char;
+  }
+}
+
+/**
+ * g_variant_type_class_is_container:
+ * @tclass: a #GVariantTypeClass
+ * @returns: %TRUE if @class is a container class
+ *
+ * Determines if @class is a container class.
+ *
+ * The following are considered to be container classes: maybe, array,
+ * tuple, dict_entry and variant.
+ **/
+gboolean
+g_variant_type_class_is_container (GVariantTypeClass tclass)
+{
+  switch (tclass)
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+    case G_VARIANT_TYPE_CLASS_TUPLE:
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      return TRUE;
+
+    default:
+      return FALSE;
+  }
+}
+
+/**
+ * g_variant_type_class_is_basic:
+ * @tclass: a #GVariantTypeClass
+ * @returns: %TRUE if @class is a basic class
+ *
+ * Determines if @class is a basic class.
+ *
+ * The following are considered to be basic classes: boolean, byte,
+ * the signed and unsigned integer classes, double, string, object
+ * path and signature.  Additionally, the 'basic' type class is also
+ * considered to be basic.
+ **/
+gboolean
+g_variant_type_class_is_basic (GVariantTypeClass tclass)
+{
+  switch (tclass)
+  {
+    case G_VARIANT_TYPE_CLASS_BOOLEAN:
+    case G_VARIANT_TYPE_CLASS_BYTE:
+    case G_VARIANT_TYPE_CLASS_INT16:
+    case G_VARIANT_TYPE_CLASS_UINT16:
+    case G_VARIANT_TYPE_CLASS_INT32:
+    case G_VARIANT_TYPE_CLASS_UINT32:
+    case G_VARIANT_TYPE_CLASS_INT64:
+    case G_VARIANT_TYPE_CLASS_UINT64:
+    case G_VARIANT_TYPE_CLASS_DOUBLE:
+    case G_VARIANT_TYPE_CLASS_STRING:
+    case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+    case G_VARIANT_TYPE_CLASS_SIGNATURE:
+    case G_VARIANT_TYPE_CLASS_BASIC:
+      return TRUE;
+
+    default:
+      return FALSE;
+  }
+}
+
+/**
+ * g_variant_type_element:
+ * @type: a #GVariantType of class array or maybe
+ * @returns: the element type of @type
+ *
+ * Determines the element type of an array or maybe type.
+ *
+ * This function must be called with a type in one of the classes
+ * %G_VARIANT_TYPE_CLASS_MAYBE or %G_VARIANT_TYPE_CLASS_ARRAY.
+ **/
+const GVariantType *
+g_variant_type_element (const GVariantType *type)
+{
+  const gchar *type_string = g_variant_type_peek_string (type);
+
+  g_assert (type_string[0] == 'a' || type_string[0] == 'm');
+
+  return (const GVariantType *) &type_string[1];
+}
+
+/**
+ * g_variant_type_first:
+ * @type: a #GVariantType of class tuple or dict entry
+ * @returns: the first item type of @type, or %NULL
+ *
+ * Determines the first item type of a tuple or dictionary entry
+ * type.
+ *
+ * This function must be called with a type in one of the classes
+ * %G_VARIANT_TYPE_CLASS_TUPLE or %G_VARIANT_TYPE_CLASS_DICT_ENTRY
+ * but must not be called on the generic tuple type
+ * %G_VARIANT_TYPE_ANY_TUPLE.
+ *
+ * In the case of a dictionary entry type, this returns the type of
+ * the key.
+ *
+ * %NULL is returned in case of @type being %G_VARIANT_TYPE_UNIT.
+ *
+ * This call, together with g_variant_type_next() provides an iterator
+ * interface over tuple and dictionary entry types.
+ **/
+const GVariantType *
+g_variant_type_first (const GVariantType *type)
+{
+  const gchar *type_string = g_variant_type_peek_string (type);
+
+  g_assert (type_string[0] == '(' || type_string[0] == '{');
+
+  if (type_string[1] == ')')
+    return NULL;
+
+  return (const GVariantType *) &type_string[1];
+}
+
+/**
+ * g_variant_type_next:
+ * @type: a #GVariantType
+ * @returns: the next #GVariantType after @type, or %NULL
+ *
+ * Determines the next item type of a tuple or dictionary entry
+ * type.
+ *
+ * @type must be the result of a previous call to
+ * g_variant_type_first().  Together, these two functions provide an
+ * iterator interface over tuple and dictioanry entry types.
+ *
+ * If called on the key type of a dictionary entry then this call
+ * returns the value type.
+ *
+ * %NULL is returned when @type is the last item in a tuple or the
+ * value type of a dictionary entry.
+ **/
+const GVariantType *
+g_variant_type_next (const GVariantType *type)
+{
+  const gchar *type_string = g_variant_type_peek_string (type);
+  
+  type_string += g_variant_type_get_string_length (type);
+
+  if (*type_string == ')' || *type_string == '}')
+    return NULL;
+
+  return (const GVariantType *) type_string;
+}
+
+/**
+ * g_variant_type_n_items:
+ * @type: a #GVariantType of class tuple or dict entry
+ * @returns: the number of items in @type
+ *
+ * Determines the number of items contained in a tuple or
+ * dictionary entry type.
+ *
+ * This function must be called with a type in one of the classes
+ * %G_VARIANT_TYPE_CLASS_TUPLE or %G_VARIANT_TYPE_CLASS_DICT_ENTRY
+ * but must not be called on the generic tuple type
+ * %G_VARIANT_TYPE_ANY_TUPLE.
+ *
+ * In the case of a dictionary entry type, this function will always
+ * return 2.
+ **/
+gsize
+g_variant_type_n_items (const GVariantType *type)
+{
+  gsize count = 0;
+
+  for (type = g_variant_type_first (type);
+       type;
+       type = g_variant_type_next (type))
+    count++;
+
+  return count;
+}
+
+/**
+ * g_variant_type_key:
+ * @type: a #GVariantType of class dict entry
+ * @returns: the key type of the dictionary entry
+ *
+ * Determines the key type of a dictionary entry type.
+ *
+ * This function must be called with a type in the class
+ * %G_VARIANT_TYPE_CLASS_DICT_ENTRY.  Other than that, this call is
+ * exactly equivalent to g_variant_type_first().
+ **/
+const GVariantType *
+g_variant_type_key (const GVariantType *type)
+{
+  const gchar *type_string = g_variant_type_peek_string (type);
+
+  g_assert (type_string[0] == '{');
+
+  return (const GVariantType *) &type_string[1];
+}
+
+/**
+ * g_variant_type_value:
+ * @type: a #GVariantType of class dict entry
+ * @returns: the value type of the dictionary entry
+ *
+ * Determines the value type of a dictionary entry type.
+ *
+ * This function must be called with a type in the class
+ * %G_VARIANT_TYPE_CLASS_DICT_ENTRY.
+ **/
+const GVariantType *
+g_variant_type_value (const GVariantType *type)
+{
+  const gchar *type_string = g_variant_type_peek_string (type);
+
+  g_assert (type_string[0] == '{');
+
+  return g_variant_type_next (g_variant_type_key (type));
+}
+
+/**
+ * GVariantTypeGetter:
+ * @data: a pointer
+ * @returns: a const #GVariantType
+ *
+ * A callback function intended for use with
+ * g_variant_type_new_tuple().  This function's purpose is to extract
+ * a #GVariantType from some pointer type.  The returned type should
+ * be owned by whatever is at the end of the pointer because it won't
+ * be freed.
+ **/
+/**
+ * g_variant_type_new_tuple:
+ * @items: an array of items, one for each item
+ * @func: a function to determine each item type
+ * @length: the length of @items
+ * @returns: a new #GVariantType
+ *
+ * Constructs a new tuple type.
+ *
+ * The item types for the tuple type may be provided directly (as
+ * an array of #GVariantType), in which case @func should be %NULL.
+ *
+ * The item types can also be provided indirectly.  In this case,
+ * @items should be an array of pointers which are passed one at a
+ * time to @func to determine the corresponding #GVariantType.  For
+ * example, you might provide an array of #GVariant pointers for
+ * @items and g_variant_get_type() for @func.
+ *
+ * The result of this function must be freed with a call to
+ * g_variant_type_free().
+ **/
+static GVariantType *
+g_variant_type_new_tuple_slow (const gpointer     *items,
+                               GVariantTypeGetter  func,
+                               gsize               length)
+{
+  GString *string;
+  gsize i;
+
+  string = g_string_new ("(");
+  for (i = 0; i < length; i++)
+    {
+      const GVariantType *type;
+      gsize size;
+
+      if (func)
+        type = func (items[i]);
+      else
+        type = items[i];
+
+      size = g_variant_type_get_string_length (type);
+      g_string_append_len (string, (const gchar *) type, size);
+    }
+  g_string_append_c (string, ')');
+
+  return (GVariantType *) g_string_free (string, FALSE);
+}
+
+GVariantType *
+g_variant_type_new_tuple_ (const gpointer     *items,
+                           GVariantTypeGetter  func,
+                           gsize               length)
+{
+  char buffer[1024];
+  gsize offset;
+  gsize i;
+
+#ifdef GVARIANT_DEBUG
+    for (i = 0; i < length; i++)
+      if (func)
+        g_assert (g_variant_type_check (func (items[i])));
+      else
+        g_assert (g_variant_type_check (items[i]));
+#endif
+
+  offset = 0;
+  buffer[offset++] = '(';
+
+  for (i = 0; i < length; i++)
+    {
+      const GVariantType *type;
+      gsize size;
+
+      if (func)
+        type = func (items[i]);
+      else
+        type = items[i];
+
+      size = g_variant_type_get_string_length (type);
+
+      if (offset + size >= sizeof buffer) /* leave room for ')' */
+        return g_variant_type_new_tuple_slow (items, func, length);
+
+      memcpy (&buffer[offset], type, size);
+      offset += size;
+    }
+
+  g_assert (offset < sizeof buffer);
+  buffer[offset++] = ')';
+
+  return g_memdup (buffer, offset);
+}
+
+/**
+ * g_variant_type_new_array:
+ * @element: a #GVariantType
+ * @returns: a new array #GVariantType
+ *
+ * Constructs the type corresponding to an array of elements of the
+ * type @type.
+ *
+ * The result of this function must be freed with a call to
+ * g_variant_type_free().
+ **/
+GVariantType *
+g_variant_type_new_array (const GVariantType *element)
+{
+  gsize size;
+  gchar *new;
+
+#ifdef GVARIANT_DEBUG
+    g_assert (g_variant_type_check (element));
+#endif
+
+  size = g_variant_type_get_string_length (element);
+  new = g_malloc (size + 1);
+
+  new[0] = 'a';
+  memcpy (new + 1, element, size);
+
+  return (GVariantType *) new;
+}
+
+/**
+ * g_variant_type_new_maybe:
+ * @element: a #GVariantType
+ * @returns: a new maybe #GVariantType
+ *
+ * Constructs the type corresponding to a maybe instance containing
+ * type @type.
+ *
+ * The result of this function must be freed with a call to
+ * g_variant_type_free().
+ **/
+GVariantType *
+g_variant_type_new_maybe (const GVariantType *element)
+{
+  gsize size;
+  gchar *new;
+
+#ifdef GVARIANT_DEBUG
+    g_assert (g_variant_type_check (element));
+#endif
+
+  size = g_variant_type_get_string_length (element);
+  new = g_malloc (size + 1);
+
+  new[0] = 'm';
+  memcpy (new + 1, element, size);
+
+  return (GVariantType *) new;
+}
+
+/**
+ * g_variant_type_new_dict_entry:
+ * @key: a basic #GVariantType
+ * @value: a #GVariantType
+ * @returns: a new dictionary entry #GVariantType
+ *
+ * Constructs the type corresponding to a dictionary entry with a key
+ * of type @key and a value of type @value.
+ *
+ * The result of this function must be freed with a call to
+ * g_variant_type_free().
+ **/
+GVariantType *
+g_variant_type_new_dict_entry (const GVariantType *key,
+                               const GVariantType *value)
+{
+  gsize keysize, valsize;
+  gchar *new;
+
+#ifdef GVARIANT_DEBUG
+    g_assert (g_variant_type_check (key));
+    g_assert (g_variant_type_check (value));
+    g_assert (g_variant_type_is_basic (key));
+#endif
+
+  keysize = g_variant_type_get_string_length (key);
+  valsize = g_variant_type_get_string_length (value);
+
+  new = g_malloc (1 + keysize + valsize + 1);
+
+  new[0] = '{';
+  memcpy (new + 1, key, keysize);
+  memcpy (new + 1 + keysize, value, valsize);
+  new[1 + keysize + valsize] = '}';
+
+  return (GVariantType *) new;
+}
+
+#define __G_VARIANT_TYPE_C__
+#include "galiasdef.c"
diff --git a/glib/gvarianttype.h b/glib/gvarianttype.h
new file mode 100644
index 0000000..dcb0f49
--- /dev/null
+++ b/glib/gvarianttype.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright © 2007, 2008 Ryan Lortie
+ * Copyright © 2009 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_VARIANT_TYPE_H__
+#define __G_VARIANT_TYPE_H__
+
+#include <glib/gtypes.h>
+
+typedef struct   OPAQUE_TYPE_GVariantType   GVariantType;
+
+/**
+ * GVariantTypeClass:
+ * @G_VARIANT_TYPE_CLASS_INVALID: the class of no type
+ * @G_VARIANT_TYPE_CLASS_BOOLEAN: the class containing the type %G_VARIANT_TYPE_BOOLEAN
+ * @G_VARIANT_TYPE_CLASS_BYTE: the class containing the type %G_VARIANT_TYPE_BYTE
+ * @G_VARIANT_TYPE_CLASS_INT16: the class containing the type %G_VARIANT_TYPE_INT16
+ * @G_VARIANT_TYPE_CLASS_UINT16: the class containing the type %G_VARIANT_TYPE_UINT16
+ * @G_VARIANT_TYPE_CLASS_INT32: the class containing the type %G_VARIANT_TYPE_INT32
+ * @G_VARIANT_TYPE_CLASS_UINT32: the class containing the type %G_VARIANT_TYPE_UINT32
+ * @G_VARIANT_TYPE_CLASS_INT64: the class containing the type %G_VARIANT_TYPE_INT64
+ * @G_VARIANT_TYPE_CLASS_UINT64: the class containing the type %G_VARIANT_TYPE_UINT64
+ * @G_VARIANT_TYPE_CLASS_DOUBLE: the class containing the type %G_VARIANT_TYPE_DOUBLE
+ * @G_VARIANT_TYPE_CLASS_STRING: the class containing the type %G_VARIANT_TYPE_STRING
+ * @G_VARIANT_TYPE_CLASS_OBJECT_PATH: the class containing the type %G_VARIANT_TYPE_OBJECT_PATH
+ * @G_VARIANT_TYPE_CLASS_SIGNATURE: the class containing the type %G_VARIANT_TYPE_SIGNATURE
+ * @G_VARIANT_TYPE_CLASS_VARIANT: the class containing the type %G_VARIANT_TYPE_VARIANT
+ * @G_VARIANT_TYPE_CLASS_MAYBE: the class containing all maybe types
+ * @G_VARIANT_TYPE_CLASS_ARRAY: the class containing all array types
+ * @G_VARIANT_TYPE_CLASS_TUPLE: the class containing all tuple types
+ * @G_VARIANT_TYPE_CLASS_DICT_ENTRY: the class containing all dictionary entry types
+ * @G_VARIANT_TYPE_CLASS_BASIC: the class containing all of the basic types (including %G_VARIANT_TYPE_ANY_BASIC and anything that matches it).
+ * @G_VARIANT_TYPE_CLASS_ALL: the class containing all types (including %G_VARIANT_TYPE_ANY and anything that matches it).
+ *
+ * A enumerated type to group #GVariantType instances into classes.
+ *
+ * If you ever want to perform some sort of recursive operation on the
+ * contents of a #GVariantType you will probably end up using a switch
+ * statement over the #GVariantTypeClass of the type and its component
+ * sub-types.
+ *
+ * A #GVariantType is said to "be in" a given #GVariantTypeClass.  The
+ * type classes are overlapping, so a given #GVariantType may have
+ * more than one type class.  For example, %G_VARIANT_TYPE_BOOLEAN is
+ * of the following classes: %G_VARIANT_TYPE_CLASS_BOOLEAN,
+ * %G_VARIANT_TYPE_CLASS_BASIC, %G_VARIANT_TYPE_CLASS_ALL.
+ **/
+typedef enum
+{
+  G_VARIANT_TYPE_CLASS_INVALID           = '\0',
+  G_VARIANT_TYPE_CLASS_BOOLEAN            = 'b',
+  G_VARIANT_TYPE_CLASS_BYTE               = 'y',
+
+  G_VARIANT_TYPE_CLASS_INT16              = 'n',
+  G_VARIANT_TYPE_CLASS_UINT16             = 'q',
+  G_VARIANT_TYPE_CLASS_INT32              = 'i',
+  G_VARIANT_TYPE_CLASS_UINT32             = 'u',
+  G_VARIANT_TYPE_CLASS_INT64              = 'x',
+  G_VARIANT_TYPE_CLASS_UINT64             = 't',
+
+  G_VARIANT_TYPE_CLASS_DOUBLE             = 'd',
+
+  G_VARIANT_TYPE_CLASS_STRING             = 's',
+  G_VARIANT_TYPE_CLASS_OBJECT_PATH        = 'o',
+  G_VARIANT_TYPE_CLASS_SIGNATURE          = 'g',
+
+  G_VARIANT_TYPE_CLASS_VARIANT            = 'v',
+
+  G_VARIANT_TYPE_CLASS_MAYBE              = 'm',
+  G_VARIANT_TYPE_CLASS_ARRAY              = 'a',
+  G_VARIANT_TYPE_CLASS_TUPLE             = 'r',
+  G_VARIANT_TYPE_CLASS_DICT_ENTRY         = 'e',
+
+  G_VARIANT_TYPE_CLASS_ALL                = '*',
+  G_VARIANT_TYPE_CLASS_BASIC              = '?'
+} GVariantTypeClass;
+
+/**
+ * G_VARIANT_TYPE_BOOLEAN:
+ *
+ * The type of a value that can be either %TRUE or %FALSE.
+ **/
+#define G_VARIANT_TYPE_BOOLEAN              ((const GVariantType *) "b")
+
+/**
+ * G_VARIANT_TYPE_BYTE:
+ *
+ * The type of an integer value that can range from 0 to 255.
+ **/
+#define G_VARIANT_TYPE_BYTE                 ((const GVariantType *) "y")
+
+/**
+ * G_VARIANT_TYPE_INT16:
+ *
+ * The type of an integer value that can range from -32768 to 32767.
+ **/
+#define G_VARIANT_TYPE_INT16                ((const GVariantType *) "n")
+
+/**
+ * G_VARIANT_TYPE_UINT16:
+ *
+ * The type of an integer value that can range from 0 to 65535.
+ * There were about this many people living in Toronto In the 1870s.
+ **/
+#define G_VARIANT_TYPE_UINT16               ((const GVariantType *) "q")
+
+/**
+ * G_VARIANT_TYPE_INT32:
+ *
+ * The type of an integer value that can range from -2147483648 to
+ * 2147483647.
+ **/
+#define G_VARIANT_TYPE_INT32                ((const GVariantType *) "i")
+
+/**
+ * G_VARIANT_TYPE_UINT32:
+ *
+ * The type of an integer value that can range from 0 to 4294967295.
+ * That's one number for everyone who was around in the late 1970s.
+ **/
+#define G_VARIANT_TYPE_UINT32               ((const GVariantType *) "u")
+
+/**
+ * G_VARIANT_TYPE_INT64:
+ *
+ * The type of an integer value that can range from
+ * -9223372036854775808 to 9223372036854775807.
+ **/
+#define G_VARIANT_TYPE_INT64                ((const GVariantType *) "x")
+
+/**
+ * G_VARIANT_TYPE_UINT64:
+ *
+ * The type of an integer value that can range from 0 to
+ * 18446744073709551616.  That's a really big number, but a Rubik's
+ * cube can have a bit more than twice as many possible positions.
+ **/
+#define G_VARIANT_TYPE_UINT64               ((const GVariantType *) "t")
+
+/**
+ * G_VARIANT_TYPE_DOUBLE:
+ *
+ * The type of a double precision IEEE754 floating point number.
+ * These guys go up to about 1.80e308 (plus and minus) but miss out on
+ * some numbers in between.  In any case, that's far greater than the
+ * estimated number of fundamental particles in the observable
+ * universe.
+ **/
+#define G_VARIANT_TYPE_DOUBLE               ((const GVariantType *) "d")
+
+/**
+ * G_VARIANT_TYPE_STRING:
+ *
+ * The type of a string.  "" is a string.  %NULL is not a string.
+ **/
+#define G_VARIANT_TYPE_STRING               ((const GVariantType *) "s")
+
+/**
+ * G_VARIANT_TYPE_OBJECT_PATH:
+ *
+ * The type of a DBus object reference.  These are strings of a
+ * specific format used to identify objects at a given destination on
+ * the bus.
+ **/
+#define G_VARIANT_TYPE_OBJECT_PATH          ((const GVariantType *) "o")
+
+/**
+ * G_VARIANT_TYPE_SIGNATURE:
+ *
+ * The type of a DBus type signature.  These are strings of a specific
+ * format used as type signatures for DBus methods and messages.
+ *
+ * Any valid concrete #GVariantType signature string is a valid DBus
+ * type signature.  In addition, a concatenation of any number of
+ * valid concrete #GVariantType signature strings is also a valid DBus
+ * type signature, subject to the restriction that DBus signature
+ * strings have a maximum length of 255 characters.
+ **/
+#define G_VARIANT_TYPE_SIGNATURE            ((const GVariantType *) "g")
+
+/**
+ * G_VARIANT_TYPE_VARIANT:
+ *
+ * The type of a box that contains any other value (including another
+ * variant).
+ **/
+#define G_VARIANT_TYPE_VARIANT              ((const GVariantType *) "v")
+
+/**
+ * G_VARIANT_TYPE_UNIT:
+ *
+ * The empty tuple type.  Has only one instance.  Known also as "triv"
+ * or "void".
+ **/
+#define G_VARIANT_TYPE_UNIT                 ((const GVariantType *) "()")
+
+/**
+ * G_VARIANT_TYPE_ANY:
+ *
+ * The wildcard type.  Matches any type.
+ **/
+#define G_VARIANT_TYPE_ANY                  ((const GVariantType *) "*")
+
+/**
+ * G_VARIANT_TYPE_ANY_BASIC:
+ *
+ * A wildcard type matching any basic type.
+ **/
+#define G_VARIANT_TYPE_ANY_BASIC            ((const GVariantType *) "?")
+
+/**
+ * G_VARIANT_TYPE_ANY_MAYBE:
+ *
+ * A wildcard type matching any maybe type.
+ **/
+#define G_VARIANT_TYPE_ANY_MAYBE            ((const GVariantType *) "m*")
+
+/**
+ * G_VARIANT_TYPE_ANY_ARRAY:
+ *
+ * A wildcard type matching any array type.
+ **/
+#define G_VARIANT_TYPE_ANY_ARRAY            ((const GVariantType *) "a*")
+
+/**
+ * G_VARIANT_TYPE_ANY_TUPLE:
+ *
+ * A wildcard type matching any tuple type.
+ **/
+#define G_VARIANT_TYPE_ANY_TUPLE            ((const GVariantType *) "r")
+
+/**
+ * G_VARIANT_TYPE_ANY_DICT_ENTRY:
+ *
+ * A wildcard type matching any dictionary entry type.
+ **/
+#define G_VARIANT_TYPE_ANY_DICT_ENTRY       ((const GVariantType *) "{?*}")
+
+/**
+ * G_VARIANT_TYPE_ANY_DICTIONARY:
+ *
+ * A wildcard type matching any dictionary type.
+ **/
+#define G_VARIANT_TYPE_ANY_DICTIONARY       ((const GVariantType *) "ae")
+
+G_BEGIN_DECLS
+
+/* type string checking */
+gboolean                        g_variant_type_string_is_valid          (const gchar         *type_string);
+gboolean                        g_variant_type_string_scan              (const gchar         *string,
+                                                                         const gchar         *limit,
+                                                                         const gchar        **endptr);
+
+/* create/destroy */
+void                            g_variant_type_free                     (GVariantType        *type);
+GVariantType *                  g_variant_type_copy                     (const GVariantType  *type);
+GVariantType *                  g_variant_type_new                      (const gchar         *type_string);
+
+/* getters */
+gsize                           g_variant_type_get_string_length        (const GVariantType  *type);
+const gchar *                   g_variant_type_peek_string              (const GVariantType  *type);
+gchar *                         g_variant_type_dup_string               (const GVariantType  *type);
+
+/* classification */
+gboolean                        g_variant_type_is_concrete              (const GVariantType  *type);
+gboolean                        g_variant_type_is_container             (const GVariantType  *type);
+gboolean                        g_variant_type_is_basic                 (const GVariantType  *type);
+
+/* for hash tables */
+guint                           g_variant_type_hash                     (gconstpointer        type);
+gboolean                        g_variant_type_equal                    (gconstpointer        type1,
+                                                                         gconstpointer        type2);
+
+/* matching */
+gboolean                        g_variant_type_matches                  (const GVariantType  *type,
+                                                                         const GVariantType  *pattern);
+
+/* class functions */
+gboolean                        g_variant_type_is_in_class              (const GVariantType  *type,
+                                                                         GVariantTypeClass    tclass);
+GVariantTypeClass               g_variant_type_get_class                (const GVariantType  *type);
+gboolean                        g_variant_type_class_is_container       (GVariantTypeClass    tclass);
+gboolean                        g_variant_type_class_is_basic           (GVariantTypeClass    tclass);
+
+/* type iterator interface */
+const GVariantType *            g_variant_type_element                  (const GVariantType  *type);
+const GVariantType *            g_variant_type_first                    (const GVariantType  *type);
+const GVariantType *            g_variant_type_next                     (const GVariantType  *type);
+gsize                           g_variant_type_n_items                  (const GVariantType  *type);
+const GVariantType *            g_variant_type_key                      (const GVariantType  *type);
+const GVariantType *            g_variant_type_value                    (const GVariantType  *type);
+
+/* constructors */
+GVariantType *                  g_variant_type_new_array                (const GVariantType  *element);
+GVariantType *                  g_variant_type_new_maybe                (const GVariantType  *element);
+typedef const GVariantType   *(*GVariantTypeGetter)                     (gpointer             data);
+GVariantType *                  g_variant_type_new_tuple_               (const gpointer      *items,
+                                                                         GVariantTypeGetter   func,
+                                                                         gsize                length);
+GVariantType *                  g_variant_type_new_dict_entry           (const GVariantType  *key,
+                                                                         const GVariantType  *value);
+
+/*< private >*/
+const GVariantType *            g_variant_type_check_string_            (const gchar         *type_string);
+
+G_END_DECLS
+
+#define G_VARIANT_TYPE(type_string) \
+  (g_variant_type_check_string_ (type_string))
+
+#define g_variant_type_new_tuple(items, func, length) \
+  (g_variant_type_new_tuple_ ((const gpointer *) items, (GVariantTypeGetter) (1 ? func : \
+                               (const GVariantType *(*)(typeof (items[0]))) NULL), length))
+
+#endif /* __G_VARIANT_TYPE_H__ */
diff --git a/glib/gvarianttypeinfo.c b/glib/gvarianttypeinfo.c
new file mode 100644
index 0000000..344f3d0
--- /dev/null
+++ b/glib/gvarianttypeinfo.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright © 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gvarianttypeinfo.h"
+#include <glib.h>
+
+#include "galias.h"
+
+struct OPAQUE_TYPE__GVariantTypeInfo
+{
+  GVariantType *type;
+  const gchar *str; /* XXX to make my debugging life easier... */
+
+  gsize fixed_size;
+  guchar info_class;
+  guchar alignment;
+  gint ref_count;
+};
+
+typedef struct
+{
+  GVariantTypeInfo self;
+
+  GVariantTypeInfo *element;
+} ArrayInfo;
+
+typedef struct
+{
+  GVariantTypeInfo self;
+
+  GVariantMemberInfo *members;
+  gsize n_members;
+} TupleInfo;
+
+/* == query == */
+const GVariantType *
+g_variant_type_info_get_type (GVariantTypeInfo *info)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  return info->type;
+}
+
+const gchar *
+g_variant_type_info_get_string (GVariantTypeInfo *info)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  return (const gchar *) info->type;
+}
+
+GVariantTypeClass
+g_variant_type_info_get_type_class (GVariantTypeInfo *info)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  return g_variant_type_get_class (info->type);
+}
+
+void
+g_variant_type_info_query (GVariantTypeInfo *info,
+                           guint            *alignment,
+                           gsize           *fixed_size)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  if (alignment)
+    *alignment = info->alignment;
+
+  if (fixed_size)
+    *fixed_size = info->fixed_size;
+}
+
+/* == array == */
+#define ARRAY_INFO_CLASS 'a'
+static ArrayInfo *
+ARRAY_INFO (GVariantTypeInfo *info)
+{
+  g_assert (info->info_class == ARRAY_INFO_CLASS);
+  return (ArrayInfo *) info;
+}
+
+static void
+array_info_free (GVariantTypeInfo *info)
+{
+  ArrayInfo *array_info = ARRAY_INFO (info);
+
+  g_variant_type_info_unref (array_info->element);
+  g_slice_free (ArrayInfo, array_info);
+}
+
+static GVariantTypeInfo *
+array_info_new (const GVariantType *type)
+{
+  ArrayInfo *info;
+
+  info = g_slice_new (ArrayInfo);
+  info->self.info_class = ARRAY_INFO_CLASS;
+
+  info->element = g_variant_type_info_get (g_variant_type_element (type));
+  info->self.alignment = info->element->alignment;
+  info->self.fixed_size = 0;
+
+  return (GVariantTypeInfo *) info;
+}
+
+GVariantTypeInfo *
+g_variant_type_info_element (GVariantTypeInfo *info)
+{
+  return ARRAY_INFO (info)->element;
+}
+
+void
+g_variant_type_info_query_element (GVariantTypeInfo *info,
+                                   guint            *alignment,
+                                   gsize            *fixed_size)
+{
+  g_variant_type_info_query (ARRAY_INFO (info)->element,
+                             alignment, fixed_size);
+}
+
+/* == tupleure == */
+#define TUPLE_INFO_CLASS 's'
+static TupleInfo *
+TUPLE_INFO (GVariantTypeInfo *info)
+{
+  g_assert (info->info_class == TUPLE_INFO_CLASS);
+  return (TupleInfo *) info;
+}
+
+static void
+tuple_info_free (GVariantTypeInfo *info)
+{
+  TupleInfo *tuple_info = TUPLE_INFO (info);
+  gint i;
+
+  for (i = 0; i < tuple_info->n_members; i++)
+    g_variant_type_info_unref (tuple_info->members[i].type);
+
+  g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members,
+                 tuple_info->members);
+  g_slice_free (TupleInfo, tuple_info);
+}
+
+static void
+tuple_allocate_members (const GVariantType  *type,
+                        GVariantMemberInfo **members,
+                        gsize               *n_members)
+{
+  const GVariantType *item_type;
+  gsize i = 0;
+
+  *n_members = g_variant_type_n_items (type);
+  *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members);
+
+  item_type = g_variant_type_first (type);
+  while (item_type)
+    {
+      (*members)[i++].type = g_variant_type_info_get (item_type);
+      item_type = g_variant_type_next (item_type);
+    }
+
+  g_assert (i == *n_members);
+}
+
+static gboolean
+tuple_get_item (TupleInfo         *info,
+                GVariantMemberInfo *item,
+                gsize              *d,
+                gsize              *e)
+{
+  if (&info->members[info->n_members] == item)
+    return FALSE;
+
+  *d = item->type->alignment;
+  *e = item->type->fixed_size;
+  return TRUE;
+}
+
+static void
+tuple_table_append (GVariantMemberInfo **items,
+                    gsize                i,
+                    gsize                a,
+                    gsize                b,
+                    gsize                c)
+{
+  GVariantMemberInfo *item = (*items)++;
+
+  /* §4.1.3 */
+  a += ~b & c;
+  c &= b;
+
+  /* XXX not documented anywhere */
+  a += b;
+  b = ~b;
+
+  item->i = i;
+  item->a = a;
+  item->b = b;
+  item->c = c;
+}
+
+static gsize
+tuple_align (gsize offset,
+             guint alignment)
+{
+  return offset + ((-offset) & alignment);
+}
+
+static void
+tuple_generate_table (TupleInfo *info)
+{
+  GVariantMemberInfo *items = info->members;
+  gsize i = -1, a = 0, b = 0, c = 0, d, e;
+
+  /* §4.1.2 */
+  while (tuple_get_item (info, items, &d, &e))
+    {
+      if (d <= b)
+        c = tuple_align (c, d);
+      else
+        a += tuple_align (c, b), b = d, c = 0;
+
+      tuple_table_append (&items, i, a, b, c);
+
+      if (e == 0)
+        i++, a = b = c = 0;
+      else
+        c += e;
+    }
+}
+
+static void
+tuple_set_self_info (TupleInfo *info)
+{
+  if (info->n_members > 0)
+    {
+      GVariantMemberInfo *m;
+
+      info->self.alignment = 0;
+      for (m = info->members; m < &info->members[info->n_members]; m++)
+        info->self.alignment |= m->type->alignment;
+      m--;
+
+      if (m->i == -1 && m->type->fixed_size)
+        info->self.fixed_size = tuple_align (((m->a & m->b) | m->c) + m->type->fixed_size,
+                                              info->self.alignment);
+      else
+        info->self.fixed_size = 0;
+    }
+  else
+    {
+      info->self.alignment = 0;
+      info->self.fixed_size = 1;
+    }
+}
+
+static GVariantTypeInfo *
+tuple_info_new (const GVariantType *type)
+{
+  TupleInfo *info;
+
+  info = g_slice_new (TupleInfo);
+  info->self.info_class = TUPLE_INFO_CLASS;
+
+  tuple_allocate_members (type, &info->members, &info->n_members); 
+  tuple_generate_table (info);
+  tuple_set_self_info (info);
+
+  return (GVariantTypeInfo *) info;
+}
+
+gsize
+g_variant_type_info_n_members (GVariantTypeInfo *info)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  return TUPLE_INFO (info)->n_members;
+}
+
+const GVariantMemberInfo *
+g_variant_type_info_member_info (GVariantTypeInfo *info,
+                                 gsize             index)
+{
+  TupleInfo *tuple_info = TUPLE_INFO (info);
+
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  if (index < tuple_info->n_members)
+    return &tuple_info->members[index];
+
+  return NULL;
+}
+
+/* == base == */
+#define BASE_INFO_CLASS '\0'
+static GVariantTypeInfo *
+base_info_new (const GVariantTypeClass class)
+{
+  GVariantTypeInfo *info;
+
+  info = g_slice_new (GVariantTypeInfo);
+  info->info_class = BASE_INFO_CLASS;
+
+  switch (class)
+  {
+    case G_VARIANT_TYPE_CLASS_BOOLEAN:
+    case G_VARIANT_TYPE_CLASS_BYTE:
+      info->alignment = 1 - 1;
+      info->fixed_size = 1;
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT16:
+    case G_VARIANT_TYPE_CLASS_INT16:
+      info->alignment = 2 - 1;
+      info->fixed_size = 2;
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT32:
+    case G_VARIANT_TYPE_CLASS_INT32:
+      info->alignment = 4 - 1;
+      info->fixed_size = 4;
+      break;
+
+    case G_VARIANT_TYPE_CLASS_UINT64:
+    case G_VARIANT_TYPE_CLASS_INT64:
+    case G_VARIANT_TYPE_CLASS_DOUBLE:
+      info->alignment = 8 - 1;
+      info->fixed_size = 8;
+      break;
+
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+      info->alignment = 8 - 1;
+      info->fixed_size = 0;
+      break;
+
+    case G_VARIANT_TYPE_CLASS_STRING:
+    case G_VARIANT_TYPE_CLASS_OBJECT_PATH:
+    case G_VARIANT_TYPE_CLASS_SIGNATURE:
+      info->alignment = 1 - 1;
+      info->fixed_size = 0;
+      break;
+
+    default:
+      g_error ("GVariantTypeInfo: not a valid type character: '%c'", class);
+  }
+
+  return info;
+}
+
+static void
+base_info_free (GVariantTypeInfo *info)
+{
+  g_slice_free (GVariantTypeInfo, info);
+}
+
+/* == new/ref/unref == */
+static GStaticRecMutex g_variant_type_info_lock = G_STATIC_REC_MUTEX_INIT;
+static GHashTable *g_variant_type_info_table;
+
+GVariantTypeInfo *
+g_variant_type_info_get (const GVariantType *type)
+{
+  GVariantTypeInfo *info;
+
+  if G_UNLIKELY (g_variant_type_info_table == NULL)
+    g_variant_type_info_table = g_hash_table_new (g_variant_type_hash,
+                                                  g_variant_type_equal);
+
+  g_static_rec_mutex_lock (&g_variant_type_info_lock);
+  info = g_hash_table_lookup (g_variant_type_info_table, type);
+
+  if (info == NULL)
+    {
+      GVariantTypeClass class;
+
+      class = g_variant_type_get_class (type);
+
+      switch (class)
+      {
+        case G_VARIANT_TYPE_CLASS_MAYBE:
+        case G_VARIANT_TYPE_CLASS_ARRAY:
+          info = array_info_new (type);
+          break;
+
+        case G_VARIANT_TYPE_CLASS_TUPLE:
+        case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+          info = tuple_info_new (type);
+          break;
+
+        default:
+          info = base_info_new (class);
+          break;
+      }
+
+      info->type = g_variant_type_copy (type);
+      g_hash_table_insert (g_variant_type_info_table, info->type, info);
+      info->ref_count = 1;
+      info->str = (const gchar *) info->type;
+    }
+  else
+    g_variant_type_info_ref (info);
+
+  g_static_rec_mutex_unlock (&g_variant_type_info_lock);
+
+  return info;
+}
+
+GVariantTypeInfo *
+g_variant_type_info_ref (GVariantTypeInfo *info)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+  g_atomic_int_inc (&info->ref_count);
+
+  return info;
+}
+
+void
+g_variant_type_info_unref (GVariantTypeInfo *info)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  if (g_atomic_int_dec_and_test (&info->ref_count))
+    {
+      g_static_rec_mutex_lock (&g_variant_type_info_lock);
+      g_hash_table_remove (g_variant_type_info_table, info->type);
+      g_static_rec_mutex_unlock (&g_variant_type_info_lock);
+
+      g_variant_type_free (info->type);
+
+      switch (info->info_class)
+      {
+        case ARRAY_INFO_CLASS:
+          array_info_free (info);
+          break;
+
+        case TUPLE_INFO_CLASS:
+          tuple_info_free (info);
+          break;
+
+        case BASE_INFO_CLASS:
+          base_info_free (info);
+          break;
+
+        default:
+          g_error ("GVariantTypeInfo with invalid class '%c'",
+                   info->info_class);
+      }
+    }
+}
+
+#define __G_VARIANT_TYPE_INFO_C__
+#include "galiasdef.c"
diff --git a/glib/gvarianttypeinfo.h b/glib/gvarianttypeinfo.h
new file mode 100644
index 0000000..643b178
--- /dev/null
+++ b/glib/gvarianttypeinfo.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2008 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_VARIANT_TYPE_INFO_H__
+#define __G_VARIANT_TYPE_INFO_H__
+
+#include "gvarianttype.h"
+
+typedef struct OPAQUE_TYPE__GVariantTypeInfo GVariantTypeInfo;
+
+typedef struct
+{
+  GVariantTypeInfo *type;
+
+  gsize i, a;
+  gint8 b, c;
+} GVariantMemberInfo;
+
+#define STRUCT_MEMBER_LAST         (-1)
+#define STRUCT_MEMBER_VARIABLE     (-2)
+
+/* query */
+const GVariantType *            g_variant_type_info_get_type            (GVariantTypeInfo   *typeinfo);
+GVariantTypeClass               g_variant_type_info_get_type_class      (GVariantTypeInfo   *typeinfo);
+const gchar *                   g_variant_type_info_get_string          (GVariantTypeInfo   *typeinfo);
+
+void                            g_variant_type_info_query               (GVariantTypeInfo   *typeinfo,
+                                                                         guint              *alignment,
+                                                                         gsize              *size);
+
+/* array */
+GVariantTypeInfo *              g_variant_type_info_element             (GVariantTypeInfo   *typeinfo);
+void                            g_variant_type_info_query_element       (GVariantTypeInfo   *typeinfo,
+                                                                         guint              *alignment,
+                                                                         gsize              *size);
+
+/* structure */
+gsize                           g_variant_type_info_n_members           (GVariantTypeInfo   *typeinfo);
+const GVariantMemberInfo *      g_variant_type_info_member_info         (GVariantTypeInfo   *typeinfo,
+                                                                         gsize               index);
+
+/* new/ref/unref */
+GVariantTypeInfo *              g_variant_type_info_get                 (const GVariantType *type);
+GVariantTypeInfo *              g_variant_type_info_ref                 (GVariantTypeInfo   *typeinfo);
+void                            g_variant_type_info_unref               (GVariantTypeInfo   *typeinfo);
+
+#endif /* __G_VARIANT_TYPE_INFO_H__ */
diff --git a/glib/tests/.gitignore b/glib/tests/.gitignore
index 76c25a8..a8e8597 100644
--- a/glib/tests/.gitignore
+++ b/glib/tests/.gitignore
@@ -10,3 +10,12 @@ strfuncs
 string
 testing
 tmpsample.xml
+gvariant-big
+gvariant-endian
+gvariant-fuzz
+gvariant-markup
+gvariant-printer-parser
+gvariant-random
+gvariant-serialiser
+gvariant-varargs
+gvariant-varargs-strings
diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am
index 673d338..659337a 100644
--- a/glib/tests/Makefile.am
+++ b/glib/tests/Makefile.am
@@ -44,6 +44,33 @@ markup_subparser_LDADD    = $(progs_ldadd)
 TEST_PROGS         += array-test
 array_test_LDADD    = $(progs_ldadd)
 
+TEST_PROGS     += gvariant-big
+gvariant_big_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-endian
+gvariant_endian_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-fuzz
+gvariant_fuzz_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-markup
+gvariant_markup_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-printer-parser
+gvariant_printer_parser_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-random
+gvariant_random_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-serialiser
+gvariant_serialiser_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-varargs
+gvariant_varargs_LDADD    = $(progs_ldadd)
+
+TEST_PROGS     += gvariant-varargs-strings
+gvariant_varargs_strings_LDADD    = $(progs_ldadd)
+
 TEST_PROGS         += hostutils
 hostutils_LDADD     = $(progs_ldadd)
 
diff --git a/glib/tests/gvariant-basic.c b/glib/tests/gvariant-basic.c
new file mode 100644
index 0000000..30eb063
--- /dev/null
+++ b/glib/tests/gvariant-basic.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright © 2008 Philip Van Hoof, Ryan Lortie
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#include <glib.h>
+
+static void
+test_int64 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gint64 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0xffffffffffffffff) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x7fffffffffffffff) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x0000000000000000) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(4242424242424242) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x0101010101010101) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x1010101010101010) },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new (values[i].type, values[i].value);
+      gint64 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+
+static void
+test_uint64 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    guint64 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0xffffffffffffffff) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x7fffffffffffffff) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x0000000000000000) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(4242424242424242) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x0101010101010101) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x1010101010101010) },  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new (values[i].type, values[i].value);
+      guint64 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+static void
+test_double (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gdouble value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_DOUBLE, 0.123 },
+    { (const char *) G_SIGNATURE_DOUBLE, 123 },
+    { (const char *) G_SIGNATURE_DOUBLE, 0.1234 },
+    { (const char *) G_SIGNATURE_DOUBLE, 1.5567 },
+    { (const char *) G_SIGNATURE_DOUBLE, 1.5563 },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new (values[i].type, values[i].value);
+      gdouble test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+static void
+test_int32 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gint32 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_INT32, 0xffffffff },
+    { (const char *) G_SIGNATURE_INT32, 0x7fffffff },
+    { (const char *) G_SIGNATURE_INT32, 0x00000000 },
+    { (const char *) G_SIGNATURE_INT32, 42424242 },
+    { (const char *) G_SIGNATURE_INT32, 0x01010101 },
+    { (const char *) G_SIGNATURE_INT32, 0x10101010 },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new (values[i].type, values[i].value);
+      gint32 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+
+static void
+test_uint32 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    guint32 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_UINT32, 0xffffffff },
+    { (const char *) G_SIGNATURE_UINT32, 0x7fffffff },
+    { (const char *) G_SIGNATURE_UINT32, 0x00000000 },
+    { (const char *) G_SIGNATURE_UINT32, 42424242 },
+    { (const char *) G_SIGNATURE_UINT32, 0x01010101 },
+    { (const char *) G_SIGNATURE_UINT32, 0x10101010 },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new (values[i].type, values[i].value);
+      guint32 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+static void
+test_int16 (void)
+{
+  int i;
+  gint16 b;
+
+  for (b = 0xffff; b < 0x7fff; b++)
+    {
+      GVariant *variant = g_variant_new ((const char *) G_SIGNATURE_INT16, b);
+      gint16 test;
+      g_variant_get (variant, (const char *) G_SIGNATURE_INT16, &test);
+
+      g_assert_cmpint (test, ==, b);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+
+static void
+test_uint16 (void)
+{
+  int i;
+  guint16 b;
+
+  for (b = 0xffff; b < 0x7fff; b++)
+    {
+      GVariant *variant = g_variant_new ((const char *) G_SIGNATURE_UINT16, b);
+      guint16 test;
+      g_variant_get (variant, (const char *) G_SIGNATURE_UINT16, &test);
+
+      g_assert_cmpint (test, ==, b);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+static void
+test_byte (void)
+{
+  int i;
+  guint8 b;
+
+  for (b = 0; b < 255; b++)
+    {
+      GVariant *variant = g_variant_new ((const char *) G_SIGNATURE_BYTE, b);
+      guint8 test;
+      g_variant_get (variant, (const char *) G_SIGNATURE_BYTE, &test);
+
+      g_assert_cmpint (test, ==, b);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+
+static void
+test_string (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    const char *value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_STRING, "" },
+    { (const char *) G_SIGNATURE_STRING, "the anti string" },
+    { (const char *) G_SIGNATURE_STRING, "<xml attr=\"test\"><![CDATA[ abdbdb\n\n%&*@&#\n]]></xml>" },
+    { (const char *) G_SIGNATURE_STRING, "I do, I in fact , I insist" },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new (values[i].type, values[i].value);
+      char* test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpstr (test, ==, values[i].value);
+      g_free (test);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+static void
+test_boolean (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gboolean value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_BOOLEAN, TRUE },
+    { (const char *) G_SIGNATURE_BOOLEAN, FALSE },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new (values[i].type, values[i].value);
+      gboolean test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+
+  return;
+}
+
+int
+main (int argc, char **argv)
+{
+  guint limit = 1000;
+  int i;
+
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/variant/basic/boolean", test_boolean);
+  g_test_add_func ("/variant/basic/string", test_string);
+  g_test_add_func ("/variant/basic/byte", test_byte);
+  g_test_add_func ("/variant/basic/uint16", test_uint16);
+  g_test_add_func ("/variant/basic/int16", test_int16);
+  g_test_add_func ("/variant/basic/int32", test_int32);
+  g_test_add_func ("/variant/basic/uint32", test_int32);
+  g_test_add_func ("/variant/basic/double", test_double);
+  g_test_add_func ("/variant/basic/int64", test_int64);
+  g_test_add_func ("/variant/basic/uint64", test_uint64);
+
+  return g_test_run ();
+}
+
diff --git a/glib/tests/gvariant-big.c b/glib/tests/gvariant-big.c
new file mode 100644
index 0000000..be334e9
--- /dev/null
+++ b/glib/tests/gvariant-big.c
@@ -0,0 +1,217 @@
+#include <glib.h>
+
+gdouble
+ieee754ify (gdouble floating)
+{
+  volatile double value = floating;
+  return value;
+}
+
+static const gchar *
+random_string (GRand *rand)
+{
+  static char string[512];
+  int i = 0;
+
+  do
+    string[i] = g_rand_int_range (rand, 0, 128);
+  while (string[i++] != '\0' && i < 512);
+
+  string[511] = '\0';
+
+  return string;
+}
+
+static void
+verify2 (GVariant *value,
+         GRand    *rand)
+{
+  gsize length = g_rand_int_range (rand, 1, 1000);
+  const gchar *possible = "ybquds";
+  const GVariantType *type;
+  GVariantTypeClass class;
+  GVariantIter iter;
+  GVariant *child;
+  gsize actual;
+
+  type = (const GVariantType *) (possible + g_rand_int_range (rand, 0, 6));
+  class = g_variant_type_get_class (type);
+
+  actual = g_variant_iter_init (&iter, value);
+  g_assert_cmpint (actual, ==, length);
+
+  actual = 0;
+  while ((child = g_variant_iter_next_value (&iter)))
+    {
+      switch (class)
+        {
+          case G_VARIANT_TYPE_CLASS_BOOLEAN:
+            g_assert_cmpint (g_variant_get_boolean (child), ==,
+                             g_rand_int_range (rand, 0, 1));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_BYTE:
+            g_assert_cmpint (g_variant_get_byte (child), ==,
+                             g_rand_int_range (rand, 0, 256));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_UINT16:
+            g_assert_cmpint (g_variant_get_uint16 (child), ==,
+                             g_rand_int_range (rand, 0, 65536));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_UINT32:
+            g_assert_cmpint (g_variant_get_uint32 (child), ==,
+                             g_rand_int (rand));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_DOUBLE:
+            g_assert_cmpfloat (g_variant_get_double (child), ==,
+                               ieee754ify (g_rand_double (rand)));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_STRING:
+            g_assert_cmpstr (g_variant_get_string (child, NULL), ==,
+                             random_string (rand));
+            break;
+            
+          default:
+            g_assert_not_reached ();
+        }
+      actual++;
+    }
+
+  g_assert_cmpint (actual, ==, length);
+  g_variant_unref (value);
+}
+
+
+static GVariant *
+generate2 (GRand *rand)
+{
+  gsize length = g_rand_int_range (rand, 1, 1000);
+  const gchar *possible = "ybquds";
+  const GVariantType *type;
+  GVariantTypeClass class;
+  GVariantBuilder *builder;
+  gsize i;
+
+  type = (const GVariantType *) (possible + g_rand_int_range (rand, 0, 6));
+  class = g_variant_type_get_class (type);
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY, NULL);
+  for (i = 0; i < length; i++)
+    {
+      switch (class)
+        {
+          case G_VARIANT_TYPE_CLASS_BOOLEAN:
+            g_variant_builder_add (builder, "b",
+                                   g_rand_int_range (rand, 0, 1));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_BYTE:
+            g_variant_builder_add (builder, "y",
+                                   g_rand_int_range (rand, 0, 256));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_UINT16:
+            g_variant_builder_add (builder, "q",
+                                   g_rand_int_range (rand, 0, 65536));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_UINT32:
+            g_variant_builder_add (builder, "u", g_rand_int (rand));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_DOUBLE:
+            g_variant_builder_add (builder, "d", g_rand_double (rand));
+            break;
+
+          case G_VARIANT_TYPE_CLASS_STRING:
+            g_variant_builder_add (builder, "s", random_string (rand));
+            break;
+            
+          default:
+            g_assert_not_reached ();
+        }
+    }
+
+  return g_variant_new_variant (g_variant_builder_end (builder));
+}
+
+static GVariant *
+generate (GRand *rand)
+{
+  gsize length = g_rand_int_range (rand, 0, 20);
+  GVariantBuilder *builder;
+  gsize i;
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                   G_VARIANT_TYPE ("av"));
+
+  /* length will fall into 0, 0..255, 256..65535, and >65536 ranges */
+  for (i = 0; i < length; i++)
+    g_variant_builder_add_value (builder, generate2 (rand));
+
+  return g_variant_builder_end (builder);
+}
+
+static void
+verify (GVariant *value,
+        GRand    *rand)
+{
+  gsize length = g_rand_int_range (rand, 0, 20);
+  GVariantIter iter;
+  GVariant *child;
+  gsize actual;
+
+  actual = g_variant_iter_init (&iter, value);
+  g_assert_cmpint (actual, ==, length);
+  actual = 0;
+
+  while ((child = g_variant_iter_next_value (&iter)))
+    {
+      verify2 (g_variant_get_variant (child), rand);
+      actual++;
+    }
+
+  g_assert_cmpint (actual, ==, length);
+}
+
+static void
+test (void)
+{
+  GRand *one, *two;
+  GVariant *value;
+  guint32 seed;
+
+  seed = g_test_rand_int ();
+  one = g_rand_new_with_seed (seed);
+  two = g_rand_new_with_seed (seed);
+
+  {
+    GVariant *test_double = g_variant_new_double (g_rand_double (one));
+    g_assert_cmpfloat (ieee754ify (g_rand_double (two)), ==,
+                       g_variant_get_double (test_double));
+    g_variant_unref (test_double);
+  }
+
+  value = generate (one);
+  g_rand_free (one);
+
+  g_variant_flatten (value);
+
+  verify (value, two);
+  g_rand_free (two);
+
+  g_variant_unref (value);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/gvariant/big", test);
+
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-complex.c b/glib/tests/gvariant-complex.c
new file mode 100644
index 0000000..f57669f
--- /dev/null
+++ b/glib/tests/gvariant-complex.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright © 2008 Philip Van Hoof, Ryan Lortie
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#include <glib.h>
+
+static void
+test_s_ii_v (void)
+{
+  GVariant *t, *variant;
+  gchar *s;
+  gint i1,i2;
+  gboolean mybool;
+
+  variant = g_variant_new ("(s(ii)v)",
+                           "Hello World", 800, 600,
+                           g_variant_new_boolean (TRUE));
+
+  g_variant_get (variant, "(s(ii)v)", &s, &i1, &i2, &t);
+
+  g_variant_get (t, "b", &mybool);
+  g_assert_cmpint (mybool, ==, TRUE);
+  g_variant_unref (t);
+
+  g_assert_cmpstr (s, ==, "Hello World");
+  g_assert_cmpint (i1, ==, 800);
+  g_assert_cmpint (i2, ==, 600);
+  g_free (s);
+
+  g_variant_unref (variant);
+}
+
+static void
+test_a_si (void)
+{
+  gchar *szero = NULL, *sone = NULL, *stwo = NULL, *sextra = NULL;
+  gint zero, one, two, extra, len;
+  GVariantIter iter;
+  GVariant *variant;
+  GVariant *array;
+
+  variant = g_variant_new ("(a{si}s)",
+                           3,
+                             "zero", 0,
+                             "one",  1,
+                             "two",  2,
+                           "");
+
+  array = g_variant_get_child (variant, 0);
+  len = g_variant_iter_init (&iter, array);
+  g_variant_unref (variant);
+
+  g_assert_cmpint (len, ==, 3);
+
+  g_variant_iterate (&iter, "{si}", &szero, &zero);
+  g_variant_iterate (&iter, "{si}", &sone, &one);
+  g_variant_iterate (&iter, "{si}", &stwo, &two);
+
+  g_assert (g_variant_iterate (&iter, "si", &sextra, &extra) == FALSE);
+
+  g_assert_cmpstr (szero, ==, "zero");
+  g_assert_cmpint (zero, ==, 0);
+
+  g_assert_cmpstr (sone, ==, "one");
+  g_assert_cmpint (one, ==, 1);
+
+  g_assert_cmpstr (stwo, ==, "two");
+  g_assert_cmpint (two, ==, 2);
+
+  g_variant_unref (array);
+  g_free (szero);
+  g_free (sone);
+  g_free (stwo);
+}
+
+static void
+test_s_vvvv_v (void)
+{
+  GVariant *variant;
+  guint16 myuint16;
+  gboolean myboolean;
+  gchar *mystring, *s;
+  guint8 mymaxbyte, myminbyte;
+  GVariant *t1, *t2, *t3, *t4, *t5;
+
+  variant = g_variant_new ("(s(vvvv)v)",
+                           "Crack example",
+                           g_variant_new_boolean (TRUE),
+                           g_variant_new_string ("abc"),
+                           g_variant_new_uint16 (0xfff3),
+                           g_variant_new_byte (255),
+                           g_variant_new_byte (0));
+
+  g_variant_get (variant, "(s(vvvv)v)", &s,
+                 &t1, &t2, &t3, &t4, &t5);
+
+  g_variant_get (t1, (const char *) G_SIGNATURE_BOOLEAN, &myboolean);
+  g_variant_get (t2, (const char *) G_SIGNATURE_STRING, &mystring);
+  g_variant_get (t3, (const char *) G_SIGNATURE_UINT16, &myuint16);
+  g_variant_get (t4, (const char *) G_SIGNATURE_BYTE, &mymaxbyte);
+  g_variant_get (t5, (const char *) G_SIGNATURE_BYTE, &myminbyte);
+
+  g_assert_cmpstr (s, ==, "Crack example");
+  g_assert_cmpint (myboolean, ==, TRUE);
+  g_assert_cmpstr (mystring, ==, "abc");
+  g_assert_cmpint (myuint16, ==, 0xfff3);
+  g_assert_cmpint (mymaxbyte, ==, 255);
+  g_assert_cmpint (myminbyte, ==, 0);
+
+  g_variant_unref (variant);
+  g_variant_unref (t1);
+  g_variant_unref (t2);
+  g_variant_unref (t3);
+  g_variant_unref (t4);
+  g_variant_unref (t5);
+  g_free (mystring);
+  g_free (s);
+}
+
+static void
+test_vvvv (void)
+{
+  guint16 myuint16;
+  gboolean myboolean;
+  gchar *mystring;
+  guint8 mymaxbyte, myminbyte;
+  GVariant *t1, *t2, *t3, *t4;
+  GVariant *variant;
+
+  variant = g_variant_new ("(vvvv)",
+                           g_variant_new_boolean (TRUE),
+                           g_variant_new_string ("abc"),
+                           g_variant_new_uint16 (0xfff3),
+                           g_variant_new_byte (255));
+
+  g_variant_get (variant, "(vvvv)",
+                 &t1, &t2, &t3, &t4);
+
+  g_variant_get (t1, (const char *) G_SIGNATURE_BOOLEAN, &myboolean);
+  g_variant_get (t2, (const char *) G_SIGNATURE_STRING, &mystring);
+  g_variant_get (t3, (const char *) G_SIGNATURE_UINT16, &myuint16);
+  g_variant_get (t4, (const char *) G_SIGNATURE_BYTE, &mymaxbyte);
+
+  g_assert_cmpint (myboolean, ==, TRUE);
+  g_assert_cmpstr (mystring, ==, "abc");
+  g_assert_cmpint (myuint16, ==, 0xfff3);
+  g_assert_cmpint (mymaxbyte, ==, 255);
+
+  g_variant_unref (variant);
+  g_variant_unref (t1);
+  g_variant_unref (t2);
+  g_variant_unref (t3);
+  g_variant_unref (t4);
+  g_free (mystring);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/variant/complex/s(ii)v", test_s_ii_v);
+  g_test_add_func ("/variant/complex/vvvv", test_vvvv);
+
+  g_test_add_func ("/variant/complex/a{si}", test_a_si);
+  g_test_add_func ("/variant/complex/s(vvvv)v", test_s_vvvv_v);
+
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-constructors.c b/glib/tests/gvariant-constructors.c
new file mode 100644
index 0000000..e5ad24a
--- /dev/null
+++ b/glib/tests/gvariant-constructors.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright © 2008 Philip Van Hoof, Ryan Lortie
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#include <glib.h>
+
+static void
+test_int64 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gint64 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0xffffffffffffffff) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x7fffffffffffffff) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x0000000000000000) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(4242424242424242) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x0101010101010101) },
+    { (const char *) G_SIGNATURE_INT64, G_GINT64_CONSTANT(0x1010101010101010) },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new_int64 (values[i].value);
+      gint64 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+}
+
+
+static void
+test_uint64 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    guint64 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0xffffffffffffffff) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x7fffffffffffffff) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x0000000000000000) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(4242424242424242) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x0101010101010101) },
+    { (const char *) G_SIGNATURE_UINT64, G_GUINT64_CONSTANT(0x1010101010101010) },  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new_uint64 (values[i].value);
+      guint64 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+}
+
+static void
+test_double (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gdouble value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_DOUBLE, 0.123 },
+    { (const char *) G_SIGNATURE_DOUBLE, 123 },
+    { (const char *) G_SIGNATURE_DOUBLE, 0.1234 },
+    { (const char *) G_SIGNATURE_DOUBLE, 1.5567 },
+    { (const char *) G_SIGNATURE_DOUBLE, 1.5563 },
+    { (const char *) G_SIGNATURE_DOUBLE, 0. / 0. },
+    { (const char *) G_SIGNATURE_DOUBLE, 1. / 0. },
+    { (const char *) G_SIGNATURE_DOUBLE, -1. / 0. },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new_double (values[i].value);
+      gdouble test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+}
+
+static void
+test_int32 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gint32 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_INT32, 0xffffffff },
+    { (const char *) G_SIGNATURE_INT32, 0x7fffffff },
+    { (const char *) G_SIGNATURE_INT32, 0x00000000 },
+    { (const char *) G_SIGNATURE_INT32, 42424242 },
+    { (const char *) G_SIGNATURE_INT32, 0x01010101 },
+    { (const char *) G_SIGNATURE_INT32, 0x10101010 },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new_int32 (values[i].value);
+      gint32 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+}
+
+
+static void
+test_uint32 (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    guint32 value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_UINT32, 0xffffffff },
+    { (const char *) G_SIGNATURE_UINT32, 0x7fffffff },
+    { (const char *) G_SIGNATURE_UINT32, 0x00000000 },
+    { (const char *) G_SIGNATURE_UINT32, 42424242 },
+    { (const char *) G_SIGNATURE_UINT32, 0x01010101 },
+    { (const char *) G_SIGNATURE_UINT32, 0x10101010 },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new_uint32 (values[i].value);
+      guint32 test;
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+}
+
+static void
+test_int16 (void)
+{
+  int i;
+  gint16 b;
+
+  for (b = 0xffff; b < 0x7fff; b++)
+    {
+      GVariant *variant = g_variant_new_int16 (b);
+      gint16 test;
+      g_variant_get (variant, (const char *) G_SIGNATURE_INT16, &test);
+
+      g_assert_cmpint (test, ==, b);
+
+      g_variant_unref (variant);
+    }
+}
+
+
+static void
+test_uint16 (void)
+{
+  int i;
+  guint16 b;
+
+  for (b = 0xffff; b < 0x7fff; b++)
+    {
+      GVariant *variant = g_variant_new_uint16 (b);
+      guint16 test;
+      g_variant_get (variant, (const char *) G_SIGNATURE_UINT16, &test);
+
+      g_assert_cmpint (test, ==, b);
+
+      g_variant_unref (variant);
+    }
+}
+
+static void
+test_byte (void)
+{
+  int i;
+  guint8 b;
+
+  for (b = 0; b < 255; b++)
+    {
+      GVariant *variant = g_variant_new_byte (b);
+      guint8 test;
+      g_variant_get (variant, (const char *) G_SIGNATURE_BYTE, &test);
+
+      g_assert_cmpint (test, ==, b);
+
+      g_variant_unref (variant);
+    }
+}
+
+
+static void
+test_string (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    const char *value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_STRING, "" },
+    { (const char *) G_SIGNATURE_STRING, "the anti string" },
+    { (const char *) G_SIGNATURE_STRING, "<xml attr=\"test\"><![CDATA[ abdbdb\n\n%&*@&#\n]]></xml>" },
+    { (const char *) G_SIGNATURE_STRING, "I do, I in fact , I insist" },
+    { (const char *) G_SIGNATURE_STRING, "%s %d %s %*.*s printf, anyone?" },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new_string (values[i].value);
+      char *test;
+
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpstr (test, ==, values[i].value);
+      g_free (test);
+
+      g_variant_unref (variant);
+    }
+}
+
+static void
+test_boolean (void)
+{
+  int i;
+
+  static struct
+  {
+    const char *type;
+    gboolean value;
+  } values[] = {
+    { (const char *) G_SIGNATURE_BOOLEAN, TRUE },
+    { (const char *) G_SIGNATURE_BOOLEAN, FALSE },
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (values); i++)
+    {
+      GVariant *variant = g_variant_new_boolean (values[i].value);
+      gboolean test;
+
+      g_variant_get (variant, values[i].type, &test);
+
+      g_assert_cmpint (test, ==, values[i].value);
+
+      g_variant_unref (variant);
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/variant/basic/boolean", test_boolean);
+  g_test_add_func ("/variant/basic/string", test_string);
+  g_test_add_func ("/variant/basic/byte", test_byte);
+  g_test_add_func ("/variant/basic/uint16", test_uint16);
+  g_test_add_func ("/variant/basic/int16", test_int16);
+  g_test_add_func ("/variant/basic/int32", test_int32);
+  g_test_add_func ("/variant/basic/uint32", test_int32);
+  g_test_add_func ("/variant/basic/double", test_double);
+  g_test_add_func ("/variant/basic/int64", test_int64);
+  g_test_add_func ("/variant/basic/uint64", test_uint64);
+
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-endian.c b/glib/tests/gvariant-endian.c
new file mode 100644
index 0000000..3200852
--- /dev/null
+++ b/glib/tests/gvariant-endian.c
@@ -0,0 +1,32 @@
+#include <glib.h>
+
+static void
+test_byteswap (void)
+{
+  const guchar data[] = {
+    0, 0, 0, 0, 0, 0, 0, 8,
+    0, 0, 0, 1,
+    0, 2,
+    1,
+    0,
+    '\0', '(', 't', 'u', 'q', 'y', 'b', ')'
+  };
+  GVariant *value;
+  gchar *string;
+
+  value = g_variant_load (NULL, data, sizeof data,
+                          G_BIG_ENDIAN | G_VARIANT_LAZY_BYTESWAP);
+  string = g_variant_markup_print (value, FALSE, 0, 0);
+  g_variant_unref (value);
+
+  g_assert_cmpstr (string, ==, "<struct><uint64>8</uint64><uint32>1</uint32><uint16>2</uint16><byte>0x01</byte><false/></struct>");
+  g_free (string);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/gvariant/endian/0", test_byteswap);
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-fuzz.c b/glib/tests/gvariant-fuzz.c
new file mode 100644
index 0000000..fa00b8d
--- /dev/null
+++ b/glib/tests/gvariant-fuzz.c
@@ -0,0 +1,428 @@
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <string.h>
+
+#define TESTS                     500
+#define MAXIMUM_DEPTH             5
+#define MAXIMUM_ARRAY_SIZE        8
+#define MAXIMUM_STRUCT_SIZE       8
+/* the probability of an empty array/struct */
+#define PROBABILITY_OF_NOTHING    0.1
+#define PROBABILITY_OF_BASIC_TYPE 0.3
+/* log base 2 of maximum string length */
+#define LOG_2_MAXIMUM_STRING_SIZE 8
+
+/* change these only when protocol changes */
+#define NUMBER_OF_BASIC_TYPES     9
+#define NUMBER_OF_CONTAINER_TYPES 5
+
+static void         random_signature             (GString *signature,
+                                                  int      depth);
+static gchar       *random_string                (void);
+static const gchar *next_type_in_signature       (const gchar *sig);
+static const gchar *random_markup_from_signature (GString     *markup,
+                                                  const gchar *signature,
+                                                  int          depth);
+static void         random_markup                (GString *markup,
+                                                  int      depth);
+
+static void
+random_signature (GString *signature,
+                  int      depth)
+{
+  if (!depth || g_test_rand_double_range (0, 1) < PROBABILITY_OF_BASIC_TYPE)
+    {
+      const gchar *c = "ybnqiuxts";
+      int i = g_test_rand_int_range (0, NUMBER_OF_BASIC_TYPES);
+      g_string_append_c (signature, c[i]);
+    }
+  else
+    {
+      switch (g_test_rand_int_range (0, NUMBER_OF_CONTAINER_TYPES))
+        {
+          case 0:
+            g_string_append_c (signature, 'a');
+            random_signature (signature, depth - 1);
+            break;
+
+          case 1:
+            g_string_append_c (signature, '(');
+
+            if (g_test_rand_double_range (0, 1) >= PROBABILITY_OF_NOTHING)
+              {
+                int size = g_test_rand_int_range (1, MAXIMUM_STRUCT_SIZE + 1);
+
+                while (size--)
+                  random_signature (signature, depth - 1);
+              }
+
+            g_string_append_c (signature, ')');
+            break;
+
+          case 2:
+            g_string_append_c (signature, 'v');
+            break;
+
+          case 3:
+            g_string_append_c (signature, '{');
+            random_signature (signature, 0);
+            random_signature (signature, depth - 1);
+            g_string_append_c (signature, '}');
+            break;
+
+          case 4:
+            g_string_append_c (signature, 'm');
+            random_signature (signature, depth - 1);
+            break;
+        }
+    }
+}
+
+static gchar *
+random_string (void)
+{
+  gchar str[1 << LOG_2_MAXIMUM_STRING_SIZE];
+  int   size;
+  int   i;
+
+  size = 1 << g_test_rand_int_range (0, LOG_2_MAXIMUM_STRING_SIZE);
+  size += g_test_rand_int_range (-1, size - 1);
+
+  for (i = 0; i < size; i++)
+    str[i] = (gchar) g_test_rand_int_range (' ', '~' + 1);
+
+  return g_markup_escape_text (str, size);
+}
+
+static const gchar *
+next_type_in_signature (const gchar *sig)
+{
+  while (*sig == 'a' || *sig == 'm')
+    sig++;
+
+  if (*sig == '(' || *sig == '{')
+    for (sig++; *sig != ')' && *sig != '}';
+         sig = next_type_in_signature (sig));
+
+  return sig + 1;
+}
+
+static const gchar *
+random_markup_from_signature (GString     *markup,
+                              const gchar *signature,
+                              int          depth)
+{
+  switch (*signature)
+    {
+      case 'y':
+        g_string_append_printf (markup,
+                                "<byte>0x%02x</byte>",
+                                (guint8) g_test_rand_int ());
+        return signature + 1;
+
+      case 'b':
+        g_string_append_printf (markup,
+                                "<%s/>",
+                                g_test_rand_bit () ? "true" : "false");
+        return signature + 1;
+
+      case 'n':
+        g_string_append_printf (markup,
+                                "<int16>%d</int16>",
+                                (gint16) g_test_rand_int ());
+        return signature + 1;
+
+      case 'q':
+        g_string_append_printf (markup,
+                                "<uint16>%u</uint16>",
+                                (guint16) g_test_rand_int ());
+        return signature + 1;
+
+      case 'i':
+        g_string_append_printf (markup,
+                                "<int32>%d</int32>",
+                                (gint32) g_test_rand_int ());
+        return signature + 1;
+
+      case 'u':
+        g_string_append_printf (markup,
+                                "<uint32>%u</uint32>",
+                                (guint32) g_test_rand_int ());
+        return signature + 1;
+
+      case 'x':
+        g_string_append_printf (markup,
+                                "<int64>%d</int64>",
+                                g_test_rand_int ());
+        return signature + 1;
+
+      case 't':
+        g_string_append_printf (markup,
+                                "<uint64>%u</uint64>",
+                                g_test_rand_int ());
+        return signature + 1;
+
+      case 'd':
+        g_string_append_printf (markup,
+                                "<double>%lf</double>",
+                                g_test_rand_double ());
+        return signature + 1;
+
+      case 's':
+        {
+          gchar *escaped = random_string ();
+
+          g_string_append_printf (markup,
+                                  "<string>%s</string>",
+                                  escaped);
+
+          g_free (escaped);
+          return signature + 1;
+        }
+
+      case 'a':
+        if (g_test_rand_double_range (0, 1) >= PROBABILITY_OF_NOTHING)
+          {
+            const gchar *next = NULL;
+            int size;
+
+            g_string_append (markup, "<array>");
+
+            for (size = g_test_rand_int_range (1, MAXIMUM_ARRAY_SIZE + 1);
+                 size--;
+                 next = random_markup_from_signature (markup,
+                                                      signature + 1,
+                                                      depth - 1));
+
+            signature = next;
+
+            g_string_append (markup, "</array>");
+          }
+        else
+          {
+            const gchar *next = next_type_in_signature (signature);
+
+            g_string_append (markup, "<array type='");
+            g_string_append_len (markup, signature, next - signature);
+            g_string_append (markup, "'/>");
+
+            signature = next;
+          }
+
+        return signature;
+
+      case '(':
+        if (*++signature == ')')
+          g_string_append (markup, "<triv/>");
+        else
+          {
+            g_string_append (markup, "<struct>");
+
+            for (;
+                 *signature != ')';
+                 signature = random_markup_from_signature (markup,
+                                                           signature,
+                                                           depth - 1));
+
+            g_string_append (markup, "</struct>");
+          }
+
+        return signature + 1;
+
+      case 'v':
+        g_string_append (markup, "<variant>");
+        random_markup (markup, depth - 1);
+        g_string_append (markup, "</variant>");
+        return signature + 1;
+
+      case '{':
+        g_string_append (markup, "<dictionary-entry>");
+
+        for (signature++;
+             *signature != '}';
+             signature = random_markup_from_signature (markup,
+                                                       signature,
+                                                       depth - 1));
+
+        g_string_append (markup, "</dictionary-entry>");
+        return signature + 1;
+
+      case 'm':
+        if (g_test_rand_bit ())
+          {
+            g_string_append (markup, "<maybe>");
+
+            signature = random_markup_from_signature (markup,
+                                                      signature + 1,
+                                                      depth - 1);
+
+            g_string_append (markup, "</maybe>");
+          }
+        else
+          {
+            const gchar *next = next_type_in_signature (signature);
+
+            g_string_append (markup, "<nothing type='");
+            g_string_append_len (markup, signature, next - signature);
+            g_string_append (markup, "'/>");
+
+            signature = next;
+          }
+
+        return signature;
+
+      default:
+        g_assert_not_reached ();
+    }
+}
+
+static void
+random_markup (GString *markup,
+               int      depth)
+{
+  GString *signature = g_string_new ("");
+
+  random_signature (signature, depth);
+  random_markup_from_signature (markup, signature->str, depth);
+  /* g_message ("%s", signature->str); */
+
+  g_string_free (signature, TRUE);
+}
+
+static void
+fuzz (guchar  *data,
+      gsize    length,
+      gdouble  fuzziness)
+{
+  gboolean have_fuzz = FALSE;
+  gsize i;
+
+  g_assert (length > 0);
+
+  for (i = 0; i < length; i++)
+    if (g_test_rand_double () < fuzziness)
+      {
+        guchar new = g_test_rand_int_range (0, 255);
+
+        if (new >= data[i])
+          new++;
+
+        data[i] = new;
+
+        have_fuzz = TRUE;
+      }
+
+  if (!have_fuzz)
+    data[0]++;
+}
+
+gboolean g_variant_is_normal_ (GVariant *);
+
+static void
+fuzz_iteration (gdouble fuzziness)
+{
+  GError *error = NULL;
+  GVariant *value;
+  GString *markup;
+  gint depth;
+  gsize size;
+
+  markup = g_string_new ("");
+  depth = g_test_rand_int_range (3, MAXIMUM_DEPTH);
+  random_markup (markup, depth);
+
+  value = g_variant_markup_parse (markup->str, markup->len, NULL, &error);
+  if (value == NULL)
+    g_error ("%s", error->message);
+
+  size = g_variant_get_size (value);
+
+  if (size)
+    {
+      const guchar *internal_data;
+      GVariant *fuzzed_value;
+      guchar *slice;
+     
+      slice = g_slice_alloc (size);
+      g_variant_store (value, slice);
+      internal_data = g_variant_get_data (value);
+
+      g_assert (memcmp (slice, internal_data, size) == 0);
+      fuzz (slice, size, fuzziness);
+      g_assert (memcmp (slice, internal_data, size) != 0);
+
+      fuzzed_value = g_variant_from_slice (g_variant_get_type (value),
+                                           slice, size, 0);
+
+      if (g_variant_is_normal_ (fuzzed_value))
+        {
+          gchar *different;
+
+          different = g_variant_markup_print (fuzzed_value, 0, 0, 0);
+          g_assert_cmpstr (markup->str, !=, different);
+          g_free (different);
+        }
+      else
+        {
+          GVariant *reconstructed;
+          gchar *fuzzy_markup;
+
+          fuzzy_markup = g_variant_markup_print (fuzzed_value, 0, 0, 0);
+          reconstructed = g_variant_markup_parse (fuzzy_markup,
+                                                  -1, NULL, &error);
+
+          if (reconstructed == NULL)
+            g_error ("parsing reconstructed document '%s': %s\n",
+                     fuzzy_markup, error->message);
+
+          if (g_variant_get_size (reconstructed) == size)
+            {
+              const guchar *reconstructed_data;
+
+              reconstructed_data = g_variant_get_data (reconstructed);
+              g_assert (memcmp (reconstructed_data, slice, size) != 0);
+            }
+
+          g_free (fuzzy_markup);
+          g_variant_unref (reconstructed);
+        }
+
+      g_variant_unref (fuzzed_value);
+    }
+
+  g_string_free (markup, TRUE);
+  g_variant_unref (value);
+}
+
+static void
+fuzz_test (gpointer data)
+{
+  gdouble fuzziness;
+  gint percent;
+  int i;
+
+  percent = GPOINTER_TO_INT (data);
+  fuzziness = percent / 100.0;
+
+  for (i = 0; i < TESTS; i++)
+    fuzz_iteration (fuzziness);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  int i;
+
+  g_test_init (&argc, &argv, NULL);
+
+  for (i = 1; i <= 20; i += 4)
+    {
+      char testname[80];
+
+      g_sprintf (testname, "/gvariant/fuzz/fuzziness/%d%%", i);
+      g_test_add_data_func (testname, GINT_TO_POINTER (i), (gpointer) fuzz_test);
+    }
+
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-markup.c b/glib/tests/gvariant-markup.c
new file mode 100644
index 0000000..1fe8aa8
--- /dev/null
+++ b/glib/tests/gvariant-markup.c
@@ -0,0 +1,111 @@
+#include <glib.h>
+
+#define add_tests(func, basename, array) \
+  G_STMT_START { \
+    int __add_tests_i;                                                  \
+                                                                        \
+    for (__add_tests_i  = 0;                                            \
+         __add_tests_i < G_N_ELEMENTS (array);                          \
+         __add_tests_i++)                                               \
+      {                                                                 \
+        char *testname;                                                 \
+                                                                        \
+        testname = g_strdup_printf ("%s/%d", basename, __add_tests_i);  \
+        g_test_add_data_func (testname, array[__add_tests_i], func);    \
+        g_free (testname);                                              \
+      }                                                                 \
+  } G_STMT_END
+
+const char *verbatim_tests[] = {
+  "<array type='ai'/>",
+
+  "<struct>"
+    "<array>"
+      "<int32>1</int32>"
+      "<int32>2</int32>"
+      "<int32>3</int32>"
+    "</array>"
+    "<array>"
+      "<array type='aaai'/>"
+      "<array type='aaai'/>"
+      "<array type='aaai'/>"
+    "</array>"
+  "</struct>",
+
+  "<array>"
+    "<string>hello world</string>"
+    "<string>how &lt;are&gt; you</string>"
+    "<string> working -- i -- hope    </string>"
+    "<string>%s %d %s %d</string>"
+    "<string></string>"
+  "</array>",
+
+  "<array>"
+    "<array>"
+      "<string>foo</string>"
+    "</array>"
+    "<array type='as'/>"
+  "</array>",
+
+  "<maybe>"
+    "<struct>"
+      "<uint32>42</uint32>"
+      "<byte>0x42</byte>"
+      "<int32>-1</int32>"
+      "<double>37.500000</double>"
+      "<int64>-35383472451088536</int64>"
+      "<uint64>9446744073709551616</uint64>"
+    "</struct>"
+  "</maybe>",
+
+  "<struct>"
+    "<array>"
+      "<true/>"
+      "<false/>"
+      "<true/>"
+      "<true/>"
+      "<false/>"
+      "<false/>"
+      "<true/>"
+      "<true/>"
+    "</array>"
+    "<array>"
+      "<triv/>"
+      "<triv/>"
+    "</array>"
+  "</struct>"
+};
+
+static void
+check_verbatim (gconstpointer data)
+{
+  const char *markup = data;
+  GError *error = NULL;
+  GVariant *value;
+  gchar *out;
+
+  value = g_variant_markup_parse (markup, -1, NULL, &error);
+
+  if (value == NULL)
+    {
+      g_assert (error != NULL);
+      g_error ("%s", error->message);
+    }
+  else
+    g_assert (error == NULL);
+
+  g_variant_flatten (value);
+
+  out = g_variant_markup_print (value, FALSE, 0, 0);
+  g_assert_cmpstr (markup, ==, out);
+  g_free (out);
+  g_variant_unref (value);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+  add_tests (check_verbatim, "/gvariant/markup/verbatim", verbatim_tests);
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-printer-parser.c b/glib/tests/gvariant-printer-parser.c
new file mode 100644
index 0000000..c6179ed
--- /dev/null
+++ b/glib/tests/gvariant-printer-parser.c
@@ -0,0 +1,75 @@
+#include <glib.h>
+
+#define add_tests(func, basename, array) \
+  G_STMT_START { \
+    int __add_tests_i;                                                  \
+                                                                        \
+    for (__add_tests_i  = 0;                                            \
+         __add_tests_i < G_N_ELEMENTS (array);                          \
+         __add_tests_i++)                                               \
+      {                                                                 \
+        char *testname;                                                 \
+                                                                        \
+        testname = g_strdup_printf ("%s/%d", basename, __add_tests_i);  \
+        g_test_add_data_func (testname, array[__add_tests_i], func);    \
+        g_free (testname);                                              \
+      }                                                                 \
+  } G_STMT_END
+
+const char *verbatim_tests[] = {
+  "-34",
+
+  /* Floats are printed at the minimum precision to lose no accuracy. */
+  "37.5",
+  
+  /* FIXME: use sf when it makes sense */
+  /*"4.3e16",*/
+  "3.1415926535897931",
+
+  "\"Hello\"",
+
+  "[[3, 4], [5, 6]]",
+
+  "<(byte 0x04, int16 -16, 6.0, 3.141, \"Hello\", <<<<<5>>>>>)>",
+
+  "([true, false, false, true, false], [(), ()])",
+
+  "<@ai []>",
+
+  "<[int16 1, -1, 2, -3, -1, -2, 1, -3, 4, -7, 3, -10, 13, -23, 26]>"
+};
+
+static void
+check_verbatim (gconstpointer data)
+{
+  const char *string = data;
+  GError *error = NULL;
+  GVariant *value;
+  gchar *out;
+
+  value = g_variant_parse (string, -1, NULL, &error);
+
+  if (value == NULL)
+    {
+      g_assert (error != NULL);
+      g_error ("%s", error->message);
+    }
+  else
+    g_assert (error == NULL);
+
+  //g_variant_flatten (value);
+
+  out = g_variant_print (value, FALSE);
+  g_assert_cmpstr (string, ==, out);
+  g_free (out);
+  g_variant_unref (value);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  add_tests (check_verbatim, "/gvariant/parse/verbatim", verbatim_tests);
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-random.c b/glib/tests/gvariant-random.c
new file mode 100644
index 0000000..0b6e37a
--- /dev/null
+++ b/glib/tests/gvariant-random.c
@@ -0,0 +1,334 @@
+#include <glib.h>
+
+#define TESTS                     100
+#define MAXIMUM_DEPTH             16
+#define MAXIMUM_ARRAY_SIZE        8
+#define MAXIMUM_STRUCT_SIZE       8
+/* the probability of an empty array/struct */
+#define PROBABILITY_OF_NOTHING    0.1
+#define PROBABILITY_OF_BASIC_TYPE 0.3
+/* log base 2 of maximum string length */
+#define LOG_2_MAXIMUM_STRING_SIZE 8
+
+/* change these only when protocol changes */
+#define NUMBER_OF_BASIC_TYPES     10
+#define NUMBER_OF_CONTAINER_TYPES 5
+
+static void         random_signature             (GString *signature,
+                                                  int      depth);
+static gchar       *random_string                (void);
+static const gchar *next_type_in_signature       (const gchar *sig);
+static const gchar *random_markup_from_signature (GString     *markup,
+                                                  const gchar *signature,
+                                                  int          depth);
+static void         random_markup                (GString *markup,
+                                                  int      depth);
+static void         test                         (void);
+
+static void
+random_signature (GString *signature,
+                  int      depth)
+{
+  if (!depth || g_test_rand_double_range (0, 1) < PROBABILITY_OF_BASIC_TYPE)
+    {
+      const gchar *c = "ybnqiuxtds";
+      int i = g_test_rand_int_range (0, NUMBER_OF_BASIC_TYPES);
+      g_string_append_c (signature, c[i]);
+    }
+  else
+    {
+      switch (g_test_rand_int_range (0, NUMBER_OF_CONTAINER_TYPES))
+        {
+          case 0:
+            g_string_append_c (signature, 'a');
+            random_signature (signature, depth - 1);
+            break;
+
+          case 1:
+            g_string_append_c (signature, '(');
+
+            if (g_test_rand_double_range (0, 1) >= PROBABILITY_OF_NOTHING)
+              {
+                int size = g_test_rand_int_range (1, MAXIMUM_STRUCT_SIZE + 1);
+
+                while (size--)
+                  random_signature (signature, depth - 1);
+              }
+
+            g_string_append_c (signature, ')');
+            break;
+
+          case 2:
+            g_string_append_c (signature, 'v');
+            break;
+
+          case 3:
+            g_string_append_c (signature, '{');
+            random_signature (signature, 0);
+            random_signature (signature, depth - 1);
+            g_string_append_c (signature, '}');
+            break;
+
+          case 4:
+            g_string_append_c (signature, 'm');
+            random_signature (signature, depth - 1);
+            break;
+        }
+    }
+}
+
+static gchar *
+random_string (void)
+{
+  gchar str[1 << LOG_2_MAXIMUM_STRING_SIZE];
+  int   size;
+  int   i;
+
+  size = 1 << g_test_rand_int_range (0, LOG_2_MAXIMUM_STRING_SIZE);
+  size += g_test_rand_int_range (-1, size - 1);
+
+  for (i = 0; i < size; i++)
+    str[i] = (gchar) g_test_rand_int_range (' ', '~' + 1);
+
+  return g_markup_escape_text (str, size);
+}
+
+static const gchar *
+next_type_in_signature (const gchar *sig)
+{
+  while (*sig == 'a' || *sig == 'm')
+    sig++;
+
+  if (*sig == '(' || *sig == '{')
+    for (sig++; *sig != ')' && *sig != '}';
+         sig = next_type_in_signature (sig));
+
+  return sig + 1;
+}
+
+static const gchar *
+random_markup_from_signature (GString     *markup,
+                              const gchar *signature,
+                              int          depth)
+{
+  switch (*signature)
+    {
+      case 'y':
+        g_string_append_printf (markup,
+                                "<byte>0x%02x</byte>",
+                                (guint8) g_test_rand_int ());
+        return signature + 1;
+
+      case 'b':
+        g_string_append_printf (markup,
+                                "<%s/>",
+                                g_test_rand_bit () ? "true" : "false");
+        return signature + 1;
+
+      case 'n':
+        g_string_append_printf (markup,
+                                "<int16>%d</int16>",
+                                (gint16) g_test_rand_int ());
+        return signature + 1;
+
+      case 'q':
+        g_string_append_printf (markup,
+                                "<uint16>%u</uint16>",
+                                (guint16) g_test_rand_int ());
+        return signature + 1;
+
+      case 'i':
+        g_string_append_printf (markup,
+                                "<int32>%d</int32>",
+                                (gint32) g_test_rand_int ());
+        return signature + 1;
+
+      case 'u':
+        g_string_append_printf (markup,
+                                "<uint32>%u</uint32>",
+                                (guint32) g_test_rand_int ());
+        return signature + 1;
+
+      case 'x':
+        g_string_append_printf (markup,
+                                "<int64>%d</int64>",
+                                g_test_rand_int ());
+        return signature + 1;
+
+      case 't':
+        g_string_append_printf (markup,
+                                "<uint64>%u</uint64>",
+                                g_test_rand_int ());
+        return signature + 1;
+
+      case 'd':
+        g_string_append_printf (markup,
+                                "<double>%lf</double>",
+                                g_test_rand_double ());
+        return signature + 1;
+
+      case 's':
+        {
+          gchar *escaped = random_string ();
+
+          g_string_append_printf (markup,
+                                  "<string>%s</string>",
+                                  escaped);
+
+          g_free (escaped);
+          return signature + 1;
+        }
+
+      case 'a':
+        if (g_test_rand_double_range (0, 1) >= PROBABILITY_OF_NOTHING)
+          {
+            const gchar *next = NULL;
+            int size;
+
+            g_string_append (markup, "<array>");
+
+            for (size = g_test_rand_int_range (1, MAXIMUM_ARRAY_SIZE + 1);
+                 size--;
+                 next = random_markup_from_signature (markup,
+                                                      signature + 1,
+                                                      depth - 1));
+
+            signature = next;
+
+            g_string_append (markup, "</array>");
+          }
+        else
+          {
+            const gchar *next = next_type_in_signature (signature);
+
+            g_string_append (markup, "<array type='");
+            g_string_append_len (markup, signature, next - signature);
+            g_string_append (markup, "'/>");
+
+            signature = next;
+          }
+
+        return signature;
+
+      case '(':
+        if (*++signature == ')')
+          g_string_append (markup, "<triv/>");
+        else
+          {
+            g_string_append (markup, "<struct>");
+
+            for (;
+                 *signature != ')';
+                 signature = random_markup_from_signature (markup,
+                                                           signature,
+                                                           depth - 1));
+
+            g_string_append (markup, "</struct>");
+          }
+
+        return signature + 1;
+
+      case 'v':
+        g_string_append (markup, "<variant>");
+        random_markup (markup, depth - 1);
+        g_string_append (markup, "</variant>");
+        return signature + 1;
+
+      case '{':
+        g_string_append (markup, "<dictionary-entry>");
+
+        for (signature++;
+             *signature != '}';
+             signature = random_markup_from_signature (markup,
+                                                       signature,
+                                                       depth - 1));
+
+        g_string_append (markup, "</dictionary-entry>");
+        return signature + 1;
+
+      case 'm':
+        if (g_test_rand_bit ())
+          {
+            g_string_append (markup, "<maybe>");
+
+            signature = random_markup_from_signature (markup,
+                                                      signature + 1,
+                                                      depth - 1);
+
+            g_string_append (markup, "</maybe>");
+          }
+        else
+          {
+            const gchar *next = next_type_in_signature (signature);
+
+            g_string_append (markup, "<nothing type='");
+            g_string_append_len (markup, signature, next - signature);
+            g_string_append (markup, "'/>");
+
+            signature = next;
+          }
+
+        return signature;
+
+      default:
+        g_assert_not_reached ();
+    }
+}
+
+static void
+random_markup (GString *markup,
+               int      depth)
+{
+  GString *signature = g_string_new ("");
+
+  random_signature (signature, depth);
+  random_markup_from_signature (markup, signature->str, depth);
+  /* g_message ("%s", signature->str); */
+
+  g_string_free (signature, TRUE);
+}
+
+static void
+test (void)
+{
+  GError   *error = NULL;
+  GString  *markup1;
+  GString  *markup2;
+  GVariant *variant;
+  int       depth;
+  int       i;
+
+  for (i = 0; i < TESTS; i++)
+    {
+      markup1 = g_string_new ("");
+      depth = g_test_rand_int_range (0, MAXIMUM_DEPTH);
+
+      random_markup (markup1, depth);
+      /* g_message ("%s", markup1->str); */
+      variant = g_variant_markup_parse (markup1->str, -1, NULL, &error);
+      g_variant_flatten (variant);
+
+      if (variant == NULL)
+        {
+          g_assert (error != NULL);
+          g_error ("%s", error->message);
+        }
+      else
+        g_assert (error == NULL);
+
+      markup2 = g_variant_markup_print_string (variant, NULL, FALSE, 0, 0);
+      g_assert_cmpstr (markup1->str, ==, markup2->str);
+      g_string_free (markup1, TRUE);
+      g_string_free (markup2, TRUE);
+      g_variant_unref (variant);
+    }
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/gvariant/random/0", test);
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-serialiser.c b/glib/tests/gvariant-serialiser.c
new file mode 100644
index 0000000..2784622
--- /dev/null
+++ b/glib/tests/gvariant-serialiser.c
@@ -0,0 +1,90 @@
+#include <glib.h>
+#include <stdio.h>
+
+#define add_tests(func, basename, array) \
+  G_STMT_START { \
+    int __add_tests_i;                                                  \
+                                                                        \
+    for (__add_tests_i  = 0;                                            \
+         __add_tests_i < G_N_ELEMENTS (array);                          \
+         __add_tests_i++)                                               \
+      {                                                                 \
+        char *testname;                                                 \
+                                                                        \
+        testname = g_strdup_printf ("%s/%d", basename, __add_tests_i);  \
+        g_test_add_data_func (testname, &array[__add_tests_i], func);   \
+        g_free (testname);                                              \
+      }                                                                 \
+  } G_STMT_END
+
+struct test_case
+{
+  const char *type;
+  int size;
+  const char *data;
+  const char *markup;
+};
+
+#define testcase(type, binary, markup) \
+  { type, sizeof binary - 1, binary, markup }
+
+struct test_case test_cases[] = {
+  testcase ("as", "foo\0bar\0se\0\x4\x8\xb",
+            "<array>"
+              "<string>foo</string>"
+              "<string>bar</string>"
+              "<string>se</string>"
+            "</array>"),
+  testcase ("(syus)", "str\0\xaa\0\0\0\x1\x1\x1\x1theend\0\x4",
+            "<struct>"
+              "<string>str</string>"
+              "<byte>0xaa</byte>"
+              "<uint32>16843009</uint32>"
+              "<string>theend</string>"
+            "</struct>"),
+  testcase ("a(sss)", "hello\0world\0gvariant\0\xc\x6" /*         0x17 */
+                      "this\0hopefully\0works\0\xf\x5" /* +0x17 = 0x2e */
+                      "k\0thx\0bye\0\x6\x2"            /* +0x0c = 0x3a */
+                      "\x17\x2e\x3a",
+            "<array>"
+              "<struct>"
+                "<string>hello</string>"
+                "<string>world</string>"
+                "<string>gvariant</string>"
+              "</struct>"
+              "<struct>"
+                "<string>this</string>"
+                "<string>hopefully</string>"
+                "<string>works</string>"
+              "</struct>"
+              "<struct>"
+                "<string>k</string>"
+                "<string>thx</string>"
+                "<string>bye</string>"
+              "</struct>"
+            "</array>")
+
+};
+
+void
+test (gconstpointer data)
+{
+  const struct test_case *tc = data;
+  GVariant *variant;
+  gchar *markup;
+
+  variant = g_variant_load (G_VARIANT_TYPE (tc->type), tc->data, tc->size, 0);
+  markup = g_variant_markup_print (variant, FALSE, 0, 0);
+  g_variant_unref (variant);
+
+  g_assert_cmpstr (tc->markup, ==, markup);
+  g_free (markup);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+  add_tests (test, "/gvariant/serialiser", test_cases);
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-varargs-strings.c b/glib/tests/gvariant-varargs-strings.c
new file mode 100644
index 0000000..76454da
--- /dev/null
+++ b/glib/tests/gvariant-varargs-strings.c
@@ -0,0 +1,62 @@
+#include <glib.h>
+
+static void
+check (gchar **a, const gchar *expected)
+{
+  gchar *actual;
+
+  actual = g_strjoinv (",", a);
+  g_assert_cmpstr (actual, ==, expected);
+  g_free (actual);
+}
+
+static void
+test (void)
+{
+  const gchar *array[] = {"one", "two", "three", "four", NULL};
+  GVariant *one, *two;
+  gchar **a;
+  gint num;
+
+  one = g_variant_new_strv (array, -1);
+  two = g_variant_new_strv (array, 3);
+
+  g_variant_get (one, "^as", &a, &num);
+  g_assert_cmpint (num, ==, 4);
+  check (a, "one,two,three,four");
+  g_strfreev (a);
+
+  g_variant_get (two, "^as", &a, &num);
+  g_assert_cmpint (num, ==, 3);
+  check (a, "one,two,three");
+  g_strfreev (a);
+
+  g_variant_get (one, "^a&s", &a, &num);
+  g_assert_cmpint (num, ==, 4);
+  check (a, "one,two,three,four");
+  g_free (a);
+
+  g_variant_get (two, "^a&s", &a, &num);
+  g_assert_cmpint (num, ==, 3);
+  check (a, "one,two,three");
+  g_free (a);
+
+  g_variant_unref (one);
+
+  g_variant_get (two, "^as", &a, &num);
+  g_variant_unref (two);
+
+  g_assert_cmpint (num, ==, 3);
+  check (a, "one,two,three");
+  g_strfreev (a);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/gvariant/varargs-strings", test);
+
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-varargs.c b/glib/tests/gvariant-varargs.c
new file mode 100644
index 0000000..5a6b1d7
--- /dev/null
+++ b/glib/tests/gvariant-varargs.c
@@ -0,0 +1,289 @@
+#include <glib.h>
+
+struct structure
+{
+  guchar boolean;
+  guint16 uint16;
+  guint32 uint32;
+  guint64 uint64;
+  gdouble floating;
+};
+
+static GVariant *
+make_value (void)
+{
+  GVariantBuilder *array, *builder, *ebuilder1, *ebuilder2, *ebuilder3;
+  struct structure fixed_struct[] =
+  {
+    { FALSE, 16161, 3232323232u, 6464646464646464ull, 42.0 },
+    { FALSE, 16161, 3232323232u, 6464646464646464ull, 42.0 },
+    { FALSE, 16161, 3232323232u, 6464646464646464ull, 42.0 }
+  };
+
+  array = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY, NULL);
+
+  ebuilder1 = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                     G_VARIANT_TYPE ("an"));
+  ebuilder2 = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                     G_VARIANT_TYPE ("an"));
+  ebuilder3 = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                     G_VARIANT_TYPE ("an"));
+  builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                   G_VARIANT_TYPE ("an"));
+  g_variant_builder_add (builder, "n", 234);
+
+  g_variant_builder_add (array, "(bym(nquisv)xtd? s@?* *r@r&(bqutd)"
+                         "&a(bqutd)msmvm*m?m sm@*anmanm&(bqutd))",
+                         TRUE, 0x8c,
+                         TRUE, 0x1316, 0xccff, -344763333, 0xf77f5aa5,
+                         "crikey!!", g_variant_new_int32 (-11111111),
+                         G_GINT64_CONSTANT (0x123),
+                         G_GUINT64_CONSTANT (0xaabbccddeeff),
+                         1234.75,
+                         g_variant_new_uint16 (0xfcfb),
+                         g_variant_new_string ("i'm here"),
+                         g_variant_new_boolean (TRUE),
+                         g_variant_new_double (37.5),
+                         g_variant_new_object_path ("/usr/local"),
+                         g_variant_new ("()"),
+                         g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
+                         fixed_struct, fixed_struct, 3,
+                         "i'm gone",
+                         g_variant_new_uint16 (44444),
+                         g_variant_new_uint16 (22222),
+                         g_variant_new_int16 (-7777),
+                         g_variant_new_string ("this is getting insane"),
+                         g_variant_new_string ("ugh"),
+                         ebuilder1, builder, fixed_struct, 72727);
+
+  g_variant_builder_add (array, "(bym(nquisv)xtd? s@?* *r@r&(bqutd)"
+                         "&a(bqutd)msmvm qm@nm sm@sanmanm&(bqutd))",
+                         TRUE, 0x8c,
+                         FALSE,
+                         G_GINT64_CONSTANT (0x123),
+                         G_GUINT64_CONSTANT (0xaabbccddeeff),
+                         1234.75,
+                         g_variant_new_uint16 (0xfcfb),
+                         g_variant_new_string ("i'm here"),
+                         g_variant_new_boolean (TRUE),
+                         g_variant_new_double (37.5),
+                         g_variant_new_object_path ("/usr/local"),
+                         g_variant_new ("()"),
+                         g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
+                         fixed_struct, fixed_struct, 3,
+                         NULL, NULL, NULL, NULL, NULL, NULL,
+                         ebuilder2, NULL, NULL);
+
+  g_variant_builder_add (array, "(bym(nquisv)xtd? s@?* *r@r&(bqutd)"
+                         "&a(bqutd)msmvm qm@nm sm@sanmanm&(bqutd))",
+                         TRUE, 0x8c,
+                         FALSE,
+                         G_GINT64_CONSTANT (0x123),
+                         G_GUINT64_CONSTANT (0xaabbccddeeff),
+                         1234.75,
+                         g_variant_new_uint16 (0xfcfb),
+                         g_variant_new_string ("i'm here"),
+                         g_variant_new_boolean (TRUE),
+                         g_variant_new_double (37.5),
+                         g_variant_new_object_path ("/usr/local"),
+                         g_variant_new ("()"),
+                         g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
+                         fixed_struct, fixed_struct, 3,
+                         NULL, NULL, NULL, NULL, NULL, NULL,
+                         ebuilder3, NULL, NULL);
+
+  return g_variant_builder_end (array);
+}
+
+static void
+test_iterate (void)
+{
+  GVariant *maybe_one, *maybe_two, *maybe_three, *maybe_four, *maybe_five;
+  GVariant *one, *two, *three, *four, *five, *six, *seven = (gpointer) 0xcccccccc, *eight;
+  struct structure *fixed_array; gint fixed_array_size;
+  struct structure *maybe_fixed_struct, *fixed_struct;
+  const gchar *string, *maybe_string;
+  GVariantIter maybe_array, array;
+  guchar byte; gboolean boolean;
+  guint16 uint16; gint16 int16;
+  guint32 uint32; gint32 int32;
+  guint64 uint64; gint64 int64;
+  gdouble floating;
+  gboolean is_just;
+
+  GVariantIter iter;
+  gboolean first;
+  gint count;
+
+  GVariant *value = make_value ();
+
+  g_variant_iter_init (&iter, value);
+  g_variant_unref (value);
+  first = TRUE;
+  count = 0;
+
+  while (g_variant_iter_next (&iter, "(bym(nquisv)xtd? s@?* *r@r&(bqutd)"
+                                     "&a(bqutd)msmvm*m?m sm@*anmanm&(bqutd))",
+                                     &boolean, &byte, /* by */
+
+                                     &is_just, &int16, &uint16, &int32,
+                                               &uint32, &string, &one,
+
+                                     &int64, &uint64, &floating, /* xtd */
+
+                                     &two, &three, &four, &five, &six, &seven,
+                                     &eight, /* ? s@?* *r@r */
+
+                                     &fixed_struct,
+                                     &fixed_array, &fixed_array_size,
+
+                                     &maybe_string, &maybe_one, &maybe_two,
+                                     &maybe_three, &maybe_four, &maybe_five,
+
+                                     &array, &maybe_array, &maybe_fixed_struct))
+    {
+      g_assert_cmpint (boolean, ==, TRUE);
+      g_assert_cmpint (byte, ==, 0x8c);
+      g_assert_cmpint (is_just, ==, first);
+
+      if (is_just)
+        {
+          g_assert_cmpint (int16, ==, 0x1316);
+          g_assert_cmpint (uint16, ==, 0xccff);
+          g_assert_cmpint (int32, ==, -344763333);
+          g_assert_cmpint (uint32, ==, 0xf77f5aa5);
+          g_assert_cmpstr (string, ==, "crikey!!");
+          g_assert_cmpint (g_variant_get_int32 (one), ==, -11111111);
+        }
+
+      g_assert (int64 == 0x123);
+      g_assert (uint64 == 0xaabbccddeeffull);
+      g_assert_cmpfloat (floating, ==, 1234.75);
+      
+      g_assert_cmpint (g_variant_get_uint16 (two), ==, 0xfcfb);
+      g_assert_cmpstr (g_variant_get_string (three, NULL), ==, "i'm here");
+      g_assert_cmpint (g_variant_get_boolean (four), ==, TRUE);
+      g_assert_cmpfloat (g_variant_get_double (five), ==, 37.5);
+      g_assert_cmpstr (g_variant_get_string (six, NULL), ==, "/usr/local");
+      g_assert_cmpint (g_variant_n_children (seven), ==, 0);
+      g_assert_cmpint (g_variant_n_children (eight), ==, 3);
+
+      g_assert_cmpint (fixed_struct->boolean, ==, FALSE);
+      g_assert_cmpint (fixed_struct->uint16, ==, 16161);
+      g_assert_cmpint (fixed_struct->uint32, ==, 3232323232u);
+      g_assert (fixed_struct->uint64 == 6464646464646464ull);
+      g_assert_cmpfloat (fixed_struct->floating, ==, 42.0);
+
+      g_assert_cmpint (fixed_array->boolean, ==, FALSE);
+      g_assert_cmpint (fixed_array->uint16, ==, 16161);
+      g_assert_cmpint (fixed_array->uint32, ==, 3232323232u);
+      g_assert (fixed_array->uint64 == 6464646464646464ull);
+      g_assert_cmpfloat (fixed_array->floating, ==, 42.0);
+
+      g_assert_cmpint (fixed_array_size, ==, 3);
+
+      g_assert_cmpint ((maybe_string != NULL), ==, first);
+      g_assert_cmpint ((maybe_one != NULL), ==, first);
+      g_assert_cmpint ((maybe_two != NULL), ==, first);
+      g_assert_cmpint ((maybe_three != NULL), ==, first);
+      g_assert_cmpint ((maybe_four != NULL), ==, first);
+      g_assert_cmpint ((maybe_five != NULL), ==, first);
+      g_assert_cmpint (g_variant_iter_was_cancelled (&array), ==, FALSE);
+      g_assert_cmpint (g_variant_iter_was_cancelled (&maybe_array), !=, first);
+      g_assert_cmpint ((maybe_fixed_struct != NULL), ==, first);
+
+      if (first)
+        {
+          g_assert_cmpstr (maybe_string, ==, "i'm gone");
+          g_assert_cmpint (g_variant_get_uint16 (maybe_one), ==, 44444);
+          g_assert_cmpint (g_variant_get_uint16 (maybe_two), ==, 22222);
+          g_assert_cmpint (g_variant_get_int16 (maybe_three), ==, -7777);
+          g_assert_cmpstr (g_variant_get_string (maybe_four, NULL),
+                           ==, "this is getting insane");
+          g_assert_cmpstr (g_variant_get_string (maybe_five, NULL),
+                           ==, "ugh");
+
+          g_assert_cmpint (maybe_fixed_struct->boolean, ==, FALSE);
+          g_assert_cmpint (maybe_fixed_struct->uint16, ==, 16161);
+          g_assert_cmpint (maybe_fixed_struct->uint32, ==, 3232323232u);
+          g_assert (maybe_fixed_struct->uint64 == 6464646464646464ull);
+          g_assert_cmpfloat (maybe_fixed_struct->floating, ==, 42.0);
+
+          /* access this once, but not the other time, to make sure
+           * freeing works in both cases */
+          g_assert (g_variant_iter_next_value (&array) == NULL);
+        }
+
+      first = FALSE;
+      count++;
+    }
+
+  g_assert_cmpint (count, ==, 3);
+}
+
+static void
+test_null (void)
+{
+  GVariant *value = make_value ();
+  GVariantIter iter;
+  gboolean is_just;
+  gboolean first;
+
+  g_variant_iter_init (&iter, value);
+  g_variant_unref (value);
+  first = TRUE;
+
+  while (g_variant_iter_next (&iter, "(bym(nquisv)xtd? s@?* *r@r&(bqutd)"
+                                     "&a(bqutd)msmvm*m?m sm@*anmanm&(bqutd))",
+                                     NULL, NULL,
+                                     &is_just, NULL, NULL, NULL,
+                                               NULL, NULL, NULL,
+                                     NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL))
+    {
+      g_assert_cmpint (is_just, ==, first);
+      first = FALSE;
+    }
+}
+
+static void
+test_totally_null (void)
+{
+  GVariant *value = make_value ();
+  GVariantIter iter;
+  gint count;
+
+  g_variant_iter_init (&iter, value);
+  g_variant_unref (value);
+  count = 0;
+
+  while (g_variant_iter_next (&iter, "(bym(nquisv)xtd? s@?* *r@r&(bqutd)"
+                                     "&a(bqutd)msmvm*m?m sm@*anmanm&(bqutd))",
+                                     NULL, NULL,
+
+                                     NULL, /* skip the ones for the struct */
+
+                                     NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL))
+    count++;
+
+  g_assert_cmpint (count, ==, 3);
+}
+ 
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/gvariant/varargs/iterate", test_iterate);
+  g_test_add_func ("/gvariant/varargs/iterate/null", test_null);
+  g_test_add_func ("/gvariant/varargs/iterate/very-null", test_totally_null);
+
+  return g_test_run ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]