]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-json: json-ostream - Add support for writing directly to the underlying stream
authorStephan Bosch <stephan.bosch@open-xchange.com>
Thu, 7 May 2020 12:56:17 +0000 (14:56 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Sat, 18 Nov 2023 18:58:04 +0000 (18:58 +0000)
src/lib-json/json-ostream.c
src/lib-json/json-ostream.h
src/lib-json/test-json-ostream.c

index 9f7176fb9865e606f448efc8bd4282d53e348815..777073c39be2591d76d5c01702d868caddbf3518 100644 (file)
@@ -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;
 }
+
+/*
+ * <space>
+ */
+
+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;
+}
index 3d0356f63ada1f8aaba05e1d024bfd8f624c880c..bc0d5baf4be492ea49c58446dab7b65da0aa5695 100644 (file)
@@ -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);
 
+/*
+ * <space>
+ */
+
+/* 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
index 14e95f3553c987dd27be01fea6920407d4a7162b..d5eee45f1000d8f27a589bfb62cc1437cb7ddc02 100644 (file)
@@ -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);
+
+       /* <space> */
+       test_begin("json ostream nwrite space - <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;
+
+       /* [ <space> ] */
+       test_begin("json ostream nwrite space - [ <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;
+
+       /* [ <space>, string ] */
+       test_begin("json ostream nwrite space - [ <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, <space> ] */
+       test_begin("json ostream nwrite space - [ string, <space> ]");
+       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;
+
+       /* [ <space>, <space> ] */
+       test_begin("json ostream nwrite space - [ <space>, <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": <space> } */
+       test_begin("json ostream nwrite space - { \"a\": <space> }");
+       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": <space>, "b": string } */
+       test_begin("json ostream nwrite space - "
+                  "{ \"a\": <space>, \"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": <space> } */
+       test_begin("json ostream nwrite space - "
+                  "{ \"a\": string, \"b\": <space> }");
+       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": <space>, "b": <space> } */
+       test_begin("json ostream nwrite space - "
+                  "{ \"a\": <space>, \"b\": <space> }");
+       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;
+
+       /* { <space> } */
+       test_begin("json ostream nwrite space - { <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;
+
+       /* { <space>, "b": string } */
+       test_begin("json ostream nwrite space - { <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, <space> } */
+       test_begin("json ostream nwrite space - "
+                  "{ \"a\": string, <space> }");
+       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;
+
+       /* { <space>, <space> } */
+       test_begin("json ostream nwrite space - { <space>, <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": <space>, <space> } */
+       test_begin("json ostream nwrite space - { \"a\": <space>, <space> }");
+       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;
+
+       /* { <space>, "b": <space> } */
+       test_begin("json ostream nwrite space - { <space>, \"b\": <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, "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
        };