[glib/gsettings: 3/327] Import GVariant



commit 0c12dd92e48a8cb633be803920d83fc01df34aa6
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Apr 5 22:57:40 2009 -0400

    Import GVariant
    
    A minimal first step to importing GVariant.  Mostly just a copy/paste at
    this point.

 glib/Makefile.am           |   13 +
 glib/glib.h                |    3 +
 glib/gvariant-core.c       | 1434 +++++++++++++++++++++++++++++++++++
 glib/gvariant-loadstore.h  |   48 ++
 glib/gvariant-markup.c     |  980 ++++++++++++++++++++++++
 glib/gvariant-private.h    |   37 +
 glib/gvariant-serialiser.c | 1248 +++++++++++++++++++++++++++++++
 glib/gvariant-serialiser.h |   47 ++
 glib/gvariant-util.c       | 1764 ++++++++++++++++++++++++++++++++++++++++++++
 glib/gvariant-valist.c     | 1130 ++++++++++++++++++++++++++++
 glib/gvariant.h            |  164 ++++
 glib/gvarianttype.c        | 1022 +++++++++++++++++++++++++
 glib/gvarianttype.h        |  318 ++++++++
 glib/gvarianttypeinfo.c    |  455 ++++++++++++
 glib/gvarianttypeinfo.h    |   54 ++
 15 files changed, 8717 insertions(+), 0 deletions(-)
---
diff --git a/glib/Makefile.am b/glib/Makefile.am
index c6f20bb..392a393 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -163,6 +163,16 @@ libglib_2_0_la_SOURCES = 	\
 	gunicodeprivate.h	\
 	gurifuncs.c 		\
 	gutils.c		\
+	gvariant-private.h	\
+	gvariant-core.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
@@ -239,6 +249,9 @@ glibsubinclude_HEADERS =   \
 	gunicode.h	\
 	gurifuncs.h 		\
 	gutils.h	\
+	gvarianttype.h	\
+	gvariant-loadstore.h	\
+	gvariant.h	\
 	gwin32.h	\
 	gprintf.h
 
diff --git a/glib/glib.h b/glib/glib.h
index 000d417..01b8487 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -84,6 +84,9 @@
 #include <glib/gunicode.h>
 #include <glib/gurifuncs.h>
 #include <glib/gutils.h>
