[glib] GVariant: improve bytestring support



commit d9e90c3894739bdfa642e35bdea866c6d0ab7ef2
Author: Ryan Lortie <desrt desrt ca>
Date:   Wed Jul 7 10:37:16 2010 -0400

    GVariant: improve bytestring support
    
     - add G_VARIANT_TYPE_BYTESTRING, _BYTESTRING_ARRAY, _STRING_ARRAY
    
     - remove g_variant_{new,get}_byte_array functions
    
     - add g_variant_{new,get,dup}_bytestring{,_array} functions
    
     - remove undocumented support for deserialising arrays of objectpaths
       or signature strngs using g_variant_get_strv()
    
     - add and document new format strings '^ay', '^&ay', '^aay' and '^a&ay'
    
     - update GApplication to use the new API
    
     - update GSettings binding code to use the new API
    
     - add tests

 docs/reference/glib/glib-sections.txt     |    9 +-
 docs/reference/glib/gvariant-varargs.xml  |  184 +++++++++--
 docs/reference/glib/tmpl/glib-unused.sgml |   40 +---
 gio/gapplication.c                        |   32 +--
 gio/gsettings-mapping.c                   |    8 +-
 gio/tests/gsettings.c                     |    2 +-
 gio/tests/testapp.c                       |    4 +-
 glib/glib.symbols                         |    8 +-
 glib/gvariant-parser.c                    |  179 ++++++++++-
 glib/gvariant.c                           |  477 +++++++++++++++++++++--------
 glib/gvariant.h                           |   14 +-
 glib/gvarianttype.h                       |   25 ++
 glib/tests/gvariant.c                     |  129 ++++++--
 13 files changed, 836 insertions(+), 275 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 8f16358..b0a9c8f 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -2842,8 +2842,9 @@ g_variant_is_object_path
 g_variant_new_signature
 g_variant_is_signature
 g_variant_new_variant
-g_variant_new_byte_array
 g_variant_new_strv
+g_variant_new_bytestring
+g_variant_new_bytestring_array
 
 <SUBSECTION>
 g_variant_get_boolean
@@ -2859,10 +2860,12 @@ g_variant_get_double
 g_variant_get_string
 g_variant_dup_string
 g_variant_get_variant
-g_variant_new_byte_array
-g_variant_get_byte_array
 g_variant_get_strv
 g_variant_dup_strv
+g_variant_get_bytestring
+g_variant_dup_bytestring
+g_variant_get_bytestring_array
+g_variant_dup_bytestring_array
 
 <SUBSECTION>
 g_variant_new_maybe
diff --git a/docs/reference/glib/gvariant-varargs.xml b/docs/reference/glib/gvariant-varargs.xml
index 2eb945f..2d92af0 100644
--- a/docs/reference/glib/gvariant-varargs.xml
+++ b/docs/reference/glib/gvariant-varargs.xml
@@ -46,8 +46,8 @@
    <listitem>
     <para>
      '<literal>&amp;s</literal>' '<literal>&amp;o</literal>', '<literal>&amp;g</literal>', '<literal>^as</literal>',
-     '<literal>^ao</literal>', '<literal>^ag</literal>', '<literal>^a&amp;s</literal>', '<literal>^a&amp;o</literal>' or
-     '<literal>^a&amp;g</literal>'
+     '<literal>^a&amp;s</literal>', '<literal>^ay</literal>', '<literal>^&amp;ay</literal>', '<literal>^aay</literal>'
+     or '<literal>^a&amp;ay</literal>'.
     </para>
    </listitem>
    <listitem>
@@ -935,39 +935,155 @@ value2 = g_variant_new ("(@(iii)*)", value1, g_variant_new_string ("foo"));
    </para>
 
    <para>
-    The '<literal>^</literal>' character currently only has one purpose: to convert to and from
-    <link linkend='G-TYPE-STRV'><literal>G_TYPE_STRV</literal></link> type arrays of strings.  It is always used with
-    arrays of strings (or other string types).  It has two forms.
-   </para>
-   <itemizedlist>
-    <listitem>
-     <para>
-      '<literal>^as</literal>' (or <literal>o</literal> or <literal>g</literal>)
-     </para>
-    </listitem>
-    <listitem>
-     <para>
-      '<literal>^a&amp;s</literal>' (or <literal>o</literal> or <literal>g</literal>)
-     </para>
-    </listitem>
-   </itemizedlist>
-   <para>
-    When used with <link linkend='g-variant-new'><function>g_variant_new()</function></link> both forms are equivalent.
-    A <code>(const <link linkend='gchar'>gchar</link> * const *)</code> is collected.  This must be a pointer to the
-    array of <link linkend='NULL--CAPS'><literal>NULL</literal></link>-terminated pointers to strings.  This array is
-    converted to a <link linkend='GVariant'><type>GVariant</type></link> instance.  Copies are made, so the original
-    array may be freed immediately.
-   </para>
-   <para>
-    When used with <link linkend='g-variant-get'><function>g_variant_get()</function></link> the two forms have
-    different meaning.  Both return a freshly allocated
-    <link linkend='NULL--CAPS'><literal>NULL</literal></link>-terminated array of pointers to strings.  In the case of
-    '<literal>^as</literal>', the strings are owned by the caller -- it is appropriate to free the array with
-    <link linkend='g-strfreev'><function>g_strfreev()</function></link>.  In the case of '<literal>^a&amp;s</literal>',
-    a shallow copy is made; the strings themselves are embedded in the serialised data and owned by the original
-    <link linkend='GVariant'><type>GVariant</type></link> instance -- it is only appropriate to free the outer array
-    with <link linkend='g-free'><function>g_free()</function></link>.
+    The '<literal>^</literal>' character currently supports conversion to and from bytestrings or to and from arrays
+    of strings or bytestrings.  It has a number of forms.
    </para>
