Commits

Serge Zaitsev committed 58b4b4f

added way to specify json string length, added test for non-strict mode

Comments (0)

Files changed (3)

  * Fills next available token with JSON primitive.
  */
 static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
-		jsmntok_t *tokens, size_t num_tokens) {
+		size_t len, jsmntok_t *tokens, size_t num_tokens) {
 	jsmntok_t *token;
 	int start;
 
 	start = parser->pos;
 
-	for (; js[parser->pos] != '\0'; parser->pos++) {
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
 		switch (js[parser->pos]) {
 #ifndef JSMN_STRICT
 			/* In strict mode primitive must be followed by "," or "}" or "]" */
  * Filsl next token with JSON string.
  */
 static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
-		jsmntok_t *tokens, size_t num_tokens) {
+		size_t len, jsmntok_t *tokens, size_t num_tokens) {
 	jsmntok_t *token;
 
 	int start = parser->pos;
 	parser->pos++;
 
 	/* Skip starting quote */
-	for (; js[parser->pos] != '\0'; parser->pos++) {
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
 		char c = js[parser->pos];
 
 		/* Quote: end of string */
 /**
  * Parse JSON string and fill tokens.
  */
-jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, 
-		unsigned int num_tokens) {
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+		jsmntok_t *tokens, unsigned int num_tokens) {
 	jsmnerr_t r;
 	int i;
 	jsmntok_t *token;
 	int count = 0;
 
-	for (; js[parser->pos] != '\0'; parser->pos++) {
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
 		char c;
 		jsmntype_t type;
 
 		c = js[parser->pos];
 		switch (c) {
 			case '{': case '[':
+				count++;
 				if (tokens == NULL) {
-					count++;
 					break;
 				}
 				token = jsmn_alloc_token(parser, tokens, num_tokens);
 #endif
 				break;
 			case '\"':
-				r = jsmn_parse_string(parser, js, tokens, num_tokens);
+				r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
 				if (r < 0) return r;
 				count++;
 				if (parser->toksuper != -1 && tokens != NULL)
 			/* In non-strict mode every unquoted value is a primitive */
 			default:
 #endif
-				r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
+				r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
 				if (r < 0) return r;
 				count++;
 				if (parser->toksuper != -1 && tokens != NULL)
  * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
  * a single JSON object.
  */
-jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, 
+jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
 		jsmntok_t *tokens, unsigned int num_tokens);
 
 #endif /* __JSMN_H_ */
 	printf("start: %d, end: %d, type: %d, size: %d\n", \
 			(t).start, (t).end, (t).type, (t).size)
 
+#define JSMN_STRICT
 #include "jsmn.c"
 
 int test_empty() {
 
 	js = "{}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, t, 10);
+	r = jsmn_parse(&p, js, strlen(js), t, 10);
 	check(r >= 0);
 	check(t[0].type == JSMN_OBJECT);
 	check(t[0].start == 0 && t[0].end == 2);
 
 	js = "[]";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, t, 10);
+	r = jsmn_parse(&p, js, strlen(js), t, 10);
 	check(r >= 0);
 	check(t[0].type == JSMN_ARRAY);
 	check(t[0].start == 0 && t[0].end == 2);
 
 	js = "{\"a\":[]}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, t, 10);
+	r = jsmn_parse(&p, js, strlen(js), t, 10);
 	check(r >= 0);
 	check(t[0].type == JSMN_OBJECT && t[0].start == 0 && t[0].end == 8);
 	check(t[1].type == JSMN_STRING && t[1].start == 2 && t[1].end == 3);
 
 	js = "[{},{}]";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, t, 10);
+	r = jsmn_parse(&p, js, strlen(js), t, 10);
 	check(r >= 0);
 	check(t[0].type == JSMN_ARRAY && t[0].start == 0 && t[0].end == 7);
 	check(t[1].type == JSMN_OBJECT && t[1].start == 1 && t[1].end == 3);
 	js = "{\"a\": 0}";
 
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 	check(TOKEN_EQ(tokens[0], 0, 8, JSMN_OBJECT));
 	check(TOKEN_EQ(tokens[1], 2, 3, JSMN_STRING));
 
 	jsmn_init(&p);
 	js = "[\"a\":{},\"b\":{}]";
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	jsmn_init(&p);
 	js = "{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }";
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	return 0;
 #ifndef JSMN_STRICT
 	js = "\"boolVar\" : true";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_PRIMITIVE);
 	check(TOKEN_STRING(js, tok[0], "boolVar"));
 
 	js = "\"boolVar\" : false";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_PRIMITIVE);
 	check(TOKEN_STRING(js, tok[0], "boolVar"));
 
 	js = "\"intVar\" : 12345";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_PRIMITIVE);
 	check(TOKEN_STRING(js, tok[0], "intVar"));
 
 	js = "\"floatVar\" : 12.345";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_PRIMITIVE);
 	check(TOKEN_STRING(js, tok[0], "floatVar"));
 
 	js = "\"nullVar\" : null";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_PRIMITIVE);
 	check(TOKEN_STRING(js, tok[0], "nullVar"));
 
 	js = "\"strVar\" : \"hello world\"";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_STRING);
 	check(TOKEN_STRING(js, tok[0], "strVar"));
 
 	js = "\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_STRING);
 	check(TOKEN_STRING(js, tok[0], "strVar"));
 
 	js = "\"strVar\" : \"\"";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING 
 			&& tok[1].type == JSMN_STRING);
 	check(TOKEN_STRING(js, tok[0], "strVar"));
 
 	jsmn_init(&p);
 	js = "\"x\": \"va";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING);
 	check(TOKEN_STRING(js, tok[0], "x"));
 	check(p.toknext == 1);
 
 	js = "\"x\": \"valu";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING);
 	check(TOKEN_STRING(js, tok[0], "x"));
 	check(p.toknext == 1);
 
 	js = "\"x\": \"value\"";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING
 			&& tok[1].type == JSMN_STRING);
 	check(TOKEN_STRING(js, tok[0], "x"));
 	check(TOKEN_STRING(js, tok[1], "value"));
 
 	js = "\"x\": \"value\", \"y\": \"value y\"";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_STRING
 			&& tok[1].type == JSMN_STRING && tok[2].type == JSMN_STRING
 			&& tok[3].type == JSMN_STRING);
 	jsmn_init(&p);
 	js = "key1: \"value\"\nkey2 : 123";
 
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_PRIMITIVE
 			&& tok[1].type == JSMN_STRING && tok[2].type == JSMN_PRIMITIVE
 			&& tok[3].type == JSMN_PRIMITIVE);
 
 	jsmn_init(&p);
 	js = "  [ 1, true, ";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY 
 			&& tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE);
 
 	js = "  [ 1, true, [123, \"hello";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY 
 			&& tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE
 			&& tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE);
 
 	js = "  [ 1, true, [123, \"hello\"]";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY 
 			&& tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE
 			&& tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE
 	check(tok[3].size == 2);
 
 	js = "  [ 1, true, [123, \"hello\"]]";