+#include <glib/gvarianttype.h>
+#include <glib/gvariant-loadstore.h>
+#include <glib/gvariant.h>
 #ifdef G_PLATFORM_WIN32
 #include <glib/gwin32.h>
 #endif
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
new file mode 100644
index 0000000..bad16ed
--- /dev/null
+++ b/glib/gvariant-core.c
@@ -0,0 +1,1434 @@
+/*
+ * Copyright © 2007, 2008 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.
+ */
+
+/**
+ * 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>
+
+/* == 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_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, structures 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 structures it is the number of structure 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_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: 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, structures 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 (GVariant *value,
+                     gsize     index)
+{
+  GVariant *child;
+
+  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_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_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_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_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 structure 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_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_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_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_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;
+
+  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->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;
+
+  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->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;
+
+  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);
+    }
+}
diff --git a/glib/gvariant-loadstore.h b/glib/gvariant-loadstore.h
new file mode 100644
index 0000000..4249c6a
--- /dev/null
+++ b/glib/gvariant-loadstore.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2007, 2008 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.
+ */
+
+#ifndef _gvariant_loadstore_h_
+#define _gvariant_loadstore_h_
+
+#include <glib/gvariant.h>
+
+#pragma GCC visibility push (default)
+
+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);
+
+void                            g_variant_store                         (GVariant           *value,
+                                                                         gpointer            data);
+gconstpointer                   g_variant_get_data                      (GVariant           *value);
+gsize                           g_variant_get_size                      (GVariant           *value);
+
+void                            g_variant_normalise                     (GVariant           *value);
+
+#pragma GCC visibility pop
+
+#endif /* _gvariant_loadstore_h_ */
diff --git a/glib/gvariant-markup.c b/glib/gvariant-markup.c
new file mode 100644
index 0000000..365ae09
--- /dev/null
+++ b/glib/gvariant-markup.c
@@ -0,0 +1,980 @@
+/*
+ * Copyright © 2008 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/gtestutils.h>
+#include <glib/gmessages.h>
+#include <glib/gvariant.h>
+
+#include <string.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
+ * @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.
+ *
+ * If @string is non-%NULL then it is appended to and returned.  Else,
+ * a new empty #GString is allocated and it is returned.
+ *
+ * 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.
+ **/
+GString *
+g_variant_markup_print (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 (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, 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_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 (&iter)))
+              g_variant_markup_print (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_STRUCT:
+      {
+        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 (&iter)))
+              g_variant_markup_print (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 (&iter)))
+          g_variant_markup_print (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 (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_STRUCT,           "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, 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;
+}
diff --git a/glib/gvariant-private.h b/glib/gvariant-private.h
new file mode 100644
index 0000000..bfecd96
--- /dev/null
+++ b/glib/gvariant-private.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2007, 2008 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.
+ */
+
+#ifndef _gvariant_private_h_
+#define _gvariant_private_h_
+
+#include "gvariant-loadstore.h"
+#include "gvarianttypeinfo.h"
+
+/* gvariant-core.c */
+GVariant                       *g_variant_new_tree                      (const GVariantType  *type,
+                                                                         GVariant           **children,
+                                                                         gsize                n_children,
+                                                                         gboolean             trusted);
+void                            g_variant_ensure_native_endian          (GVariant            *value);
+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 */
+#pragma GCC visibility push (default)
+gboolean                        _g_variant_is_normal                    (GVariant            *value);
+#pragma GCC visibility pop
+
+#endif /* _gvariant_private_h_ */
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
new file mode 100644
index 0000000..3fab8eb
--- /dev/null
+++ b/glib/gvariant-serialiser.c
@@ -0,0 +1,1248 @@
+/*
+ * Copyright © 2007, 2008 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 "gvariant-serialiser.h"
+
+#include <glib/gtestutils.h>
+
+#include <string.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_STRUCT:
+      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_STRUCT:
+    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_STRUCT:
+    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_STRUCT:
+    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 structures here because
+           * fixed sized structures 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 struct 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_STRUCT:
+    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 ();
+  }
+}
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
new file mode 100644
index 0000000..643269a
--- /dev/null
+++ b/glib/gvariant-serialiser.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2007, 2008 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.
+ */
+
+#ifndef _gvariant_serialiser_h_
+#define _gvariant_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 /* _gvariant_serialiser_h_ */
diff --git a/glib/gvariant-util.c b/glib/gvariant-util.c
new file mode 100644
index 0000000..a8ba778
--- /dev/null
+++ b/glib/gvariant-util.c
@@ -0,0 +1,1764 @@
+/*
+ * Copyright © 2007, 2008 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 <string.h>
+#include <glib.h>
+
+#include "gvariant-private.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_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:
+ * @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 (GVariantIter *iter)
+{
+  GVariantIterReal *real = (GVariantIterReal *) iter;
+
+  if (real->child)
+    {
+      g_variant_unref (real->child);
+      real->child = NULL;
+    }
+
+  if (real->value == NULL)
+    return NULL;
+
+  real->child = g_variant_get_child (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;
+
+  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;
+
+  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)
+{
+  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_assert (g_variant_is_object_path (string));
+
+  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;
+
+  /* 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_assert (g_variant_is_signature (string));
+
+  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;
+
+  /* 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))
+      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;
+
+  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_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_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_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_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_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_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_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_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_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;
+
+  class = g_variant_get_type_class (value);
+  g_assert (class == G_VARIANT_TYPE_CLASS_STRING ||
+            class == G_VARIANT_TYPE_CLASS_OBJECT_PATH ||
+            class == G_VARIANT_TYPE_CLASS_SIGNATURE);
+
+  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)
+{
+  int size;
+
+  g_assert (g_variant_matches (value, G_VARIANT_TYPE_STRING) ||
+            g_variant_matches (value, G_VARIANT_TYPE_OBJECT_PATH) ||
+            g_variant_matches (value, G_VARIANT_TYPE_SIGNATURE));
+
+  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_assert (g_variant_matches (value, G_VARIANT_TYPE_VARIANT));
+
+  return g_variant_get_child (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;
+
+  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_STRUCT ||
+       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_STRUCT ||
+       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
+ * @class: 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   class,
+                        const GVariantType *type)
+{
+  GVariantBuilder *child;
+  GError *error = NULL;
+
+  if G_UNLIKELY (!g_variant_builder_check_add (parent, class, 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 (class, type);
+
+  if (parent->expected2)
+    {
+      if (class == G_VARIANT_TYPE_CLASS_MAYBE ||
+          class == G_VARIANT_TYPE_CLASS_ARRAY)
+        child->expected2 = g_variant_type_element (parent->expected2);
+      
+      if (class == G_VARIANT_TYPE_CLASS_STRUCT ||
+          class == 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_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:
+ * @class: a container #GVariantTypeClass
+ * @type: a type contained in @class, or %NULL
+ * @returns: a #GVariantBuilder
+ *
+ * Creates a new #GVariantBuilder.
+ *
+ * @class 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 @class is not %G_VARIANT_TYPE_CLASS_VARIANT
+ * then @type must be contained in @class and will match the type of
+ * the final value.  If @class 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   class,
+                       const GVariantType *type)
+{
+  GVariantBuilder *builder;
+
+  g_assert (g_variant_type_class_is_container (class));
+  g_assert (class == G_VARIANT_TYPE_CLASS_VARIANT ||
+            type == NULL || g_variant_type_get_class (type) == class);
+
+  builder = g_slice_new (GVariantBuilder);
+  builder->parent = NULL;
+  builder->offset = 0;
+  builder->has_child = FALSE;
+  builder->class = class;
+  builder->type = type ? g_variant_type_copy (type) : NULL;
+  builder->expected = NULL;
+  builder->trusted = TRUE;
+  builder->expected2 = FALSE;
+
+  switch (class)
+  {
+    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_STRUCT:
+      builder->children_allocated = 8;
+
+      if (builder->type &&
+          g_variant_type_equal (builder->type, G_VARIANT_TYPE_ANY_STRUCT))
+        /* 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_assert (builder->parent == 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_STRUCT:
+        my_type = g_variant_type_new_struct (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_assert (builder != NULL);
+  g_assert (builder->has_child == 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_STRUCT:
+      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 structure 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
+ * @class: 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 @class
+ * (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 @class (except, as with g_variant_builder_new(), if
+ * @class is %G_VARIANT_TYPE_CLASS_VARIANT then any @type is OK).
+ *
+ * The function then checks if any child of class @class (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 @class
+ * 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 @class 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 @class
+ * (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    class,
+                             const GVariantType  *type,
+                             GError             **error)
+{
+  g_assert (builder != NULL);
+  g_assert (builder->has_child == FALSE);
+  g_assert (class != G_VARIANT_TYPE_CLASS_INVALID &&
+            class != G_VARIANT_TYPE_CLASS_ALL &&
+            class != G_VARIANT_TYPE_CLASS_BASIC);
+
+  if (class == G_VARIANT_TYPE_CLASS_VARIANT)
+    type = NULL;
+
+  /* basically, we need to check the following:
+   *
+   * expected2 <= type <= class <= expected
+   *
+   * but since any combination of the types may be %NULL, we need
+   * explicit checks:
+   *
+   *   type <= class
+   *   class <= expected
+   *   type <= expected
+   *   expected2 <= class
+   *   expected2 <= type
+   *
+   * (we already know expected2 <= expected)
+   */
+
+  /* type <= class */
+  if (type && g_variant_type_get_class (type) != class)
+    {
+      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", type_str);
+      g_free (type_str);
+      return FALSE;
+    }
+
+  /* class <= 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 (class)) &&
+          expected_class != class)
+        {
+          g_set_error (error, G_VARIANT_BUILDER_ERROR,
+                       G_VARIANT_BUILDER_ERROR_TYPE,
+                       "expecting value of class '%c', not '%c'",
+                       expected_class, class);
+          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 <= class && expected2 <= type */
+  if (builder->expected2 &&
+      ((!g_variant_type_is_in_class (builder->expected2, class)) ||
+       (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 (class))
+            {
+              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_STRUCT:
+      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 structure 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_assert (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_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)
+{
+  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)
+{
+  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)
+{
+  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)
+{
+  return g_variant_type_class_is_container (g_variant_get_type_class (value));
+}
+
+#include <glib/gstdio.h>
+void
+g_variant_dump_data (GVariant *value)
+{
+  const guchar *data;
+  const guchar *end;
+  char row[3*16+2];
+  gsize data_size;
+  gsize i;
+
+  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)
+        g_sprintf (&row[3 * (i & 15) + (i & 8)/8], "%02x  ", *data++);
+      else
+        g_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_STRUCT:
+    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 (&iter)))
+          g_variant_builder_add_value (builder, g_variant_deep_copy (child));
+
+        return g_variant_builder_end (builder);
+      }
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+
diff --git a/glib/gvariant-valist.c b/glib/gvariant-valist.c
new file mode 100644
index 0000000..4b1a555
--- /dev/null
+++ b/glib/gvariant-valist.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright © 2007, 2008 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 "gvariant-private.h"
+
+#include <glib/gtestutils.h>
+#include <glib/gmessages.h>
+#include <string.h>
+
+/**
+ * g_variant_format_string_scan:
+ * @format_string: a pass-by-reference pointer to the start of a
+ *                 possible format string
+ * @returns: %TRUE if a format string was scanned
+ *
+ * Checks the string pointed to by @format_string for starting with a
+ * properly formed #GVariant varargs format string.  If a format
+ * string is fount, @format_string is updated to point to the first
+ * character following the format string and %TRUE is returned.
+ *
+ * If no valid format string is found, %FALSE is returned and the
+ * state of the @format_string pointer is undefined.
+ *
+ * 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.
+ *
+ * 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 **format_string)
+{
+  switch (*(*format_string)++)
+  {
+    case '\0':
+      return FALSE;
+
+    case '(':
+      while (**format_string != ')')
+        if (!g_variant_format_string_scan (format_string))
+          return FALSE;
+
+      (*format_string)++; /* ')' */
+
+      return TRUE;
+
+    case '{':
+      if (**format_string == '\0')
+        return FALSE;
+
+      /* key may only be a basic type.  three posibilities for that:
+       */
+      if (strchr ("bynqiuxtdsog?", **format_string))
+        *format_string += 1;
+
+      else if ((*format_string)[0] == '@' &&
+               strchr ("bynqiuxtdsog?", (*format_string)[1]))
+        *format_string += 2;
+
+      else if ((*format_string)[0] == '&' &&
+               strchr ("bynqiuxtdsog", (*format_string)[1]))
+        *format_string += 2;
+
+      else
+        return FALSE;
+
+      if (!g_variant_format_string_scan (format_string))    /* value */
+        return FALSE;
+
+      if (*(*format_string)++ != '}')
+        return FALSE;
+
+      return TRUE;
+
+    case 'm':
+      return g_variant_format_string_scan (format_string);
+
+    case 'a':
+    case '@':
+      return g_variant_type_string_scan (format_string, NULL);
+
+    case '&':
+      {
+        const gchar *start;
+
+        start = *format_string;
+        if (!g_variant_type_string_scan (format_string, NULL))
+          return FALSE;
+
+        if (*start == 'a')
+          start++;
+
+        while (start != *format_string)
+          if (!strchr ("bynqiuxtd(){}", *start++))
+            return FALSE;
+
+        return TRUE;
+      }
+
+    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':
+      return TRUE;
+
+    default:
+      return FALSE;
+  }
+}
+
+#if 0
+static gboolean
+g_variant_format_string_is_valid (const gchar *format_string)
+{
+  return g_variant_format_string_scan (&format_string) &&
+         *format_string == '\0';
+}
+#endif
+
+static GVariantType *
+g_variant_format_string_get_type (const gchar **format_string)
+{
+  const gchar *src;
+  gchar *dest;
+  gchar *new;
+
+  src = *format_string;
+  if G_UNLIKELY (!g_variant_format_string_scan (format_string))
+    g_error ("format string is invalid");
+
+  dest = new = g_malloc (*format_string - src + 1);
+  while (src != *format_string)
+    {
+      if (*src != '@' && *src != '&')
+        *dest++ = *src;
+      src++;
+    }
+  *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);
+      return va_arg (*app, GVariant *);
+
+    case '&':
+      {
+        GVariantType *type;
+        gconstpointer ptr;
+        GVariant *value;
+        gsize n_items;
+
+        type = g_variant_format_string_get_type (format_string);
+        g_assert (g_variant_type_is_concrete (type));
+
+        ptr = va_arg (*app, gconstpointer);
+        if (g_variant_type_is_in_class (type, G_VARIANT_TYPE_CLASS_ARRAY))
+          n_items = va_arg (*app, gsize);
+        else
+          n_items = 0;
+
+        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);
+      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_get_type (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_get_type (&string);
+                  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_STRUCT,
+                                           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':
+      {
+        const gchar **ptr = va_arg (*app, const gchar **);
+
+        if (ptr)
+          {
+            if (value)
+              *ptr = g_variant_get_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);
+        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);
+        return;
+      }
+
+    case '&':
+      {
+        gconstpointer *ptr;
+        gsize *n_items;
+
+        ptr = va_arg (*app, gconstpointer *);
+
+        if ((*format_string)[1] == 'a') /* '&a..' */
+          {
+            n_items = va_arg (*app, gsize *);
+
+            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);
+        return;
+      }
+
+    case 'm':
+      {
+        GVariant *just;
+
+        if (value && g_variant_n_children (value))
+          just = g_variant_get_child (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);
+          }
+        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 (&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 (&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.  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.
+ *
+ * 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 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 first character of the format string must not be '*' '?' '@' or
+ * 'r'.
+ *
+ * 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, &ap);
+  g_assert (*format_string == '\0');
+  va_end (ap);
+
+  return value;
+}
+
+/**
+ * g_variant_new_va:
+ * @must_be_null: %NULL (for future expansion)
+ * @format_string: a pointer to a format string
+ * @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
+ * need not be nul-terminated.  Instead, @format_string is updated to
+ * point to the first character past the end of the given 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,
+                  va_list      *app)
+{
+  GVariant *value;
+
+  g_assert (must_be_null == NULL);
+
+  value = g_variant_valist_new (format_string, app);
+  g_variant_flatten (value);
+
+  return value;
+}
+
+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, &ap);
+  va_end (ap);
+}
+
+/**
+ * g_variant_get_va:
+ * @value: a #GVariant
+ * @must_be_null: %NULL (for future expansion)
+ * @format_string: a pointer to a format string
+ * @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_new()-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
+ * need not be nul-terminated.  Instead, @format_string is updated to
+ * point to the first character past the end of the given 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,
+                  va_list      *app)
+{
+  GVariantType *type;
+  const gchar *fmt;
+
+  g_assert (must_be_null == NULL);
+  g_assert (value != NULL);
+
+  fmt = *format_string;
+  type = g_variant_format_string_get_type (&fmt);
+  g_assert (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_iterate:
+ * @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_iterate (iter, "{ss}", &key, &value))
+ *     printf ("dict['%s'] = '%s'\n", key, value);
+ * }
+ * </programlisting>
+ **/
+gboolean
+g_variant_iterate (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 (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, &ap);
+  g_assert (*format_string == '\0');
+  va_end (ap);
+
+  g_variant_builder_add_value (builder, variant);
+}
diff --git a/glib/gvariant.h b/glib/gvariant.h
new file mode 100644
index 0000000..338e4ef
--- /dev/null
+++ b/glib/gvariant.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2007, 2008 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.
+ */
+
+#ifndef _gvariant_h_
+#define _gvariant_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 private[8];
+};
+
+#pragma GCC visibility push (default)
+
+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         **format_string);
+GVariant                       *g_variant_new_va                        (gpointer              must_be_null,
+                                                                         const gchar         **format_string,
+                                                                         va_list              *app);
+void                            g_variant_get_va                        (GVariant             *value,
+                                                                         gpointer              must_be_null,
+                                                                         const gchar         **format_string,
+                                                                         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);
+
+/* 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);
+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);
+GVariant                       *g_variant_get_child                     (GVariant             *value,
+                                                                         gsize                 index);
+gsize                           g_variant_n_children                    (GVariant             *value);
+
+/* GVariantIter */
+gsize                           g_variant_iter_init                     (GVariantIter         *iter,
+                                                                         GVariant             *value);
+GVariant                       *g_variant_iter_next                     (GVariantIter         *iter);
+void                            g_variant_iter_cancel                   (GVariantIter         *iter);
+gboolean                        g_variant_iter_was_cancelled            (GVariantIter         *iter);
+gboolean                        g_variant_iterate                       (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     class,
+                                                                         const GVariantType   *type);
+GVariantBuilder                *g_variant_builder_close                 (GVariantBuilder      *child);
+gboolean                        g_variant_builder_check_add             (GVariantBuilder      *builder,
+                                                                         GVariantTypeClass     class,
+                                                                         const GVariantType   *type,
+                                                                         GError              **error);
+gboolean                        g_variant_builder_check_end             (GVariantBuilder      *builder,
+                                                                         GError              **error);
+GVariantBuilder                *g_variant_builder_new                   (GVariantTypeClass     class,
+                                                                         const GVariantType   *type);
+GVariant                       *g_variant_builder_end                   (GVariantBuilder      *builder);
+void                            g_variant_builder_cancel                (GVariantBuilder      *builder);
+
+/* markup printing/parsing */
+GString                        *g_variant_markup_print                  (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);
+
+#pragma GCC visibility pop
+
+#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;
+
+#endif /* _gvariant_h_ */
diff --git a/glib/gvarianttype.c b/glib/gvarianttype.c
new file mode 100644
index 0000000..15d056e
--- /dev/null
+++ b/glib/gvarianttype.c
@@ -0,0 +1,1022 @@
+/*
+ * Copyright © 2007, 2008 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 "gvarianttype.h"
+
+#include <glib/gtestutils.h>
+
+#include <string.h>
+
+/**
+ * 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 structure types,
+ * where each type string contained between the brackets corresponds
+ * to an item type of that structure 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 exactly 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);
+}
+#endif
+
+/**
+ * g_variant_type_string_scan:
+ * @type_string: a pointer to any string
+ * @limit: the end of @string, or %NULL
+ * @returns: %TRUE if a valid type string was found
+ *
+ * Scan for a single complete and valid #GVariantType type string in
+ * @type_string.  The memory pointed to by @limit (or bytes beyond it)
+ * is never accessed.
+ *
+ * If a valid type string is found, @type_string 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 @type_string, or if
+ * the type string does not end before @limit then %FALSE is returned
+ * and the state of the @type_string pointer is undefined.
+ *
+ * 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 **type_string,
+                            const gchar  *limit)
+{
+  if (*type_string == limit)
+    return FALSE;
+
+  switch (*(*type_string)++)
+  {
+    case '\0':
+      return FALSE;
+
+    case '(':
+      while (*type_string != limit && **type_string != ')')
+        if (!g_variant_type_string_scan (type_string, limit))
+          return FALSE;
+
+      if (*type_string == limit)
+        return FALSE;
+
+      (*type_string)++; /* ')' */
+
+      return TRUE;
+
+    case '{':
+      if (*type_string == limit || **type_string == '\0')
+        return FALSE;
+
+      if (!strchr ("bynqiuxtdsog?", *(*type_string)++))        /* key */
+        return FALSE;
+
+      if (!g_variant_type_string_scan (type_string, limit))    /* value */
+        return FALSE;
+
+      if (*type_string == limit || *(*type_string)++ != '}')
+        return FALSE;
+
+      return TRUE;
+
+    case 'm': case 'a':
+      return g_variant_type_string_scan (type_string, limit);  /* tailcall */
+
+    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 '?':
+      return TRUE;
+
+    default:
+      return FALSE;
+  }
+}
+
+/**
+ * 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))
+    return FALSE;
+
+  if (*type_string != '\0')
+    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 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, structure, 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
+ * @class: 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   class)
+{
+  char first_char = *(const gchar *) type;
+
+  switch (class)
+  {
+    case G_VARIANT_TYPE_CLASS_STRUCT:
+      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 class == 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, although %G_VARIANT_TYPE_CLASS_ALL matches all types,
+ * it will never be returned by this function except for the type
+ * %G_VARIANT_TYPE_ANY.
+ **/
+GVariantTypeClass
+g_variant_type_get_class (const GVariantType *type)
+{
+  char first_char = *(const gchar *) type;
+
+  switch (first_char)
+  {
+    case '(':
+      return G_VARIANT_TYPE_CLASS_STRUCT;
+
+    case '{':
+      return G_VARIANT_TYPE_CLASS_DICT_ENTRY;
+
+    default:
+      return first_char;
+  }
+}
+
+/**
+ * g_variant_type_class_is_container:
+ * @class: 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,
+ * struct, dict_entry and variant.
+ **/
+gboolean
+g_variant_type_class_is_container (GVariantTypeClass class)
+{
+  switch (class)
+  {
+    case G_VARIANT_TYPE_CLASS_VARIANT:
+    case G_VARIANT_TYPE_CLASS_MAYBE:
+    case G_VARIANT_TYPE_CLASS_ARRAY:
+    case G_VARIANT_TYPE_CLASS_STRUCT:
+    case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+      return TRUE;
+
+    default:
+      return FALSE;
+  }
+}
+
+/**
+ * g_variant_type_class_is_basic:
+ * @class: 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 class)
+{
+  switch (class)
+  {
+    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 struct or dict entry
+ * @returns: the first item type of @type, or %NULL
+ *
+ * Determines the first item type of a structure or dictionary entry
+ * type.
+ *
+ * This function must be called with a type in one of the classes
+ * %G_VARIANT_TYPE_CLASS_STRUCT or %G_VARIANT_TYPE_CLASS_DICT_ENTRY
+ * but must not be called on the generic structure type
+ * %G_VARIANT_TYPE_ANY_STRUCT.
+ *
+ * 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 structure 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 structure 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 structure 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 structure 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 struct or dict entry
+ * @returns: the number of items in @type
+ *
+ * Determines the number of items contained in a structure or
+ * dictionary entry type.
+ *
+ * This function must be called with a type in one of the classes
+ * %G_VARIANT_TYPE_CLASS_STRUCT or %G_VARIANT_TYPE_CLASS_DICT_ENTRY
+ * but must not be called on the generic structure type
+ * %G_VARIANT_TYPE_ANY_STRUCT.
+ *
+ * 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_struct().  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_struct:
+ * @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 structure type.
+ *
+ * The item types for the structure 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_struct_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_struct (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_struct_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;
+}
diff --git a/glib/gvarianttype.h b/glib/gvarianttype.h
new file mode 100644
index 0000000..f95582b
--- /dev/null
+++ b/glib/gvarianttype.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright © 2007, 2008 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.
+ */
+
+#ifndef _gvarianttype_h_
+#define _gvarianttype_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_STRUCT: the class containing all structure 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_STRUCT             = '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 #GVariantType signature string is a valid DBus type
+ * signature.  In addition, a concatenation of any number of valid
+ * #GVariantType  signature strings is also a valid DBus type
+ * signature.
+ **/
+#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 structure type.  Has only one valid instance.
+ **/
+#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_STRUCT:
+ *
+ * A wildcard type matching any structure type.
+ **/
+#define G_VARIANT_TYPE_ANY_STRUCT           ((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")
+
+#pragma GCC visibility push (default)
+
+/* type string checking */
+gboolean                        g_variant_type_string_is_valid          (const gchar         *type_string);
+gboolean                        g_variant_type_string_scan              (const gchar        **type_string,
+                                                                         const gchar         *limit);
+
+/* 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    class);
+GVariantTypeClass               g_variant_type_get_class                (const GVariantType  *type);
+gboolean                        g_variant_type_class_is_container       (GVariantTypeClass    class);
+gboolean                        g_variant_type_class_is_basic           (GVariantTypeClass    class);
+
+/* 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_struct              (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);
+
+#pragma GCC visibility pop
+
+#define G_VARIANT_TYPE(type_string) \
+  (_g_variant_type_check_string (type_string))
+
+#define g_variant_type_new_struct(items, func, length) \
+  (_g_variant_type_new_struct ((const gpointer *) items, (GVariantTypeGetter) (1 ? func : \
+                               (const GVariantType *(*)(typeof (items[0]))) NULL), length))
+
+#endif /* _gvarianttype_h_ */
diff --git a/glib/gvarianttypeinfo.c b/glib/gvarianttypeinfo.c
new file mode 100644
index 0000000..95a2d11
--- /dev/null
+++ b/glib/gvarianttypeinfo.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright © 2008 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 "gvarianttypeinfo.h"
+#include <glib.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;
+} StructInfo;
+
+/* == 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);
+}
+
+/* == structure == */
+#define STRUCT_INFO_CLASS 's'
+static StructInfo *
+STRUCT_INFO (GVariantTypeInfo *info)
+{
+  g_assert (info->info_class == STRUCT_INFO_CLASS);
+  return (StructInfo *) info;
+}
+
+static void
+struct_info_free (GVariantTypeInfo *info)
+{
+  StructInfo *struct_info = STRUCT_INFO (info);
+  gint i;
+
+  for (i = 0; i < struct_info->n_members; i++)
+    g_variant_type_info_unref (struct_info->members[i].type);
+
+  g_slice_free1 (sizeof (GVariantMemberInfo) * struct_info->n_members,
+                 struct_info->members);
+  g_slice_free (StructInfo, struct_info);
+}
+
+static void
+struct_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
+struct_get_item (StructInfo         *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
+struct_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
+struct_align (gsize offset,
+              guint alignment)
+{
+  return offset + ((-offset) & alignment);
+}
+
+static void
+struct_generate_table (StructInfo *info)
+{
+  GVariantMemberInfo *items = info->members;
+  gsize i = -1, a = 0, b = 0, c = 0, d, e;
+
+  /* §4.1.2 */
+  while (struct_get_item (info, items, &d, &e))
+    {
+      if (d <= b)
+        c = struct_align (c, d);
+      else
+        a += struct_align (c, b), b = d, c = 0;
+
+      struct_table_append (&items, i, a, b, c);
+
+      if (e == 0)
+        i++, a = b = c = 0;
+      else
+        c += e;
+    }
+}
+
+static void
+struct_set_self_info (StructInfo *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 = struct_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 *
+struct_info_new (const GVariantType *type)
+{
+  StructInfo *info;
+
+  info = g_slice_new (StructInfo);
+  info->self.info_class = STRUCT_INFO_CLASS;
+
+  struct_allocate_members (type, &info->members, &info->n_members); 
+  struct_generate_table (info);
+  struct_set_self_info (info);
+
+  return (GVariantTypeInfo *) info;
+}
+
+gsize
+g_variant_type_info_n_members (GVariantTypeInfo *info)
+{
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  return STRUCT_INFO (info)->n_members;
+}
+
+const GVariantMemberInfo *
+g_variant_type_info_member_info (GVariantTypeInfo *info,
+                                 gsize             index)
+{
+  StructInfo *struct_info = STRUCT_INFO (info);
+
+  g_assert_cmpint (info->ref_count, >, 0);
+
+  if (index < struct_info->n_members)
+    return &struct_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_STRUCT:
+        case G_VARIANT_TYPE_CLASS_DICT_ENTRY:
+          info = struct_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 STRUCT_INFO_CLASS:
+          struct_info_free (info);
+          break;
+
+        case BASE_INFO_CLASS:
+          base_info_free (info);
+          break;
+
+        default:
+          g_error ("GVariantTypeInfo with invalid class '%c'",
+                   info->info_class);
+      }
+    }
+}
diff --git a/glib/gvarianttypeinfo.h b/glib/gvarianttypeinfo.h
new file mode 100644
index 0000000..a34695c
--- /dev/null
+++ b/glib/gvarianttypeinfo.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2008 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.
+ */
+
+#ifndef _gvarianttypeinfo_h_
+#define _gvarianttypeinfo_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 /* _gvarianttypeinfo_h_ */



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