+
+   <para>
+    In all forms, when used with <link linkend='g-variant-new'><function>g_variant_new()</function></link> one
+    pointer value is collected from the variable arguments and passed to a function (as given in the table below).
+    The result of that function is used as the value for this position.  When used with
+    <link linkend='g-variant-get'><function>g_variant_get()</function></link> one pointer value is produced by using
+    the function (given in the table) and returned by reference.
+   </para>
+
+   <informaltable>
+    <tgroup cols='2'>
+     <colspec colname='col_0'/>
+     <colspec colname='col_1'/>
+     <colspec colname='col_2'/>
+     <tbody>
+
+      <row rowsep='1'>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         <emphasis role='strong'>Conversion</emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+          <emphasis role='strong'>
+            Used with <link linkend='g-variant-new'><function>g_variant_new()</function></link>
+          </emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+          <emphasis role='strong'>
+            Used with <link linkend='g-variant-get'><function>g_variant_get()</function></link>
+          </emphasis>
+        </para>
+       </entry>
+      </row>
+
+      <row rowsep='1'>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         <emphasis role='strong'>
+          <literal>^as</literal>
+         </emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1' morerows='1'>
+        <para>
+         equivalent to <link linkend='g-variant-new-strv'><function>g_variant_new_strv()</function></link>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         equivalent to <link linkend='g-variant-dup-strv'><function>g_variant_dup_strv()</function></link>
+        </para>
+       </entry>
+      </row>
+
+      <row rowsep='1'>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         <emphasis role='strong'>
+          <literal>^a&amp;s</literal>
+         </emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         equivalent to <link linkend='g-variant-get-strv'><function>g_variant_get_strv()</function></link>
+        </para>
+       </entry>
+      </row>
+
+      <row rowsep='1'>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         <emphasis role='strong'>
+          <literal>^ay</literal>
+         </emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1' morerows='1'>
+        <para>
+         equivalent to <link linkend='g-variant-new-bytestring'><function>g_variant_new_bytestring()</function></link>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         equivalent to <link linkend='g-variant-dup-bytestring'><function>g_variant_dup_bytestring()</function></link>
+        </para>
+       </entry>
+      </row>
+
+      <row rowsep='1'>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         <emphasis role='strong'>
+          <literal>^&amp;ay</literal>
+         </emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         equivalent to <link linkend='g-variant-get-bytestring'><function>g_variant_get_bytestring()</function></link>
+        </para>
+       </entry>
+      </row>
+
+      <row rowsep='1'>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         <emphasis role='strong'>
+          <literal>^aay</literal>
+         </emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1' morerows='1'>
+        <para>
+         equivalent to <link linkend='g-variant-new-bytestring-array'><function>g_variant_new_bytestring_array()</function></link>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         equivalent to <link linkend='g-variant-dup-bytestring-array'><function>g_variant_dup_bytestring_array()</function></link>
+        </para>
+       </entry>
+      </row>
+
+      <row rowsep='1'>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         <emphasis role='strong'>
+          <literal>^a&amp;ay</literal>
+         </emphasis>
+        </para>
+       </entry>
+       <entry colsep='1' rowsep='1'>
+        <para>
+         equivalent to <link linkend='g-variant-get-bytestring-array'><function>g_variant_get_bytestring_array()</function></link>
+        </para>
+       </entry>
+      </row>
+
+     </tbody>
+    </tgroup>
+   </informaltable>
   </refsect2>
  </refsect1>
 </refentry>
diff --git a/docs/reference/glib/tmpl/glib-unused.sgml b/docs/reference/glib/tmpl/glib-unused.sgml
index 8824266..7469a50 100644
--- a/docs/reference/glib/tmpl/glib-unused.sgml
+++ b/docs/reference/glib/tmpl/glib-unused.sgml
@@ -877,7 +877,7 @@ Turns the argument into a string literal by using the '#' stringizing operator.
 
 @x: text to convert to a literal string.
 
-<!-- ##### FUNCTION g_variant_dup_bytestring ##### -->
+<!-- ##### FUNCTION g_variant_get_byte_array ##### -->
 <para>
 
 </para>
@@ -886,46 +886,12 @@ Turns the argument into a string literal by using the '#' stringizing operator.
 @length: 
 @Returns: 
 
-<!-- ##### FUNCTION g_variant_dup_bytestring_array ##### -->
+<!-- ##### FUNCTION g_variant_new_byte_array ##### -->
 <para>
 
 </para>
 
- value: 
- length: 
- Returns: 
-
-<!-- ##### FUNCTION g_variant_get_bytestring ##### -->
-<para>
-
-</para>
-
- value: 
- Returns: 
-
-<!-- ##### FUNCTION g_variant_get_bytestring_array ##### -->
-<para>
-
-</para>
-
- value: 
- length: 
- Returns: 
-
-<!-- ##### FUNCTION g_variant_new_bytestring ##### -->
-<para>
-
-</para>
-
- string: 
- Returns: 
-
-<!-- ##### FUNCTION g_variant_new_bytestring_array ##### -->
-<para>
-
-</para>
-
- strv: 
+ array: 
 @length: 
 @Returns: 
 
diff --git a/gio/gapplication.c b/gio/gapplication.c
index 2996d1b..2e3a3e5 100644
--- a/gio/gapplication.c
+++ b/gio/gapplication.c
@@ -332,7 +332,7 @@ append_cwd_to_platform_data (GVariant *platform_data)
   if (cwd)
     g_variant_builder_add (&builder, "{sv}",
 			   "cwd",
-			   g_variant_new_byte_array (cwd, -1));
+			   g_variant_new_bytestring (cwd));
   g_free (cwd);
 
   if (platform_data)
@@ -351,27 +351,6 @@ append_cwd_to_platform_data (GVariant *platform_data)
   return result;
 }
 
