Commits

Gabriel Gritsch committed 51b0e1e

Integrate changes made by Serge Zaitsev.

  • Participants
  • Parent commits d7ff763

Comments (0)

Files changed (4)

 
 	$ hg clone http://bitbucket.org/zserge/jsmn jsmn
 
-Repository layout is simple: jsmn.c and jsmn.h are library files; demo.c is an
-example of how to use jsmn (it is also used in unit tests); test.sh is a test
-script. You will also find README, LICENSE and Makefile files inside.
+Repository layout is simple: jsmn.c and jsmn.h are library files, tests are in
+the jsmn\_test.c, you will also find README, LICENSE and Makefile files inside.
 
 To build the library, run `make`. It is also recommended to run `make test`.
 Let me know, if some tests fail.
 #include <stdlib.h>
-//#include <limits.h>
-#include <stdint.h>		//	for SIZE_MAX
-
-#ifndef SIZE_MAX		// SIZE_MAX came in C99
-	#define SIZE_MAX ((size_t)-1)
-#endif
 
 #include "jsmn.h"
 
  * 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 "]" */
 #endif
 
 found:
+	if (tokens == NULL) {
+		parser->pos--;
+		return 0;
+	}
 	token = jsmn_alloc_token(parser, tokens, num_tokens);
 	if (token == NULL) {
 		parser->pos = start;
 	token->parent = parser->toksuper;
 #endif
 	parser->pos--;
-	return JSMN_SUCCESS;
+	return 0;
 }
 
 /**
  * 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 */
 		if (c == '\"') {
+			if (tokens == NULL) {
+				return 0;
+			}
 			token = jsmn_alloc_token(parser, tokens, num_tokens);
 			if (token == NULL) {
 				parser->pos = start;
 #ifdef JSMN_PARENT_LINKS
 			token->parent = parser->toksuper;
 #endif
-			return JSMN_SUCCESS;
+			return 0;
 		}
 
 		/* Backslash: Quoted symbol expected */
 	return JSMN_ERROR_PART;
 }
 
-static inline jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, unsigned int num_tokens)
-{
-	return jsmn_parseV2(parser, js, SIZE_MAX, tokens, num_tokens);
-}
-
 /**
  * Parse JSON string and fill tokens.
  */
-jsmnerr_t jsmn_parseV2(jsmn_parser *parser, const char *js, size_t js_length, jsmntok_t *tokens, 
-		unsigned int num_tokens) {
-	if (js==NULL || js_length==0)
-	{
-		return JSMN_ERROR_PART;
-	}
-	
+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 (; parser->pos < js_length && 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) {
+					break;
+				}
 				token = jsmn_alloc_token(parser, tokens, num_tokens);
 				if (token == NULL)
 					return JSMN_ERROR_NOMEM;
 				parser->toksuper = parser->toknext - 1;
 				break;
 			case '}': case ']':
+				if (tokens == NULL)
+					break;
 				type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
 #ifdef JSMN_PARENT_LINKS
 				if (parser->toknext < 1) {
 #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;
-				if (parser->toksuper != -1)
+				count++;
+				if (parser->toksuper != -1 && tokens != NULL)
 					tokens[parser->toksuper].size++;
 				break;
 			case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': 
 			/* 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;
-				if (parser->toksuper != -1)
+				count++;
+				if (parser->toksuper != -1 && tokens != NULL)
 					tokens[parser->toksuper].size++;
 				break;
 
 			default:
 				return JSMN_ERROR_INVAL;
 #endif
-
 		}
 	}
 
 		}
 	}
 
-	return JSMN_SUCCESS;
+	return count;
 }
 
 /**
 #ifndef __JSMN_H_
 #define __JSMN_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * JSON type identifier. Basic types are:
  * 	o Object
 	JSMN_ERROR_INVAL = -2,
 	/* The string is not a full JSON packet, more bytes expected */
 	JSMN_ERROR_PART = -3,
-	/* Everything was fine */
-	JSMN_SUCCESS = 0
 } jsmnerr_t;
 
 /**
 void jsmn_init(jsmn_parser *parser);
 
 /**
- * Run JSON parser. It parses a JSON data null-terminated-string into and array of tokens, each describing
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
  * a single JSON object.
  */
-static inline 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);
 
-/**
- * Run JSON parser. It parses a JSON data buffer with specified length into and array of tokens, each describing
- * a single JSON object.
- */
-jsmnerr_t jsmn_parseV2(jsmn_parser *parser, const char *js, size_t js_length,
-		jsmntok_t *tokens, unsigned int num_tokens);
-
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* __JSMN_H_ */
 #include <stdlib.h>
 #include <string.h>
 
