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