-static GVariant *
-variant_from_argv (int    argc,
-		   char **argv)
-{
-  GVariantBuilder builder;
-  int i;
-
-  g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
-
-  for (i = 1; i < argc; i++)
-    {
-      guint8 *argv_bytes;
-
-      argv_bytes = (guint8*) argv[i];
-      g_variant_builder_add_value (&builder,
-				   g_variant_new_byte_array (argv_bytes, -1));
-    }
-  
-  return g_variant_builder_end (&builder);
-}
-
 static gboolean
 timeout_handle_actions_changed (gpointer user_data)
 {
@@ -468,6 +447,7 @@ g_application_new (const gchar *appid,
 		   int          argc,
 		   char       **argv)
 {
+  const gchar * const *args = (const gchar **) argv;
   GObject *app;
   GError *error = NULL;
   GVariant *argv_variant;
@@ -476,7 +456,7 @@ g_application_new (const gchar *appid,
 
   g_return_val_if_fail (appid != NULL, NULL);
   
-  argv_variant = variant_from_argv (argc, argv);
+  argv_variant = g_variant_new_bytestring_array (args, argc);
   
   app = g_initable_new (G_TYPE_APPLICATION, 
 			NULL,
@@ -514,13 +494,14 @@ g_application_try_new (const gchar *appid,
 		       char       **argv,
 		       GError     **error)
 {
+  const gchar * const *args = (const gchar **) argv;
   GVariant *argv_variant;
 
   g_type_init ();
 
   g_return_val_if_fail (appid != NULL, NULL);
   
-  argv_variant = variant_from_argv (argc, argv);
+  argv_variant = g_variant_new_bytestring_array (args, argc);
   
   return G_APPLICATION (g_initable_new (G_TYPE_APPLICATION, 
 					NULL,
@@ -551,13 +532,14 @@ g_application_unregistered_try_new (const gchar *appid,
 				    char       **argv,
 				    GError     **error)
 {
+  const gchar * const *args = (const gchar **) argv;
   GVariant *argv_variant;
 
   g_type_init ();
 
   g_return_val_if_fail (appid != NULL, NULL);
   
-  argv_variant = variant_from_argv (argc, argv);
+  argv_variant = g_variant_new_bytestring_array (args, argc);
   
   return G_APPLICATION (g_initable_new (G_TYPE_APPLICATION, 
 					NULL,
diff --git a/gio/gsettings-mapping.c b/gio/gsettings-mapping.c
index d85fae1..f83e238 100644
--- a/gio/gsettings-mapping.c
+++ b/gio/gsettings-mapping.c
@@ -369,8 +369,8 @@ g_settings_set_mapping (const GValue       *value,
         return NULL;
       else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_STRING))
         return g_variant_new_string (g_value_get_string (value));
-      else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE ("ay")))
-        return g_variant_new_byte_array (g_value_get_string (value), -1);
+      else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_BYTESTRING))
+        return g_variant_new_bytestring (g_value_get_string (value));
       else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_OBJECT_PATH))
         return g_variant_new_object_path (g_value_get_string (value));
       else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_SIGNATURE))
@@ -528,9 +528,9 @@ g_settings_get_mapping (GValue   *value,
           return TRUE;
         }
     }
-  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("ay")))
+  else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING))
     {
-      g_value_set_string (value, g_variant_get_byte_array (variant, NULL));
+      g_value_set_string (value, g_variant_get_bytestring (variant));
       return TRUE;
     }
 
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index 06f98bf..f3727e0 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -1006,7 +1006,7 @@ test_simple_binding (void)
 
   g_object_set (obj, "string", "non-unicode:\315", NULL);
   value = g_settings_get_value (settings, "chararray");
-  g_assert_cmpstr (g_variant_get_byte_array (value, NULL), ==, "non-unicode:\315");
+  g_assert_cmpstr (g_variant_get_bytestring (value), ==, "non-unicode:\315");
   g_variant_unref (value);
 
   g_settings_bind (settings, "double", obj, "double", G_SETTINGS_BIND_DEFAULT);
