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