[glib/gvariant] Change string handling for GVariant varargs



commit febc0a74d6770a35e533e9fd415b327b24707d5e
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Jun 14 13:12:43 2009 -0400

    Change string handling for GVariant varargs
    
      format string "s" now means "allocated string" (ie: must be freed)
    
      "&s" is now how you get the string out of the serialised data
    
      also add "&as" to mean a gchar ** (free with g_strfreev) and
      "&a&s" to mean a const gchar ** (free with g_free).

 glib/gvariant-valist.c                |  145 +++++++++++++++++++++++++++-----
 glib/tests/.gitignore                 |    1 +
 glib/tests/Makefile.am                |    3 +
 glib/tests/gvariant-varargs-strings.c |   62 ++++++++++++++
 glib/tests/gvariant-varargs.c         |    8 +-
 5 files changed, 192 insertions(+), 27 deletions(-)
---
diff --git a/glib/gvariant-valist.c b/glib/gvariant-valist.c
index 16a629f..e31a11c 100644
--- a/glib/gvariant-valist.c
+++ b/glib/gvariant-valist.c
@@ -41,6 +41,11 @@
  * character.  No wildcard type is a fixed-width type.  Like '@', '&'
  * characters may not be nested.
  *
+ * Additionally, any array of fixed-width types may be prefixed with a
+ * '&'.
+ *
+ * Additionally, '&s', '&as' and '&a&s' may appear.
+ *
  * No '@' or '&' character, however, may appear as part of an array
  * type.
  *
@@ -107,6 +112,26 @@ g_variant_format_string_scan (const gchar **format_string)
         const gchar *start;
 
         start = *format_string;
+
+        if (start[0] == 's') /* '&s' */
+          {
+            *format_string = start + 1;
+            return TRUE;
+          }
+        else if (start[0] == 'a')
+          {
+            if (start[1] == 's')  /* '&as' */
+              {
+                *format_string = start + 2;
+                return TRUE;
+              }
+            else if (start[1] == '&' && start[2] == 's') /* '&a&s' */
+              {
+                *format_string = start + 3;
+                return TRUE;
+              }
+          }
+
         if (!g_variant_type_string_scan (format_string, NULL))
           return FALSE;
 