diff --git a/gio/tests/testapp.c b/gio/tests/testapp.c
index caed830..c583645 100644
--- a/gio/tests/testapp.c
+++ b/gio/tests/testapp.c
@@ -41,11 +41,11 @@ on_app_activated (GApplication  *application,
   while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
     {
       const char *activate_cwd;
-      gsize *len;
+
       if (strcmp (key, "cwd") != 0)
 	continue;
 
-      activate_cwd = g_variant_get_byte_array (value, &len);
+      activate_cwd = g_variant_get_bytestring (value);
       g_assert_cmpstr (cwd, ==, activate_cwd);
       g_variant_unref (value);
     }
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 41ef82b..648812a 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1743,8 +1743,9 @@ g_variant_is_object_path
 g_variant_new_signature
 g_variant_is_signature
 g_variant_new_variant
-g_variant_new_byte_array
 g_variant_new_strv
+g_variant_new_bytestring
+g_variant_new_bytestring_array
 
 g_variant_get_boolean
 g_variant_get_byte
@@ -1759,9 +1760,12 @@ g_variant_get_double
 g_variant_get_string
 g_variant_dup_string
 g_variant_get_variant
-g_variant_get_byte_array
 g_variant_get_strv
 g_variant_dup_strv
+g_variant_get_bytestring
+g_variant_dup_bytestring
+g_variant_get_bytestring_array
+g_variant_dup_bytestring_array
 
 g_variant_new_maybe
 g_variant_new_array
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
index 93a5dd3..7932d85 100644
--- a/glib/gvariant-parser.c
+++ b/glib/gvariant-parser.c
@@ -163,7 +163,22 @@ token_stream_prepare (TokenStream *stream)
           break;
       break;
 
-    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+    case 'b':
+      if (stream->stream[1] == '\'' || stream->stream[1] == '"')
+        {
+          for (end = stream->stream + 2; end != stream->end; end++)
+            if (*end == stream->stream[1] || *end == '\0' ||
+                (*end == '\\' && (++end == stream->end || *end == '\0')))
+              break;
+
+          if (end != stream->end && *end)
+            end++;
+          break;
+        }
+
+      else    /* â??â??â?? */;
+
+    case 'a': /* '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':
@@ -173,13 +188,23 @@ token_stream_prepare (TokenStream *stream)
           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;
+
     case '@': case '%':
       /* stop at the first space, comma, colon or unmatched bracket.
        * deals nicely with cases like (%i, %i) or {%i: %i}.
        */
       for (end = stream->stream + 1;
            end != stream->end && *end != ',' &&
-           *end != ':' && !g_ascii_isspace (*end);
+           *end != ':' && *end != '>' && !g_ascii_isspace (*end);
            end++)
 
         if (*end == '(' || *end == '{')
@@ -190,16 +215,6 @@ token_stream_prepare (TokenStream *stream)
 
       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;
@@ -225,11 +240,23 @@ token_stream_peek (TokenStream *stream,
 }
 
 static gboolean
+token_stream_peek2 (TokenStream *stream,
+                    gchar        first_char,
+                    gchar        second_char)
+{
+  token_stream_prepare (stream);
+
+  return stream->this[0] == first_char &&
+         stream->this[1] == second_char;
+}
+
+static gboolean
 token_stream_is_keyword (TokenStream *stream)
 {
   token_stream_prepare (stream);
 
-  return g_ascii_isalpha (stream->this[0]);
+  return g_ascii_isalpha (stream->this[0]) &&
+         g_ascii_isalpha (stream->this[1]);
 }
 
 static gboolean
@@ -1542,6 +1569,128 @@ string_parse (TokenStream  *stream,
 typedef struct
 {
   AST ast;
+  gchar *string;
+} ByteString;
+
+static gchar *
+bytestring_get_pattern (AST     *ast,
+                        GError **error)
+{
+  return g_strdup ("May");
+}
+
+static GVariant *
+bytestring_get_value (AST                 *ast,
+                      const GVariantType  *type,
+                      GError             **error)
+{
+  ByteString *string = (ByteString *) ast;
+
+  g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_BYTESTRING));
+
+  return g_variant_new_bytestring (string->string);
+}
+
+static void
+bytestring_free (AST *ast)
+{
+  ByteString *string = (ByteString *) ast;
+
+  g_free (string->string);
+  g_slice_free (ByteString, string);
+}
+
+static AST *
+bytestring_parse (TokenStream  *stream,
+                  va_list      *app,
+                  GError      **error)
+{
+  static const ASTClass bytestring_class = {
+    bytestring_get_pattern,
+    maybe_wrapper, bytestring_get_value,
+    bytestring_free
+  };
+  ByteString *string;
+  SourceRef ref;
+  gchar *token;
+  gsize length;
+  gchar quote;
+  gchar *str;
+  gint i, j;
+
+  token_stream_start_ref (stream, &ref);
+  token = token_stream_get (stream);
+  token_stream_end_ref (stream, &ref);
+  g_assert (token[0] == 'b');
+  length = strlen (token);
+  quote = token[1];
+
+  str = g_malloc (length);
+  g_assert (quote == '"' || quote == '\'');
+  j = 0;
+  i = 2;
+  while (token[i] != quote)
+    switch (token[i])
+      {
+      case '\0':
+        parser_set_error (error, &ref, NULL,
+                          "unterminated string constant");
+        g_free (token);
+        return NULL;
+
+      case '\\':
+        switch (token[++i])
+          {
+          case '\0':
+            parser_set_error (error, &ref, NULL,
+                              "unterminated string constant");
+            g_free (token);
+            return NULL;
+
+          case '0': case '1': case '2': case '3':
+          case '4': case '5': case '6': case '7':
+            {
+              /* up to 3 characters */
+              guchar val = token[i++] - '0';
+
+              if ('0' <= token[i] && token[i] < '8')
+                val = (val << 3) | (token[i++] - '0');
+
+              if ('0' <= token[i] && token[i] < '8')
+                val = (val << 3) | (token[i++] - '0');
+
+              str[j++] = val;
+            }
+            continue;
+
+          case 'a': str[j++] = '\a'; i++; continue;
+          case 'b': str[j++] = '\b'; i++; continue;
+          case 'f': str[j++] = '\f'; i++; continue;
+          case 'n': str[j++] = '\n'; i++; continue;
+          case 'r': str[j++] = '\r'; i++; continue;
+          case 't': str[j++] = '\t'; i++; continue;
+          case 'v': str[j++] = '\v'; i++; continue;
+          case '\n': i++; continue;
+          }
+
+      default:
+        str[j++] = token[i++];
+      }
+  str[j++] = '\0';
+  g_free (token);
+
+  string = g_slice_new (ByteString);
+  string->ast.class = &bytestring_class;
+  string->string = str;
+
+  token_stream_next (stream);
+
+  return (AST *) string;
+}
+
+typedef struct
+{
+  AST ast;
 
   gchar *token;
 } Number;
@@ -2043,6 +2192,10 @@ parse (TokenStream  *stream,
            token_stream_peek (stream, '"'))
     result = string_parse (stream, app, error);
 