-	r = jsmn_parse(&p, js, tok, 10);
+	r = jsmn_parse(&p, js, strlen(js), tok, 10);
 	check(r >= 0 && tok[0].type == JSMN_ARRAY 
 			&& tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE
 			&& tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE
 		jsmn_init(&p);
 		memset(toksmall, 0, sizeof(toksmall));
 		memset(toklarge, 0, sizeof(toklarge));
-		r = jsmn_parse(&p, js, toksmall, i);
+		r = jsmn_parse(&p, js, strlen(js), toksmall, i);
 		check(r == JSMN_ERROR_NOMEM);
 
 		memcpy(toklarge, toksmall, sizeof(toksmall));
 
-		r = jsmn_parse(&p, js, toklarge, 10);
+		r = jsmn_parse(&p, js, strlen(js), toklarge, 10);
 		check(r >= 0);
 
 		check(toklarge[0].type == JSMN_ARRAY && toklarge[0].size == 3);
 
 	js = "[10}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r == JSMN_ERROR_INVAL);
 
 	js = "[10]";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	js = "{\"a\": 1]";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r == JSMN_ERROR_INVAL);
 
 	js = "{\"a\": 1}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	return 0;
 		"\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], "
 		"\"tilewidth\":32, \"version\":1, \"width\":10 }";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 128);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 128);
 	check(r >= 0);
 #if 0
 	for (i = 1; tokens[i].end < tokens[0].end; i++) {
 	int r;
 	js = "{\"a\":\"\\uAbcD\"}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	js = "{\"a\":\"str\\u0000\"}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	js = "{\"a\":\"\\uFFFFstr\"}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	js = "{\"a\":\"str\\uFFGFstr\"}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r == JSMN_ERROR_INVAL);
 
 	js = "{\"a\":\"str\\u@FfF\"}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r == JSMN_ERROR_INVAL);
 
 	js = "{\"a\":[\"\\u028\"]}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r == JSMN_ERROR_INVAL);
 
 	js = "{\"a\":[\"\\u0280\"]}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
 	check(r >= 0);
 
 	return 0;
 }
 
+int test_input_length() {
+	const char *js;
+	int r;
+	jsmn_parser p;
+	jsmntok_t tokens[10];
+
+	js = "{\"a\": 0}garbage";
+
+	jsmn_init(&p);
+	r = jsmn_parse(&p, js, 8, tokens, 10);
+	check(r == 3);
+	check(TOKEN_STRING(js, tokens[0], "{\"a\": 0}"));
+	check(TOKEN_STRING(js, tokens[1], "a"));
+	check(TOKEN_STRING(js, tokens[2], "0"));
+
+	return 0;
+}
+
 int test_count() {
 	jsmn_parser p;
 	const char *js;
 
 	js = "{}";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 1);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
 
 	js = "[]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 1);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
 
 	js = "[[]]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 2);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2);
 
 	js = "[[], []]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 3);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
 
 	js = "[[], []]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 3);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
 
 	js = "[[], [[]], [[], []]]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 7);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
 	
 	js = "[\"a\", [[], []]]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 5);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
 
 	js = "[[], \"[], [[]]\", [[]]]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 5);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
 
 	js = "[1, 2, 3]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 4);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4);
 
 	js = "[1, 2, [3, \"a\"], null]";
 	jsmn_init(&p);
-	check(jsmn_parse(&p, js, NULL, 0) == 7);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
 	
 	return 0;
 }
 
+/** A huge redefinition of everything to include jsmn in non-script mode */
+#define jsmn_init jsmn_init_nonstrict
+#define jsmn_parse jsmn_parse_nonstrict
+#define jsmn_parser jsmn_parser_nonstrict
+#define jsmn_alloc_token jsmn_alloc_token_nonstrict
+#define jsmn_fill_token jsmn_fill_token_nonstrict
+#define jsmn_parse_primitive jsmn_parse_primitive_nonstrict
+#define jsmn_parse_string jsmn_parse_string_nonstrict
+#define jsmntype_t jsmntype_nonstrict_t
+#define jsmnerr_t jsmnerr_nonstrict_t
+#define jsmntok_t jsmntok_nonstrict_t
+#define JSMN_PRIMITIVE JSMN_PRIMITIVE_NONSTRICT
+#define JSMN_OBJECT JSMN_OBJECT_NONSTRICT
+#define JSMN_ARRAY JSMN_ARRAY_NONSTRICT
+#define JSMN_STRING JSMN_STRING_NONSTRICT
+#define JSMN_ERROR_NOMEM JSMN_ERROR_NOMEM_NONSTRICT
+#define JSMN_ERROR_INVAL JSMN_ERROR_INVAL_NONSTRICT
+#define JSMN_ERROR_PART JSMN_ERROR_PART_NONSTRICT
+#undef __JSMN_H_
+#undef JSMN_STRICT
+#include "jsmn.c"
+
+int test_nonstrict() {
+	const char *js;
+	int r;
+	jsmn_parser p;
+	jsmntok_t tokens[10];
+
+	js = "a: 0garbage";
+
+	jsmn_init(&p);
+	r = jsmn_parse(&p, js, 4, tokens, 10);
+	check(r == 2);
+	check(TOKEN_STRING(js, tokens[0], "a"));
+	check(TOKEN_STRING(js, tokens[1], "0"));
+
+	js = "Day : 26\nMonth : Sep\n\nYear: 12";
+	jsmn_init(&p);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
+	check(r == 6);
+	return 0;
+}
+
 int main() {
 	test(test_empty, "general test for a empty JSON objects/arrays");
 	test(test_simple, "general test for a simple JSON string");
 	test(test_unquoted_keys, "test unquoted keys (like in JavaScript)");
 	test(test_objects_arrays, "test objects and arrays");
 	test(test_unicode_characters, "test unicode characters");
+	test(test_input_length, "test strings that are not null-terminated");
 	test(test_issue_22, "test issue #22");
 	test(test_count, "test tokens count estimation");
+	test(test_nonstrict, "test for non-strict mode");
 	printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed);
 	return 0;
 }