[glib/gvariant-varargs: 1/5] GVariant varargs support
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/gvariant-varargs: 1/5] GVariant varargs support
- Date: Mon, 8 Mar 2010 00:10:56 +0000 (UTC)
commit f6b39aa624d2ffc395fa6f866a3c85ff86ed7cf8
Author: Ryan Lortie <desrt desrt ca>
Date: Tue Mar 2 03:17:54 2010 -0500
GVariant varargs support
glib/glib.symbols | 14 +-
glib/gvariant.c | 1388 +++++++++++++++++++++++++++++++++++++++++++------
glib/gvariant.h | 28 +
glib/tests/gvariant.c | 662 +++++++++++++++++++++++-
4 files changed, 1926 insertions(+), 166 deletions(-)
---
diff --git a/glib/glib.symbols b/glib/glib.symbols
index bc5d514..e126b44 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1767,8 +1767,6 @@ g_variant_iter_free
g_variant_iter_init
g_variant_iter_n_children
g_variant_iter_new
-g_variant_iter_next
-g_variant_iter_loop
g_variant_iter_next_value
g_variant_builder_add_value
@@ -1780,6 +1778,18 @@ g_variant_builder_end
g_variant_builder_new
g_variant_builder_unref
g_variant_builder_ref
+
+g_variant_format_string_scan
+g_variant_format_string_scan_type
+g_variant_new_va
+g_variant_get_va
+g_variant_new
+g_variant_get
+
+g_variant_builder_add
+g_variant_get_child
+g_variant_iter_next
+g_variant_iter_loop
#endif
#endif
diff --git a/glib/gvariant.c b/glib/gvariant.c
index 4f2b560..78c8d2e 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -2133,167 +2133,6 @@ g_variant_iter_next_value (GVariantIter *iter)
return NULL;
}
-/**
- * g_variant_iter_loop:
- * @iter: a #GVariantIter
- * @format_string: a GVariant format string
- * @...: the arguments to unpack the value into
- * @returns: %TRUE if a value was unpacked, or %FALSE if there as no
- * value
- *
- * Gets the next item in the container and unpacks it into the variable
- * argument list according to @format_string, returning %TRUE.
- *
- * If no more items remain then %FALSE is returned.
- *
- * On the first call to this function, the pointers appearing on the
- * variable argument list are assumed to point at uninitialised memory.
- * On the second and later calls, it is assumed that the same pointers
- * will be given and that they will point to the memory as set by the
- * previous call to this function. This allows the previous values to
- * be freed, as appropriate.
- *
- * This function is intended to be used with a while loop as
- * demonstrated in the following example. This function can only be
- * used when iterating over an array. It is only valid to call this
- * function with a string constant for the format string and the same
- * string constant must be used each time. Mixing calls to this
- * function and g_variant_iter_next() or g_variant_iter_next_value() on
- * the same iterator is not recommended.
- *
- * <example>
- * <title>Memory management with g_variant_iter_loop()</title>
- * <programlisting>
- * /<!-- -->* Iterates a dictionary of type 'a{sv}' *<!-- -->/
- * void
- * iterate_dictionary (GVariant *dictionary)
- * {
- * GVariantIter iter;
- * GVariant *value;
- * gchar *key;
- *
- * g_variant_iter_init (&iter, dictionary);
- * while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
- * {
- * g_print ("Item '%s' has type '%s'\n", key,
- * g_variant_get_type_string (value));
- *
- * /<!-- -->* no need to free 'key' and 'value' here *<!-- -->/
- * }
- * }
- * </programlisting>
- * </example>
- *
- * If you want a slightly less magical alternative that requires more
- * typing, see g_variant_iter_next().
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_iter_loop (GVariantIter *iter,
- const gchar *format_string,
- ...)
-{
- gboolean first_time = GVSI(iter)->loop_format == NULL;
- GVariant *value;
-
- g_return_val_if_fail (first_time ||
- format_string == GVSI(iter)->loop_format,
- FALSE);
-
- if (first_time)
- {
- TYPE_CHECK (GVSI(iter)->value, G_VARIANT_TYPE_ARRAY, FALSE);
- GVSI(iter)->loop_format = format_string;
- }
-
- value = g_variant_iter_next_value (iter);
-
- if (value != NULL)
- {
- va_list ap;
-
- va_start (ap, format_string);
- /* varargs get stuff */
- va_end (ap);
-
- g_variant_unref (value);
- }
-
- return value != NULL;
-}
-
-/**
- * g_variant_iter_next:
- * @iter: a #GVariantIter
- * @format_string: a GVariant format string
- * @...: the arguments to unpack the value into
- * @returns: %TRUE if a value was unpacked, or %FALSE if there as no
- * value
- *
- * Gets the next item in the container and unpacks it into the variable
- * argument list according to @format_string, returning %TRUE.
- *
- * If no more items remain then %FALSE is returned.
- *
- * All of the pointers given on the variable arguments list of this
- * function are assumed to point at uninitialised memory. It is the
- * responsibility of the caller to free all of the values returned by
- * the unpacking process.
- *
- * <example>
- * <title>Memory management with g_variant_iter_next()</title>
- * <programlisting>
- * /<!-- -->* Iterates a dictionary of type 'a{sv}' *<!-- -->/
- * void
- * iterate_dictionary (GVariant *dictionary)
- * {
- * GVariantIter iter;
- * GVariant *value;
- * gchar *key;
- *
- * g_variant_iter_init (&iter, dictionary);
- * while (g_variant_iter_next (&iter, "{sv}", &key, &value))
- * {
- * g_print ("Item '%s' has type '%s'\n", key,
- * g_variant_get_type_string (value));
- *
- * /<!-- -->* must free data for ourselves *<!-- -->/
- * g_variant_unref (value);
- * g_free (key);
- * }
- * }
- * </programlisting>
- * </example>
- *
- * For a solution that is likely to be more convenient to C programmers,
- * see g_variant_iter_loop().
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_iter_next (GVariantIter *iter,
- const gchar *format_string,
- ...)
-{
- GVariant *value;
-
- value = g_variant_iter_next_value (iter);
-
- if (value != NULL)
- {
- va_list ap;
-
- va_start (ap, format_string);
- /* varargs get stuff */
- va_end (ap);
-
- g_variant_unref (value);
- }
-
- return value != NULL;
-}
-
/* GVariantBuilder {{{1 */
/**
* GVariantBuilder:
@@ -2837,6 +2676,1233 @@ g_variant_builder_end (GVariantBuilder *builder)
return value;
}
+/* Format strings {{{1 */
+/**
+ * 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 then %TRUE is
+ * returned. If @endptr is non-%NULL then it is updated to point to the
+ * first character after the format string.
+ *
+ * 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.
+ *
+ * See XXX for format string infoz.
+ **/
+gboolean
+g_variant_format_string_scan (const gchar *string,
+ const gchar *limit,
+ const gchar **endptr)
+{
+#define next_char() (string == limit ? '\0' : *string++)
+#define peek_char() (string == limit ? '\0' : *string)
+ /* ISO/IEC 9899:1999 (C99) §7.21.5.2:
+ * The terminating null character is considered to be
+ * part of the string.
+ */
+#define is_elem(c,str) ((c) != '\0' && strchr (str, c) != NULL)
+ char c;
+
+ switch (next_char())
+ {
+ case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
+ case 'x': case 't': case 'h': case 'd': case 's': case 'o':
+ case 'g': case 'v': case '*': case '?': case 'r':
+ break;
+
+ case 'm':
+ return g_variant_format_string_scan (string, limit, endptr);
+
+ case 'a':
+ case '@':
+ return g_variant_type_string_scan (string, limit, endptr);
+
+ case '(':
+ while (peek_char() != ')')
+ if (!g_variant_format_string_scan (string, limit, &string))
+ return FALSE;
+
+ next_char(); /* consume ')' */
+ break;
+
+ case '{':
+ c = next_char();
+
+ if (c == '@')
+ c = next_char ();
+
+ if (!is_elem (c, "bynqiuxthdsog?"))
+ return FALSE;
+
+ if (!g_variant_format_string_scan (string, limit, &string))
+ return FALSE;
+
+ if (next_char() != '}')
+ return FALSE;
+
+ break;
+
+ case '^': /* '^as' or '^a&s' only */
+ if (next_char() != 'a')
+ return FALSE;
+
+ if (peek_char() == '&')
+ next_char ();
+
+ c = next_char ();
+
+ if (c != 's' && c != 'o' && c != 'g')
+ return FALSE;
+
+ break;
+
+ case '&':
+ c = next_char();
+
+ if (c != 's' && c != 'o' && c != 'g')
+ return FALSE;
+
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ if (endptr != NULL)
+ *endptr = string;
+
+#undef next_char
+#undef peek_char
+#undef is_elem
+
+ 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.
+ *
+ * Use g_variant_type_free() to free the return value when you no longer
+ * need it.
+ *
+ * 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 gboolean
+g_variant_check_valid_format (const gchar *function,
+ const gchar *location,
+ const gchar *format_string,
+ gboolean single,
+ GVariant *value)
+{
+ const gchar *endptr;
+ GVariantType *type;
+
+ type = g_variant_format_string_scan_type (format_string, NULL, &endptr);
+
+ if G_UNLIKELY (type == NULL || (single && *endptr != '\0'))
+ {
+ if (g_str_has_prefix (function, "IA__"))
+ function += 4;
+
+ if (single)
+ g_critical ("%s: %s: `%s' is not a valid GVariant format string",
+ location, function, format_string);
+ else
+ g_critical ("%s: %s: `%s' does not have a valid GVariant format "
+ "string as a prefix", location, function, format_string);
+
+ if (type != NULL)
+ g_variant_type_free (type);
+
+ return FALSE;
+ }
+
+ if G_UNLIKELY (value && !g_variant_is_of_type (value, type))
+ {
+ gchar *fragment;
+ gchar *typestr;
+
+ fragment = g_strndup (format_string, endptr - format_string);
+ typestr = g_variant_type_dup_string (type);
+
+ g_critical ("%s: %s: the GVariant format string `%s' has a type of "
+ "`%s' but the given value has a type of `%s'",
+ location, function, fragment, typestr,
+ g_variant_get_type_string (value));
+
+ g_variant_type_free (type);
+
+ return FALSE;
+ }
+
+ g_variant_type_free (type);
+
+ return TRUE;
+}
+
+#define CHECK_VALID_FORMAT(str,single,value,retval) \
+ G_STMT_START { \
+ if G_UNLIKELY (!g_variant_check_valid_format (G_STRFUNC, G_STRLOC, \
+ str, single, value)) \
+ return retval; \
+ } G_STMT_END
+
+/* Variable Arguments {{{1 */
+#undef DEBUG_GVARIANT_VARARGS
+
+#ifdef DEBUG_GVARIANT_VARARGS
+#define get_arg(a,t) \
+ ({ g_print (G_STRLOC " (%s): get %s\n", G_STRFUNC, #t); va_arg (a, t); })
+#else
+#define get_arg va_arg
+#endif
+
+
+/* We consider 2 main classes of format strings:
+ *
+ * - recursive format strings
+ * these are ones that result in recursion and the collection of
+ * possibly more than one argument. Maybe types, tuples,
+ * dictionary entries.
+ *
+ * - leaf format string
+ * these result in the collection of a single argument.
+ *
+ * Leaf format strings are further subdivided into two categories:
+ *
+ * - single non-null pointer ("nnp")
+ * these either collect or return a single non-null pointer.
+ *
+ * - other
+ * these collect or return something else (bool, number, etc).
+ *
+ * Based on the above, the varargs handling code is split into 4 main parts:
+ *
+ * - nnp handling code
+ * - leaf handling code (which may invoke nnp code)
+ * - generic handling code (may be recursive, may invoke leaf code)
+ * - user-facing API (which invokes the generic code)
+ *
+ * Each section implements some of the following functions:
+ *
+ * - skip:
+ * collect the arguments for the format string as if
+ * g_variant_new() had been called, but do nothing with them. used
+ * for skipping over arguments when constructing a Nothing maybe
+ * type.
+ *
+ * - new:
+ * create a GVariant *
+ *
+ * - get:
+ * unpack a GVariant *
+ *
+ * - free (nnp only):
+ * free a previously allocated item
+ */
+
+static gboolean
+g_variant_format_string_is_leaf (const gchar *str)
+{
+ return str[0] != 'm' && str[0] != '(' && str[0] != '{';
+}
+
+static gboolean
+g_variant_format_string_is_nnp (const gchar *str)
+{
+ return str[0] == 'a' || str[0] == 's' || str[0] == 'o' || str[0] == 'g' ||
+ str[0] == '^' || str[0] == '@' || str[0] == '*' || str[0] == '?' ||
+ str[0] == 'r' || str[0] == 'v' || str[0] == '&';
+}
+
+/* Single non-null pointer ("nnp") {{{2 */
+static void
+g_variant_valist_free_nnp (const gchar *str,
+ gpointer ptr)
+{
+ switch (*str)
+ {
+ case 'a':
+ g_variant_iter_free (ptr);
+ break;
+
+ case '^':
+ if (str[2] != '&') /* '^as' */
+ g_strfreev (ptr);
+ else /* '^a&s' */
+ g_free (ptr);
+ break;
+
+ case 's': case 'o': case 'g':
+ g_free (ptr);
+ break;
+
+ case '@': case '*': case '?': case 'v':
+ g_variant_unref (ptr);
+ break;
+
+ case '&':
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static GVariant *
+g_variant_valist_new_nnp (const gchar **str,
+ gpointer ptr)
+{
+ if (**str == '&')
+ (*str)++;
+
+ switch (*(*str)++)
+ {
+ case 'a':
+ {
+ const GVariantType *type;
+ GVariant *value;
+
+ value = g_variant_builder_end (ptr);
+ type = g_variant_get_type (value);
+
+ if G_UNLIKELY (!g_variant_type_is_array (type))
+ g_error ("g_variant_new: expected array GVariantBuilder but "
+ "the built value has type `%s'",
+ g_variant_get_type_string (value));
+
+ type = g_variant_type_element (type);
+
+ if G_UNLIKELY (!g_variant_type_is_subtype_of (type, (GVariantType *) *str))
+ g_error ("g_variant_new: expected GVariantBuilder array element "
+ "type `%s' but the built value has element type `%s'",
+ g_variant_type_dup_string ((GVariantType *) *str),
+ g_variant_get_type_string (value) + 1);
+
+ g_variant_type_string_scan (*str, NULL, str);
+
+ return value;
+ }
+
+ case 's':
+ return g_variant_new_string (ptr);
+
+ case 'o':
+ return g_variant_new_object_path (ptr);
+
+ case 'g':
+ return g_variant_new_signature (ptr);
+
+ case '^':
+ {
+ const GVariantType *type;
+ GVariantType *array_type;
+ GVariant **children;
+ GVariant *value;
+ guint length, i;
+ gchar **strv;
+
+ if ((*str)[1] == '&') /* '^a&s' */
+ (*str) += 2;
+ else /* '^as' */
+ (*str)++;
+
+ type = (GVariantType *) (*str)++;
+ array_type = g_variant_type_new_array (type);
+ length = g_strv_length (strv = ptr);
+ children = g_new (GVariant *, length);
+ for (i = 0; i < length; i++)
+ children[i] = g_variant_ref_sink (
+ g_variant_new_from_trusted (type, strv[i], strlen (strv[i]) + 1));
+
+ value = g_variant_new_from_children (array_type, children,
+ length, TRUE);
+ g_variant_type_free (array_type);
+
+ return value;
+ }
+
+ case '@':
+ if G_UNLIKELY (!g_variant_is_of_type (ptr, (GVariantType *) *str))
+ g_error ("g_variant_new: expected GVariant of type `%s' but "
+ "received value has type `%s'",
+ g_variant_type_dup_string ((GVariantType *) *str),
+ g_variant_get_type_string (ptr));
+
+ g_variant_type_string_scan (*str, NULL, str);
+
+ return ptr;
+
+ case '*':
+ return ptr;
+
+ case '?':
+ if G_UNLIKELY (!g_variant_type_is_basic (g_variant_get_type (ptr)))
+ g_error ("g_variant_new: format string `?' expects basic-typed "
+ "GVariant, but received value has type `%s'",
+ g_variant_get_type_string (ptr));
+
+ return ptr;
+
+ case 'r':
+ if G_UNLIKELY (!g_variant_type_is_tuple (g_variant_get_type (ptr)))
+ g_error ("g_variant_new: format string `r` expects tuple-typed "
+ "GVariant, but received value has type `%s'",
+ g_variant_get_type_string (ptr));
+
+ return ptr;
+
+ case 'v':
+ return g_variant_new_variant (ptr);
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gpointer
+g_variant_valist_get_nnp (const gchar **str,
+ GVariant *value)
+{
+ switch (*(*str)++)
+ {
+ case 'a':
+ g_variant_type_string_scan (*str, NULL, str);
+ return g_variant_iter_new (value);
+
+ case '&':
+ (*str)++;
+ return (gchar *) g_variant_get_string (value, NULL);
+
+ case 's': case 'o': case 'g':
+ return g_variant_dup_string (value, NULL);
+
+ case '^':
+ if ((*str)[1] == '&') /* '^a&s' */
+ {
+ (*str) += 3;
+ return g_variant_get_strv (value, NULL);
+ }
+ else /* '^as' */
+ {
+ (*str) += 2;
+ return g_variant_dup_strv (value, NULL);
+ }
+
+ case '@':
+ g_variant_type_string_scan (*str, NULL, str);
+ /* fall through */
+
+ case '*': case '?': case 'r':
+ return g_variant_ref (value);
+
+ case 'v':
+ return g_variant_get_variant (value);
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/* Leaves {{{2 */
+static void
+g_variant_valist_skip_leaf (const gchar **str,
+ va_list *app)
+{
+ if (g_variant_format_string_is_nnp (*str))
+ {
+ g_variant_format_string_scan (*str, NULL, str);
+ get_arg (*app, gpointer);
+ return;
+ }
+
+ switch (*(*str)++)
+ {
+ case 'b': case 'y': case 'n': case 'q':
+ case 'i': case 'u': case 'h':
+ get_arg (*app, int);
+ return;
+
+ case 'x': case 't':
+ get_arg (*app, guint64);
+ return;
+
+ case 'd':
+ get_arg (*app, gdouble);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static GVariant *
+g_variant_valist_new_leaf (const gchar **str,
+ va_list *app)
+{
+ if (g_variant_format_string_is_nnp (*str))
+ return g_variant_valist_new_nnp (str, get_arg (*app, gpointer));
+
+ switch (*(*str)++)
+ {
+ case 'b':
+ return g_variant_new_boolean (get_arg (*app, gboolean));
+
+ case 'y':
+ return g_variant_new_byte (get_arg (*app, guint));
+
+ case 'n':
+ return g_variant_new_int16 (get_arg (*app, gint));
+
+ case 'q':
+ return g_variant_new_uint16 (get_arg (*app, guint));
+
+ case 'i':
+ return g_variant_new_int32 (get_arg (*app, gint));
+
+ case 'u':
+ return g_variant_new_uint32 (get_arg (*app, guint));
+
+ case 'x':
+ return g_variant_new_int64 (get_arg (*app, gint64));
+
+ case 't':
+ return g_variant_new_uint64 (get_arg (*app, guint64));
+
+ case 'h':
+ return g_variant_new_handle (get_arg (*app, gint));
+
+ case 'd':
+ return g_variant_new_double (get_arg (*app, gdouble));
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+g_variant_valist_get_leaf (const gchar **str,
+ GVariant *value,
+ gboolean free,
+ va_list *app)
+{
+ gpointer ptr = get_arg (*app, gpointer);
+
+ if (ptr == NULL)
+ {
+ g_variant_format_string_scan (*str, NULL, str);
+ return;
+ }
+
+ if (g_variant_format_string_is_nnp (*str))
+ {
+ gpointer *nnp = (gpointer *) ptr;
+
+ if (free && *nnp != NULL)
+ g_variant_valist_free_nnp (*str, *nnp);
+
+ *nnp = NULL;
+
+ if (value != NULL)
+ *nnp = g_variant_valist_get_nnp (str, value);
+ else
+ g_variant_format_string_scan (*str, NULL, str);
+
+ return;
+ }
+
+ if (value != NULL)
+ {
+ switch (*(*str)++)
+ {
+ case 'b':
+ *(gboolean *) ptr = g_variant_get_boolean (value);
+ return;
+
+ case 'y':
+ *(guchar *) ptr = g_variant_get_byte (value);
+ return;
+
+ case 'n':
+ *(gint16 *) ptr = g_variant_get_int16 (value);
+ return;
+
+ case 'q':
+ *(guint16 *) ptr = g_variant_get_uint16 (value);
+ return;
+
+ case 'i':
+ *(gint32 *) ptr = g_variant_get_int32 (value);
+ return;
+
+ case 'u':
+ *(guint32 *) ptr = g_variant_get_uint32 (value);
+ return;
+
+ case 'x':
+ *(gint64 *) ptr = g_variant_get_int64 (value);
+ return;
+
+ case 't':
+ *(guint64 *) ptr = g_variant_get_uint64 (value);
+ return;
+
+ case 'h':
+ *(gint32 *) ptr = g_variant_get_handle (value);
+ return;
+
+ case 'd':
+ *(gdouble *) ptr = g_variant_get_double (value);
+ return;
+ }
+ }
+ else
+ {
+ switch (*(*str)++)
+ {
+ case 'y':
+ *(guchar *) ptr = 0;
+ return;
+
+ case 'n': case 'q':
+ *(guint16 *) ptr = 0;
+ return;
+
+ case 'i': case 'u': case 'h': case 'b':
+ g_assert (sizeof (gboolean) == sizeof (guint32));
+ *(guint32 *) ptr = 0;
+ return;
+
+ case 'x': case 't': case 'd':
+ g_assert (sizeof (gdouble) == sizeof (guint64));
+ *(guint64 *) ptr = 0;
+ return;
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
+/* Generic (recursive) {{{2 */
+static void
+g_variant_valist_skip (const gchar **str,
+ va_list *app)
+{
+ if (g_variant_format_string_is_leaf (*str))
+ g_variant_valist_skip_leaf (str, app);
+
+ else if (**str == 'm') /* maybe */
+ {
+ (*str)++;
+
+ if (!g_variant_format_string_is_nnp (*str))
+ get_arg (*app, gboolean);
+
+ g_variant_valist_skip (str, app);
+ }
+ else /* tuple, dictionary entry */
+ {
+ g_assert (**str == '(' || **str == '{');
+ (*str)++;
+ while (**str != ')' && **str != '}')
+ g_variant_valist_skip (str, app);
+ (*str)++;
+ }
+}
+
+static GVariant *
+g_variant_valist_new (const gchar **str,
+ va_list *app)
+{
+ if (g_variant_format_string_is_leaf (*str))
+ return g_variant_valist_new_leaf (str, app);
+
+ if (**str == 'm') /* maybe */
+ {
+ GVariantType *type = NULL;
+ GVariant *value = NULL;
+
+ (*str)++;
+
+ if (g_variant_format_string_is_nnp (*str))
+ {
+ gpointer nnp = get_arg (*app, gpointer);
+
+ if (nnp != NULL)
+ value = g_variant_valist_new_nnp (str, nnp);
+ else
+ type = g_variant_format_string_scan_type (*str, NULL, str);
+ }
+ else
+ {
+ gboolean just = get_arg (*app, gboolean);
+
+ if (just)
+ value = g_variant_valist_new (str, app);
+ else
+ {
+ type = g_variant_format_string_scan_type (*str, NULL, NULL);
+ g_variant_valist_skip (str, app);
+ }
+ }
+
+ value = g_variant_new_maybe (type, value);
+
+ if (type != NULL)
+ g_variant_type_free (type);
+
+ return value;
+ }
+ else /* tuple, dictionary entry */
+ {
+ GVariantBuilder b;
+
+ if (**str == '(')
+ g_variant_builder_init (&b, G_VARIANT_TYPE_TUPLE);
+ else
+ {
+ g_assert (**str == '{');
+ g_variant_builder_init (&b, G_VARIANT_TYPE_DICT_ENTRY);
+ }
+
+ (*str)++; /* '(' */
+ while (**str != ')' && **str != '}')
+ g_variant_builder_add_value (&b, g_variant_valist_new (str, app));
+ (*str)++; /* ')' */
+
+ return g_variant_builder_end (&b);
+ }
+}
+
+static void
+g_variant_valist_get (const gchar **str,
+ GVariant *value,
+ gboolean free,
+ va_list *app)
+{
+ if (g_variant_format_string_is_leaf (*str))
+ g_variant_valist_get_leaf (str, value, free, app);
+
+ else if (**str == 'm')
+ {
+ (*str)++;
+
+ if (value != NULL)
+ value = g_variant_get_maybe (value);
+
+ if (!g_variant_format_string_is_nnp (*str))
+ {
+ gboolean *ptr = get_arg (*app, gboolean *);
+
+ if (ptr != NULL)
+ *ptr = value != NULL;
+ }
+
+ g_variant_valist_get (str, value, free, app);
+
+ if (value != NULL)
+ g_variant_unref (value);
+ }
+
+ else /* tuple, dictionary entry */
+ {
+ GVariantIter iter;
+
+ g_assert (**str == '(' || **str == '{');
+ g_variant_iter_init (&iter, value);
+
+ (*str)++;
+ while (**str != ')' && **str != '}')
+ {
+ value = g_variant_iter_next_value (&iter);
+ g_variant_valist_get (str, value, free, app);
+ g_variant_unref (value);
+ }
+ (*str)++;
+ }
+}
+
+/* User-facing API {{{2 */
+/**
+ * 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. Format
+ * strings are described in more detail XXX.
+ *
+ * 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;
+
+ CHECK_VALID_FORMAT(format_string, TRUE, NULL, NULL);
+
+ g_return_val_if_fail (format_string[0] != '?' && format_string[0] != '@' &&
+ format_string[0] != '*' && format_string[0] != 'r',
+ 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;
+
+ CHECK_VALID_FORMAT(format_string, endptr == NULL, 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;
+
+ 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.
+ *
+ * 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;
+
+ CHECK_VALID_FORMAT(format_string, TRUE, value,);
+
+ /* if any direct-pointer-access formats are in use, flatten first */
+ if (strchr (format_string, '&'))
+ g_variant_get_data (value);
+
+ 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 (must_be_null == NULL);
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (app != NULL);
+
+ CHECK_VALID_FORMAT(format_string, endptr == NULL, value,);
+
+ /* if any direct-pointer-access formats are in use, flatten first */
+ if (strchr (format_string, '&'))
+ g_variant_get_data (value);
+
+ g_variant_valist_get (&format_string, value, FALSE, app);
+
+ if (endptr != NULL)
+ *endptr = format_string;
+}
+
+/* Varargs-enabled Utility Functions {{{1 */
+
+/**
+ * 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++)
+ * {
+ * gchar 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,
+ gsize index_,
+ const gchar *format_string,
+ ...)
+{
+ GVariant *child;
+ va_list ap;
+
+ child = g_variant_get_child_value (value, index_);
+
+ CHECK_VALID_FORMAT(format_string, TRUE, child,);
+
+ va_start (ap, format_string);
+ g_variant_get_va (child, NULL, format_string, NULL, &ap);
+ va_end (ap);
+
+ g_variant_unref (child);
+}
+
+/**
+ * g_variant_iter_next:
+ * @iter: a #GVariantIter
+ * @format_string: a GVariant format string
+ * @...: the arguments to unpack the value into
+ * @returns: %TRUE if a value was unpacked, or %FALSE if there as no
+ * value
+ *
+ * Gets the next item in the container and unpacks it into the variable
+ * argument list according to @format_string, returning %TRUE.
+ *
+ * If no more items remain then %FALSE is returned.
+ *
+ * All of the pointers given on the variable arguments list of this
+ * function are assumed to point at uninitialised memory. It is the
+ * responsibility of the caller to free all of the values returned by
+ * the unpacking process.
+ *
+ * <example>
+ * <title>Memory management with g_variant_iter_next()</title>
+ * <programlisting>
+ * /<!-- -->* Iterates a dictionary of type 'a{sv}' *<!-- -->/
+ * void
+ * iterate_dictionary (GVariant *dictionary)
+ * {
+ * GVariantIter iter;
+ * GVariant *value;
+ * gchar *key;
+ *
+ * g_variant_iter_init (&iter, dictionary);
+ * while (g_variant_iter_next (&iter, "{sv}", &key, &value))
+ * {
+ * g_print ("Item '%s' has type '%s'\n", key,
+ * g_variant_get_type_string (value));
+ *
+ * /<!-- -->* must free data for ourselves *<!-- -->/
+ * g_variant_unref (value);
+ * g_free (key);
+ * }
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * For a solution that is likely to be more convenient to C programmers
+ * when dealing with loops, see g_variant_iter_loop().
+ *
+ * Since: 2.24
+ **/
+gboolean
+g_variant_iter_next (GVariantIter *iter,
+ const gchar *format_string,
+ ...)
+{
+ GVariant *value;
+
+ value = g_variant_iter_next_value (iter);
+
+ CHECK_VALID_FORMAT(format_string, TRUE, value, FALSE);
+
+ if (value != NULL)
+ {
+ va_list ap;
+
+ va_start (ap, format_string);
+ g_variant_valist_get (&format_string, value, FALSE, &ap);
+ va_end (ap);
+
+ g_variant_unref (value);
+ }
+
+ return value != NULL;
+}
+
+/**
+ * g_variant_iter_loop:
+ * @iter: a #GVariantIter
+ * @format_string: a GVariant format string
+ * @...: the arguments to unpack the value into
+ * @returns: %TRUE if a value was unpacked, or %FALSE if there as no
+ * value
+ *
+ * Gets the next item in the container and unpacks it into the variable
+ * argument list according to @format_string, returning %TRUE.
+ *
+ * If no more items remain then %FALSE is returned.
+ *
+ * On the first call to this function, the pointers appearing on the
+ * variable argument list are assumed to point at uninitialised memory.
+ * On the second and later calls, it is assumed that the same pointers
+ * will be given and that they will point to the memory as set by the
+ * previous call to this function. This allows the previous values to
+ * be freed, as appropriate.
+ *
+ * This function is intended to be used with a while loop as
+ * demonstrated in the following example. This function can only be
+ * used when iterating over an array. It is only valid to call this
+ * function with a string constant for the format string and the same
+ * string constant must be used each time. Mixing calls to this
+ * function and g_variant_iter_next() or g_variant_iter_next_value() on
+ * the same iterator is not recommended.
+ *
+ * <example>
+ * <title>Memory management with g_variant_iter_loop()</title>
+ * <programlisting>
+ * /<!-- -->* Iterates a dictionary of type 'a{sv}' *<!-- -->/
+ * void
+ * iterate_dictionary (GVariant *dictionary)
+ * {
+ * GVariantIter iter;
+ * GVariant *value;
+ * gchar *key;
+ *
+ * g_variant_iter_init (&iter, dictionary);
+ * while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
+ * {
+ * g_print ("Item '%s' has type '%s'\n", key,
+ * g_variant_get_type_string (value));
+ *
+ * /<!-- -->* no need to free 'key' and 'value' here *<!-- -->/
+ * }
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * If you want a slightly less magical alternative that requires more
+ * typing, see g_variant_iter_next().
+ *
+ * Since: 2.24
+ **/
+gboolean
+g_variant_iter_loop (GVariantIter *iter,
+ const gchar *format_string,
+ ...)
+{
+ gboolean first_time = GVSI(iter)->loop_format == NULL;
+ GVariant *value;
+ va_list ap;
+
+ g_return_val_if_fail (first_time ||
+ format_string == GVSI(iter)->loop_format,
+ FALSE);
+
+ if (first_time)
+ {
+ TYPE_CHECK (GVSI(iter)->value, G_VARIANT_TYPE_ARRAY, FALSE);
+ GVSI(iter)->loop_format = format_string;
+
+ if (strchr (format_string, '&'))
+ g_variant_get_data (GVSI(iter)->value);
+ }
+
+ value = g_variant_iter_next_value (iter);
+
+ va_start (ap, format_string);
+ g_variant_valist_get (&format_string, value, !first_time, &ap);
+ va_end (ap);
+
+ if (value != NULL)
+ g_variant_unref (value);
+
+ return value != NULL;
+}
+
/* Epilogue {{{1 */
#define __G_VARIANT_C__
#include "galiasdef.c"
diff --git a/glib/gvariant.h b/glib/gvariant.h
index d8ea126..be8dd12 100644
--- a/glib/gvariant.h
+++ b/glib/gvariant.h
@@ -114,6 +114,10 @@ GVariant * g_variant_new_dict_entry (GVarian
GVariant * g_variant_get_maybe (GVariant *value);
gsize g_variant_n_children (GVariant *value);
+void g_variant_get_child (GVariant *value,
+ gsize index_,
+ const gchar *format_string,
+ ...);
GVariant * g_variant_get_child_value (GVariant *value,
gsize index_);
gconstpointer g_variant_get_fixed_array (GVariant *value,
@@ -178,6 +182,30 @@ void g_variant_builder_add (GVarian
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 (const gchar *format_string,
+ ...);
+void g_variant_get (GVariant *value,
+ const gchar *format_string,
+ ...);
+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);
+
+
G_END_DECLS
#endif /* __G_VARIANT_H__ */
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index ea01500..8588ebe 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -12,6 +12,7 @@
*/
#include <string.h>
+#include <stdlib.h>
#include <glib.h>
#define BASIC "bynqiuxthdsog?"
@@ -2530,7 +2531,8 @@ tree_instance_check_gvariant (TreeInstance *tree,
static void
tree_instance_build_gvariant (TreeInstance *tree,
- GVariantBuilder *builder)
+ GVariantBuilder *builder,
+ gboolean guess_ok)
{
const GVariantType *type;
@@ -2540,10 +2542,28 @@ tree_instance_build_gvariant (TreeInstance *tree,
{
gsize i;
+ /* force GVariantBuilder to guess the type half the time */
+ if (guess_ok && randomly (0.5))
+ {
+ if (g_variant_type_is_array (type) && tree->n_children)
+ type = G_VARIANT_TYPE_ARRAY;
+
+ if (g_variant_type_is_maybe (type) && tree->n_children)
+ type = G_VARIANT_TYPE_MAYBE;
+
+ if (g_variant_type_is_tuple (type))
+ type = G_VARIANT_TYPE_TUPLE;
+
+ if (g_variant_type_is_dict_entry (type))
+ type = G_VARIANT_TYPE_DICT_ENTRY;
+ }
+ else
+ guess_ok = FALSE;
+
g_variant_builder_open (builder, type);
for (i = 0; i < tree->n_children; i++)
- tree_instance_build_gvariant (tree->children[i], builder);
+ tree_instance_build_gvariant (tree->children[i], builder, guess_ok);
g_variant_builder_close (builder);
}
@@ -2626,7 +2646,7 @@ test_container (void)
gchar *s3;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARIANT);
- tree_instance_build_gvariant (tree, &builder);
+ tree_instance_build_gvariant (tree, &builder, TRUE);
built = g_variant_builder_end (&builder);
g_variant_ref_sink (built);
g_variant_get_data (built);
@@ -2661,6 +2681,638 @@ test_containers (void)
}
}
+static void
+test_format_strings (void)
+{
+ GVariantType *type;
+ const gchar *end;
+
+ g_assert (g_variant_format_string_scan ("i", NULL, &end) && *end == '\0');
+ g_assert (g_variant_format_string_scan ("@i", NULL, &end) && *end == '\0');
+ g_assert (g_variant_format_string_scan ("@ii", NULL, &end) && *end == 'i');
+ g_assert (g_variant_format_string_scan ("^a&s", NULL, &end) && *end == '\0');
+ g_assert (g_variant_format_string_scan ("(^as)", NULL, &end) &&
+ *end == '\0');
+ g_assert (!g_variant_format_string_scan ("(^s)", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("(^a)", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("(z)", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("az", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("{**}", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("{ **}", NULL, &end));
+ g_assert (g_variant_format_string_scan ("{ y*}", NULL, &end) &&
+ *end == '\0');
+ g_assert (g_variant_format_string_scan ("{yv}", NULL, &end) &&
+ *end == '\0');
+ g_assert (!g_variant_format_string_scan ("{vv}", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("{y}", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("{yyy}", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("{ya}", NULL, &end));
+ g_assert (g_variant_format_string_scan ("&s", NULL, &end) && *end == '\0');
+ g_assert (!g_variant_format_string_scan ("&as", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("@z", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("az", NULL, &end));
+ g_assert (!g_variant_format_string_scan ("a&s", NULL, &end));
+
+ type = g_variant_format_string_scan_type ("mm(@xy^a&s*? ?)", NULL, &end);
+ g_assert (type && *end == '\0');
+ g_assert (g_variant_type_equal (type, G_VARIANT_TYPE ("mm(xyas*?\?)")));
+ g_variant_type_free (type);
+
+ type = g_variant_format_string_scan_type ("mm(@xy^a&*? ?)", NULL, NULL);
+ g_assert (type == NULL);
+}
+
+static void
+exit_on_abort (int signal)
+{
+ exit (signal);
+}
+
+static gboolean
+do_failed_test (const gchar *pattern)
+{
+ if (g_test_trap_fork (1000000, G_TEST_TRAP_SILENCE_STDERR))
+ {
+ signal (SIGABRT, exit_on_abort);
+ return TRUE;
+ }
+
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr (pattern);
+
+ return FALSE;
+}
+
+static void
+test_invalid_varargs (void)
+{
+ if (do_failed_test ("*not a valid GVariant format string*"))
+ {
+ g_variant_new ("z");
+ abort ();
+ }
+
+ if (do_failed_test ("*valid GVariant format string as a prefix*"))
+ {
+ const gchar *end;
+
+ g_variant_new_va (NULL, "z", &end, NULL);
+ abort ();
+ }
+
+ if (do_failed_test ("*type of `q' but * has a type of `y'*"))
+ {
+ g_variant_get (g_variant_new ("y", 'a'), "q");
+ abort ();
+ }
+
+ if (do_failed_test ("*must_be_null*"))
+ {
+ int q;
+
+ g_variant_new_va (&q, "u", NULL, NULL);
+ abort ();
+ }
+
+ if (do_failed_test ("*must_be_null*"))
+ {
+ int q;
+
+ g_variant_get_va (g_variant_new ("u", 1), &q, "u", NULL, NULL);
+ abort ();
+ }
+}
+
+static void
+check_and_free (GVariant *value,
+ const gchar *str)
+{
+ gchar *valstr = g_variant_print (value, FALSE);
+ g_assert_cmpstr (str, ==, valstr);
+ g_variant_unref (value);
+ g_free (valstr);
+}
+
+static void
+test_varargs (void)
+{
+ {
+ GVariantBuilder array;
+
+ g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add (&array, "{sv}", "size",
+ g_variant_new ("(ii)", 800, 600));
+ g_variant_builder_add (&array, "{sv}", "title",
+ g_variant_new_string ("Test case"));
+ g_variant_builder_add_value (&array,
+ g_variant_new_dict_entry (g_variant_new_string ("temperature"),
+ g_variant_new_variant (
+ g_variant_new_double (37.5))));
+ check_and_free (g_variant_new ("(ma{sv}m(a{sv})ma{sv}ii)",
+ NULL, FALSE, NULL, &array, 7777, 8888),
+ "(Nothing, Nothing, {'size': <(800, 600)>, "
+ "'title': <'Test case'>, "
+ "'temperature': <37.5>}, "
+ "7777, 8888)");
+
+ check_and_free (g_variant_new ("(imimimmimmimmi)",
+ 123,
+ FALSE, 321,
+ TRUE, 123,
+ FALSE, TRUE, 321,
+ TRUE, FALSE, 321,
+ TRUE, TRUE, 123),
+ "(123, Nothing, 123, Nothing, Just Nothing, 123)");
+
+ check_and_free (g_variant_new ("(ybnixd)",
+ 'a', 1, 22, 33, (guint64) 44, 5.5),
+ "(0x61, true, 22, 33, 44, 5.5)");
+
+ check_and_free (g_variant_new ("(@y?*rv)",
+ g_variant_new ("y", 'a'),
+ g_variant_new ("y", 'b'),
+ g_variant_new ("y", 'c'),
+ g_variant_new ("(y)", 'd'),
+ g_variant_new ("y", 'e')),
+ "(0x61, 0x62, 0x63, (0x64,), <byte 0x65>)");
+ }
+
+ {
+ GVariantBuilder array;
+ GVariantIter iter;
+ GVariant *value;
+ gchar *number;
+ gboolean just;
+ gint i, val;
+
+ g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < 100; i++)
+ {
+ number = g_strdup_printf ("%d", i);
+ g_variant_builder_add (&array, "s", number);
+ g_free (number);
+ }
+
+ value = g_variant_builder_end (&array);
+ g_variant_iter_init (&iter, value);
+
+ i = 0;
+ while (g_variant_iter_loop (&iter, "s", &number))
+ {
+ gchar *check = g_strdup_printf ("%d", i++);
+ g_assert_cmpstr (number, ==, check);
+ g_free (check);
+ }
+ g_assert (number == NULL);
+ g_assert (i == 100);
+
+ g_variant_unref (value);
+
+ g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < 100; i++)
+ g_variant_builder_add (&array, "mi", i % 2 == 0, i);
+ value = g_variant_builder_end (&array);
+
+ i = 0;
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_loop (&iter, "mi", NULL, &val))
+ g_assert (val == i++ || val == 0);
+ g_assert (i == 100);
+
+ i = 0;
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_loop (&iter, "mi", &just, &val))
+ {
+ gint this = i++;
+
+ if (this % 2 == 0)
+ {
+ g_assert (just);
+ g_assert (val == this);
+ }
+ else
+ {
+ g_assert (!just);
+ g_assert (val == 0);
+ }
+ }
+ g_assert (i == 100);
+
+ g_variant_unref (value);
+ }
+
+ {
+ const gchar *strvector[] = {"/hello", "/world", NULL};
+ const gchar *test_strs[] = {"/foo", "/bar", "/baz" };
+ GVariantBuilder builder;
+ GVariantIter *array;
+ GVariantIter tuple;
+ const gchar **strv;
+ gchar **my_strv;
+ GVariant *value;
+ gchar *str;
+ gint i;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
+ g_variant_builder_add (&builder, "o", "/foo");
+ g_variant_builder_add (&builder, "o", "/bar");
+ g_variant_builder_add (&builder, "o", "/baz");
+ value = g_variant_new("(ao^ao^a&o)", &builder, strvector, strvector);
+ g_variant_iter_init (&tuple, value);
+ g_variant_iter_next (&tuple, "ao", &array);
+
+ i = 0;
+ while (g_variant_iter_loop (array, "o", &str))
+ g_assert_cmpstr (str, ==, test_strs[i++]);
+ g_assert (i == 3);
+
+ g_variant_iter_free (array);
+
+ /* start over */
+ g_variant_iter_init (&tuple, value);
+ g_variant_iter_next (&tuple, "ao", &array);
+
+ i = 0;
+ while (g_variant_iter_loop (array, "&o", &str))
+ g_assert_cmpstr (str, ==, test_strs[i++]);
+ g_assert (i == 3);
+
+ g_variant_iter_free (array);
+
+ g_variant_iter_next (&tuple, "^a&o", &strv);
+ g_variant_iter_next (&tuple, "^ao", &my_strv);
+
+ g_assert_cmpstr (strv[0], ==, "/hello");
+ g_assert_cmpstr (strv[1], ==, "/world");
+ g_assert (strv[2] == NULL);
+ g_assert_cmpstr (my_strv[0], ==, "/hello");
+ g_assert_cmpstr (my_strv[1], ==, "/world");
+ g_assert (my_strv[2] == NULL);
+
+ g_variant_unref (value);
+ g_strfreev (my_strv);
+ g_free (strv);
+ }
+
+ {
+ const gchar *strvector[] = { "i", "ii", "iii", "iv", "v", "vi", NULL };
+ GVariantBuilder builder;
+ GVariantIter iter;
+ GVariantIter *i2;
+ GVariant *value;
+ GVariant *sub;
+ gchar **strv;
+ gint i;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aag"));
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("ag"));
+ for (i = 0; i < 6; i++)
+ if (i & 1)
+ g_variant_builder_add (&builder, "g", strvector[i]);
+ else
+ g_variant_builder_add (&builder, "&g", strvector[i]);
+ g_variant_builder_close (&builder);
+ g_variant_builder_add (&builder, "^ag", strvector);
+ g_variant_builder_add (&builder, "^ag", strvector);
+ value = g_variant_new ("aag", &builder);
+
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_loop (&iter, "^ag", &strv))
+ for (i = 0; i < 6; i++)
+ g_assert_cmpstr (strv[i], ==, strvector[i]);
+
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_loop (&iter, "^a&g", &strv))
+ for (i = 0; i < 6; i++)
+ g_assert_cmpstr (strv[i], ==, strvector[i]);
+
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_loop (&iter, "ag", &i2))
+ {
+ gchar *str;
+
+ i = 0;
+ while (g_variant_iter_loop (i2, "g", &str))
+ g_assert_cmpstr (str, ==, strvector[i++]);
+ g_assert (i == 6);
+ }
+
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_loop (&iter, "@ag", &sub))
+ {
+ gchar *str = g_variant_print (sub, TRUE);
+ g_assert_cmpstr (str, ==,
+ "[signature 'i', 'ii', 'iii', 'iv', 'v', 'vi']");
+ g_free (str);
+ }
+
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_loop (&iter, "*", &sub))
+ {
+ gchar *str = g_variant_print (sub, TRUE);
+ g_assert_cmpstr (str, ==,
+ "[signature 'i', 'ii', 'iii', 'iv', 'v', 'vi']");
+ g_free (str);
+ }
+
+ for (i = 0; i < g_variant_n_children (value); i++)
+ {
+ gint j;
+
+ g_variant_get_child (value, i, "*", &sub);
+
+ for (j = 0; j < g_variant_n_children (sub); j++)
+ {
+ const gchar *str = NULL;
+ GVariant *cval;
+
+ g_variant_get_child (sub, j, "&g", &str);
+ g_assert_cmpstr (str, ==, strvector[j]);
+
+ cval = g_variant_get_child_value (sub, j);
+ g_variant_get (cval, "&g", &str);
+ g_assert_cmpstr (str, ==, strvector[j]);
+ g_variant_unref (cval);
+ }
+
+ g_variant_unref (sub);
+ }
+
+ g_variant_unref (value);
+ }
+
+ {
+ gboolean justs[10];
+ GVariant *value;
+
+ GVariant *vval;
+ guchar byteval;
+ gboolean bval;
+ gint16 i16val;
+ guint16 u16val;
+ gint32 i32val;
+ guint32 u32val;
+ gint64 i64val;
+ guint64 u64val;
+ gdouble dval;
+ gint32 hval;
+
+ /* test all 'Nothing' */
+ value = g_variant_new ("(mymbmnmqmimumxmtmhmdmv)",
+ FALSE, 'a',
+ FALSE, TRUE,
+ FALSE, (gint16) 123,
+ FALSE, (guint16) 123,
+ FALSE, (gint32) 123,
+ FALSE, (guint32) 123,
+ FALSE, (gint64) 123,
+ FALSE, (guint64) 123,
+ FALSE, (gint32) -1,
+ FALSE, (gdouble) 37.5,
+ NULL);
+
+ /* both NULL */
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL);
+
+ /* NULL values */
+ memset (justs, 1, sizeof justs);
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ &justs[0], NULL,
+ &justs[1], NULL,
+ &justs[2], NULL,
+ &justs[3], NULL,
+ &justs[4], NULL,
+ &justs[5], NULL,
+ &justs[6], NULL,
+ &justs[7], NULL,
+ &justs[8], NULL,
+ &justs[9], NULL,
+ NULL);
+ g_assert (!(justs[0] || justs[1] || justs[2] || justs[3] || justs[4] ||
+ justs[5] || justs[6] || justs[7] || justs[8] || justs[9]));
+
+ /* both non-NULL */
+ memset (justs, 1, sizeof justs);
+ byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
+ vval = (void *) 1;
+ bval = TRUE;
+ dval = 88.88;
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ &justs[0], &byteval,
+ &justs[1], &bval,
+ &justs[2], &i16val,
+ &justs[3], &u16val,
+ &justs[4], &i32val,
+ &justs[5], &u32val,
+ &justs[6], &i64val,
+ &justs[7], &u64val,
+ &justs[8], &hval,
+ &justs[9], &dval,
+ &vval);
+ g_assert (!(justs[0] || justs[1] || justs[2] || justs[3] || justs[4] ||
+ justs[5] || justs[6] || justs[7] || justs[8] || justs[9]));
+ g_assert (byteval == '\0' && bval == FALSE);
+ g_assert (i16val == 0 && u16val == 0 && i32val == 0 &&
+ u32val == 0 && i64val == 0 && u64val == 0 &&
+ hval == 0 && dval == 0.0);
+ g_assert (vval == NULL);
+
+ /* NULL justs */
+ byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
+ vval = (void *) 1;
+ bval = TRUE;
+ dval = 88.88;
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ NULL, &byteval,
+ NULL, &bval,
+ NULL, &i16val,
+ NULL, &u16val,
+ NULL, &i32val,
+ NULL, &u32val,
+ NULL, &i64val,
+ NULL, &u64val,
+ NULL, &hval,
+ NULL, &dval,
+ &vval);
+ g_assert (byteval == '\0' && bval == FALSE);
+ g_assert (i16val == 0 && u16val == 0 && i32val == 0 &&
+ u32val == 0 && i64val == 0 && u64val == 0 &&
+ hval == 0 && dval == 0.0);
+ g_assert (vval == NULL);
+
+ g_variant_unref (value);
+
+
+ /* test all 'Just' */
+ value = g_variant_new ("(mymbmnmqmimumxmtmhmdmv)",
+ TRUE, 'a',
+ TRUE, TRUE,
+ TRUE, (gint16) 123,
+ TRUE, (guint16) 123,
+ TRUE, (gint32) 123,
+ TRUE, (guint32) 123,
+ TRUE, (gint64) 123,
+ TRUE, (guint64) 123,
+ TRUE, (gint32) -1,
+ TRUE, (gdouble) 37.5,
+ g_variant_new ("()"));
+
+ /* both NULL */
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL);
+
+ /* NULL values */
+ memset (justs, 0, sizeof justs);
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ &justs[0], NULL,
+ &justs[1], NULL,
+ &justs[2], NULL,
+ &justs[3], NULL,
+ &justs[4], NULL,
+ &justs[5], NULL,
+ &justs[6], NULL,
+ &justs[7], NULL,
+ &justs[8], NULL,
+ &justs[9], NULL,
+ NULL);
+ g_assert (justs[0] && justs[1] && justs[2] && justs[3] && justs[4] &&
+ justs[5] && justs[6] && justs[7] && justs[8] && justs[9]);
+
+ /* both non-NULL */
+ memset (justs, 0, sizeof justs);
+ byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
+ vval = (void *) 1;
+ bval = FALSE;
+ dval = 88.88;
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ &justs[0], &byteval,
+ &justs[1], &bval,
+ &justs[2], &i16val,
+ &justs[3], &u16val,
+ &justs[4], &i32val,
+ &justs[5], &u32val,
+ &justs[6], &i64val,
+ &justs[7], &u64val,
+ &justs[8], &hval,
+ &justs[9], &dval,
+ &vval);
+ g_assert (justs[0] && justs[1] && justs[2] && justs[3] && justs[4] &&
+ justs[5] && justs[6] && justs[7] && justs[8] && justs[9]);
+ g_assert (byteval == 'a' && bval == TRUE);
+ g_assert (i16val == 123 && u16val == 123 && i32val == 123 &&
+ u32val == 123 && i64val == 123 && u64val == 123 &&
+ hval == -1 && dval == 37.5);
+ g_assert (g_variant_is_of_type (vval, G_VARIANT_TYPE_UNIT));
+ g_variant_unref (vval);
+
+ /* NULL justs */
+ byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
+ vval = (void *) 1;
+ bval = TRUE;
+ dval = 88.88;
+ g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
+ NULL, &byteval,
+ NULL, &bval,
+ NULL, &i16val,
+ NULL, &u16val,
+ NULL, &i32val,
+ NULL, &u32val,
+ NULL, &i64val,
+ NULL, &u64val,
+ NULL, &hval,
+ NULL, &dval,
+ &vval);
+ g_assert (byteval == 'a' && bval == TRUE);
+ g_assert (i16val == 123 && u16val == 123 && i32val == 123 &&
+ u32val == 123 && i64val == 123 && u64val == 123 &&
+ hval == -1 && dval == 37.5);
+ g_assert (g_variant_is_of_type (vval, G_VARIANT_TYPE_UNIT));
+ g_variant_unref (vval);
+
+ g_variant_unref (value);
+ }
+}
+
+static void
+hash_get (GVariant *value,
+ const gchar *format,
+ ...)
+{
+ const gchar *endptr = NULL;
+ gboolean hash;
+ va_list ap;
+
+ hash = g_str_has_suffix (format, "#");
+
+ va_start (ap, format);
+ g_variant_get_va (value, NULL, format, hash ? &endptr : NULL, &ap);
+ va_end (ap);
+
+ if (hash)
+ g_assert (*endptr == '#');
+}
+
+static GVariant *
+hash_new (const gchar *format,
+ ...)
+{
+ const gchar *endptr = NULL;
+ GVariant *value;
+ gboolean hash;
+ va_list ap;
+
+ hash = g_str_has_suffix (format, "#");
+
+ va_start (ap, format);
+ value = g_variant_new_va (NULL, format, hash ? &endptr : NULL, &ap);
+ va_end (ap);
+
+ if (hash)
+ g_assert (*endptr == '#');
+
+ return value;
+}
+
+static void
+test_valist (void)
+{
+ GVariant *value;
+ gint32 x;
+
+ x = 0;
+ value = hash_new ("i", 234);
+ hash_get (value, "i", &x);
+ g_assert (x == 234);
+ g_variant_unref (value);
+
+ x = 0;
+ value = hash_new ("i#", 234);
+ hash_get (value, "i#", &x);
+ g_assert (x == 234);
+ g_variant_unref (value);
+}
+
int
main (int argc, char **argv)
{
@@ -2688,6 +3340,10 @@ main (int argc, char **argv)
}
g_test_add_func ("/gvariant/containers", test_containers);
+ g_test_add_func ("/gvariant/format-strings", test_format_strings);
+ g_test_add_func ("/gvariant/invalid-varargs", test_invalid_varargs);
+ g_test_add_func ("/gvariant/varargs", test_varargs);
+ g_test_add_func ("/gvariant/valist", test_valist);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]