GVariant: add support for single precision floats

Add a new type 'f' to correspond to single precision floating point
values.

This type was never added to D-Bus for two reasons:

 1) there is no benefit to using float rather than doubles as parameters
    for RPC

 2) classically, you shouldn't move bulk data over D-Bus

Now that we've decided that we want to use D-Bus for bulk data
transfers, it makes a good deal of sense to want to send an array of
floats or an array of fixed-sized tuples containing floats.

https://bugzilla.gnome.org/show_bug.cgi?id=740897
This commit is contained in:
Ryan Lortie
2014-11-29 14:07:46 -05:00
parent 04240e4154
commit 37a2fdf134
8 changed files with 169 additions and 36 deletions

View File

@@ -224,6 +224,7 @@
<literal>handle</literal>,
<literal>int64</literal>,
<literal>uint64</literal>,
<literal>float</literal>,
<literal>double</literal>,
<literal>string</literal>,
<literal>objectpath</literal>,
@@ -534,9 +535,9 @@
Type keywords can be seen as more verbose (and more legible) versions of a common subset of the type codes.
The type keywords <literal>boolean</literal>, <literal>byte</literal>, <literal>int16</literal>,
<literal>uint16</literal>, <literal>int32</literal>, <literal>uint32</literal>, <literal>handle</literal>,
<literal>int64</literal>, <literal>uint64</literal>, <literal>double</literal>, <literal>string</literal>,
<literal>objectpath</literal> and literal <literal>signature</literal> are each exactly equivalent to their
corresponding type code.
<literal>int64</literal>, <literal>uint64</literal>, <literal>float</literal>, <literal>double</literal>,
<literal>string</literal>, <literal>objectpath</literal> and literal <literal>signature</literal> are each
exactly equivalent to their corresponding type code.
</para>
<para>
Type codes are an <literal>@</literal> ("at" sign) followed by a definite GVariant type string. Some

View File

