structure: Change NULL and empty string handling

Don't forbid the empty string "" in generic structures, only in taglists.
Properly allow the NULL string by adding special cases for serialising
and deserialising it. prop1=(string)NULL is the NULL string,
prop1=(string)"NULL" is the actual string with the value "NULL"
This commit is contained in:
Jan Schmidt 2009-07-02 12:40:05 +01:00
parent 082b410567
commit 7bf3554228
4 changed files with 118 additions and 54 deletions

View file

@ -657,18 +657,19 @@ gst_structure_set_field (GstStructure * structure, GstStructureField * field)
s = g_value_get_string (&field->value); s = g_value_get_string (&field->value);
/* only check for NULL strings in taglists, as they are allowed in message /* only check for NULL strings in taglists, as they are allowed in message
* structs, e.g. error message debug strings */ * structs, e.g. error message debug strings */
if (G_UNLIKELY (s == NULL && IS_TAGLIST (structure))) { if (G_UNLIKELY (IS_TAGLIST (structure) && (s == NULL || *s == '\0'))) {
g_warning ("Trying to set NULL string on field '%s' on taglist. " if (s == NULL) {
"Please file a bug.", g_quark_to_string (field->name)); g_warning ("Trying to set NULL string on field '%s' on taglist. "
g_value_unset (&field->value); "Please file a bug.", g_quark_to_string (field->name));
return; g_value_unset (&field->value);
} else if (G_UNLIKELY (s != NULL && *s == '\0')) { return;
/* empty strings never make sense */ } else {
g_warning ("Trying to set empty string on %s field '%s'. Please file a " /* empty strings never make sense */
"bug.", IS_TAGLIST (structure) ? "taglist" : "structure", g_warning ("Trying to set empty string on taglist field '%s'. "
g_quark_to_string (field->name)); "Please file a bug.", g_quark_to_string (field->name));
g_value_unset (&field->value); g_value_unset (&field->value);
return; return;
}
} else if (G_UNLIKELY (s != NULL && !g_utf8_validate (s, -1, NULL))) { } else if (G_UNLIKELY (s != NULL && !g_utf8_validate (s, -1, NULL))) {
g_warning ("Trying to set string on %s field '%s', but string is not " g_warning ("Trying to set string on %s field '%s', but string is not "
"valid UTF-8. Please file a bug.", "valid UTF-8. Please file a bug.",
@ -1602,7 +1603,7 @@ priv_gst_structure_append_to_gstring (const GstStructure * structure,
g_string_append_len (s, "=(", 2); g_string_append_len (s, "=(", 2);
g_string_append (s, gst_structure_to_abbr (type)); g_string_append (s, gst_structure_to_abbr (type));
g_string_append_c (s, ')'); g_string_append_c (s, ')');
g_string_append (s, GST_STR_NULL (t)); g_string_append (s, t == NULL ? "NULL" : t);
g_free (t); g_free (t);
} }
@ -1653,7 +1654,8 @@ gst_structure_to_string (const GstStructure * structure)
* THIS FUNCTION MODIFIES THE STRING AND DETECTS INSIDE A NONTERMINATED STRING * THIS FUNCTION MODIFIES THE STRING AND DETECTS INSIDE A NONTERMINATED STRING
*/ */
static gboolean static gboolean
gst_structure_parse_string (gchar * s, gchar ** end, gchar ** next) gst_structure_parse_string (gchar * s, gchar ** end, gchar ** next,
gboolean unescape)
{ {
gchar *w; gchar *w;
@ -1669,21 +1671,32 @@ gst_structure_parse_string (gchar * s, gchar ** end, gchar ** next)
return ret; return ret;
} }
w = s; if (unescape) {
s++; w = s;
while (*s != '"') { s++;
if (*s == 0) while (*s != '"') {
return FALSE; if (G_UNLIKELY (*s == 0))
return FALSE;
if (*s == '\\') { if (G_UNLIKELY (*s == '\\'))
s++;
*w = *s;
w++;
s++; s++;
} }
*w = *s;
w++;
s++; s++;
} else {
/* Find the closing quotes */
s++;
while (*s != '"') {
if (G_UNLIKELY (*s == 0))
return FALSE;
if (G_UNLIKELY (*s == '\\'))
s++;
s++;
}
s++;
w = s;
} }
s++;
*end = w; *end = w;
*next = s; *next = s;
@ -1931,11 +1944,7 @@ gst_structure_parse_value (gchar * str,
ret = gst_structure_parse_array (s, &s, value, type); ret = gst_structure_parse_array (s, &s, value, type);
} else { } else {
value_s = s; value_s = s;
if (G_UNLIKELY (!gst_structure_parse_string (s, &value_end, &s)))
return FALSE;
c = *value_end;
*value_end = '\0';
if (G_UNLIKELY (type == G_TYPE_INVALID)) { if (G_UNLIKELY (type == G_TYPE_INVALID)) {
GType try_types[] = GType try_types[] =
{ G_TYPE_INT, G_TYPE_DOUBLE, GST_TYPE_FRACTION, G_TYPE_BOOLEAN, { G_TYPE_INT, G_TYPE_DOUBLE, GST_TYPE_FRACTION, G_TYPE_BOOLEAN,
@ -1943,6 +1952,12 @@ gst_structure_parse_value (gchar * str,
}; };
int i; int i;
if (G_UNLIKELY (!gst_structure_parse_string (s, &value_end, &s, TRUE)))
return FALSE;
/* Set NULL terminator for deserialization */
c = *value_end;
*value_end = '\0';
for (i = 0; i < G_N_ELEMENTS (try_types); i++) { for (i = 0; i < G_N_ELEMENTS (try_types); i++) {
g_value_init (value, try_types[i]); g_value_init (value, try_types[i]);
ret = gst_value_deserialize (value, value_s); ret = gst_value_deserialize (value, value_s);
@ -1953,6 +1968,13 @@ gst_structure_parse_value (gchar * str,
} else { } else {
g_value_init (value, type); g_value_init (value, type);
if (G_UNLIKELY (!gst_structure_parse_string (s, &value_end, &s,
(type != G_TYPE_STRING))))
return FALSE;
/* Set NULL terminator for deserialization */
c = *value_end;
*value_end = '\0';
ret = gst_value_deserialize (value, value_s); ret = gst_value_deserialize (value, value_s);
if (G_UNLIKELY (!ret)) if (G_UNLIKELY (!ret))
g_value_unset (value); g_value_unset (value);
@ -1999,7 +2021,7 @@ gst_structure_from_string (const gchar * string, gchar ** end)
r++; r++;
name = r; name = r;
if (G_UNLIKELY (!gst_structure_parse_string (r, &w, &r))) { if (G_UNLIKELY (!gst_structure_parse_string (r, &w, &r, TRUE))) {
GST_WARNING ("Failed to parse structure string"); GST_WARNING ("Failed to parse structure string");
goto error; goto error;
} }

View file

@ -1803,8 +1803,10 @@ gst_value_deserialize_float (GValue * dest, const gchar * s)
static gint static gint
gst_value_compare_string (const GValue * value1, const GValue * value2) gst_value_compare_string (const GValue * value1, const GValue * value2)
{ {
if (!value1->data[0].v_pointer || !value2->data[0].v_pointer) { if (G_UNLIKELY (!value1->data[0].v_pointer || !value2->data[0].v_pointer)) {
return GST_VALUE_UNORDERED; /* if only one is NULL, no match - otherwise both NULL == EQUAL */
if (value1->data[0].v_pointer != value2->data[0].v_pointer)
return GST_VALUE_UNORDERED;
} else { } else {
int x = strcmp (value1->data[0].v_pointer, value2->data[0].v_pointer); int x = strcmp (value1->data[0].v_pointer, value2->data[0].v_pointer);
@ -1812,8 +1814,9 @@ gst_value_compare_string (const GValue * value1, const GValue * value2)
return GST_VALUE_LESS_THAN; return GST_VALUE_LESS_THAN;
if (x > 0) if (x > 0)
return GST_VALUE_GREATER_THAN; return GST_VALUE_GREATER_THAN;
return GST_VALUE_EQUAL;
} }
return GST_VALUE_EQUAL;
} }
static int static int
@ -1822,9 +1825,13 @@ gst_string_measure_wrapping (const gchar * s)
int len; int len;
gboolean wrap = FALSE; gboolean wrap = FALSE;
if (s == NULL) if (G_UNLIKELY (s == NULL))
return -1; return -1;
/* Special case: the actual string NULL needs wrapping */
if (G_UNLIKELY (strcmp (s, "NULL") == 0))
return 4;
len = 0; len = 0;
while (*s) { while (*s) {
if (GST_ASCII_IS_STRING (*s)) { if (GST_ASCII_IS_STRING (*s)) {
@ -1839,7 +1846,9 @@ gst_string_measure_wrapping (const gchar * s)
s++; s++;
} }
return wrap ? len : -1; /* Wrap the string if we found something that needs
* wrapping, or the empty string (len == 0) */
return (wrap || len == 0) ? len : -1;
} }
static gchar * static gchar *
@ -1866,6 +1875,7 @@ gst_string_wrap_inner (const gchar * s, int len)
*e++ = '\"'; *e++ = '\"';
*e = 0; *e = 0;
g_assert (e - d <= len + 3);
return d; return d;
} }
@ -1875,7 +1885,7 @@ gst_string_wrap (const gchar * s)
{ {
int len = gst_string_measure_wrapping (s); int len = gst_string_measure_wrapping (s);
if (len < 0) if (G_LIKELY (len < 0))
return g_strdup (s); return g_strdup (s);
return gst_string_wrap_inner (s, len); return gst_string_wrap_inner (s, len);
@ -1888,7 +1898,7 @@ gst_string_take_and_wrap (gchar * s)
gchar *out; gchar *out;
int len = gst_string_measure_wrapping (s); int len = gst_string_measure_wrapping (s);
if (len < 0) if (G_LIKELY (len < 0))
return s; return s;
out = gst_string_wrap_inner (s, len); out = gst_string_wrap_inner (s, len);
@ -1991,14 +2001,16 @@ gst_value_serialize_string (const GValue * value)
static gboolean static gboolean
gst_value_deserialize_string (GValue * dest, const gchar * s) gst_value_deserialize_string (GValue * dest, const gchar * s)
{ {
if (*s != '"') { if (G_UNLIKELY (strcmp (s, "NULL") == 0)) {
g_value_set_string (dest, NULL);
return TRUE;
} else if (G_LIKELY (*s != '"')) {
if (!g_utf8_validate (s, -1, NULL)) if (!g_utf8_validate (s, -1, NULL))
return FALSE; return FALSE;
g_value_set_string (dest, s); g_value_set_string (dest, s);
return TRUE; return TRUE;
} else { } else {
gchar *str = gst_string_unwrap (s); gchar *str = gst_string_unwrap (s);
if (G_UNLIKELY (!str)) if (G_UNLIKELY (!str))
return FALSE; return FALSE;
g_value_take_string (dest, str); g_value_take_string (dest, str);

View file

@ -213,6 +213,40 @@ GST_START_TEST (test_complete_structure)
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_string_properties)
{
GstCaps *caps1, *caps2;
GstStructure *st1, *st2;
gchar *str, *res1, *res2;
/* test escaping/unescaping */
st1 = gst_structure_new ("RandomStructure", "prop1", G_TYPE_STRING, "foo",
"prop2", G_TYPE_STRING, "", "prop3", G_TYPE_STRING, NULL,
"prop4", G_TYPE_STRING, "NULL", NULL);
str = gst_structure_to_string (st1);
st2 = gst_structure_from_string (str, NULL);
g_free (str);
fail_unless (st2 != NULL);
/* need to put stuctures into caps to compare */
caps1 = gst_caps_new_empty ();
gst_caps_append_structure (caps1, st1);
caps2 = gst_caps_new_empty ();
gst_caps_append_structure (caps2, st2);
res1 = gst_caps_to_string (caps1);
res2 = gst_caps_to_string (caps2);
fail_unless (gst_caps_is_equal (caps1, caps2),
"Structures did not match:\n\tStructure 1: %s\n\tStructure 2: %s\n",
res1, res2);
gst_caps_unref (caps1);
gst_caps_unref (caps2);
g_free (res1);
g_free (res2);
}
GST_END_TEST;
GST_START_TEST (test_structure_new) GST_START_TEST (test_structure_new)
{ {
GstStructure *s; GstStructure *s;
@ -421,18 +455,6 @@ GST_START_TEST (test_structure_nested_from_and_to_string)
GST_END_TEST; GST_END_TEST;
GST_START_TEST (test_empty_string_fields)
{
GstStructure *s;
s = gst_structure_empty_new ("con/struct");
ASSERT_WARNING (gst_structure_set (s, "layout", G_TYPE_STRING, "", NULL));
gst_structure_set (s, "debug-string", G_TYPE_STRING, NULL, NULL);
gst_structure_free (s);
}
GST_END_TEST;
GST_START_TEST (test_vararg_getters) GST_START_TEST (test_vararg_getters)
{ {
GstStructure *s; GstStructure *s;
@ -546,13 +568,13 @@ gst_structure_suite (void)
tcase_add_test (tc_chain, test_from_string); tcase_add_test (tc_chain, test_from_string);
tcase_add_test (tc_chain, test_to_string); tcase_add_test (tc_chain, test_to_string);
tcase_add_test (tc_chain, test_to_from_string); tcase_add_test (tc_chain, test_to_from_string);
tcase_add_test (tc_chain, test_string_properties);
tcase_add_test (tc_chain, test_complete_structure); tcase_add_test (tc_chain, test_complete_structure);
tcase_add_test (tc_chain, test_structure_new); tcase_add_test (tc_chain, test_structure_new);
tcase_add_test (tc_chain, test_fixate); tcase_add_test (tc_chain, test_fixate);
tcase_add_test (tc_chain, test_fixate_frac_list); tcase_add_test (tc_chain, test_fixate_frac_list);
tcase_add_test (tc_chain, test_structure_nested); tcase_add_test (tc_chain, test_structure_nested);
tcase_add_test (tc_chain, test_structure_nested_from_and_to_string); tcase_add_test (tc_chain, test_structure_nested_from_and_to_string);
tcase_add_test (tc_chain, test_empty_string_fields);
tcase_add_test (tc_chain, test_vararg_getters); tcase_add_test (tc_chain, test_vararg_getters);
return s; return s;
} }

View file

@ -428,7 +428,8 @@ GST_START_TEST (test_string)
gchar *try[] = { gchar *try[] = {
"Dude", "Dude",
"Hi, I'm a string", "Hi, I'm a string",
"tüüüt!" "tüüüt!",
"\"\"" /* Empty string */
}; };
gchar *tmp; gchar *tmp;
GValue v = { 0, }; GValue v = { 0, };
@ -465,7 +466,8 @@ GST_START_TEST (test_deserialize_string)
{ {
"", ""}, /* empty strings */ "", ""}, /* empty strings */
{ {
"\"\"", ""}, /* FAILURES */ "\"\"", ""}, /* quoted empty string -> empty string */
/* Expected FAILURES: */
{ {
"\"", NULL}, /* missing second quote */ "\"", NULL}, /* missing second quote */
{ {
@ -538,6 +540,12 @@ GST_START_TEST (test_value_compare)
fail_unless (gst_value_compare (&value1, &value2) == GST_VALUE_LESS_THAN); fail_unless (gst_value_compare (&value1, &value2) == GST_VALUE_LESS_THAN);
fail_unless (gst_value_compare (&value2, &value1) == GST_VALUE_GREATER_THAN); fail_unless (gst_value_compare (&value2, &value1) == GST_VALUE_GREATER_THAN);
fail_unless (gst_value_compare (&value1, &value1) == GST_VALUE_EQUAL); fail_unless (gst_value_compare (&value1, &value1) == GST_VALUE_EQUAL);
/* Test some NULL string comparisons */
g_value_set_string (&value2, NULL);
fail_unless (gst_value_compare (&value1, &value2) == GST_VALUE_UNORDERED);
fail_unless (gst_value_compare (&value2, &value1) == GST_VALUE_UNORDERED);
fail_unless (gst_value_compare (&value2, &value2) == GST_VALUE_EQUAL);
g_value_unset (&value1); g_value_unset (&value1);
g_value_unset (&value2); g_value_unset (&value2);