diff --git a/lib/wp/spa-json.c b/lib/wp/spa-json.c index 6fa1e231..1bbab339 100644 --- a/lib/wp/spa-json.c +++ b/lib/wp/spa-json.c @@ -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; } /*! diff --git a/tests/wp/spa-json.c b/tests/wp/spa-json.c index 80b78dd1..a57fcba4 100644 --- a/tests/wp/spa-json.c +++ b/tests/wp/spa-json.c @@ -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);