dovecot-2.2: JSON parser: Added json_parse_skip_next() to skip o...
dovecot at dovecot.org
dovecot at dovecot.org
Thu Nov 29 09:38:57 EET 2012
details: http://hg.dovecot.org/dovecot-2.2/rev/4f7e0be9cc35
changeset: 15438:4f7e0be9cc35
user: Timo Sirainen <tss at iki.fi>
date: Thu Nov 29 09:38:34 2012 +0200
description:
JSON parser: Added json_parse_skip_next() to skip over unwanted values.
diffstat:
src/lib/json-parser.c | 148 ++++++++++++++++++++++++++++----------------
src/lib/json-parser.h | 4 +-
src/lib/test-json-parser.c | 17 ++++-
3 files changed, 110 insertions(+), 59 deletions(-)
diffs (truncated from 356 to 300 lines):
diff -r 014be18f7130 -r 4f7e0be9cc35 src/lib/json-parser.c
--- a/src/lib/json-parser.c Thu Nov 29 07:52:51 2012 +0200
+++ b/src/lib/json-parser.c Thu Nov 29 09:38:34 2012 +0200
@@ -15,7 +15,7 @@
JSON_STATE_OBJECT_COLON,
JSON_STATE_OBJECT_VALUE,
JSON_STATE_OBJECT_VALUE_NEXT,
- JSON_STATE_STRINPUT_FINISH,
+ JSON_STATE_SKIP_STRING,
JSON_STATE_DONE
};
@@ -30,12 +30,10 @@
enum json_state state;
unsigned int nested_object_count;
+ unsigned int nested_skip_count;
+ bool skipping;
};
-static int
-json_try_parse_next(struct json_parser *parser, enum json_type *type_r,
- const char **value_r);
-
static int json_parser_read_more(struct json_parser *parser)
{
uoff_t cur_highwater = parser->input->v_offset +
@@ -144,12 +142,51 @@
return FALSE;
}
-static int json_parse_string(struct json_parser *parser, const char **value_r)
+static int json_skip_string(struct json_parser *parser)
+{
+ for (; parser->data != parser->end; parser->data++) {
+ if (*parser->data == '"') {
+ parser->data++;
+ json_parser_update_input_pos(parser);
+ return 1;
+ }
+ if (*parser->data == '\\') {
+ switch (*++parser->data) {
+ case '"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ break;
+ case 'u':
+ if (parser->end - parser->data < 4)
+ return -1;
+ parser->data += 3;
+ break;
+ default:
+ return -1;
+ }
+ }
+ }
+ json_parser_update_input_pos(parser);
+ return 0;
+}
+
+static int json_parse_string(struct json_parser *parser, bool allow_skip,
+ const char **value_r)
{
if (*parser->data != '"')
return -1;
parser->data++;
+ if (parser->skipping && allow_skip) {
+ *value_r = NULL;
+ return json_skip_string(parser);
+ }
+
str_truncate(parser->value, 0);
for (; parser->data != parser->end; parser->data++) {
if (*parser->data == '"') {
@@ -200,37 +237,6 @@
return 0;
}
-static int json_skip_string(struct json_parser *parser)
-{
- for (; parser->data != parser->end; parser->data++) {
- if (*parser->data == '"') {
- parser->data++;
- return 0;
- }
- if (*parser->data == '\\') {
- switch (*++parser->data) {
- case '"':
- case '\\':
- case '/':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- break;
- case 'u':
- if (parser->end - parser->data < 4)
- return -1;
- parser->data += 3;
- break;
- default:
- return -1;
- }
- }
- }
- return -1;
-}
-
static int
json_parse_digits(struct json_parser *parser)
{
@@ -313,8 +319,7 @@
}
static int
-json_parse_object_close(struct json_parser *parser, enum json_type *type_r,
- const char **value_r)
+json_parse_object_close(struct json_parser *parser, enum json_type *type_r)
{
parser->data++;
json_parser_update_input_pos(parser);
@@ -323,17 +328,22 @@
/* closing a nested object */
parser->nested_object_count--;
parser->state = JSON_STATE_OBJECT_VALUE_NEXT;
+ if (parser->nested_skip_count > 0) {
+ parser->nested_skip_count--;
+ return 0;
+ }
*type_r = JSON_TYPE_OBJECT_END;
- return 0;
+ return 1;
}
parser->state = JSON_STATE_DONE;
- return json_try_parse_next(parser, type_r, value_r);
+ return 0;
}
static int
json_try_parse_next(struct json_parser *parser, enum json_type *type_r,
const char **value_r)
{
+ bool skipping = parser->skipping;
int ret;
if (!json_parse_whitespace(parser))
@@ -348,7 +358,7 @@
parser->data++;
parser->state = JSON_STATE_OBJECT_OPEN;
json_parser_update_input_pos(parser);
- return json_try_parse_next(parser, type_r, value_r);
+ return 0;
case JSON_STATE_OBJECT_VALUE:
if (*parser->data == '[') {
parser->error = "Arrays not supported";
@@ -358,10 +368,15 @@
parser->state = JSON_STATE_OBJECT_OPEN;
parser->nested_object_count++;
json_parser_update_input_pos(parser);
+
+ if (parser->skipping) {
+ parser->nested_skip_count++;
+ return 0;
+ }
*type_r = JSON_TYPE_OBJECT;
- return 0;
+ return 1;
}
- if ((ret = json_parse_string(parser, value_r)) >= 0) {
+ if ((ret = json_parse_string(parser, TRUE, value_r)) >= 0) {
*type_r = JSON_TYPE_STRING;
} else if ((ret = json_parse_number(parser, value_r)) >= 0) {
*type_r = JSON_TYPE_NUMBER;
@@ -380,6 +395,12 @@
}
if (ret == 0) {
i_assert(parser->data == parser->end);
+ if (parser->skipping && *type_r == JSON_TYPE_STRING) {
+ /* a large string that we want to skip over. */
+ json_parser_update_input_pos(parser);
+ parser->state = JSON_STATE_SKIP_STRING;
+ return 0;
+ }
return -1;
}
parser->state = parser->state == JSON_STATE_ROOT ?
@@ -388,11 +409,11 @@
break;
case JSON_STATE_OBJECT_OPEN:
if (*parser->data == '}')
- return json_parse_object_close(parser, type_r, value_r);
+ return json_parse_object_close(parser, type_r);
parser->state = JSON_STATE_OBJECT_KEY;
/* fall through */
case JSON_STATE_OBJECT_KEY:
- if (json_parse_string(parser, value_r) <= 0) {
+ if (json_parse_string(parser, FALSE, value_r) <= 0) {
parser->error = "Expected string as object key";
return -1;
}
@@ -407,10 +428,14 @@
parser->data++;
parser->state = JSON_STATE_OBJECT_VALUE;
json_parser_update_input_pos(parser);
- return json_try_parse_next(parser, type_r, value_r);
+ return 0;
case JSON_STATE_OBJECT_VALUE_NEXT:
+ if (parser->skipping && parser->nested_skip_count == 0) {
+ /* we skipped over the previous value */
+ parser->skipping = FALSE;
+ }
if (*parser->data == '}')
- return json_parse_object_close(parser, type_r, value_r);
+ return json_parse_object_close(parser, type_r);
if (*parser->data != ',') {
parser->error = "Expected ',' or '}' after object value";
return -1;
@@ -418,18 +443,18 @@
parser->state = JSON_STATE_OBJECT_KEY;
parser->data++;
json_parser_update_input_pos(parser);
- return json_try_parse_next(parser, type_r, value_r);
- case JSON_STATE_STRINPUT_FINISH:
- if (json_skip_string(parser) < 0)
+ return 0;
+ case JSON_STATE_SKIP_STRING:
+ if (json_skip_string(parser) <= 0)
return -1;
parser->state = JSON_STATE_OBJECT_VALUE_NEXT;
- return json_try_parse_next(parser, type_r, value_r);
+ return 0;
case JSON_STATE_DONE:
parser->error = "Unexpected data at the end";
return -1;
}
json_parser_update_input_pos(parser);
- return 0;
+ return skipping ? 0 : 1;
}
int json_parse_next(struct json_parser *parser, enum json_type *type_r,
@@ -442,7 +467,9 @@
*value_r = NULL;
while ((ret = json_parser_read_more(parser)) > 0) {
- if (json_try_parse_next(parser, type_r, value_r) == 0)
+ while ((ret = json_try_parse_next(parser, type_r, value_r)) == 0)
+ ;
+ if (ret > 0)
break;
if (parser->data != parser->end)
return -1;
@@ -454,6 +481,16 @@
return ret;
}
+void json_parse_skip_next(struct json_parser *parser)
+{
+ i_assert(!parser->skipping);
+ i_assert(parser->strinput == NULL);
+ i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
+ parser->state == JSON_STATE_OBJECT_VALUE);
+
+ parser->skipping = TRUE;
+}
+
static void json_strinput_destroyed(struct json_parser *parser)
{
i_assert(parser->strinput != NULL);
@@ -484,7 +521,7 @@
parser->data++;
json_parser_update_input_pos(parser);
- parser->state = JSON_STATE_STRINPUT_FINISH;
+ parser->state = JSON_STATE_SKIP_STRING;
parser->strinput = i_stream_create_jsonstr(parser->input);
i_stream_set_destroy_callback(parser->strinput,
json_strinput_destroyed, parser);
@@ -498,6 +535,7 @@
{
int ret;
+ i_assert(!parser->skipping);
i_assert(parser->strinput == NULL);
i_assert(parser->state == JSON_STATE_OBJECT_COLON ||
parser->state == JSON_STATE_OBJECT_VALUE);
diff -r 014be18f7130 -r 4f7e0be9cc35 src/lib/json-parser.h
--- a/src/lib/json-parser.h Thu Nov 29 07:52:51 2012 +0200
+++ b/src/lib/json-parser.h Thu Nov 29 09:38:34 2012 +0200
@@ -25,7 +25,9 @@
non-blocking and needs more input, -1 if input stream is at EOF. */
int json_parse_next(struct json_parser *parser, enum json_type *type_r,
const char **value_r);
More information about the dovecot-cvs
mailing list