gstvalue: expose gst_value_deserialize_with_pspec()

Typing hints can only be passed to gst_value_deserialize()
through the type of the passed-in value. This means deserialization
can only target the desired type for the top-level elements,
making it for example impossible to deserialize an array of
flags to the expected type.

This commit exposes a new function, gst_value_deserialize_full(),
that takes an optional pspec as the extra parameter, and updates
the deserialization code to pass around that pspec, or the
element_spec when recursively parsing the elements of a list-type
value.

This allows for example passing arrays of flags through the
command line or gst_util_set_object_arg, eg:

foo="<bar,bar+baz>"

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/629>
This commit is contained in:
Mathieu Duponchelle 2020-09-17 20:44:43 +02:00
parent 220ce9c3fd
commit acdb4ce03d
7 changed files with 224 additions and 33 deletions

View file

@ -172,7 +172,7 @@ G_GNUC_INTERNAL const char * _priv_gst_value_gtype_to_abbr (GType type);
G_GNUC_INTERNAL gboolean _priv_gst_value_parse_string (gchar * s, gchar ** end, gchar ** next, gboolean unescape); G_GNUC_INTERNAL gboolean _priv_gst_value_parse_string (gchar * s, gchar ** end, gchar ** next, gboolean unescape);
G_GNUC_INTERNAL gboolean _priv_gst_value_parse_simple_string (gchar * str, gchar ** end); G_GNUC_INTERNAL gboolean _priv_gst_value_parse_simple_string (gchar * str, gchar ** end);
G_GNUC_INTERNAL gboolean _priv_gst_value_parse_value (gchar * str, gchar ** after, GValue * value, GType default_type); G_GNUC_INTERNAL gboolean _priv_gst_value_parse_value (gchar * str, gchar ** after, GValue * value, GType default_type, GParamSpec *pspec);
G_GNUC_INTERNAL gchar * _priv_gst_value_serialize_any_list (const GValue * value, const gchar * begin, const gchar * end, gboolean print_type); G_GNUC_INTERNAL gchar * _priv_gst_value_serialize_any_list (const GValue * value, const gchar * begin, const gchar * end, gboolean print_type);
/* Used in GstBin for manual state handling */ /* Used in GstBin for manual state handling */

View file

@ -2204,7 +2204,7 @@ gst_structure_parse_field (gchar * str,
*name_end = c; *name_end = c;
if (G_UNLIKELY (!_priv_gst_value_parse_value (s, &s, &field->value, if (G_UNLIKELY (!_priv_gst_value_parse_value (s, &s, &field->value,
G_TYPE_INVALID))) { G_TYPE_INVALID, NULL))) {
GST_WARNING ("failed to parse value %s", str); GST_WARNING ("failed to parse value %s", str);
return FALSE; return FALSE;
} }

View file

@ -213,7 +213,7 @@ gst_util_set_object_arg (GObject * object, const gchar * name,
goto done; goto done;
} }
if (!gst_value_deserialize (&v, value)) if (!gst_value_deserialize_with_pspec (&v, value, pspec))
return; return;
done: done:

View file

