]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-json: json-generator - Add support for writing literal JSON text from an input...
authorStephan Bosch <stephan.bosch@open-xchange.com>
Wed, 7 Aug 2019 21:25:24 +0000 (23:25 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Sat, 18 Nov 2023 18:58:04 +0000 (18:58 +0000)
src/lib-json/json-generator.c
src/lib-json/json-generator.h
src/lib-json/test-json-generator.c

index bc22a85210f474cf37afcd42f515283e7c2d9924..6b215f3803bbd24a8a404f18a34268967cacddb6 100644 (file)
@@ -61,9 +61,12 @@ struct json_generator {
        /* We opened an input stream string */
        bool string_stream:1;         /* API state */
        bool string_stream_written:1; /* write state */
+       /* We opened an input stream JSON-text */
+       bool text_stream:1;           /* API state */
 };
 
 static int json_generator_flush_string_input(struct json_generator *generator);
+static int json_generator_flush_text_input(struct json_generator *generator);
 
 static struct json_generator *
 json_generator_new(enum json_generator_flags flags)
@@ -396,6 +399,16 @@ int json_generator_flush(struct json_generator *generator)
                }
                generator->write_state = JSON_GENERATOR_STATE_VALUE_END;
        }
+       /* flush string stream */
+       if (generator->text_stream) {
+               i_assert(generator->value_input != NULL);
+               /* flush the stream */
+               ret = json_generator_flush_text_input(generator);
+               if (ret <= 0)
+                       return ret;
+               generator->text_stream = FALSE;
+               generator->write_state = JSON_GENERATOR_STATE_VALUE_END;
+       }
        return 1;
 }
 
@@ -982,6 +995,56 @@ int json_generate_text(struct json_generator *generator, const char *str)
                                       strlen(str));
 }
 
+static int
+json_generator_flush_text_input(struct json_generator *generator)
+{
+       const unsigned char *data;
+       size_t size;
+       ssize_t sret;
+       int ret;
+
+       while ((ret = i_stream_read_more(generator->value_input,
+                                        &data, &size)) > 0) {
+               sret = json_generate_text_write_data(generator, data,
+                                                    size, FALSE);
+               if (sret < 0)
+                       return -1;
+               if (sret == 0)
+                       return 0;
+               i_stream_skip(generator->value_input, (size_t)sret);
+       }
+       if (ret < 0) {
+               if (generator->value_input->stream_errno != 0)
+                       return -1;
+
+               i_assert(!i_stream_have_bytes_left(generator->value_input));
+               i_stream_unref(&generator->value_input);
+               return 1;
+       }
+       return 0;
+}
+
+int json_generate_text_stream(struct json_generator *generator,
+                             struct istream *input)
+{
+       i_assert(generator->value_input == NULL);
+       json_generator_value_begin(generator);
+       generator->value_input = input;
+       i_stream_ref(input);
+       generator->text_stream = TRUE;
+       if (generator->write_state == JSON_GENERATOR_STATE_VALUE_END)
+               generator->write_state = JSON_GENERATOR_STATE_VALUE_NEXT;
+       if (generator->level_stack_pos == 0)
+               generator->state = JSON_GENERATOR_STATE_END;
+       else if (generator->object_level)
+               generator->state = JSON_GENERATOR_STATE_OBJECT_MEMBER;
+       else
+               generator->state = JSON_GENERATOR_STATE_VALUE;
+       if (json_generator_flush(generator) < 0)
+               return -1;
+       return 1;
+}
+
 /*
  * value
  */
@@ -1039,6 +1102,9 @@ int json_generate_value(struct json_generator *generator,
                        return json_generate_text_data(
                                generator, value->content.data->data,
                                value->content.data->size);
+               case JSON_CONTENT_TYPE_STREAM:
+                       return json_generate_text_stream(
+                               generator, value->content.stream);
                default:
                        break;
                }
index b296bcfede9d7e306cbd604c84f20bc5cc5eee60..dfa5096788e30b409806dfec6d914cc629187df5 100644 (file)
@@ -84,6 +84,9 @@ int json_generate_text_data(struct json_generator *generator,
                            const void *data, size_t size);
 int json_generate_text(struct json_generator *generator, const char *str);
 