+  else if (token_stream_peek2 (stream, 'b', '\'') ||
+           token_stream_peek2 (stream, 'b', '"'))
+    result = bytestring_parse (stream, app, error);
+
   else
     {
       token_stream_set_error (stream, error, FALSE, "expected value");
diff --git a/glib/gvariant.c b/glib/gvariant.c
index 2819eda..03c09ff 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -1165,101 +1165,248 @@ g_variant_dup_string (GVariant *value,
 }
 
 /**
- * g_variant_new_byte_array:
- * @array: (array length=length): a pointer to an array of bytes
- * @length: the length of @array, or -1
+ * g_variant_new_strv:
+ * @strv: (array length=length): an array of strings
+ * @length: the length of @strv, or -1
  * @returns: a new floating #GVariant instance
  *
- * Constructs an array of bytes #GVariant from the given array of bytes.
+ * Constructs an array of strings #GVariant from the given array of
+ * strings.
  *
- * If @length is -1 then @array is taken to be a normal C string (in the
- * sense that it is terminated by a nul character).  The nul character
- * is included in the array.  If length is not -1 then it gives the
- * length of @array which may then contain nul chracters with no special
- * meaning.
+ * If @length is -1 then @strv is %NULL-terminated.
  *
- * Since: 2.26
+ * Since: 2.24
  **/
 GVariant *
-g_variant_new_byte_array (gconstpointer array,
-                          gssize        length)
+g_variant_new_strv (const gchar * const *strv,
+                    gssize               length)
 {
-  if (length == -1)
+  GVariant **strings;
+  gsize i;
+
+  g_return_val_if_fail (length == 0 || strv != NULL, NULL);
+
+  if (length < 0)
+    length = g_strv_length ((gchar **) strv);
+
+  strings = g_new (GVariant *, length);
+  for (i = 0; i < length; i++)
+    strings[i] = g_variant_ref_sink (g_variant_new_string (strv[i]));
+
+  return g_variant_new_from_children (G_VARIANT_TYPE_STRING_ARRAY,
+                                      strings, length, TRUE);
+}
+
+/**
+ * g_variant_get_strv:
+ * @value: an array of strings #GVariant
+ * @length: (allow-none): the length of the result, or %NULL
+ * @returns: (array length=length): 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.
+ *
+ * Since: 2.24
+ **/
+const gchar **
+g_variant_get_strv (GVariant *value,
+                    gsize    *length)
+{
+  const gchar **strv;
+  gsize n;
+  gsize i;
+
+  TYPE_CHECK (value, G_VARIANT_TYPE_STRING_ARRAY, NULL);
+
+  g_variant_get_data (value);
+  n = g_variant_n_children (value);
+  strv = g_new (const gchar *, n + 1);
+
+  for (i = 0; i < n; i++)
     {
-      const gchar *bytes = array;
+      GVariant *string;
 
-      length = 0;
-      while (bytes[length++]);
+      string = g_variant_get_child_value (value, i);
+      strv[i] = g_variant_get_string (string, NULL);
+      g_variant_unref (string);
     }
+  strv[i] = NULL;
 
-  return g_variant_new_from_trusted (G_VARIANT_TYPE ("ay"),
-                                     array, length);
+  if (length)
+    *length = n;
+
+  return strv;
 }
 
 /**
- * g_variant_get_byte_array:
- * @value: an array of bytes #GVariant
+ * g_variant_dup_strv:
+ * @value: an array of strings #GVariant
  * @length: (allow-none): the length of the result, or %NULL
- * @returns: (array length=length): a pointer to the byte data, or %NULL
+ * @returns: (array length=length): an array of 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.
+ *
+ * Since: 2.24
+ **/
+gchar **
+g_variant_dup_strv (GVariant *value,
+                    gsize    *length)
+{
+  gchar **strv;
+  gsize n;
+  gsize i;
+
+  TYPE_CHECK (value, G_VARIANT_TYPE_STRING_ARRAY, NULL);
+
+  n = g_variant_n_children (value);
+  strv = g_new (gchar *, n + 1);
+
+  for (i = 0; i < n; i++)
+    {
+      GVariant *string;
+
+      string = g_variant_get_child_value (value, i);
+      strv[i] = g_variant_dup_string (string, NULL);
+      g_variant_unref (string);
+    }
+  strv[i] = NULL;
+
+  if (length)
+    *length = n;
+
+  return strv;
+}
+
+/**
+ * g_variant_new_bytestring:
+ * @string: a normal utf8 nul-terminated string
+ * @returns: a new bytestring #GVariant instance
+ *
+ * Creates an array-of-bytes #GVariant with the contents of @string.
+ * This function is just like g_variant_new_string() except that the
+ * string need not be valid utf8.
+ *
+ * The nul terminator character at the end of the string is stored in
+ * the array.
+ *
+ * Since: 2.26
+ **/
+GVariant *
+g_variant_new_bytestring (const gchar *string)
+{
+  g_return_val_if_fail (string != NULL, NULL);
+
+  return g_variant_new_from_trusted (G_VARIANT_TYPE_BYTESTRING,
+                                     string, strlen (string) + 1);
+}
+
+/**
+ * g_variant_get_bytestring:
+ * @value: an array-of-bytes #GVariant instance
+ * @returns: the constant string
+ *
+ * Returns the string value of a #GVariant instance with an
+ * array-of-bytes type.  The string has no particular encoding.
  *
- * Gets the contents of an array of bytes #GVariant.
+ * If the array does not end with a nul terminator character, the empty
+ * string is returned.  For this reason, you can always trust that a
+ * non-%NULL nul-terminated string will be returned by this function.
  *
- * If @length is non-%NULL then it points to a location at which to
- * store the length of the array and nul bytes contained within the
- * array have no special meaning.
+ * If the array contains a nul terminator character somewhere other than
+ * the last byte then the returned string is the string, up to the first
+ * such nul character.
  *
- * If @length is %NULL then the caller has no way to determine what the
- * length of the returned data might be.  In this case, the function
- * ensures that the last byte of the array is a nul byte and, if it is
- * not, returns %NULL instead.  In this way, the caller is assured that
- * any non-%NULL pointer that is returned will be nul-terminated.
+ * It is an error to call this function with a @value that is not an
+ * array of bytes.
  *
  * The return value remains valid as long as @value exists.
  *
  * Since: 2.26
  **/
-gconstpointer
-g_variant_get_byte_array (GVariant *value,
-                          gsize    *length)
+const gchar *
+g_variant_get_bytestring (GVariant *value)
 {
-  gconstpointer data;
+  const gchar *string;
   gsize size;
 
-  TYPE_CHECK (value, G_VARIANT_TYPE ("ay"), NULL);
+  TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING, NULL);
 
-  data = g_variant_get_data (value);
+  /* Won't be NULL since this is an array type */
+  string = g_variant_get_data (value);
   size = g_variant_get_size (value);
 
-  if (length == NULL)
-    {
-      const gchar *bytes = data;
-
-      if (bytes[size - 1] != '\0')
-        return NULL;
-    }
+  if (string[size - 1] == '\0')
+    return string;
   else
+    return "";
+}
+
+/**
+ * g_variant_dup_bytestring:
+ * @value: an array-of-bytes #GVariant instance
+ * @length: (allow-none) (default NULL): a pointer to a #gsize, to store
+ *          the length (not including the nul terminator)
+ * @returns: a newly allocated string
+ *
+ * Similar to g_variant_get_bytestring() except that instead of
+ * returning a constant string, the string is duplicated.
+ *
+ * The return value must be freed using g_free().
+ *
+ * Since: 2.26
+ **/
+gchar *
+g_variant_dup_bytestring (GVariant *value,
+                          gsize    *length)
+{
+  const gchar *original = g_variant_get_bytestring (value);
+  gsize size;
+
+  /* don't crash in case get_bytestring() had an assert failure */
+  if (original == NULL)
+    return NULL;
+
+  size = strlen (original);
+
+  if (length)
     *length = size;
 
-  return data;
+  return g_memdup (original, size + 1);
 }
 
 /**
- * g_variant_new_strv:
- * @strv: an array of strings
+ * g_variant_new_bytestring_array:
+ * @strv (array length=length): an array of strings
  * @length: the length of @strv, or -1
- * @returns: (array length=length): a new floating #GVariant instance
+ * @returns: a new floating #GVariant instance
  *
- * Constructs an array of strings #GVariant from the given array of
+ * Constructs an array of bytestring #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.
+ * If @length is -1 then @strv is %NULL-terminated.
  *
- * Since: 2.24
+ * Since: 2.26
  **/
 GVariant *