@@ -229,24 +254,53 @@ g_variant_valist_new (const gchar **format_string,
 
     case '&':
       {
-        GVariantType *type;
+        const gchar *string = (*format_string + 1);
         gconstpointer ptr;
-        GVariant *value;
-        gsize n_items;
-
-        type = g_variant_format_string_get_type (format_string);
-        g_assert (g_variant_type_is_concrete (type));
+        gint n_items = 0;
 
         ptr = va_arg (*app, gconstpointer);
-        if (g_variant_type_is_in_class (type, G_VARIANT_TYPE_CLASS_ARRAY))
-          n_items = va_arg (*app, gsize);
-        else
-          n_items = 0;
 
-        value = g_variant_load_fixed (type, ptr, n_items);
-        g_variant_type_free (type);
+        switch (*string++)
+          {
+          case 's': /* '&s' */
+            /* for building, just the same as normal 's' */
+            *format_string += 2;
+            return g_variant_new_string (ptr);
+
+          case 'a':
+            n_items = va_arg (*app, gint);
+
+            if (string[1] == 's') /* '&as' */
+              {
+                *format_string += 3;
+                return g_variant_new_strv (va_arg (*app, const gchar **), n_items);
+              }
+
+            if (string[1] == '&' && string[2] == 's') /* '&a&s' */
+              {
+                *format_string += 4;
+                return g_variant_new_strv (va_arg (*app, const gchar **), n_items);
+              }
+
+            if (n_items < 0)
+              g_error ("size of -1 can only be specified for string arrays");
+
+            /* fall through */
+
+          default:
+            {
+              GVariantType *type;
+              GVariant *value;
+
+              type = g_variant_format_string_get_type (format_string);
+              g_assert (g_variant_type_is_concrete (type));
 
-        return value;
+              value = g_variant_load_fixed (type, ptr, n_items);
+              g_variant_type_free (type);
+
+              return value;
+            }
+          }
       }
 
     case 'a':
@@ -540,12 +594,15 @@ g_variant_valist_get (GVariant     *value,
     case 'o':
     case 'g':
       {
-        const gchar **ptr = va_arg (*app, const gchar **);
+        gchar **ptr = va_arg (*app, gchar **);
 
         if (ptr)
           {
+            if (free && *ptr)
+              g_free (*ptr);
+
             if (value)
-              *ptr = g_variant_get_string (value, NULL);
+              *ptr = g_variant_dup_string (value, NULL);
             else
               *ptr = NULL;
           }
@@ -619,14 +676,30 @@ g_variant_valist_get (GVariant     *value,
 
     case '&':
       {
-        gconstpointer *ptr;
-        gsize *n_items;
+        /* this can be 'const gchar *' but also 'gchar **' so
+         * we lose a little type safety here...
+         * but srsly, it's varargs!
+         */
+        gpointer *ptr;
+
+        ptr = va_arg (*app, gpointer *);
 
-        ptr = va_arg (*app, gconstpointer *);
+        if ((*format_string)[1] == 's') /* '&s' */
+          {
+            /* cannot just get_data() like below.
+             * might not be nul-terminated */
+            if (ptr)
+              {
+                if (value)
+                  *ptr = (gpointer) g_variant_get_string (value, NULL);
+                else
+                  *ptr = NULL;
+              }
+          }
 
-        if ((*format_string)[1] == 'a') /* '&a..' */
+        else if ((*format_string)[1] == 'a') /* '&a..' */
           {
-            n_items = va_arg (*app, gsize *);
+            gint *n_items = va_arg (*app, gint *);
 
             if (n_items)
               {
@@ -639,10 +712,36 @@ g_variant_valist_get (GVariant     *value,
 
         if (ptr)
           {
-            if (value)
-              *ptr = g_variant_get_data (value);
+            if ((*format_string)[1] == 'a' &&
+                (*format_string)[2] == 's')
+              {
+                if (free && *ptr)
+                  g_strfreev (*ptr);
+
+                if (value)
+                  *ptr = g_variant_dup_strv (value, NULL);
+                else
+                  *ptr = NULL;
+              }
+            else if ((*format_string)[1] == 'a' &&
+                     (*format_string)[2] == '&' &&
+                     (*format_string)[3] == 's')
+              {
+                if (free && *ptr)
+                  g_free (*ptr);
+
+                if (value)
+                  *ptr = g_variant_get_strv (value, NULL);
+                else
+                  *ptr = NULL;
+              }
             else
-              *ptr = NULL;
+              {
+                if (value)
+                  *ptr = (gpointer) g_variant_get_data (value);
+                else
+                  *ptr = NULL;
+              }
           }
 
         g_variant_format_string_scan (format_string);
diff --git a/glib/tests/.gitignore b/glib/tests/.gitignore
index ba59ff2..93dc95b 100644
--- a/glib/tests/.gitignore
+++ b/glib/tests/.gitignore
@@ -17,3 +17,4 @@ gvariant-markup
 gvariant-random
 gvariant-serialiser
 gvariant-varargs
+gvariant-varargs-strings
diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am
index b6475c3..90b6ca1 100644
--- a/glib/tests/Makefile.am
+++ b/glib/tests/Makefile.am
@@ -65,6 +65,9 @@ gvariant_serialiser_LDADD    = $(progs_ldadd)
 TEST_PROGS     += gvariant-varargs
 gvariant_varargs_LDADD    = $(progs_ldadd)
 
+TEST_PROGS     += gvariant-varargs-strings
+gvariant_varargs_strings_LDADD    = $(progs_ldadd)
+
 TEST_PROGS         += hostutils
 hostutils_LDADD     = $(progs_ldadd)
 
diff --git a/glib/tests/gvariant-varargs-strings.c b/glib/tests/gvariant-varargs-strings.c
new file mode 100644
index 0000000..46b9573
--- /dev/null
+++ b/glib/tests/gvariant-varargs-strings.c
@@ -0,0 +1,62 @@
+#include <glib.h>
+
+static void
+check (gchar **a, const gchar *expected)
+{
+  gchar *actual;
+
+  actual = g_strjoinv (",", a);
+  g_assert_cmpstr (actual, ==, expected);
+  g_free (actual);
+}
+
+static void
+test (void)
+{
+  const gchar *array[] = {"one", "two", "three", "four", NULL};
+  GVariant *one, *two;
+  gchar **a;
+  gint num;
+
+  one = g_variant_new_strv (array, -1);
+  two = g_variant_new_strv (array, 3);
+
+  g_variant_get (one, "&as", &a, &num);
+  g_assert_cmpint (num, ==, 4);
+  check (a, "one,two,three,four");
+  g_strfreev (a);
+
+  g_variant_get (two, "&as", &a, &num);
+  g_assert_cmpint (num, ==, 3);
+  check (a, "one,two,three");
+  g_strfreev (a);
+
+  g_variant_get (one, "&a&s", &a, &num);
+  g_assert_cmpint (num, ==, 4);
+  check (a, "one,two,three,four");
+  g_free (a);
+
+  g_variant_get (two, "&a&s", &a, &num);
+  g_assert_cmpint (num, ==, 3);
+  check (a, "one,two,three");
+  g_free (a);
+
+  g_variant_unref (one);
+
+  g_variant_get (two, "&as", &a, &num);
+  g_variant_unref (two);
+
+  g_assert_cmpint (num, ==, 3);
+  check (a, "one,two,three");
+  g_strfreev (a);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/gvariant/varargs-strings", test);
+
+  return g_test_run ();
+}
diff --git a/glib/tests/gvariant-varargs.c b/glib/tests/gvariant-varargs.c
index 318d91b..f0d0621 100644
--- a/glib/tests/gvariant-varargs.c
+++ b/glib/tests/gvariant-varargs.c
@@ -47,7 +47,7 @@ make_value (void)
                          g_variant_new_object_path ("/usr/local"),
                          g_variant_new ("()"),
                          g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
-                         fixed_struct, fixed_struct, (gsize) 3,
+                         fixed_struct, fixed_struct, 3,
                          "i'm gone",
                          g_variant_new_uint16 (44444),
                          g_variant_new_uint16 (22222),
@@ -70,7 +70,7 @@ make_value (void)
                          g_variant_new_object_path ("/usr/local"),
                          g_variant_new ("()"),
                          g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
-                         fixed_struct, fixed_struct, (gsize) 3,
+                         fixed_struct, fixed_struct, 3,
                          NULL, NULL, NULL, NULL, NULL, NULL,
                          ebuilder2, NULL, NULL);
 
@@ -88,7 +88,7 @@ make_value (void)
                          g_variant_new_object_path ("/usr/local"),
                          g_variant_new ("()"),
                          g_variant_new ("(bbb)", TRUE, FALSE, TRUE),
-                         fixed_struct, fixed_struct, (gsize) 3,
+                         fixed_struct, fixed_struct, 3,
                          NULL, NULL, NULL, NULL, NULL, NULL,
                          ebuilder3, NULL, NULL);
 
@@ -100,7 +100,7 @@ test_iterate (void)
 {
   GVariant *maybe_one, *maybe_two, *maybe_three, *maybe_four, *maybe_five;
   GVariant *one, *two, *three, *four, *five, *six, *seven = (gpointer) 0xcccccccc, *eight;
-  struct structure *fixed_array; gsize fixed_array_size;
+  struct structure *fixed_array; gint fixed_array_size;
   struct structure *maybe_fixed_struct, *fixed_struct;
   const gchar *string, *maybe_string;
   GVariantIter maybe_array, array;



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