dovecot-2.1: Added a simple JSON parser for parsing an object.

dovecot at dovecot.org dovecot at dovecot.org
Sun Jul 8 09:01:11 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.1/rev/01cdca5817f2
changeset: 14605:01cdca5817f2
user:      Timo Sirainen <tss at iki.fi>
date:      Sun Jul 08 07:37:28 2012 +0300
description:
Added a simple JSON parser for parsing an object.

diffstat:

 src/lib/Makefile.am   |    2 +
 src/lib/json-parser.c |  272 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/lib/json-parser.h |   22 ++++
 3 files changed, 296 insertions(+), 0 deletions(-)

diffs (truncated from 321 to 300 lines):

diff -r 8ef2b31b125f -r 01cdca5817f2 src/lib/Makefile.am
--- a/src/lib/Makefile.am	Sat Jul 07 16:27:59 2012 +0300
+++ b/src/lib/Makefile.am	Sun Jul 08 07:37:28 2012 +0300
@@ -74,6 +74,7 @@
 	ioloop-select.c \
 	ioloop-epoll.c \
 	ioloop-kqueue.c \
+	json-parser.c \
 	lib.c \
 	lib-signals.c \
 	md4.c \
@@ -183,6 +184,7 @@
 	ioloop-iolist.h \
 	ioloop-private.h \
 	ioloop-notify-fd.h \
+	json-parser.h \
 	lib.h \
 	lib-signals.h \
 	llist.h \
