From: Stephan Bosch Date: Wed, 7 Aug 2019 21:25:24 +0000 (+0200) Subject: lib-json: json-generator - Add support for writing literal JSON text from an input... X-Git-Tag: 2.4.0~2389 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4ad9d41d71768a19ba1c17f34171b7d927928c56;p=thirdparty%2Fdovecot%2Fcore.git lib-json: json-generator - Add support for writing literal JSON text from an input stream --- diff --git a/src/lib-json/json-generator.c b/src/lib-json/json-generator.c index bc22a85210..6b215f3803 100644 --- a/src/lib-json/json-generator.c +++ b/src/lib-json/json-generator.c @@ -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; } diff --git a/src/lib-json/json-generator.h b/src/lib-json/json-generator.h index b296bcfede..dfa5096788 100644 --- a/src/lib-json/json-generator.h +++ b/src/lib-json/json-generator.h @@ -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, diff --git a/src/lib-json/test-json-generator.c b/src/lib-json/test-json-generator.c index 8ebe91bc84..8122b465f2 100644 --- a/src/lib-json/test-json-generator.c +++ b/src/lib-json/test-json-generator.c @@ -286,6 +286,22 @@ static void test_json_generate_buffer(void) str_truncate(buffer, 0); output->offset = 0; + /* - input stream */ + test_begin("json write - 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 - input stream nested */ + test_begin("json write array - 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 - input stream nested, second */ + test_begin("json write array - 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 - input stream nested */ + test_begin("json write object - 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); }