-#include "jsmn.c"
-
 static int test_passed = 0;
 static int test_failed = 0;
 
 	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() {
 	const char *js;
 	int r;
 
 	js = "{}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, t, 10);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS);
+	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);
 	check(t[2].type == JSMN_ARRAY && t[2].start == 5 && t[2].end == 7);
 
 	js = "[{},{}]";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, t, 10);
-	check(r == JSMN_SUCCESS);
+	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);
 	check(t[2].type == JSMN_OBJECT && t[2].start == 4 && t[2].end == 6);
 	js = "{\"a\": 0}";
 
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
-	check(r == JSMN_SUCCESS);
+	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));
 	check(TOKEN_EQ(tokens[2], 6, 7, JSMN_PRIMITIVE));
 
 	jsmn_init(&p);
 	js = "[\"a\":{},\"b\":{}]";
-	r = jsmn_parse(&p, js, tokens, 10);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], "true"));
 
 	js = "\"boolVar\" : false";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], "false"));
 
 	js = "\"intVar\" : 12345";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], "12345"));
 
 	js = "\"floatVar\" : 12.345";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], "12.345"));
 
 	js = "\"nullVar\" : null";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], "null"));
 
 	js = "\"strVar\" : \"hello world\"";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], "hello world"));
 
 	js = "\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\"));
 
 	js = "\"strVar\" : \"\"";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING 
+	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"));
 	check(TOKEN_STRING(js, tok[1], ""));
 
 	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);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+	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);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_STRING
+	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);
 	check(TOKEN_STRING(js, tok[0], "x"));
 	jsmn_init(&p);
 	js = "key1: \"value\"\nkey2 : 123";
 
-	r = jsmn_parse(&p, js, tok, 10);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_PRIMITIVE
+	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);
 	check(TOKEN_STRING(js, tok[0], "key1"));
 
 	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);
-	check(r == JSMN_SUCCESS && tok[0].type == JSMN_ARRAY 
+	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
 			&& tok[5].type == JSMN_STRING);
 		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);
-		check(r == JSMN_SUCCESS);
+		r = jsmn_parse(&p, js, strlen(js), toklarge, 10);
+		check(r >= 0);
 
 		check(toklarge[0].type == JSMN_ARRAY && toklarge[0].size == 3);
 		check(toklarge[3].type == JSMN_ARRAY && toklarge[3].size == 2);
 
 	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);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 10);
+	check(r >= 0);
 
 	return 0;
 }
 
+int test_issue_22() {
+	int i;
+	int r;
+	jsmn_parser p;
+	jsmntok_t tokens[128];
+	const char *js;
+
+	js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, "
+		"\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", "
+		"\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], "
+		"\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, "
+		"\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", "
+		"\"imageheight\":64, \"imagewidth\":160, \"margin\":0, \"name\":\"Tiles\", "
+		"\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], "
+		"\"tilewidth\":32, \"version\":1, \"width\":10 }";
+	jsmn_init(&p);
+	r = jsmn_parse(&p, js, strlen(js), tokens, 128);
+	check(r >= 0);
+#if 0
+	for (i = 1; tokens[i].end < tokens[0].end; i++) {
+		if (tokens[i].type == JSMN_STRING || tokens[i].type == JSMN_PRIMITIVE) {
+			printf("%.*s\n", tokens[i].end - tokens[i].start, js + tokens[i].start);
+		} else if (tokens[i].type == JSMN_ARRAY) {
+			printf("[%d elems]\n", tokens[i].size);
+		} else if (tokens[i].type == JSMN_OBJECT) {
+			printf("{%d elems}\n", tokens[i].size);
+		} else {
+			TOKEN_PRINT(tokens[i]);
+		}
+	}
+#endif
+	return 0;
+}
+
 int test_unicode_characters() {
 	jsmn_parser p;
 	jsmntok_t tokens[10];
 	int r;
 	js = "{\"a\":\"\\uAbcD\"}";
 	jsmn_init(&p);
-	r = jsmn_parse(&p, js, tokens, 10);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS);
+	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);
-	check(r == JSMN_SUCCESS);
+	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, strlen(js), NULL, 0) == 1);
+
+	js = "[]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
+
+	js = "[[]]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2);
+
+	js = "[[], []]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
+
+	js = "[[], []]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
+
+	js = "[[], [[]], [[], []]]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
+	
+	js = "[\"a\", [[], []]]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
+
+	js = "[[], \"[], [[]]\", [[]]]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
+
+	js = "[1, 2, 3]";
+	jsmn_init(&p);
+	check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4);
+
+	js = "[1, 2, [3, \"a\"], null]";
+	jsmn_init(&p);
+	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;
 }