diff -r 8ef2b31b125f -r 01cdca5817f2 src/lib/json-parser.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/json-parser.c	Sun Jul 08 07:37:28 2012 +0300
@@ -0,0 +1,272 @@
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "hex-dec.h"
+#include "unichar.h"
+#include "json-parser.h"
+
+enum json_state {
+	JSON_STATE_ROOT = 0,
+	JSON_STATE_OBJECT_KEY,
+	JSON_STATE_OBJECT_COLON,
+	JSON_STATE_OBJECT_VALUE,
+	JSON_STATE_OBJECT_VALUE_NEXT,
+	JSON_STATE_DONE
+};
+
+struct json_parser {
+	const unsigned char *data, *end;
+	const char *error;
+	string_t *value;
+
+	enum json_state state;
+};
+
+struct json_parser *
+json_parser_init(const unsigned char *data, unsigned int len)
+{
+	struct json_parser *parser;
+
+	parser = i_new(struct json_parser, 1);
+	parser->data = data;
+	parser->end = data + len;
+	parser->value = str_new(default_pool, 128);
+	return parser;
+}
+
+int json_parser_deinit(struct json_parser **_parser, const char **error_r)
+{
+	struct json_parser *parser = *_parser;
+
+	*_parser = NULL;
+
+	if (parser->error == NULL && parser->data == parser->end &&
+	    parser->state != JSON_STATE_ROOT &&
+	    parser->state != JSON_STATE_DONE)
+		parser->error = "Missing '}'";
+
+	*error_r = parser->error;
+	str_free(&parser->value);
+	i_free(parser);
+	return *error_r != NULL ? -1 : 0;
+}
+
+static bool json_parse_whitespace(struct json_parser *parser)
+{
+	for (; parser->data != parser->end; parser->data++) {
+		switch (*parser->data) {
+		case ' ':
+		case '\t':
+		case '\r':
+		case '\n':
+			break;
+		default:
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static int json_parse_string(struct json_parser *parser, const char **value_r)
+{
+	const unsigned char *p;
+
+	if (*parser->data != '"')
+		return -1;
+
+	str_truncate(parser->value, 0);
+	for (p = parser->data + 1; p < parser->end; p++) {
+		if (*p == '"') {
+			parser->data = p + 1;
+			*value_r = str_c(parser->value);
+			return 0;
+		}
+		if (*p != '\\')
+			str_append_c(parser->value, *p);
+		else {
+			switch (*++p) {
+			case '"':
+			case '\\':
+			case '/':
+				str_append_c(parser->value, *p);
+				break;
+			case 'b':
+				str_append_c(parser->value, '\b');
+				break;
+			case 'f':
+				str_append_c(parser->value, '\f');
+				break;
+			case 'n':
+				str_append_c(parser->value, '\n');
+				break;
+			case 'r':
+				str_append_c(parser->value, '\r');
+				break;
+			case 't':
+				str_append_c(parser->value, '\t');
+				break;
+			case 'u':
+				if (parser->end - p < 4)
+					return -1;
+				uni_ucs4_to_utf8_c(hex2dec(p, 4),
+						   parser->value);
+				p += 3;
+				break;
+			default:
+				return -1;
+			}
+		}
+	}
+	return -1;
+}
+
+static int
+json_parse_digits(struct json_parser *parser, const unsigned char **_p)
+{
+	const unsigned char *p = *_p;
+
+	if (p >= parser->end || *p < '0' || *p > '9')
+		return -1;
+
+	for (; p < parser->end && *p >= '0' && *p <= '9'; p++)
+		str_append_c(parser->value, *p++);
+	*_p = p;
+	return 0;
+}
+
+static int json_parse_int(struct json_parser *parser, const unsigned char **_p)
+{
+	const unsigned char *p = *_p;
+
+	if (*p == '-') {
+		str_append_c(parser->value, *p++);
+		if (p == parser->end)
+			return -1;
+	}
+	if (*p == '0')
+		str_append_c(parser->value, *p++);
+	else {
+		if (json_parse_digits(parser, &p) < 0)
+			return -1;
+	}
+	*_p = p;
+	return 0;
+}
+
+static int json_parse_number(struct json_parser *parser, const char **value_r)
+{
+	const unsigned char *p = parser->data;
+
+	str_truncate(parser->value, 0);
+	if (json_parse_int(parser, &p) < 0)
+		return -1;
+	if (p < parser->end && *p == '.') {
+		/* frac */
+		str_append_c(parser->value, *p++);
+		if (json_parse_digits(parser, &p) < 0)
+			return -1;
+	}
+	if (p < parser->end && (*p == 'e' || *p == 'E')) {
+		/* exp */
+		str_append_c(parser->value, *p++);
+		if (p == parser->end)
+			return -1;
+		if (*p == '+' || *p == '-')
+			str_append_c(parser->value, *p++);
+		if (json_parse_digits(parser, &p) < 0)
+			return -1;
+	}
+	*value_r = str_c(parser->value);
+	return 0;
+}
+
+static int json_parse_atom(struct json_parser *parser, const char *atom)
+{
+	unsigned int len = strlen(atom);
+
+	if (parser->end - parser->data < len)
+		return -1;
+	if (memcmp(parser->data, atom, len) != 0)
+		return -1;
+	parser->data += len;
+	return 0;
+}
+
+bool json_parse_next(struct json_parser *parser, enum json_type *type_r,
+		     const char **value_r)
+{
+	*value_r = NULL;
+
+	if (!json_parse_whitespace(parser) || parser->error != NULL)
+		return FALSE;
+
+	switch (parser->state) {
+	case JSON_STATE_ROOT:
+		if (*parser->data == '{') {
+			parser->data++;
+			parser->state = JSON_STATE_OBJECT_KEY;
+			return json_parse_next(parser, type_r, value_r);
+		}
+		/* fall through */
+	case JSON_STATE_OBJECT_VALUE:
+		if (json_parse_string(parser, value_r) == 0)
+			*type_r = JSON_TYPE_STRING;
+		else if (json_parse_number(parser, value_r) == 0)
+			*type_r = JSON_TYPE_NUMBER;
+		else if (json_parse_atom(parser, "true") == 0) {
+			*type_r = JSON_TYPE_TRUE;
+			*value_r = "true";
+		} else if (json_parse_atom(parser, "false") == 0) {
+			*type_r = JSON_TYPE_FALSE;
+			*value_r = "false";
+		} else if (json_parse_atom(parser, "null") == 0) {
+			*type_r = JSON_TYPE_NULL;
+			*value_r = NULL;
+		} else if (*parser->data == '[') {
+			parser->error = "Arrays not supported";
+			return FALSE;
+		} else if (*parser->data == '{') {
+			parser->error = "Nested objects not supported";
+			return FALSE;
+		} else {
+			parser->error = "Invalid data as value";
+			return FALSE;
+		}
+		parser->state = parser->state == JSON_STATE_ROOT ?
+			JSON_STATE_DONE :
+			JSON_STATE_OBJECT_VALUE_NEXT;
+		break;
+	case JSON_STATE_OBJECT_KEY:
+		*type_r = JSON_TYPE_OBJECT_KEY;
+		if (json_parse_string(parser, value_r) < 0) {
+			parser->error = "Expected string as object key";
+			return FALSE;
+		}
+		parser->state = JSON_STATE_OBJECT_COLON;
+		break;
+	case JSON_STATE_OBJECT_COLON:
+		if (*parser->data != ':') {
+			parser->error = "Expected ':' after key";
+			return FALSE;
+		}
+		parser->data++;
+		parser->state = JSON_STATE_OBJECT_VALUE;
+		return json_parse_next(parser, type_r, value_r);
+	case JSON_STATE_OBJECT_VALUE_NEXT:
+		if (*parser->data == ',')
+			parser->state = JSON_STATE_OBJECT_KEY;
+		else if (*parser->data == '}')
+			parser->state = JSON_STATE_DONE;
+		else {
+			parser->error = "Expected ',' or '}' after object value";
+			return FALSE;
+		}
+		parser->data++;
+		return json_parse_next(parser, type_r, value_r);
+	case JSON_STATE_DONE:
+		parser->error = "Unexpected data at the end";
+		return FALSE;
+	}
+	return TRUE;
+}
diff -r 8ef2b31b125f -r 01cdca5817f2 src/lib/json-parser.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/json-parser.h	Sun Jul 08 07:37:28 2012 +0300
@@ -0,0 +1,22 @@
+#ifndef JSON_PARSER_H


More information about the dovecot-cvs mailing list