+int json_generate_text_stream(struct json_generator *generator,
+                             struct istream *input);
+
 /* value */
 
 int json_generate_value(struct json_generator *generator,
index 8ebe91bc84c6a0cb40fe8a73d8c042153616b4b5..8122b465f25e43f38cdae1c453527a21c0455453 100644 (file)
@@ -286,6 +286,22 @@ static void test_json_generate_buffer(void)
        str_truncate(buffer, 0);
        output->offset = 0;
 
+       /* <JSON-text> - input stream */
+       test_begin("json write <JSON-text> - input stream");
+       generator = json_generator_init(output, 0);
+       data = "[\"frop!\",\"friep!\",\"frml!\"]";
+       input = i_stream_create_from_data(data, strlen(data));
+       ret = json_generate_text_stream(generator, input);
+       test_assert(ret > 0);
+       i_stream_unref(&input);
+       json_generator_flush(generator);
+       test_assert(ret > 0);
+       json_generator_deinit(&generator);
+       test_assert(strcmp(data, str_c(buffer)) == 0);
+       test_end();
+       str_truncate(buffer, 0);
+       output->offset = 0;
+
        /* [ ] */
        test_begin("json write array - [ ]");
        generator = json_generator_init(output, 0);
@@ -710,6 +726,48 @@ static void test_json_generate_buffer(void)
        str_truncate(buffer, 0);
        output->offset = 0;
 
+       /* array - <JSON-text> input stream nested */
+       test_begin("json write array - <JSON-text> input stream nested");
+       generator = json_generator_init(output, 0);
+       data = "[\"frop!\",\"friep!\",\"frml!\"]";
+       input = i_stream_create_from_data(data, strlen(data));
+       json_generate_array_open(generator);
+       ret = json_generate_text_stream(generator, input);
+       test_assert(ret > 0);
+       i_stream_unref(&input);
+       ret = json_generate_array_close(generator);
+       test_assert(ret > 0);
+       json_generator_flush(generator);
+       test_assert(ret > 0);
+       json_generator_deinit(&generator);
+       test_assert(strcmp("[[\"frop!\",\"friep!\",\"frml!\"]]",
+                          str_c(buffer)) == 0);
+       test_end();
+       str_truncate(buffer, 0);
+       output->offset = 0;
+
+       /* array - <JSON-text> input stream nested, second */
+       test_begin("json write array - <JSON-text> input stream nested, second");
+       generator = json_generator_init(output, 0);
+       data = "[\"frop!\",\"friep!\",\"frml!\"]";
+       input = i_stream_create_from_data(data, strlen(data));
+       json_generate_array_open(generator);
+       ret = json_generate_string(generator, "frop");
+       test_assert(ret > 0);
+       ret = json_generate_text_stream(generator, input);
+       test_assert(ret > 0);
+       i_stream_unref(&input);
+       ret = json_generate_array_close(generator);
+       test_assert(ret > 0);
+       json_generator_flush(generator);
+       test_assert(ret > 0);
+       json_generator_deinit(&generator);
+       test_assert(strcmp("[\"frop\",[\"frop!\",\"friep!\",\"frml!\"]]",
+                          str_c(buffer)) == 0);
+       test_end();
+       str_truncate(buffer, 0);
+       output->offset = 0;
+
        /* { } */
        test_begin("json write object - { }");
        generator = json_generator_init(output, 0);
@@ -1035,6 +1093,28 @@ static void test_json_generate_buffer(void)
        str_truncate(buffer, 0);
        output->offset = 0;
 
+       /* object - <JSON-text> input stream nested */
+       test_begin("json write object - <JSON-text> input stream nested");
+       generator = json_generator_init(output, 0);
+       data = "[\"frop!\",\"friep!\",\"frml!\"]";
+       input = i_stream_create_from_data(data, strlen(data));
+       json_generate_object_open(generator);
+       ret = json_generate_object_member(generator, "a");
+       test_assert(ret > 0);
+       ret = json_generate_text_stream(generator, input);
+       test_assert(ret > 0);
+       i_stream_unref(&input);
+       ret = json_generate_object_close(generator);
+       test_assert(ret > 0);
+       json_generator_flush(generator);
+       test_assert(ret > 0);
+       json_generator_deinit(&generator);
+       test_assert(strcmp("{\"a\":[\"frop!\",\"friep!\",\"frml!\"]}",
+               str_c(buffer)) == 0);
+       test_end();
+       str_truncate(buffer, 0);
+       output->offset = 0;
+
        /* trickle [1] */
        test_begin("json write object - trickle[1]");
        o_stream_set_max_buffer_size(output, 0);
@@ -1589,6 +1669,135 @@ static void test_json_generate_buffer(void)
        str_truncate(buffer, 0);
        output->offset = 0;
 
+       /* trickle [5] */
+       test_begin("json write object - trickle[5]");
+       o_stream_set_max_buffer_size(output, 0);
+       generator = json_generator_init(output, 0);
+       json_generate_object_open(generator);
+       state = 0;
+       for (pos = 0; pos < 65535 && state <= 15; pos++) {
+               o_stream_set_max_buffer_size(output, pos);
+               switch (state) {
+               case 0:
+                       ret = json_generate_object_member(generator, "aaaaaa");
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       json_generate_array_open(generator);
+                       json_generate_object_open(generator);
+                       state++;
+                       continue;
+               case 1:
+                       ret = json_generate_object_member(generator, "dddddd");
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 2:
+                       data = "[\"GGGGG\"]";
+                       input = i_stream_create_from_data(data, strlen(data));
+                       ret = json_generate_text_stream(generator, input);
+                       test_assert(ret > 0);
+                       i_stream_unref(&input);
+                       state++;
+                       continue;
+               case 3:
+                       ret = json_generate_object_close(generator);
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 4:
+                       ret = json_generate_array_close(generator);
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 5:
+                       ret = json_generate_object_member(generator, "bbbbbb");
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       json_generate_array_open(generator);
+                       json_generate_object_open(generator);
+                       state++;
+                       continue;
+               case 6:
+                       ret = json_generate_object_member(generator, "eeeeee");
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 7:
+                       data = "[\"HHHHH\"]";
+                       input = i_stream_create_from_data(data, strlen(data));
+                       ret = json_generate_text_stream(generator, input);
+                       test_assert(ret > 0);
+                       i_stream_unref(&input);
+                       state++;
+                       continue;
+               case 8:
+                       ret = json_generate_object_close(generator);
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 9:
+                       ret = json_generate_array_close(generator);
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 10:
+                       ret = json_generate_object_member(generator, "cccccc");
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       json_generate_array_open(generator);
+                       json_generate_object_open(generator);
+                       state++;
+                       continue;
+               case 11:
+                       ret = json_generate_object_member(generator, "ffffff");
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 12:
+                       data = "[\"IIIII\"]";
+                       input = i_stream_create_from_data(data, strlen(data));
+                       ret = json_generate_text_stream(generator, input);
+                       test_assert(ret > 0);
+                       i_stream_unref(&input);
+                       state++;
+                       continue;
+               case 13:
+                       ret = json_generate_object_close(generator);
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 14:
+                       ret = json_generate_array_close(generator);
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               case 15:
+                       ret = json_generate_object_close(generator);
+                       test_assert(ret >= 0);
+                       if (ret == 0) break;
+                       state++;
+                       continue;
+               }
+       }
+       json_generator_deinit(&generator);
+       test_assert(state == 16);
+       test_assert(strcmp("{\"aaaaaa\":[{\"dddddd\":[\"GGGGG\"]}],"
+                          "\"bbbbbb\":[{\"eeeeee\":[\"HHHHH\"]}],"
+                          "\"cccccc\":[{\"ffffff\":[\"IIIII\"]}]}",
+                          str_c(buffer)) == 0);
+       test_end();
+       str_truncate(buffer, 0);
+       output->offset = 0;
+
        o_stream_destroy(&output);
        str_free(&buffer);
 }