-g_variant_new_strv (const gchar * const *strv,
-                    gssize               length)
+g_variant_new_bytestring_array (const gchar * const *strv,
+                                gssize               length)
 {
   GVariant **strings;
   gsize i;
@@ -1271,43 +1418,40 @@ g_variant_new_strv (const gchar * const *strv,
 
   strings = g_new (GVariant *, length);
   for (i = 0; i < length; i++)
-    strings[i] = g_variant_ref_sink (g_variant_new_string (strv[i]));
+    strings[i] = g_variant_ref_sink (g_variant_new_bytestring (strv[i]));
 
-  return g_variant_new_from_children (G_VARIANT_TYPE ("as"),
+  return g_variant_new_from_children (G_VARIANT_TYPE_BYTESTRING_ARRAY,
                                       strings, length, TRUE);
 }
 
 /**
- * g_variant_get_strv:
- * @value: an array of strings #GVariant
+ * g_variant_get_bytestring_array:
+ * @value: an array of array of bytes #GVariant ('aay')
  * @length: (allow-none): the length of the result, or %NULL
  * @returns: (array length=length): an array of constant strings
  *
- * Gets the contents of an array of strings #GVariant.  This call
+ * Gets the contents of an array of array of bytes #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
+ * 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.
  *
- * Since: 2.24
+ * Since: 2.26
  **/
 const gchar **
-g_variant_get_strv (GVariant *value,
-                    gsize    *length)
+g_variant_get_bytestring_array (GVariant *value,
+                                gsize    *length)
 {
   const gchar **strv;
   gsize n;
   gsize i;
 
-  g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("as")) ||
-                        g_variant_is_of_type (value, G_VARIANT_TYPE ("ao")) ||
-                        g_variant_is_of_type (value, G_VARIANT_TYPE ("ag")),
-                        NULL);
+  TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL);
 
   g_variant_get_data (value);
   n = g_variant_n_children (value);
@@ -1318,7 +1462,7 @@ g_variant_get_strv (GVariant *value,
       GVariant *string;
 
       string = g_variant_get_child_value (value, i);
-      strv[i] = g_variant_get_string (string, NULL);
+      strv[i] = g_variant_get_bytestring (string);
       g_variant_unref (string);
     }
   strv[i] = NULL;
@@ -1330,37 +1474,35 @@ g_variant_get_strv (GVariant *value,
 }
 
 /**
- * g_variant_dup_strv:
- * @value: an array of strings #GVariant
+ * g_variant_dup_bytestring_array:
+ * @value: an array of array of bytes #GVariant ('aay')
  * @length: (allow-none): the length of the result, or %NULL
- * @returns: (array length=length): an array of constant strings
+ * @returns: (array length=length): an array of strings
  *
- * Gets the contents of an array of strings #GVariant.  This call
+ * Gets the contents of an array of array of bytes #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
+ * 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.
  *
- * Since: 2.24
+ * Since: 2.26
  **/
 gchar **
-g_variant_dup_strv (GVariant *value,
-                    gsize    *length)
+g_variant_dup_bytestring_array (GVariant *value,
+                                gsize    *length)
 {
   gchar **strv;
   gsize n;
   gsize i;
 
-  g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("as")) ||
-                        g_variant_is_of_type (value, G_VARIANT_TYPE ("ao")) ||
-                        g_variant_is_of_type (value, G_VARIANT_TYPE ("ag")),
-                        NULL);
+  TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL);
 
+  g_variant_get_data (value);
   n = g_variant_n_children (value);
   strv = g_new (gchar *, n + 1);
 
@@ -1369,7 +1511,7 @@ g_variant_dup_strv (GVariant *value,
       GVariant *string;
 
       string = g_variant_get_child_value (value, i);
-      strv[i] = g_variant_dup_string (string, NULL);
+      strv[i] = g_variant_dup_bytestring (string, NULL);
       g_variant_unref (string);
     }
   strv[i] = NULL;
@@ -1573,6 +1715,45 @@ g_variant_print_string (GVariant *value,
     case G_VARIANT_CLASS_ARRAY:
       /* it's an array so the first character of the type string is 'a'
        *
+       * if the first two characters are 'ay' then it's a bytestring.
+       * under certain conditions we print those as strings.
+       */
+      if (g_variant_get_type_string (value)[1] == 'y')
+        {
+          const gchar *str;
+          gsize size;
+          gsize i;
+
+          /* first determine if it is a byte string.
+           * that's when there's a single nul character: at the end.
+           */
+          str = g_variant_get_data (value);
+          size = g_variant_get_size (value);
+
+          for (i = 0; i < size; i++)
+            if (str[i] == '\0')
+              break;
+
+          /* first nul byte is the last byte -> it's a byte string. */
+          if (i == size - 1)
+            {
+              gchar *escaped = g_strescape (str, NULL);
+
+              /* use double quotes only if a ' is in the string */
+              if (strchr (str, '\''))
+                g_string_append_printf (string, "b\"%s\"", escaped);
+              else
+                g_string_append_printf (string, "b'%s'", escaped);
+
+              g_free (escaped);
+              break;
+            }
+
+          else
+            /* fall through and handle normally... */;
+        }
+
+      /*
        * if the first two characters are 'a{' then it's an array of
        * dictionary entries (ie: a dictionary) so we print that
        * differently.
@@ -3015,19 +3196,43 @@ g_variant_format_string_scan (const gchar  *string,
 
       break;
 
-    case '^': /* '^as' or '^a&s' only */
-      if (next_char() != 'a')
-        return FALSE;
+    case '^':
+      if ((c = next_char()) == 'a')
+        {
+          if ((c = next_char()) == '&')
+            {
+              if ((c = next_char()) == 'a')
+                {
+                  if ((c = next_char()) == 'y')
+                    break;      /* '^a&ay' */
+                }
+
+              else if (c == 's')
+                break;          /* '^a&s' */
+            }
 
-      if (peek_char() == '&')
-        next_char ();
+          else if (c == 'a')
+            {
+              if ((c = next_char()) == 'y')
+                break;          /* '^aay' */
+            }
 
-      c = next_char ();
+          else if (c == 's')
+            break;              /* '^as' */
 
-      if (c != 's' && c != 'o' && c != 'g')
-        return FALSE;
+          else if (c == 'y')
+            break;              /* '^ay' */
+        }
+      else if (c == '&')
+        {
+          if ((c = next_char()) == 'a')
+            {
+              if ((c = next_char()) == 'y')
+                break;          /* '^&ay' */
+            }
+        }
 
-      break;
+      return FALSE;
 
     case '&':
       c = next_char();
@@ -3242,6 +3447,29 @@ g_variant_valist_free_nnp (const gchar *str,
     }
 }
 