@ -98,9 +98,9 @@ static void gst_value_register_subtract_func (GType minuend_type,
GType subtrahend_type, GstValueSubtractFunc func); GType subtrahend_type, GstValueSubtractFunc func);
static gboolean _priv_gst_value_parse_list (gchar * s, gchar ** after, static gboolean _priv_gst_value_parse_list (gchar * s, gchar ** after,
GValue * value, GType type); GValue * value, GType type, GParamSpec * pspec);
static gboolean _priv_gst_value_parse_array (gchar * s, gchar ** after, static gboolean _priv_gst_value_parse_array (gchar * s, gchar ** after,
GValue * value, GType type); GValue * value, GType type, GParamSpec * pspec);
typedef struct _GstValueUnionInfo GstValueUnionInfo; typedef struct _GstValueUnionInfo GstValueUnionInfo;
struct _GstValueUnionInfo struct _GstValueUnionInfo
@ -1244,10 +1244,11 @@ gst_value_serialize_value_list (const GValue * value)
} }
static gboolean static gboolean
gst_value_deserialize_value_list (GValue * dest, const gchar * s) gst_value_deserialize_value_list (GValue * dest, const gchar * s,
GParamSpec * pspec)
{ {
gchar *s2 = (gchar *) s; gchar *s2 = (gchar *) s;
return _priv_gst_value_parse_list (s2, &s2, dest, G_TYPE_INVALID); return _priv_gst_value_parse_list (s2, &s2, dest, G_TYPE_INVALID, pspec);
} }
static gchar * static gchar *
@ -1257,10 +1258,11 @@ gst_value_serialize_value_array (const GValue * value)
} }
static gboolean static gboolean
gst_value_deserialize_value_array (GValue * dest, const gchar * s) gst_value_deserialize_value_array (GValue * dest, const gchar * s,
GParamSpec * pspec)
{ {
gchar *s2 = (gchar *) s; gchar *s2 = (gchar *) s;
return _priv_gst_value_parse_array (s2, &s2, dest, G_TYPE_INVALID); return _priv_gst_value_parse_array (s2, &s2, dest, G_TYPE_INVALID, pspec);
} }
static gchar * static gchar *
@ -2469,7 +2471,7 @@ _priv_gst_value_parse_range (gchar * s, gchar ** after, GValue * value,
return FALSE; return FALSE;
s++; s++;
ret = _priv_gst_value_parse_value (s, &s, &value1, type); ret = _priv_gst_value_parse_value (s, &s, &value1, type, NULL);
if (!ret) if (!ret)
return FALSE; return FALSE;
@ -2483,7 +2485,7 @@ _priv_gst_value_parse_range (gchar * s, gchar ** after, GValue * value,
while (g_ascii_isspace (*s)) while (g_ascii_isspace (*s))
s++; s++;
ret = _priv_gst_value_parse_value (s, &s, &value2, type); ret = _priv_gst_value_parse_value (s, &s, &value2, type, NULL);
if (!ret) if (!ret)
return FALSE; return FALSE;
@ -2499,7 +2501,7 @@ _priv_gst_value_parse_range (gchar * s, gchar ** after, GValue * value,
while (g_ascii_isspace (*s)) while (g_ascii_isspace (*s))
s++; s++;
ret = _priv_gst_value_parse_value (s, &s, &value3, type); ret = _priv_gst_value_parse_value (s, &s, &value3, type, NULL);
if (!ret) if (!ret)
return FALSE; return FALSE;
@ -2562,11 +2564,15 @@ _priv_gst_value_parse_range (gchar * s, gchar ** after, GValue * value,
static gboolean static gboolean
_priv_gst_value_parse_any_list (gchar * s, gchar ** after, GValue * value, _priv_gst_value_parse_any_list (gchar * s, gchar ** after, GValue * value,
GType type, char begin, char end) GType type, char begin, char end, GParamSpec * pspec)
{ {
GValue list_value = { 0 }; GValue list_value = { 0 };
gboolean ret; gboolean ret;
GstValueList *vlist = VALUE_LIST_ARRAY (value); GstValueList *vlist = VALUE_LIST_ARRAY (value);
GParamSpec *element_spec = NULL;
if (pspec)
element_spec = GST_PARAM_SPEC_ARRAY_LIST (pspec)->element_spec;
if (*s != begin) if (*s != begin)
return FALSE; return FALSE;
@ -2588,7 +2594,8 @@ _priv_gst_value_parse_any_list (gchar * s, gchar ** after, GValue * value,
} }
memset (&list_value, 0, sizeof (list_value)); memset (&list_value, 0, sizeof (list_value));
ret = _priv_gst_value_parse_value (s, &s, &list_value, type);
ret = _priv_gst_value_parse_value (s, &s, &list_value, type, element_spec);
if (!ret) if (!ret)
return FALSE; return FALSE;
@ -2609,16 +2616,18 @@ _priv_gst_value_parse_any_list (gchar * s, gchar ** after, GValue * value,
static gboolean static gboolean
_priv_gst_value_parse_list (gchar * s, gchar ** after, GValue * value, _priv_gst_value_parse_list (gchar * s, gchar ** after, GValue * value,
GType type) GType type, GParamSpec * pspec)
{ {
return _priv_gst_value_parse_any_list (s, after, value, type, '{', '}'); return _priv_gst_value_parse_any_list (s, after, value, type, '{', '}',
pspec);
} }
static gboolean static gboolean
_priv_gst_value_parse_array (gchar * s, gchar ** after, GValue * value, _priv_gst_value_parse_array (gchar * s, gchar ** after, GValue * value,
GType type) GType type, GParamSpec * pspec)
{ {
return _priv_gst_value_parse_any_list (s, after, value, type, '<', '>'); return _priv_gst_value_parse_any_list (s, after, value, type, '<', '>',
pspec);
} }
gboolean gboolean
@ -2637,7 +2646,7 @@ _priv_gst_value_parse_simple_string (gchar * str, gchar ** end)
gboolean gboolean
_priv_gst_value_parse_value (gchar * str, _priv_gst_value_parse_value (gchar * str,
gchar ** after, GValue * value, GType default_type) gchar ** after, GValue * value, GType default_type, GParamSpec * pspec)
{ {
gchar *type_name; gchar *type_name;
gchar *type_end; gchar *type_end;
@ -2654,7 +2663,10 @@ _priv_gst_value_parse_value (gchar * str,
/* check if there's a (type_name) 'cast' */ /* check if there's a (type_name) 'cast' */
type_name = NULL; type_name = NULL;
if (*s == '(') {
if (pspec) {
type = G_PARAM_SPEC_VALUE_TYPE (pspec);
} else if (*s == '(') {
s++; s++;
while (g_ascii_isspace (*s)) while (g_ascii_isspace (*s))
s++; s++;
@ -2688,10 +2700,10 @@ _priv_gst_value_parse_value (gchar * str,
ret = _priv_gst_value_parse_range (s, &s, value, type); ret = _priv_gst_value_parse_range (s, &s, value, type);
} else if (*s == '{') { } else if (*s == '{') {
g_value_init (value, GST_TYPE_LIST); g_value_init (value, GST_TYPE_LIST);
ret = _priv_gst_value_parse_list (s, &s, value, type); ret = _priv_gst_value_parse_list (s, &s, value, type, pspec);
} else if (*s == '<') { } else if (*s == '<') {
g_value_init (value, GST_TYPE_ARRAY); g_value_init (value, GST_TYPE_ARRAY);
ret = _priv_gst_value_parse_array (s, &s, value, type); ret = _priv_gst_value_parse_array (s, &s, value, type, pspec);
} else { } else {
value_s = s; value_s = s;
@ -2725,7 +2737,7 @@ _priv_gst_value_parse_value (gchar * str,
c = *value_end; c = *value_end;
*value_end = '\0'; *value_end = '\0';
ret = gst_value_deserialize (value, value_s); ret = gst_value_deserialize_with_pspec (value, value_s, pspec);
if (G_UNLIKELY (!ret)) if (G_UNLIKELY (!ret))
g_value_unset (value); g_value_unset (value);
} }
@ -6439,19 +6451,80 @@ gst_value_deserialize (GValue * dest, const gchar * src)
type = G_VALUE_TYPE (dest); type = G_VALUE_TYPE (dest);
best = gst_value_hash_lookup_type (type); best = gst_value_hash_lookup_type (type);
if (G_UNLIKELY (!best || !best->deserialize)) { if (G_UNLIKELY (!best || (!best->deserialize
&& !best->deserialize_with_pspec))) {
len = gst_value_table->len; len = gst_value_table->len;
best = NULL; best = NULL;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
table = &g_array_index (gst_value_table, GstValueTable, i); table = &g_array_index (gst_value_table, GstValueTable, i);
if (table->deserialize && g_type_is_a (type, table->type)) { if ((table->deserialize || table->deserialize_with_pspec) &&
g_type_is_a (type, table->type)) {
if (!best || g_type_is_a (table->type, best->type)) if (!best || g_type_is_a (table->type, best->type))
best = table; best = table;
} }
} }
} }
if (G_LIKELY (best)) if (G_LIKELY (best)) {
return best->deserialize (dest, src); if (best->deserialize_with_pspec)
return best->deserialize_with_pspec (dest, src, NULL);
else
return best->deserialize (dest, src);
}
return FALSE;
}
/**
* gst_value_deserialize_with_pspec:
* @dest: (out caller-allocates): #GValue to fill with contents of
* deserialization
* @src: string to deserialize
* @pspec: (nullable): the #GParamSpec describing the expected value
*
* Tries to deserialize a string into the type specified by the given GValue.
* @pspec may be used to guide the deserializing of nested members.
* If the operation succeeds, %TRUE is returned, %FALSE otherwise.
*
* Returns: %TRUE on success
* Since: 1.20
*/
gboolean
gst_value_deserialize_with_pspec (GValue * dest, const gchar * src,
GParamSpec * pspec)
{
GstValueTable *table, *best;
guint i, len;
GType type;
g_return_val_if_fail (src != NULL, FALSE);
g_return_val_if_fail (G_IS_VALUE (dest), FALSE);
if (pspec)
g_return_val_if_fail (G_VALUE_TYPE (dest) ==
G_PARAM_SPEC_VALUE_TYPE (pspec), FALSE);
type = G_VALUE_TYPE (dest);
best = gst_value_hash_lookup_type (type);
if (G_UNLIKELY (!best || (!best->deserialize
&& !best->deserialize_with_pspec))) {
len = gst_value_table->len;
best = NULL;
for (i = 0; i < len; i++) {
table = &g_array_index (gst_value_table, GstValueTable, i);
if ((table->deserialize || table->deserialize_with_pspec) &&
g_type_is_a (type, table->type)) {
if (!best || g_type_is_a (table->type, best->type))
best = table;
}
}
}
if (G_LIKELY (best)) {
if (best->deserialize_with_pspec)
return best->deserialize_with_pspec (dest, src, pspec);
else
return best->deserialize (dest, src);
}
return FALSE; return FALSE;
} }
@ -7750,7 +7823,8 @@ gst_g_thread_get_type (void)
return G_TYPE_THREAD; return G_TYPE_THREAD;
} }
#define SERIAL_VTABLE(t,c,s,d) { t, c, s, d } #define SERIAL_VTABLE(t,c,s,d) { t, c, s, d, NULL }
#define SERIAL_VTABLE_PSPEC(t,c,s,d) { t, c, s, NULL, d }
#define REGISTER_SERIALIZATION_CONST(_gtype, _type) \ #define REGISTER_SERIALIZATION_CONST(_gtype, _type) \
G_STMT_START { \ G_STMT_START { \
@ -7769,6 +7843,15 @@ G_STMT_START { \
gst_value_register (&gst_value); \ gst_value_register (&gst_value); \
} G_STMT_END } G_STMT_END
#define REGISTER_SERIALIZATION_WITH_PSPEC(_gtype, _type) \
G_STMT_START { \
static GstValueTable gst_value = \
SERIAL_VTABLE_PSPEC (0, gst_value_compare_ ## _type, \
gst_value_serialize_ ## _type, gst_value_deserialize_ ## _type); \
gst_value.type = _gtype; \
gst_value_register (&gst_value); \
} G_STMT_END
#define REGISTER_SERIALIZATION_NO_COMPARE(_gtype, _type) \ #define REGISTER_SERIALIZATION_NO_COMPARE(_gtype, _type) \
G_STMT_START { \ G_STMT_START { \
static GstValueTable gst_value = \ static GstValueTable gst_value = \
@ -7813,8 +7896,6 @@ _priv_gst_value_initialize (void)
REGISTER_SERIALIZATION (gst_int64_range_get_type (), int64_range); REGISTER_SERIALIZATION (gst_int64_range_get_type (), int64_range);
REGISTER_SERIALIZATION (gst_double_range_get_type (), double_range); REGISTER_SERIALIZATION (gst_double_range_get_type (), double_range);
REGISTER_SERIALIZATION (gst_fraction_range_get_type (), fraction_range); REGISTER_SERIALIZATION (gst_fraction_range_get_type (), fraction_range);
REGISTER_SERIALIZATION (gst_value_list_get_type (), value_list);
REGISTER_SERIALIZATION (gst_value_array_get_type (), value_array);
REGISTER_SERIALIZATION (g_value_array_get_type (), g_value_array); REGISTER_SERIALIZATION (g_value_array_get_type (), g_value_array);
REGISTER_SERIALIZATION (gst_buffer_get_type (), buffer); REGISTER_SERIALIZATION (gst_buffer_get_type (), buffer);
REGISTER_SERIALIZATION (gst_sample_get_type (), sample); REGISTER_SERIALIZATION (gst_sample_get_type (), sample);
@ -7857,6 +7938,9 @@ _priv_gst_value_initialize (void)
REGISTER_SERIALIZATION (G_TYPE_GTYPE, gtype); REGISTER_SERIALIZATION (G_TYPE_GTYPE, gtype);
REGISTER_SERIALIZATION_WITH_PSPEC (gst_value_list_get_type (), value_list);
REGISTER_SERIALIZATION_WITH_PSPEC (gst_value_array_get_type (), value_array);
g_value_register_transform_func (GST_TYPE_INT_RANGE, G_TYPE_STRING, g_value_register_transform_func (GST_TYPE_INT_RANGE, G_TYPE_STRING,
gst_value_transform_int_range_string); gst_value_transform_int_range_string);
g_value_register_transform_func (GST_TYPE_INT64_RANGE, G_TYPE_STRING, g_value_register_transform_func (GST_TYPE_INT64_RANGE, G_TYPE_STRING,

View file

@ -472,6 +472,22 @@ typedef gchar * (* GstValueSerializeFunc) (const GValue *value1);
typedef gboolean (* GstValueDeserializeFunc) (GValue *dest, typedef gboolean (* GstValueDeserializeFunc) (GValue *dest,
const gchar *s); const gchar *s);
/**
* GstValueDeserializeWithPSpecFunc:
* @dest: a #GValue
* @s: a string
* @pspec: a #GParamSpec describing the expected value
*
* Used by gst_value_deserialize_with_pspec() to parse a non-binary form into the #GValue.
*
* Returns: %TRUE for success
* Since: 1.20
*/
typedef gboolean (* GstValueDeserializeWithPSpecFunc) (GValue *dest,
const gchar *s,
GParamSpec *pspec);
typedef struct _GstValueTable GstValueTable; typedef struct _GstValueTable GstValueTable;
/** /**
* GstValueTable: * GstValueTable:
@ -479,6 +495,7 @@ typedef struct _GstValueTable GstValueTable;
* @compare: a #GstValueCompareFunc * @compare: a #GstValueCompareFunc
* @serialize: a #GstValueSerializeFunc * @serialize: a #GstValueSerializeFunc
* @deserialize: a #GstValueDeserializeFunc * @deserialize: a #GstValueDeserializeFunc
* @deserialize_with_pspec: a #GstValueDeserializeWithPSpecFunc
* *
* VTable for the #GValue @type. * VTable for the #GValue @type.
*/ */
@ -488,8 +505,17 @@ struct _GstValueTable {
GstValueSerializeFunc serialize; GstValueSerializeFunc serialize;
GstValueDeserializeFunc deserialize; GstValueDeserializeFunc deserialize;
/**
* GstValueTable.deserialize_with_pspec:
*
* a #GstValueDeserializeWithPSpecFunc
*
* Since: 1.20
*/
GstValueDeserializeWithPSpecFunc deserialize_with_pspec;
/*< private >*/ /*< private >*/
gpointer _gst_reserved [GST_PADDING]; gpointer _gst_reserved [GST_PADDING - 1];
}; };
GST_API GST_API
@ -538,6 +564,11 @@ GST_API
gboolean gst_value_deserialize (GValue *dest, gboolean gst_value_deserialize (GValue *dest,
const gchar *src); const gchar *src);
GST_API
gboolean gst_value_deserialize_with_pspec (GValue *dest,
const gchar *src,
GParamSpec *pspec);
/* list */ /* list */
GST_API GST_API

View file

@ -328,7 +328,7 @@ static void gst_parse_new_child(GstChildProxy *child_proxy, GObject *object,
GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, child_proxy, "parsing delayed property %s as a %s from %s", GST_CAT_LOG_OBJECT (GST_CAT_PIPELINE, child_proxy, "parsing delayed property %s as a %s from %s",
pspec->name, g_type_name (value_type), set->value_str); pspec->name, g_type_name (value_type), set->value_str);
g_value_init (&v, value_type); g_value_init (&v, value_type);
if (gst_value_deserialize (&v, set->value_str)) if (gst_value_deserialize_with_pspec (&v, set->value_str, pspec))
got_value = TRUE; got_value = TRUE;
else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) { else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) {
GstElement *bin; GstElement *bin;
@ -424,7 +424,7 @@ static void gst_parse_element_set (gchar *value, GstElement *element, graph_t *g
pspec->name, g_type_name (value_type)); pspec->name, g_type_name (value_type));
g_value_init (&v, value_type); g_value_init (&v, value_type);
if (gst_value_deserialize (&v, pos)) if (gst_value_deserialize_with_pspec (&v, pos, pspec))
got_value = TRUE; got_value = TRUE;
else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) { else if (g_type_is_a (value_type, GST_TYPE_ELEMENT)) {
GstElement *bin; GstElement *bin;

View file

@ -3508,6 +3508,81 @@ GST_START_TEST (test_deserialize_array)
GST_END_TEST; GST_END_TEST;
#define TEST_FLAGS_TYPE (test_flags_get_type())
static GType
test_flags_get_type (void)
{
static const GFlagsValue values[] = {
{1, "One", "one"},
{1 << 1, "Two", "two"},
{1 << 3, "Eight", "eight"},
{0, NULL, NULL}
};
static volatile GType id = 0;
if (g_once_init_enter ((gsize *) & id)) {
GType _id;
_id = g_flags_register_static ("TestFlags", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
#define _RESULT(i) result_##i
#define RESULT(i) _RESULT(i)
GST_START_TEST (test_deserialize_with_pspec)
{
GValue value = { 0 };
GParamSpec *pspec;
const gchar *strings[] = {
"< one, 0>",
"< one+eight, two >",
"< 9, 0>",
};
int i;
gint results[3][2] = {
{1, 0},
{9, 2},
{9, 0}
};
pspec = gst_param_spec_array ("flags-array",
"Flags Array", "An array of flags",
g_param_spec_flags ("flags", "Flags", "Flags", TEST_FLAGS_TYPE, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
for (i = 0; i < G_N_ELEMENTS (strings); ++i) {
int j;
gchar *str = g_strdup (strings[i]);
g_value_init (&value, GST_TYPE_ARRAY);
fail_unless (gst_value_deserialize_with_pspec (&value, str, pspec),
"could not deserialize %s (%d)", str, i);
fail_unless (gst_value_array_get_size (&value) ==
G_N_ELEMENTS (results[i]));
for (j = 0; j < G_N_ELEMENTS (results[i]); j++) {
const GValue *elem_value = gst_value_array_get_value (&value, j);
fail_unless (G_VALUE_TYPE (elem_value) == TEST_FLAGS_TYPE);
fail_unless_equals_int (g_value_get_flags (elem_value), results[i][j]);
}
g_value_unset (&value);
g_free (str);
}
g_param_spec_unref (pspec);
}
GST_END_TEST;
static Suite * static Suite *
gst_value_suite (void) gst_value_suite (void)
{ {
@ -3562,6 +3637,7 @@ gst_value_suite (void)
tcase_add_test (tc_chain, test_transform_array); tcase_add_test (tc_chain, test_transform_array);
tcase_add_test (tc_chain, test_transform_list); tcase_add_test (tc_chain, test_transform_list);
tcase_add_test (tc_chain, test_serialize_null_aray); tcase_add_test (tc_chain, test_serialize_null_aray);
tcase_add_test (tc_chain, test_deserialize_with_pspec);
return s; return s;
} }