spa-json: encode the string directly into the builder

Some special characters like '\v' are encoded using 6 characters, which
currently does not work because the VLA size asumes a maximum of 4 characters
per encoded special character. This patch refactors this logic to avoid using
VLAs at all and encodes the string directly into the builder.

See #471
This commit is contained in:
Julian Bouzas
2023-06-19 11:24:08 -04:00
parent e91a9583d8
commit e24e52c6d3
2 changed files with 95 additions and 36 deletions

View File

@@ -126,6 +126,18 @@ wp_spa_json_builder_new (const gchar *data, size_t size)
return self;
}
static WpSpaJsonBuilder *
wp_spa_json_builder_new_empty (size_t size)
{
WpSpaJsonBuilder *self = g_rc_box_new0 (WpSpaJsonBuilder);
self->add_separator = FALSE;
self->data = g_new0 (gchar, size + 1);
self->max_size = size;
self->data[0] = '\0';
self->size = 0;
return self;
}
static WpSpaJsonBuilder *
wp_spa_json_builder_new_formatted (const gchar *fmt, ...)
{
@@ -379,6 +391,17 @@ wp_spa_json_new_float (float value)
wp_spa_json_builder_new_formatted ("%.6f", value));
}
static void
ensure_allocated_max_size (WpSpaJsonBuilder *self, size_t size)
{
size_t new_size = self->size + size + 1; /* '\0' because of vsnprintf */
if (new_size > self->max_size) {
size_t next_size = new_size * 2;
self->data = g_realloc (self->data, next_size);
self->max_size = next_size;
}
}
/*!
* \brief Creates a spa json of type string
*
@@ -389,10 +412,17 @@ wp_spa_json_new_float (float value)
WpSpaJson *
wp_spa_json_new_string (const gchar *value)
{
size_t size = (strlen (value) * 4) + 2;
gchar dst[size];
gint enc_size = spa_json_encode_string (dst, sizeof(dst), value);
return wp_spa_json_new_from_builder (wp_spa_json_builder_new (dst, enc_size));
WpSpaJsonBuilder *b = wp_spa_json_builder_new_empty (strlen (value));
size_t enc_size = spa_json_encode_string (b->data + b->size,
b->max_size - b->size, value);
if (enc_size + 1 > b->max_size - b->size) {
ensure_allocated_max_size (b, enc_size);
enc_size = spa_json_encode_string (b->data + b->size,
b->max_size - b->size, value);
g_assert (enc_size < b->max_size - b->size);
}
b->size += enc_size;
return wp_spa_json_new_from_builder (b);
}
/* Args is not a pointer in some architectures, so this needs to be a macro to
@@ -930,17 +960,6 @@ wp_spa_json_builder_new_object (void)
return self;
}
static void
ensure_allocated_max_size (WpSpaJsonBuilder *self, size_t size)
{
size_t new_size = self->size + size + 1; /* '\0' because of vsnprintf */
if (new_size > self->max_size) {
size_t next_size = new_size * 2;
self->data = g_realloc (self->data, next_size);
self->max_size = next_size;
}
}
static void
ensure_separator (WpSpaJsonBuilder *self, gboolean for_property)
{
@@ -969,14 +988,6 @@ builder_add_formatted (WpSpaJsonBuilder *self, const gchar *fmt, ...)
self->size += s;
}
static void
builder_add (WpSpaJsonBuilder *self, const gchar *data, size_t size)
{
g_return_if_fail (self->max_size - self->size >= size + 1);
snprintf (self->data + self->size, size + 1, "%s", data);
self->size += size;
}
static void
builder_add_json (WpSpaJsonBuilder *self, WpSpaJson *json)
{
@@ -995,14 +1006,18 @@ builder_add_json (WpSpaJsonBuilder *self, WpSpaJson *json)
void
wp_spa_json_builder_add_property (WpSpaJsonBuilder *self, const gchar *key)
{
size_t size = (strlen (key) * 4) + 3;
gchar dst[size];
gint enc_size;
size_t enc_size;
ensure_separator (self, TRUE);
ensure_allocated_max_size (self, size);
enc_size = spa_json_encode_string (dst, sizeof(dst), key);
builder_add (self, dst, enc_size);
builder_add (self, ":", 1);
enc_size = spa_json_encode_string (self->data + self->size,
self->max_size - self->size, key);
if (enc_size + 2 > self->max_size - self->size) {
ensure_allocated_max_size (self, enc_size + 1);
enc_size = spa_json_encode_string (self->data + self->size,
self->max_size - self->size, key);
g_assert (enc_size + 1 < self->max_size - self->size);
}
self->data[self->size + enc_size] = ':';
self->size += enc_size + 1;
}
/*!
@@ -1074,13 +1089,17 @@ wp_spa_json_builder_add_float (WpSpaJsonBuilder *self, float value)
void
wp_spa_json_builder_add_string (WpSpaJsonBuilder *self, const gchar *value)
{
size_t size = (strlen (value) * 4) + 3;
gchar dst[size];
gint enc_size;
size_t enc_size;
ensure_separator (self, FALSE);
ensure_allocated_max_size (self, size);
enc_size = spa_json_encode_string (dst, sizeof(dst), value);
builder_add (self, dst, enc_size);
enc_size = spa_json_encode_string (self->data + self->size,
self->max_size - self->size, value);
if (enc_size + 1 > self->max_size - self->size) {
ensure_allocated_max_size (self, enc_size);
enc_size = spa_json_encode_string (self->data + self->size,
self->max_size - self->size, value);
g_assert (enc_size < self->max_size - self->size);
}
self->size += enc_size;
}
/*!

View File

@@ -78,6 +78,13 @@ test_spa_json_basic (void)
g_assert_nonnull (v3);
g_assert_cmpstr (v3, ==,
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong");
g_autoptr (WpSpaJson) jsons = wp_spa_json_new_string ("\v\v\v\v");
g_assert_nonnull (jsons);
g_assert_true (wp_spa_json_is_string (jsons));
g_autofree gchar *v4 = wp_spa_json_parse_string (jsons);
g_assert_nonnull (v4);
g_assert_cmpstr (v4, ==, "\v\v\v\v");
}
/* Array */
@@ -282,6 +289,8 @@ test_spa_json_object_builder_parser_iterator (void)
wp_spa_json_builder_add_string (b, "str");
wp_spa_json_builder_add_property (b, "key-empty-string");
wp_spa_json_builder_add_string (b, "");
wp_spa_json_builder_add_property (b, "key-special-char-string");
wp_spa_json_builder_add_string (b, "\v\v\v\v");
json = wp_spa_json_builder_end (b);
}
@@ -331,6 +340,13 @@ test_spa_json_object_builder_parser_iterator (void)
g_assert_nonnull (v_empty_string);
g_assert_cmpstr (v_empty_string, ==, "");
g_autofree gchar *key_special_char_string = wp_spa_json_parser_get_string (p);
g_assert_nonnull (key_special_char_string);
g_assert_cmpstr (key_special_char_string, ==, "key-special-char-string");
g_autofree gchar *v_special_char_string = wp_spa_json_parser_get_string (p);
g_assert_nonnull (v_special_char_string);
g_assert_cmpstr (v_special_char_string, ==, "\v\v\v\v");
wp_spa_json_parser_end (p);
g_assert_false (wp_spa_json_parser_get_null (p));
}
@@ -479,6 +495,30 @@ test_spa_json_object_builder_parser_iterator (void)
g_value_unset (&next);
}
{
GValue next = G_VALUE_INIT;
g_assert_true (wp_iterator_next (it, &next));
WpSpaJson *j = g_value_get_boxed (&next);
g_assert_nonnull (j);
g_assert_true (wp_spa_json_is_string (j));
g_autofree gchar *v = wp_spa_json_parse_string (j);
g_assert_nonnull (v);
g_assert_cmpstr (v, ==, "key-special-char-string");
g_value_unset (&next);
}
{
GValue next = G_VALUE_INIT;
g_assert_true (wp_iterator_next (it, &next));
WpSpaJson *j = g_value_get_boxed (&next);
g_assert_nonnull (j);
g_assert_true (wp_spa_json_is_string (j));
g_autofree gchar *v = wp_spa_json_parse_string (j);
g_assert_nonnull (v);
g_assert_cmpstr (v, ==, "\v\v\v\v");
g_value_unset (&next);
}
g_assert_false (wp_iterator_next (it, NULL));
wp_iterator_reset (it);