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:
@@ -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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user