diff --git a/README.md b/README.md index abccffa..b33113c 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,8 @@ To clone the repository you should have mercurial installed. Just run: $ 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. diff --git a/jsmn.c b/jsmn.c index b54b941..5638139 100644 --- a/jsmn.c +++ b/jsmn.c @@ -1,10 +1,4 @@ #include -//#include -#include // for SIZE_MAX - -#ifndef SIZE_MAX // SIZE_MAX came in C99 - #define SIZE_MAX ((size_t)-1) -#endif #include "jsmn.h" @@ -41,13 +35,13 @@ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, * 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 "]" */ @@ -69,6 +63,10 @@ static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, #endif found: + if (tokens == NULL) { + parser->pos--; + return 0; + } token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->pos = start; @@ -79,14 +77,14 @@ found: 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; @@ -94,11 +92,14 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, 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; @@ -108,7 +109,7 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif - return JSMN_SUCCESS; + return 0; } /* Backslash: Quoted symbol expected */ @@ -146,32 +147,27 @@ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, 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; @@ -186,6 +182,8 @@ jsmnerr_t jsmn_parseV2(jsmn_parser *parser, const char *js, size_t js_length, js 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) { @@ -230,9 +228,10 @@ jsmnerr_t jsmn_parseV2(jsmn_parser *parser, const char *js, size_t js_length, js #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 ' ': @@ -246,9 +245,10 @@ jsmnerr_t jsmn_parseV2(jsmn_parser *parser, const char *js, size_t js_length, js /* 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; @@ -257,7 +257,6 @@ jsmnerr_t jsmn_parseV2(jsmn_parser *parser, const char *js, size_t js_length, js default: return JSMN_ERROR_INVAL; #endif - } } @@ -268,7 +267,7 @@ jsmnerr_t jsmn_parseV2(jsmn_parser *parser, const char *js, size_t js_length, js } } - return JSMN_SUCCESS; + return count; } /** diff --git a/jsmn.h b/jsmn.h index 8f10cf9..54930ad 100644 --- a/jsmn.h +++ b/jsmn.h @@ -1,10 +1,6 @@ #ifndef __JSMN_H_ #define __JSMN_H_ -#ifdef __cplusplus -extern "C" { -#endif - /** * JSON type identifier. Basic types are: * o Object @@ -26,8 +22,6 @@ typedef enum { 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; /** @@ -62,21 +56,10 @@ typedef struct { 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_ */ diff --git a/jsmn_test.c b/jsmn_test.c index fe5f000..c5bfc03 100644 --- a/jsmn_test.c +++ b/jsmn_test.c @@ -2,8 +2,6 @@ #include #include -#include "jsmn.c" - static int test_passed = 0; static int test_failed = 0; @@ -40,6 +38,9 @@ static void test(int (*func)(void), const char *name) { 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; @@ -48,30 +49,30 @@ int test_empty() { 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); @@ -87,8 +88,8 @@ int test_simple() { 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)); @@ -99,13 +100,13 @@ int test_simple() { 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; } @@ -118,40 +119,40 @@ int test_primitive() { #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")); @@ -167,24 +168,24 @@ int test_string() { 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], "")); @@ -200,27 +201,27 @@ int test_partial_string() { 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")); @@ -241,8 +242,8 @@ int test_unquoted_keys() { 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")); @@ -261,18 +262,18 @@ int test_partial_array() { 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 @@ -281,8 +282,8 @@ int test_partial_array() { 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); @@ -304,13 +305,13 @@ int test_array_nomem() { 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); @@ -327,27 +328,61 @@ int test_objects_arrays() { 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]; @@ -356,39 +391,147 @@ int test_unicode_characters() { 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; } @@ -403,6 +546,10 @@ int main() { 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; }