[glib/gvariant: 3/7] Import GVariant
- From: Ryan Lortie <ryanl src gnome org>
- To: svn-commits-list gnome org
- Subject: [glib/gvariant: 3/7] Import GVariant
- Date: Wed, 8 Apr 2009 10:18:30 -0400 (EDT)
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>
+ * <my-value>
+ * <int32>42</int32>
+ * </my-value>
+ * </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]