From: Stephan Bosch Date: Thu, 7 May 2020 12:56:17 +0000 (+0200) Subject: lib-json: json-ostream - Add support for writing directly to the underlying stream X-Git-Tag: 2.4.0~2377 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60381661f762ad9d2203f8df43d7ba0a8cd96e2d;p=thirdparty%2Fdovecot%2Fcore.git lib-json: json-ostream - Add support for writing directly to the underlying stream --- diff --git a/src/lib-json/json-ostream.c b/src/lib-json/json-ostream.c index 9f7176fb98..777073c39b 100644 --- a/src/lib-json/json-ostream.c +++ b/src/lib-json/json-ostream.c @@ -38,6 +38,8 @@ struct json_ostream { bool value_opened:1; bool value_persists:1; bool string_opened:1; + bool space_opening:1; + bool space_opened:1; bool last_errors_not_checked:1; bool error_handling_disabled:1; bool nfailed:1; @@ -261,6 +263,15 @@ static int json_ostream_do_write_more(struct json_ostream *stream) if (stream->closed) return -1; + if (stream->space_opened) { + if (stream->space_opening) { + ret = json_generate_space_open(stream->generator); + if (ret <= 0) + return ret; + stream->space_opening = FALSE; + } + return 1; + } switch (stream->node.value.content_type) { case JSON_CONTENT_TYPE_STRING: @@ -406,6 +417,7 @@ json_ostream_write_init(struct json_ostream *stream, const char *name, { int ret; + i_assert(!stream->space_opened); i_assert(name == NULL || !stream->string_opened); i_assert(!stream->string_opened || type == JSON_TYPE_STRING); @@ -426,6 +438,30 @@ json_ostream_write_init(struct json_ostream *stream, const char *name, return 1; } +static int +json_ostream_write_space_init(struct json_ostream *stream, const char *name) +{ + int ret; + + i_assert(!stream->string_opened); + + ret = json_ostream_flush(stream); + if (ret <= 0) + return ret; + + if (stream->space_opened) + return 1; + + if (name != NULL) { + i_assert(!stream->member_name_written); + ret = json_generate_object_member(stream->generator, name); + if (ret <= 0) + return ret; + } + stream->member_name_written = FALSE; + return 1; +} + /* * Values */ @@ -1198,6 +1234,8 @@ json_ostream_do_write_node(struct json_ostream *stream, int json_ostream_write_node(struct json_ostream *stream, const struct json_node *node, bool copy) { + i_assert(!stream->space_opened); + return json_ostream_do_write_node(stream, node, TRUE, copy); } @@ -1272,3 +1310,41 @@ json_ostream_nopen_string_stream(struct json_ostream *stream, const char *name) json_ostream_nwrite_post(stream, ret); return ostream; } + +/* + * + */ + +int json_ostream_open_space(struct json_ostream *stream, const char *name) +{ + int ret; + + ret = json_ostream_write_space_init(stream, name); + if (ret <= 0) + return ret; + + i_zero(&stream->node); + stream->space_opened = TRUE; + stream->space_opening = TRUE; + + return json_ostream_write_more(stream); +} + +void json_ostream_nopen_space(struct json_ostream *stream, const char *name) +{ + int ret; + + if (!json_ostream_nwrite_pre(stream)) + return; + ret = json_ostream_open_space(stream, name); + json_ostream_nwrite_post(stream, ret); +} + +void json_ostream_close_space(struct json_ostream *stream) +{ + i_assert(stream->space_opened); + i_assert(!stream->space_opening); + + json_generate_space_close(stream->generator); + stream->space_opened = FALSE; +} diff --git a/src/lib-json/json-ostream.h b/src/lib-json/json-ostream.h index 3d0356f63a..bc0d5baf4b 100644 --- a/src/lib-json/json-ostream.h +++ b/src/lib-json/json-ostream.h @@ -293,4 +293,17 @@ int json_ostream_open_string_stream(struct json_ostream *stream, struct ostream * json_ostream_nopen_string_stream(struct json_ostream *stream, const char *name); +/* + * + */ + +/* Try to prepare the stream for writing raw JSON text to the underlying output + stream directly. Returns 1 if ready, 0 if the json output stream needs to be + flushed more (tried implicitly) before the stream can be opened, -1 if error. + */ +int json_ostream_open_space(struct json_ostream *stream, const char *name); +void json_ostream_nopen_space(struct json_ostream *stream, const char *name); +/* Continue after writing raw JSON to the underlying output stream directly. */ +void json_ostream_close_space(struct json_ostream *stream); + #endif diff --git a/src/lib-json/test-json-ostream.c b/src/lib-json/test-json-ostream.c index 14e95f3553..d5eee45f10 100644 --- a/src/lib-json/test-json-ostream.c +++ b/src/lib-json/test-json-ostream.c @@ -2415,6 +2415,253 @@ static void test_json_ostream_nwrite_tree(void) str_free(&buffer); } +static void test_json_ostream_nwrite_space(void) +{ + string_t *buffer; + struct ostream *output; + struct json_ostream *joutput; + + buffer = str_new(default_pool, 256); + output = o_stream_create_buffer(buffer); + o_stream_set_no_error_handling(output, TRUE); + + /* */ + test_begin("json ostream nwrite space - "); + joutput = json_ostream_create(output, 0); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "212345"); + json_ostream_close_space(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("212345", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* [ ] */ + test_begin("json ostream nwrite space - [ ]"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_array(joutput, NULL); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nascend_array(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("[\"frop\"]", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* [ , string ] */ + test_begin("json ostream nwrite space - [ , string ]"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_array(joutput, NULL); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nwrite_string(joutput, NULL, "friep"); + json_ostream_nascend_array(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("[\"frop\",\"friep\"]", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* [ string, ] */ + test_begin("json ostream nwrite space - [ string, ]"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_array(joutput, NULL); + json_ostream_nwrite_string(joutput, NULL, "frop"); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "[13234]"); + json_ostream_close_space(joutput); + json_ostream_nascend_array(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("[\"frop\",[13234]]", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* [ , ] */ + test_begin("json ostream nwrite space - [ , ]"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_array(joutput, NULL); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"friep\""); + json_ostream_close_space(joutput); + json_ostream_nascend_array(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("[\"frop\",\"friep\"]", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { "a": } */ + test_begin("json ostream nwrite space - { \"a\": }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, "a"); + o_stream_nsend_str(output, "\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { "a": , "b": string } */ + test_begin("json ostream nwrite space - " + "{ \"a\": , \"b\": string }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, "a"); + o_stream_nsend_str(output, "\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nwrite_string(joutput, "b", "friep"); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":\"friep\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { "a": string, "b": } */ + test_begin("json ostream nwrite space - " + "{ \"a\": string, \"b\": }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nwrite_string(joutput, "a", "frop"); + json_ostream_nopen_space(joutput, "b"); + o_stream_nsend_str(output, "[13234]"); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":[13234]}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { "a": , "b": } */ + test_begin("json ostream nwrite space - " + "{ \"a\": , \"b\": }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, "a"); + o_stream_nsend_str(output, "\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nopen_space(joutput, "b"); + o_stream_nsend_str(output, "\"friep\""); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":\"friep\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { } */ + test_begin("json ostream nwrite space - { }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"a\":\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { , "b": string } */ + test_begin("json ostream nwrite space - { , \"b\": string }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"a\":\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nwrite_string(joutput, "b", "friep"); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":\"friep\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { "a": string, } */ + test_begin("json ostream nwrite space - " + "{ \"a\": string, }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nwrite_string(joutput, "a", "frop"); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"b\":[13234]"); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":[13234]}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { , } */ + test_begin("json ostream nwrite space - { , }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"a\":\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"b\":\"friep\""); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":\"friep\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { "a": , } */ + test_begin("json ostream nwrite space - { \"a\": , }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, "a"); + o_stream_nsend_str(output, "\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"b\":\"friep\""); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":\"friep\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + /* { , "b": } */ + test_begin("json ostream nwrite space - { , \"b\": }"); + joutput = json_ostream_create(output, 0); + json_ostream_ndescend_object(joutput, NULL); + json_ostream_nopen_space(joutput, NULL); + o_stream_nsend_str(output, "\"a\":\"frop\""); + json_ostream_close_space(joutput); + json_ostream_nopen_space(joutput, "b"); + o_stream_nsend_str(output, "\"friep\""); + json_ostream_close_space(joutput); + json_ostream_nascend_object(joutput); + json_ostream_nfinish_destroy(&joutput); + test_assert_strcmp("{\"a\":\"frop\",\"b\":\"friep\"}", str_c(buffer)); + test_end(); + str_truncate(buffer, 0); + output->offset = 0; + + o_stream_destroy(&output); + str_free(&buffer); +} + int main(int argc, char *argv[]) { int c; @@ -2424,6 +2671,7 @@ int main(int argc, char *argv[]) test_json_ostream_nwrite, test_json_ostream_write_tree, test_json_ostream_nwrite_tree, + test_json_ostream_nwrite_space, NULL };