+static gchar
+g_variant_scan_convenience (const gchar **str,
+                            gboolean     *constant,
+                            guint        *arrays)
+{
+  *constant = FALSE;
+  *arrays = 0;
+
+  for (;;)
+    {
+      char c = *(*str)++;
+
+      if (c == '&')
+        *constant = TRUE;
+
+      else if (c == 'a')
+        (*arrays)++;
+
+      else
+        return c;
+    }
+}
+
 static GVariant *
 g_variant_valist_new_nnp (const gchar **str,
                           gpointer      ptr)
@@ -3288,31 +3516,16 @@ g_variant_valist_new_nnp (const gchar **str,
 
     case '^':
       {
-        const GVariantType *type;
-        GVariantType *array_type;
-        GVariant **children;
-        gchar **strv = ptr;
-        GVariant *value;
-        guint length, i;
+        gboolean constant;
+        guint arrays;
 
-        if ((*str)[1] == '&')    /* '^a&s' */
-          (*str) += 2;
-        else                     /* '^as' */
-          (*str)++;
+        if (g_variant_scan_convenience (str, &constant, &arrays) == 's')
+          return g_variant_new_strv (ptr, -1);
 
-        type = (GVariantType *) (*str)++;
-        array_type = g_variant_type_new_array (type);
-        length = g_strv_length (strv);
-        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));
+        if (arrays > 1)
+          return g_variant_new_bytestring_array (ptr, -1);
 
-        value = g_variant_new_from_children (array_type, children,
-                                             length, TRUE);
-        g_variant_type_free (array_type);
-
-        return value;
+        return g_variant_new_bytestring (ptr);
       }
 
     case '@':
@@ -3373,16 +3586,34 @@ g_variant_valist_get_nnp (const gchar **str,
       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);
-        }
+      {
+        gboolean constant;
+        guint arrays;
+
+        if (g_variant_scan_convenience (str, &constant, &arrays) == 's')
+          {
+            if (constant)
+              return g_variant_get_strv (value, NULL);
+            else
+              return g_variant_dup_strv (value, NULL);
+          }
+
+        else if (arrays > 1)
+          {
+            if (constant)
+              return g_variant_get_bytestring_array (value, NULL);
+            else
+              return g_variant_dup_bytestring_array (value, NULL);
+          }
+
+        else
+          {
+            if (constant)
+              return (gchar *) g_variant_get_bytestring (value);
+            else
+              return g_variant_dup_bytestring (value, NULL);
+          }
+      }
 
     case '@':
       g_variant_type_string_scan (*str, NULL, str);
