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
This commit is contained in:
Ryan Lortie 2010-07-07 10:37:16 -04:00
parent 2d2a321a4b
commit d9e90c3894
13 changed files with 851 additions and 290 deletions

View File

@ -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

View File

@ -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.
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>
<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>.
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>

View File

@ -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:

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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,23 +188,6 @@ token_stream_prepare (TokenStream *stream)
break;
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++)
if (*end == '(' || *end == '{')
brackets++;
else if ((*end == ')' || *end == '}') && !brackets--)
break;
break;
case '\'': case '"':
for (end = stream->stream + 1; end != stream->end; end++)
if (*end == stream->stream[0] || *end == '\0' ||
@ -200,6 +198,23 @@ token_stream_prepare (TokenStream *stream)
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 != ':' && *end != '>' && !g_ascii_isspace (*end);
end++)
if (*end == '(' || *end == '{')
brackets++;
else if ((*end == ')' || *end == '}') && !brackets--)
break;
break;
default:
end = stream->stream + 1;
break;
@ -224,12 +239,24 @@ token_stream_peek (TokenStream *stream,
return stream->this[0] == first_char;
}
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
@ -1539,6 +1566,128 @@ string_parse (TokenStream *stream,
return (AST *) string;
}
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;
@ -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");

View File

@ -1164,96 +1164,16 @@ g_variant_dup_string (GVariant *value,
return g_strdup (g_variant_get_string (value, length));
}
/**
* g_variant_new_byte_array:
* @array: (array length=length): a pointer to an array of bytes
* @length: the length of @array, or -1
* @returns: a new floating #GVariant instance
*
* Constructs an array of bytes #GVariant from the given array of bytes.
*
* 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.
*
* Since: 2.26
**/
GVariant *
g_variant_new_byte_array (gconstpointer array,
gssize length)
{
if (length == -1)
{
const gchar *bytes = array;
length = 0;
while (bytes[length++]);
}
return g_variant_new_from_trusted (G_VARIANT_TYPE ("ay"),
array, length);
}
/**
* g_variant_get_byte_array:
* @value: an array of bytes #GVariant
* @length: (allow-none): the length of the result, or %NULL
* @returns: (array length=length): a pointer to the byte data, or %NULL
*
* Gets the contents of an array of bytes #GVariant.
*
* 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 @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.
*
* The return value remains valid as long as @value exists.
*
* Since: 2.26
**/
gconstpointer
g_variant_get_byte_array (GVariant *value,
gsize *length)
{
gconstpointer data;
gsize size;
TYPE_CHECK (value, G_VARIANT_TYPE ("ay"), NULL);
data = 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;
}
else
*length = size;
return data;
}
/**
* g_variant_new_strv:
* @strv: an array of strings
* @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
* 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
**/
@ -1273,7 +1193,7 @@ g_variant_new_strv (const gchar * const *strv,
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 ("as"),
return g_variant_new_from_children (G_VARIANT_TYPE_STRING_ARRAY,
strings, length, TRUE);
}
@ -1304,10 +1224,7 @@ g_variant_get_strv (GVariant *value,
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_STRING_ARRAY, NULL);
g_variant_get_data (value);
n = g_variant_n_children (value);
@ -1333,7 +1250,7 @@ g_variant_get_strv (GVariant *value,
* g_variant_dup_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
* @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
@ -1356,10 +1273,7 @@ g_variant_dup_strv (GVariant *value,
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_STRING_ARRAY, NULL);
n = g_variant_n_children (value);
strv = g_new (gchar *, n + 1);
@ -1380,6 +1294,234 @@ g_variant_dup_strv (GVariant *value,
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.
*
* 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 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.
*
* 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
**/
const gchar *
g_variant_get_bytestring (GVariant *value)
{
const gchar *string;
gsize size;
TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING, NULL);
/* Won't be NULL since this is an array type */
string = g_variant_get_data (value);
size = g_variant_get_size (value);
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 g_memdup (original, size + 1);
}
/**
* g_variant_new_bytestring_array:
* @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 bytestring #GVariant from the given array of
* strings.
*
* If @length is -1 then @strv is %NULL-terminated.
*
* Since: 2.26
**/
GVariant *
g_variant_new_bytestring_array (const gchar * const *strv,
gssize length)
{
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_bytestring (strv[i]));
return g_variant_new_from_children (G_VARIANT_TYPE_BYTESTRING_ARRAY,
strings, length, TRUE);
}
/**
* 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 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
* %NULL-terminated.
*
* For an empty array, @length will be set to 0 and a pointer to a
* %NULL pointer will be returned.
*
* Since: 2.26
**/
const gchar **
g_variant_get_bytestring_array (GVariant *value,
gsize *length)
{
const gchar **strv;
gsize n;
gsize i;
TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING_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++)
{
GVariant *string;
string = g_variant_get_child_value (value, i);
strv[i] = g_variant_get_bytestring (string);
g_variant_unref (string);
}
strv[i] = NULL;
if (length)
*length = n;
return strv;
}
/**
* 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 strings
*
* 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
* %NULL-terminated.
*
* For an empty array, @length will be set to 0 and a pointer to a
* %NULL pointer will be returned.
*
* Since: 2.26
**/
gchar **
g_variant_dup_bytestring_array (GVariant *value,
gsize *length)
{
gchar **strv;
gsize n;
gsize i;
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);
for (i = 0; i < n; i++)
{
GVariant *string;
string = g_variant_get_child_value (value, i);
strv[i] = g_variant_dup_bytestring (string, NULL);
g_variant_unref (string);
}
strv[i] = NULL;
if (length)
*length = n;
return strv;
}
/* Type checking and querying {{{1 */
/**
* g_variant_get_type:
@ -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' */
}
if (peek_char() == '&')
next_char ();
else if (c == 's')
break; /* '^a&s' */
}
c = next_char ();
else if (c == 'a')
{
if ((c = next_char()) == 'y')
break; /* '^aay' */
}
if (c != 's' && c != 'o' && c != 'g')
return FALSE;
else if (c == 's')
break; /* '^as' */
break;
else if (c == 'y')
break; /* '^ay' */
}
else if (c == '&')
{
if ((c = next_char()) == 'a')
{
if ((c = next_char()) == 'y')
break; /* '^&ay' */
}
}
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);

View File

@ -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);

View File

@ -231,6 +231,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

View File

@ -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 ();
}