@@ -455,7 +455,13 @@ pattern_coalesce (const gchar *left,
(*one)++;
}
else if (**one == 'N' && strchr ("ynqiuxthd", **the_other))
else if (**one == 'N' && strchr ("ynqiuxthfd", **the_other))
{
*out++ = *(*the_other)++;
(*one)++;
}
else if (**one == 'D' && (**the_other == 'f' || **the_other == 'd'))
{
*out++ = *(*the_other)++;
(*one)++;
@@ -616,6 +622,10 @@ ast_resolve (AST *ast,
pattern[j++] = 'i';
break;
case 'D':
pattern[j++] = 'd';
break;
default:
pattern[j++] = pattern[i];
break;
@@ -1246,7 +1256,7 @@ dictionary_get_pattern (AST *ast,
/* the basic types,
* plus undetermined number type and undetermined string type.
*/
if (!strchr ("bynqiuxthdsogNS", key_char))
if (!strchr ("bynqiuxthfdsogNDS", key_char))
{
ast_set_error (ast, error, NULL,
G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED,
@@ -1792,7 +1802,7 @@ number_get_pattern (AST *ast,
(!g_str_has_prefix (number->token, "0x") && strchr (number->token, 'e')) ||
strstr (number->token, "inf") ||
strstr (number->token, "nan"))
return g_strdup ("Md");
return g_strdup ("MD");
return g_strdup ("MN");
}
@@ -1817,17 +1827,16 @@ number_get_value (AST *ast,
Number *number = (Number *) ast;
const gchar *token;
gboolean negative;
gboolean floating;
guint64 abs_val;
gdouble dbl_val;
gchar typechar;
gchar *end;
typechar = *g_variant_type_peek_string (type);
token = number->token;
if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
if (typechar == 'f' || typechar == 'd')
{
floating = TRUE;
errno = 0;
dbl_val = g_ascii_strtod (token, &end);
if (dbl_val != 0.0 && errno == ERANGE)
@@ -1844,7 +1853,6 @@ number_get_value (AST *ast,
}
else
{
floating = FALSE;
negative = token[0] == '-';
if (token[0] == '-')
token++;
@@ -1880,10 +1888,7 @@ number_get_value (AST *ast,
return NULL;
}
if (floating)
return g_variant_new_double (dbl_val);
switch (*g_variant_type_peek_string (type))
switch (typechar)
{
case 'y':
if (negative || abs_val > G_MAXUINT8)
@@ -1925,6 +1930,12 @@ number_get_value (AST *ast,
return number_overflow (ast, type, error);
return g_variant_new_handle (negative ? -abs_val : abs_val);
case 'f':
return g_variant_new_float (dbl_val);
case 'd':
return g_variant_new_double (dbl_val);
default:
return ast_type_error (ast, type, error);
}
@@ -2210,6 +2221,9 @@ typedecl_parse (TokenStream *stream,
else if (token_stream_consume (stream, "uint64"))
type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
else if (token_stream_consume (stream, "float"))
type = g_variant_type_copy (G_VARIANT_TYPE_FLOAT);
else if (token_stream_consume (stream, "double"))
type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);

View File

@@ -112,7 +112,7 @@
* endianness, or of the length or type of the top-level variant.
*
* The amount of memory required to store a boolean is 1 byte. 16,
* 32 and 64 bit integers and double precision floating point numbers
* 32 and 64 bit integers and floating point numbers
* use their "natural" size. Strings (including object path and
* signature strings) are stored with a nul terminator, and as such
* use the length of the string plus 1 byte.
@@ -342,7 +342,7 @@ g_variant_get_boolean (GVariant *value)
}
/* the constructors and accessors for byte, int{16,32,64}, handles and
* doubles all look pretty much exactly the same, so we reduce
* floats all look pretty much exactly the same, so we reduce
* copy/pasting here.
*/
#define NUMERIC_TYPE(TYPE, type, ctype) \
@@ -566,6 +566,31 @@ NUMERIC_TYPE (UINT64, uint64, guint64)
**/
NUMERIC_TYPE (HANDLE, handle, gint32)
/**
* g_variant_new_float:
* @value: a #gfloat floating point value
*
* Creates a new float #GVariant instance.
*
* Returns: (transfer none): a floating reference to a new float #GVariant instance
*
* Since: 2.44
**/
/**
* g_variant_get_float:
* @value: a float #GVariant instance
*
* Returns the single precision floating point value of @value.
*
* It is an error to call this function with a @value of any type
* other than %G_VARIANT_TYPE_FLOAT.
*
* Returns: a #gfloat
*
* Since: 2.44
**/
NUMERIC_TYPE (FLOAT, float, gfloat)
/**
* g_variant_new_double:
* @value: a #gdouble floating point value
@@ -1081,6 +1106,7 @@ g_variant_lookup_value (GVariant *dictionary,
* - %G_VARIANT_TYPE_BOOLEAN: #guchar (not #gboolean!)
* - %G_VARIANT_TYPE_BYTE: #guchar
* - %G_VARIANT_TYPE_HANDLE: #guint32
* - %G_VARIANT_TYPE_FLOAT: #gfloat
* - %G_VARIANT_TYPE_DOUBLE: #gdouble
*
* For example, if calling this function for an array of 32-bit integers,
@@ -2113,6 +2139,8 @@ g_variant_is_container (GVariant *value)
* @G_VARIANT_CLASS_INT64: The #GVariant is a signed 64 bit integer.
* @G_VARIANT_CLASS_UINT64: The #GVariant is an unsigned 64 bit integer.
* @G_VARIANT_CLASS_HANDLE: The #GVariant is a file handle index.
* @G_VARIANT_CLASS_FLOAT: The #GVariant is a single precision floating
* point value.
* @G_VARIANT_CLASS_DOUBLE: The #GVariant is a double precision floating
* point value.
* @G_VARIANT_CLASS_STRING: The #GVariant is a normal string.
@@ -2516,6 +2544,32 @@ g_variant_print_string (GVariant *value,
g_variant_get_uint64 (value));
break;
case G_VARIANT_CLASS_FLOAT:
{
gchar buffer[100];
gint i;
g_ascii_dtostr (buffer, sizeof buffer, g_variant_get_float (value));
for (i = 0; buffer[i]; i++)
if (buffer[i] == '.' || buffer[i] == 'e' ||
buffer[i] == 'n' || buffer[i] == 'N')
break;
/* if there is no '.' or 'e' in the float then add one */
if (buffer[i] == '\0')
{
buffer[i++] = '.';
buffer[i++] = '0';
buffer[i++] = '\0';
}
if (type_annotate)
g_string_append (string, "float ");
g_string_append (string, buffer);
}
break;
case G_VARIANT_CLASS_DOUBLE:
{
gchar buffer[100];
@@ -2640,6 +2694,7 @@ g_variant_hash (gconstpointer value_)
case G_VARIANT_CLASS_INT32:
case G_VARIANT_CLASS_UINT32:
case G_VARIANT_CLASS_HANDLE:
case G_VARIANT_CLASS_FLOAT:
{
const guint *ptr;
@@ -2759,7 +2814,7 @@ g_variant_equal (gconstpointer one,
* two values that have types that are not exactly equal. For example,
* you cannot compare a 32-bit signed integer with a 32-bit unsigned
* integer. Also note that this function is not particularly
* well-behaved when it comes to comparison of doubles; in particular,
* well-behaved when it comes to comparison of floats; in particular,
* the handling of incomparable values (ie: NaN) is undefined.
*
* If you only require an equality comparison, g_variant_equal() is more
@@ -2830,6 +2885,14 @@ g_variant_compare (gconstpointer one,
return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
}
case G_VARIANT_CLASS_FLOAT:
{
gfloat a_val = g_variant_get_float (a);
gfloat b_val = g_variant_get_float (b);
return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
}
case G_VARIANT_CLASS_DOUBLE:
{
gdouble a_val = g_variant_get_double (a);
@@ -4165,8 +4228,8 @@ g_variant_format_string_scan (const gchar *string,
switch (next_char())
{
case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
case 'x': case 't': case 'h': case 'd': case 's': case 'o':
case 'g': case 'v': case '*': case '?': case 'r':
case 'x': case 't': case 'h': case 'f': case 'd': case 's':
case 'o': case 'g': case 'v': case '*': case '?': case 'r':
break;
case 'm':
@@ -4829,6 +4892,7 @@ g_variant_valist_skip_leaf (const gchar **str,
va_arg (*app, guint64);
return;
case 'f':
case 'd':
va_arg (*app, gdouble);
return;
@@ -4874,6 +4938,9 @@ g_variant_valist_new_leaf (const gchar **str,
case 'h':
return g_variant_new_handle (va_arg (*app, gint));
case 'f':
return g_variant_new_float (va_arg (*app, gdouble));
case 'd':
return g_variant_new_double (va_arg (*app, gdouble));
@@ -4884,6 +4951,7 @@ g_variant_valist_new_leaf (const gchar **str,
/* The code below assumes this */
G_STATIC_ASSERT (sizeof (gboolean) == sizeof (guint32));
G_STATIC_ASSERT (sizeof (gfloat) == sizeof (guint32));
G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
static void
@@ -4957,6 +5025,10 @@ g_variant_valist_get_leaf (const gchar **str,
*(gint32 *) ptr = g_variant_get_handle (value);
return;
case 'f':
*(gfloat *) ptr = g_variant_get_float (value);
return;
case 'd':
*(gdouble *) ptr = g_variant_get_double (value);
return;
@@ -4979,6 +5051,7 @@ g_variant_valist_get_leaf (const gchar **str,
case 'u':
case 'h':
case 'b':
case 'f':
*(guint32 *) ptr = 0;
return;
@@ -5698,6 +5771,9 @@ g_variant_deep_copy (GVariant *value)
case G_VARIANT_CLASS_HANDLE:
return g_variant_new_handle (g_variant_get_handle (value));
case G_VARIANT_CLASS_FLOAT:
return g_variant_new_float (g_variant_get_float (value));
case G_VARIANT_CLASS_DOUBLE:
return g_variant_new_double (g_variant_get_double (value));
@@ -5760,8 +5836,7 @@ g_variant_get_normal_form (GVariant *value)
* Performs a byteswapping operation on the contents of @value. The
* result is that all multi-byte numeric data contained in @value is
* byteswapped. That includes 16, 32, and 64bit signed and unsigned
* integers as well as file handles and double precision floating point
* values.
* integers as well as file handles and floating point values.
*
* This function is an identity mapping on any value that does not
* contain multi-byte numeric data. That include strings, booleans,

View File

@@ -44,6 +44,7 @@ typedef enum
G_VARIANT_CLASS_INT64 = 'x',
G_VARIANT_CLASS_UINT64 = 't',
G_VARIANT_CLASS_HANDLE = 'h',
G_VARIANT_CLASS_FLOAT = 'f',
G_VARIANT_CLASS_DOUBLE = 'd',
G_VARIANT_CLASS_STRING = 's',
G_VARIANT_CLASS_OBJECT_PATH = 'o',
@@ -95,6 +96,8 @@ GLIB_AVAILABLE_IN_ALL
GVariant * g_variant_new_uint64 (guint64 value);
GLIB_AVAILABLE_IN_ALL
GVariant * g_variant_new_handle (gint32 value);
GLIB_AVAILABLE_IN_2_44
GVariant * g_variant_new_float (gfloat value);
GLIB_AVAILABLE_IN_ALL
GVariant * g_variant_new_double (gdouble value);
GLIB_AVAILABLE_IN_ALL
@@ -148,6 +151,8 @@ GLIB_AVAILABLE_IN_ALL
guint64 g_variant_get_uint64 (GVariant *value);
GLIB_AVAILABLE_IN_ALL
gint32 g_variant_get_handle (GVariant *value);
GLIB_AVAILABLE_IN_2_44
gfloat g_variant_get_float (GVariant *value);
GLIB_AVAILABLE_IN_ALL
gdouble g_variant_get_double (GVariant *value);
GLIB_AVAILABLE_IN_ALL

View File

@@ -110,7 +110,7 @@
* A basic type string describes a basic type (as per
* g_variant_type_is_basic()) and is always a single character in length.
* The valid basic type strings are "b", "y", "n", "q", "i", "u", "x", "t",
* "h", "d", "s", "o", "g" and "?".
* "h", "f", "d", "s", "o", "g" and "?".
*
* The above definition is recursive to arbitrary depth. "aaaaai" and
* "(ui(nq((y)))s)" are both valid type strings, as is
@@ -128,6 +128,8 @@
* - `h`: the type string of %G_VARIANT_TYPE_HANDLE; a signed 32 bit value
* that, by convention, is used as an index into an array of file
* descriptors that are sent alongside a D-Bus message.
* - `f`: the type string of %G_VARIANT_TYPE_FLOAT; a single precision
* floating point value.
* - `d`: the type string of %G_VARIANT_TYPE_DOUBLE; a double precision
* floating point value.
* - `s`: the type string of %G_VARIANT_TYPE_STRING; a string.
@@ -240,7 +242,7 @@ g_variant_type_string_scan (const gchar *string,
case '{':
if (string == limit || *string == '\0' || /* { */
!strchr ("bynqihuxtdsog?", *string++) || /* key */
!strchr ("bynqihuxtfdsog?", *string++) || /* key */
!g_variant_type_string_scan (string, limit, &string) || /* value */
string == limit || *string++ != '}') /* } */
return FALSE;
@@ -252,7 +254,7 @@ g_variant_type_string_scan (const gchar *string,
case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
case 'x': case 't': case 'd': case 's': case 'o': case 'g':
case 'v': case 'r': case '*': case '?': case 'h':
case 'v': case 'r': case '*': case '?': case 'h': case 'f':
break;
default:
@@ -533,8 +535,8 @@ g_variant_type_is_container (const GVariantType *type)
*
* Determines if the given @type is a basic type.
*
* Basic types are booleans, bytes, integers, doubles, strings, object
* paths and signatures.
* Basic types are booleans, bytes, integers, floats, doubles, strings,
* object paths and signatures.
*
* Only a basic type may be used as the key of a dictionary entry.
*
@@ -564,6 +566,7 @@ g_variant_type_is_basic (const GVariantType *type)
case 'u':
case 't':
case 'x':
case 'f':
case 'd':
case 's':
case 'o':

View File

@@ -103,6 +103,15 @@ typedef struct _GVariantType GVariantType;
**/
#define G_VARIANT_TYPE_UINT64 ((const GVariantType *) "t")
/**
* G_VARIANT_TYPE_FLOAT:
*
* The type of a single precision IEEE754 floating point number. You
* can store a number as large as 3.40e38 in these (plus and minus) but
* there are some gaps on the way.
**/
#define G_VARIANT_TYPE_FLOAT ((const GVariantType *) "f")
/**
* G_VARIANT_TYPE_DOUBLE:
*

View File

@@ -115,7 +115,7 @@ static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
/* 'c' */ { not_a_type },
/* 'd' */ { fixed_aligned(8) }, /* double */
/* 'e' */ { not_a_type },
/* 'f' */ { not_a_type },
/* 'f' */ { fixed_aligned(4) }, /* float */
/* 'g' */ { unaligned }, /* signature string */
/* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */
/* 'i' */ { fixed_aligned(4) }, /* int32 */
@@ -147,7 +147,7 @@ static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
* GVariantTypeInfo itself, we save a bunch of relocations.
*/
static const char g_variant_type_info_basic_chars[24][2] = {
"b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
"b", " ", "d", " ", "f", "g", "h", "i", " ", " ", " ", " ",
"n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
};

View File

@@ -19,10 +19,10 @@
#include <stdlib.h>
#include <glib.h>
#define BASIC "bynqiuxthdsog?"
#define BASIC "bynqiuxthfdsog?"
#define N_BASIC (G_N_ELEMENTS (BASIC) - 1)
#define INVALIDS "cefjklpwz&@^$"
#define INVALIDS "cejklpwz&@^$"
#define N_INVALIDS (G_N_ELEMENTS (INVALIDS) - 1)
/* see comment in gvariant-serialiser.c about this madness.
@@ -82,6 +82,8 @@ append_type_string (GString *string,
return g_variant_type_copy (G_VARIANT_TYPE_UINT64);
case 'h':
return g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
case 'f':
return g_variant_type_copy (G_VARIANT_TYPE_FLOAT);
case 'd':
return g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
case 's':
@@ -451,6 +453,8 @@ describe_type (const GVariantType *type)
result = g_strdup ("t");
else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
result = g_strdup ("h");
else if (g_variant_type_equal (type, G_VARIANT_TYPE_FLOAT))
result = g_strdup ("f");
else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
result = g_strdup ("d");
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
@@ -754,6 +758,7 @@ calculate_type_info (const GVariantType *type,
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32) ||
g_variant_type_equal (type, G_VARIANT_TYPE_UINT32) ||
g_variant_type_equal (type, G_VARIANT_TYPE_FLOAT) ||
g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
{
al = fs = 4;
@@ -1885,6 +1890,7 @@ struct _TreeInstance
union {
guint64 integer;
gfloat single;
gdouble floating;
gchar string[200];
} data;
@@ -1999,6 +2005,11 @@ tree_instance_new (const GVariantType *type,
instance->data_size = 8;
break;
case 'f':
instance->data.single = g_test_rand_double ();
instance->data_size = 4;
break;
case 'd':
instance->data.floating = g_test_rand_double ();
instance->data_size = 8;
@@ -2437,6 +2448,10 @@ tree_instance_get_gvariant (TreeInstance *tree)
result = g_variant_new_handle (tree->data.integer);
break;
case 'f':
result = g_variant_new_float (tree->data.single);
break;
case 'd':
result = g_variant_new_double (tree->data.floating);
break;
@@ -2577,6 +2592,13 @@ tree_instance_check_gvariant (TreeInstance *tree,
case 'h':
return g_variant_get_handle (value) == (gint32) tree->data.integer;
case 'f':
{
gfloat floating = g_variant_get_float (value);
return memcmp (&floating, &tree->data.single, sizeof floating) == 0;
}
case 'd':
{
gdouble floating = g_variant_get_double (value);
@@ -3664,6 +3686,7 @@ test_gv_byteswap (void)
static void
test_parser (void)
{
GError *error = NULL;
TreeInstance *tree;
GVariant *parsed;
GVariant *value;
@@ -3677,15 +3700,18 @@ test_parser (void)
pt = g_variant_print (value, TRUE);
p = g_variant_print (value, FALSE);
parsed = g_variant_parse (NULL, pt, NULL, NULL, NULL);
parsed = g_variant_parse (NULL, pt, NULL, NULL, &error);
g_assert_no_error (error);
res = g_variant_print (parsed, FALSE);
if (!strstr (pt, "float")) /* FIXME: need reliable round-trip for floats */
g_assert_cmpstr (p, ==, res);
g_variant_unref (parsed);
g_free (res);
parsed = g_variant_parse (g_variant_get_type (value), p,
NULL, NULL, NULL);
parsed = g_variant_parse (g_variant_get_type (value), p, NULL, NULL, &error);
g_assert_no_error (error);
res = g_variant_print (parsed, TRUE);
if (!strstr (pt, "float")) /* FIXME: need reliable round-trip for floats */
g_assert_cmpstr (pt, ==, res);
g_variant_unref (parsed);
g_free (res);