diff --git a/glib/gvariant.h b/glib/gvariant.h
index aa270f6..528492b 100644
--- a/glib/gvariant.h
+++ b/glib/gvariant.h
@@ -83,10 +83,11 @@ gboolean                        g_variant_is_object_path                (const g
 GVariant *                      g_variant_new_signature                 (const gchar          *signature);
 gboolean                        g_variant_is_signature                  (const gchar          *string);
 GVariant *                      g_variant_new_variant                   (GVariant             *value);
-GVariant *                      g_variant_new_byte_array                (gconstpointer         array,
-                                                                         gssize                length);
 GVariant *                      g_variant_new_strv                      (const gchar * const  *strv,
                                                                          gssize                length);
+GVariant *                      g_variant_new_bytestring                (const gchar          *string);
+GVariant *                      g_variant_new_bytestring_array          (const gchar * const  *strv,
+                                                                         gssize                length);
 
 gboolean                        g_variant_get_boolean                   (GVariant             *value);
 guchar                          g_variant_get_byte                      (GVariant             *value);
@@ -103,12 +104,17 @@ const gchar *                   g_variant_get_string                    (GVarian
                                                                          gsize                *length);
 gchar *                         g_variant_dup_string                    (GVariant             *value,
                                                                          gsize                *length);
-gconstpointer                   g_variant_get_byte_array                (GVariant             *value,
-                                                                         gsize                *length);
 const gchar **                  g_variant_get_strv                      (GVariant             *value,
                                                                          gsize                *length);
 gchar **                        g_variant_dup_strv                      (GVariant             *value,
                                                                          gsize                *length);
+const gchar *                   g_variant_get_bytestring                (GVariant             *value);
+gchar *                         g_variant_dup_bytestring                (GVariant             *value,
+                                                                         gsize                *length);
+const gchar **                  g_variant_get_bytestring_array          (GVariant             *value,
+                                                                         gsize                *length);
+gchar **                        g_variant_dup_bytestring_array          (GVariant             *value,
+                                                                         gsize                *length);
 
 GVariant *                      g_variant_new_maybe                     (const GVariantType   *child_type,
                                                                          GVariant             *child);
diff --git a/glib/gvarianttype.h b/glib/gvarianttype.h
index 2203ccb..124fa46 100644
--- a/glib/gvarianttype.h
+++ b/glib/gvarianttype.h
@@ -232,6 +232,31 @@ typedef struct _GVariantType GVariantType;
 #define G_VARIANT_TYPE_DICTIONARY           ((const GVariantType *) "a{?*}")
 
 /**
+ * G_VARIANT_TYPE_STRING_ARRAY:
+ *
+ * The type of an array of strings.
+ **/
+#define G_VARIANT_TYPE_STRING_ARRAY         ((const GVariantType *) "as")
+
+/**
+ * G_VARIANT_TYPE_BYTESTRING:
+ *
+ * The type of an array of bytes.  This type is commonly used to pass
+ * around strings that may not be valid utf8.  In that case, the
+ * convention is that the nul terminator character should be included as
+ * the last character in the array.
+ **/
+#define G_VARIANT_TYPE_BYTESTRING           ((const GVariantType *) "ay")
+
+/**
+ * G_VARIANT_TYPE_BYTESTRING_ARRAY:
+ *
+ * The type of an array of byte strings (an array of arrays of bytes).
+ **/
+#define G_VARIANT_TYPE_BYTESTRING_ARRAY     ((const GVariantType *) "aay")
+
+
+/**
  * G_VARIANT_TYPE:
  * @type_string: a well-formed #GVariantType type string
  *
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index 23302f8..7da3830 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -2981,16 +2981,16 @@ test_varargs (void)
     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_builder_init (&builder, G_VARIANT_TYPE ("as"));
+    g_variant_builder_add (&builder, "s", "/foo");
+    g_variant_builder_add (&builder, "s", "/bar");
+    g_variant_builder_add (&builder, "s", "/baz");
+    value = g_variant_new("(as^as^a&s)", &builder, strvector, strvector);
     g_variant_iter_init (&tuple, value);
-    g_variant_iter_next (&tuple, "ao", &array);
+    g_variant_iter_next (&tuple, "as", &array);
 
     i = 0;
-    while (g_variant_iter_loop (array, "o", &str))
+    while (g_variant_iter_loop (array, "s", &str))
       g_assert_cmpstr (str, ==, test_strs[i++]);
     g_assert (i == 3);
 
@@ -2998,17 +2998,17 @@ test_varargs (void)
 
     /* start over */
     g_variant_iter_init (&tuple, value);
-    g_variant_iter_next (&tuple, "ao", &array);
+    g_variant_iter_next (&tuple, "as", &array);
 
     i = 0;
-    while (g_variant_iter_loop (array, "&o", &str))
+    while (g_variant_iter_loop (array, "&s", &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_variant_iter_next (&tuple, "^a&s", &strv);
+    g_variant_iter_next (&tuple, "^as", &my_strv);
 
     g_assert_cmpstr (strv[0], ==, "/hello");
     g_assert_cmpstr (strv[1], ==, "/world");
@@ -3033,46 +3033,46 @@ test_varargs (void)
     gchar **strv;
     gint i;
 
-    g_variant_builder_init (&builder, G_VARIANT_TYPE ("aag"));
-    g_variant_builder_open (&builder, G_VARIANT_TYPE ("ag"));
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("aas"));
+    g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
     for (i = 0; i < 6; i++)
       if (i & 1)
-        g_variant_builder_add (&builder, "g", strvector[i]);
+        g_variant_builder_add (&builder, "s", strvector[i]);
       else
-        g_variant_builder_add (&builder, "&g", strvector[i]);
+        g_variant_builder_add (&builder, "&s", 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_builder_add (&builder, "^as", strvector);
+    g_variant_builder_add (&builder, "^as", strvector);
+    value = g_variant_new ("aas", &builder);
 
     g_variant_iter_init (&iter, value);
-    while (g_variant_iter_loop (&iter, "^ag", &strv))
+    while (g_variant_iter_loop (&iter, "^as", &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))
+    while (g_variant_iter_loop (&iter, "^a&s", &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))
+    while (g_variant_iter_loop (&iter, "as", &i2))
       {
         gchar *str;
 
         i = 0;
-        while (g_variant_iter_loop (i2, "g", &str))
+        while (g_variant_iter_loop (i2, "s", &str))
           g_assert_cmpstr (str, ==, strvector[i++]);
         g_assert (i == 6);
       }
 
     g_variant_iter_init (&iter, value);
     i3 = g_variant_iter_copy (&iter);
-    while (g_variant_iter_loop (&iter, "@ag", &sub))
+    while (g_variant_iter_loop (&iter, "@as", &sub))
       {
         gchar *str = g_variant_print (sub, TRUE);
         g_assert_cmpstr (str, ==,
-                         "[signature 'i', 'ii', 'iii', 'iv', 'v', 'vi']");
+                         "['i', 'ii', 'iii', 'iv', 'v', 'vi']");
         g_free (str);
       }
 
@@ -3087,7 +3087,7 @@ test_varargs (void)
       {
         gchar *str = g_variant_print (sub, TRUE);
         g_assert_cmpstr (str, ==,
-                         "[signature 'i', 'ii', 'iii', 'iv', 'v', 'vi']");
+                         "['i', 'ii', 'iii', 'iv', 'v', 'vi']");
         g_free (str);
       }
 
@@ -3104,11 +3104,11 @@ test_varargs (void)
             const gchar *str = NULL;
             GVariant *cval;
 
-            g_variant_get_child (sub, j, "&g", &str);
+            g_variant_get_child (sub, j, "&s", &str);
             g_assert_cmpstr (str, ==, strvector[j]);
 
             cval = g_variant_get_child_value (sub, j);
-            g_variant_get (cval, "&g", &str);
+            g_variant_get (cval, "&s", &str);
             g_assert_cmpstr (str, ==, strvector[j]);
             g_variant_unref (cval);
           }
@@ -3812,6 +3812,80 @@ test_floating (void)
   g_variant_unref (value);
 }
 
+static void
+test_bytestring (void)
+{
+  const gchar *test_string = "foo,bar,baz,quux,\xffoooo";
+  GVariant *value;
+  gchar **strv;
+  gchar *str;
+
+  strv = g_strsplit (test_string, ",", 0);
+
+  value = g_variant_new_bytestring_array ((const gchar **) strv, -1);
+  g_assert (g_variant_is_floating (value));
+  g_strfreev (strv);
+
+  str = g_variant_print (value, FALSE);
+  g_variant_unref (value);
+
+  value = g_variant_parse (NULL, str, NULL, NULL, NULL);
+  g_free (str);
+
+  strv = g_variant_dup_bytestring_array (value, NULL);
+  g_variant_unref (value);
+
+  str = g_strjoinv (",", strv);
+  g_strfreev (strv);
+
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  strv = g_strsplit (test_string, ",", 0);
+  value = g_variant_new ("(^aay^a&ay^ay^&ay)",
+                         strv, strv, strv[0], strv[0]);
+  g_strfreev (strv);
+
+  g_variant_get_child (value, 0, "^a&ay", &strv);
+  str = g_strjoinv (",", strv);
+  g_free (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 0, "^aay", &strv);
+  str = g_strjoinv (",", strv);
+  g_strfreev (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 1, "^a&ay", &strv);
+  str = g_strjoinv (",", strv);
+  g_free (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 1, "^aay", &strv);
+  str = g_strjoinv (",", strv);
+  g_strfreev (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 2, "^ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+  g_free (str);
+
+  g_variant_get_child (value, 2, "^&ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+
+  g_variant_get_child (value, 3, "^ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+  g_free (str);
+
+  g_variant_get_child (value, 3, "^&ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+  g_variant_unref (value);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -3851,6 +3925,7 @@ main (int argc, char **argv)
   g_test_add_func ("/gvariant/parse-failures", test_parse_failures);
   g_test_add_func ("/gvariant/parse-positional", test_parse_positional);
   g_test_add_func ("/gvariant/floating", test_floating);
+  g_test_add_func ("/gvariant/bytestring", test_bytestring);
 
   return g_test_run ();
 }



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