Compare commits

...

12 Commits

@ -0,0 +1,90 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IndentCaseLabels: false
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...

@ -1,41 +1,36 @@
# You can put your build options here
-include config.mk
all: libjsmn.a
libjsmn.a: jsmn.o
$(AR) rc $@ $^
%.o: %.c jsmn.h
$(CC) -c $(CFLAGS) $< -o $@
test: test_default test_strict test_links test_strict_links
test_default: test/tests.c
test_default: test/tests.c jsmn.h
$(CC) $(CFLAGS) $(LDFLAGS) $< -o test/$@
./test/$@
test_strict: test/tests.c
test_strict: test/tests.c jsmn.h
$(CC) -DJSMN_STRICT=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
./test/$@
test_links: test/tests.c
test_links: test/tests.c jsmn.h
$(CC) -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
./test/$@
test_strict_links: test/tests.c
test_strict_links: test/tests.c jsmn.h
$(CC) -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@
./test/$@
jsmn_test.o: jsmn_test.c libjsmn.a
simple_example: example/simple.c jsmn.h
$(CC) $(LDFLAGS) $< -o $@
jsondump: example/jsondump.c jsmn.h
$(CC) $(LDFLAGS) $< -o $@
simple_example: example/simple.o libjsmn.a
$(CC) $(LDFLAGS) $^ -o $@
fmt:
clang-format -i jsmn.h test/*.[ch] example/*.[ch]
jsondump: example/jsondump.o libjsmn.a
$(CC) $(LDFLAGS) $^ -o $@
lint:
clang-tidy jsmn.h --checks='*'
clean:
rm -f *.o example/*.o
rm -f *.a *.so
rm -f simple_example
rm -f jsondump
.PHONY: all clean test
.PHONY: clean test

@ -76,21 +76,35 @@ object hierarchy.
This approach provides enough information for parsing any JSON data and makes
it possible to use zero-copy techniques.
Install
-------
Usage
-----
To clone the repository you should have Git installed. Just run:
Download `jsmn.h`, include it, done.
$ git clone https://github.com/zserge/jsmn
```
#include "jsmn.h"
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.
...
jsmn_parser p;
jsmntok_t t[128]; /* We expect no more than 128 JSON tokens */
To build the library, run `make`. It is also recommended to run `make test`.
Let me know, if some tests fail.
jsmn_init(&p);
r = jsmn_parse(&p, s, strlen(s), t, 128);
```
If build was successful, you should get a `libjsmn.a` library.
The header file you should include is called `"jsmn.h"`.
Since jsmn is a single-header, header-only library, for more complex use cases
you might need to define additional macros. `#define JSMN_STATIC` hides all
jsmn API symbols by making them static. Also, if you want to include `jsmn.h`
from multiple C files, to avoid duplication of symbols you may define `JSMN_HEADER` macro.
```
/* In every .c file that uses jsmn include only declarations: */
#define JSMN_HEADER
#include "jsmn.h"
/* Additionally, create one jsmn.c file for jsmn implementation: */
#include "jsmn.h"
```
API
---

@ -1,9 +1,9 @@
#include "../jsmn.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "../jsmn.h"
#include <unistd.h>
/* Function realloc_it() is a wrapper function for standard realloc()
* with one difference - it frees old memory pointer in case of realloc
@ -14,7 +14,7 @@
static inline void *realloc_it(void *ptrmem, size_t size) {
void *p = realloc(ptrmem, size);
if (!p) {
free (ptrmem);
free(ptrmem);
fprintf(stderr, "realloc(): errno=%d\n", errno);
}
return p;
@ -27,36 +27,44 @@ static inline void *realloc_it(void *ptrmem, size_t size) {
static int dump(const char *js, jsmntok_t *t, size_t count, int indent) {
int i, j, k;
jsmntok_t *key;
if (count == 0) {
return 0;
}
if (t->type == JSMN_PRIMITIVE) {
printf("%.*s", t->end - t->start, js+t->start);
printf("%.*s", t->end - t->start, js + t->start);
return 1;
} else if (t->type == JSMN_STRING) {
printf("'%.*s'", t->end - t->start, js+t->start);
printf("'%.*s'", t->end - t->start, js + t->start);
return 1;
} else if (t->type == JSMN_OBJECT) {
printf("\n");
j = 0;
for (i = 0; i < t->size; i++) {
for (k = 0; k < indent; k++) printf(" ");
j += dump(js, t+1+j, count-j, indent+1);
for (k = 0; k < indent; k++) {
printf(" ");
}
key = t + 1 + j;
j += dump(js, key, count - j, indent + 1);
if (key->size > 0) {
printf(": ");
j += dump(js, t+1+j, count-j, indent+1);
j += dump(js, t + 1 + j, count - j, indent + 1);
}
printf("\n");
}
return j+1;
return j + 1;
} else if (t->type == JSMN_ARRAY) {
j = 0;
printf("\n");
for (i = 0; i < t->size; i++) {
for (k = 0; k < indent-1; k++) printf(" ");
for (k = 0; k < indent - 1; k++) {
printf(" ");
}
printf(" - ");
j += dump(js, t+1+j, count-j, indent+1);
j += dump(js, t + 1 + j, count - j, indent + 1);
printf("\n");
}
return j+1;
return j + 1;
}
return 0;
}
@ -105,7 +113,7 @@ int main() {
strncpy(js + jslen, buf, r);
jslen = jslen + r;
again:
again:
r = jsmn_parse(&p, js, jslen, tok, tokcount);
if (r < 0) {
if (r == JSMN_ERROR_NOMEM) {

@ -1,7 +1,7 @@
#include "../jsmn.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../jsmn.h"
/*
* A small example of jsmn parsing when JSON structure is known and number of
@ -13,7 +13,7 @@ static const char *JSON_STRING =
"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start &&
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
return 0;
}
@ -27,7 +27,8 @@ int main() {
jsmntok_t t[128]; /* We expect no more than 128 tokens */
jsmn_init(&p);
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t,
sizeof(t) / sizeof(t[0]));
if (r < 0) {
printf("Failed to parse JSON: %d\n", r);
return 1;
@ -43,32 +44,32 @@ int main() {
for (i = 1; i < r; i++) {
if (jsoneq(JSON_STRING, &t[i], "user") == 0) {
/* We may use strndup() to fetch string value */
printf("- User: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
printf("- User: %.*s\n", t[i + 1].end - t[i + 1].start,
JSON_STRING + t[i + 1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {
/* We may additionally check if the value is either "true" or "false" */
printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
printf("- Admin: %.*s\n", t[i + 1].end - t[i + 1].start,
JSON_STRING + t[i + 1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
/* We may want to do strtol() here to get numeric value */
printf("- UID: %.*s\n", t[i+1].end-t[i+1].start,
JSON_STRING + t[i+1].start);
printf("- UID: %.*s\n", t[i + 1].end - t[i + 1].start,
JSON_STRING + t[i + 1].start);
i++;
} else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
int j;
printf("- Groups:\n");
if (t[i+1].type != JSMN_ARRAY) {
if (t[i + 1].type != JSMN_ARRAY) {
continue; /* We expect groups to be an array of strings */
}
for (j = 0; j < t[i+1].size; j++) {
jsmntok_t *g = &t[i+j+2];
for (j = 0; j < t[i + 1].size; j++) {
jsmntok_t *g = &t[i + j + 2];
printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start);
}
i += t[i+1].size + 1;
i += t[i + 1].size + 1;
} else {
printf("Unexpected key: %.*s\n", t[i].end-t[i].start,
printf("Unexpected key: %.*s\n", t[i].end - t[i].start,
JSON_STRING + t[i].start);
}
}

314
jsmn.c

@ -1,314 +0,0 @@
#include "jsmn.h"
/**
* Allocates a fresh unused token from the token pool.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *tok;
if (parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
tok->start = tok->end = -1;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
#endif
return tok;
}
/**
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
int start, int end) {
token->type = type;
token->start = start;
token->end = end;
token->size = 0;
}
/**
* Fills next available token with JSON primitive.
*/
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
size_t len, jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *token;
int start;
start = 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 "]" */
case ':':
#endif
case '\t' : case '\r' : case '\n' : case ' ' :
case ',' : case ']' : case '}' :
goto found;
}
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
#ifdef JSMN_STRICT
/* In strict mode primitive must be followed by a comma/object/array */
parser->pos = start;
return JSMN_ERROR_PART;
#endif
found:
if (tokens == NULL) {
parser->pos--;
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
parser->pos--;
return 0;
}
/**
* Fills next token with JSON string.
*/
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
size_t len, jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *token;
int start = parser->pos;
parser->pos++;
/* Skip starting quote */
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;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
return 0;
}
/* Backslash: Quoted symbol expected */
if (c == '\\' && parser->pos + 1 < len) {
int i;
parser->pos++;
switch (js[parser->pos]) {
/* Allowed escaped symbols */
case '\"': case '/' : case '\\' : case 'b' :
case 'f' : case 'r' : case 'n' : case 't' :
break;
/* Allows escaped symbol \uXXXX */
case 'u':
parser->pos++;
for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
/* If it isn't a hex character we have an error */
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
parser->pos = start;
return JSMN_ERROR_INVAL;
}
parser->pos++;
}
parser->pos--;
break;
/* Unexpected symbol */
default:
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
}
parser->pos = start;
return JSMN_ERROR_PART;
}
/**
* Parse JSON string and fill tokens.
*/
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens) {
int r;
int i;
jsmntok_t *token;
int count = parser->toknext;
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;
if (parser->toksuper != -1) {
tokens[parser->toksuper].size++;
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
}
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = parser->pos;
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) {
return JSMN_ERROR_INVAL;
}
token = &tokens[parser->toknext - 1];
for (;;) {
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
}
if (token->parent == -1) {
if(token->type != type || parser->toksuper == -1) {
return JSMN_ERROR_INVAL;
}
break;
}
token = &tokens[token->parent];
}
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
parser->toksuper = -1;
token->end = parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1) return JSMN_ERROR_INVAL;
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
parser->toksuper = i;
break;
}
}
#endif
break;
case '\"':
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) return r;
count++;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
break;
case '\t' : case '\r' : case '\n' : case ' ':
break;
case ':':
parser->toksuper = parser->toknext - 1;
break;
case ',':
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
parser->toksuper = tokens[parser->toksuper].parent;
#else
for (i = parser->toknext - 1; i >= 0; i--) {
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = i;
break;
}
}
}
#endif
}
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
case '-': case '0': case '1' : case '2': case '3' : case '4':
case '5': case '6': case '7' : case '8': case '9':
case 't': case 'f': case 'n' :
/* And they must not be keys of the object */
if (tokens != NULL && parser->toksuper != -1) {
jsmntok_t *t = &tokens[parser->toksuper];
if (t->type == JSMN_OBJECT ||
(t->type == JSMN_STRING && t->size != 0)) {
return JSMN_ERROR_INVAL;
}
}
#else
/* In non-strict mode every unquoted value is a primitive */
default:
#endif
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
if (r < 0) return r;
count++;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
break;
#ifdef JSMN_STRICT
/* Unexpected char in strict mode */
default:
return JSMN_ERROR_INVAL;
#endif
}
}
if (tokens != NULL) {
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
if (tokens[i].start != -1 && tokens[i].end == -1) {
return JSMN_ERROR_PART;
}
}
}
return count;
}
/**
* Creates a new parser based over a given buffer with an array of tokens
* available.
*/
void jsmn_init(jsmn_parser *parser) {
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
}

410
jsmn.h

@ -1,5 +1,28 @@
#ifndef __JSMN_H_
#define __JSMN_H_
/*
* MIT License
*
* Copyright (c) 2010 Serge Zaitsev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef JSMN_H
#define JSMN_H
#include <stddef.h>
@ -7,6 +30,12 @@
extern "C" {
#endif
#ifdef JSMN_STATIC
#define JSMN_API static
#else
#define JSMN_API extern
#endif
/**
* JSON type identifier. Basic types are:
* o Object
@ -49,28 +78,391 @@ typedef struct {
/**
* JSON parser. Contains an array of token blocks available. Also stores
* the string being parsed now and current position in that string
* the string being parsed now and current position in that string.
*/
typedef struct {
unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */
int toksuper; /* superior token node, e.g. parent object or array */
} jsmn_parser;
/**
* Create JSON parser over an array of tokens
*/
void jsmn_init(jsmn_parser *parser);
JSMN_API void jsmn_init(jsmn_parser *parser);
/**
* Run JSON parser. It parses a JSON data 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.
*/
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens);
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens);
#ifndef JSMN_HEADER
/**
* Allocates a fresh unused token from the token pool.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *tok;
if (parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
tok->start = tok->end = -1;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
#endif
return tok;
}
/**
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
const int start, const int end) {
token->type = type;
token->start = start;
token->end = end;
token->size = 0;
}
/**
* Fills next available token with JSON primitive.
*/
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start;
start = 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 "]" */
case ':':
#endif
case '\t':
case '\r':
case '\n':
case ' ':
case ',':
case ']':
case '}':
goto found;
}
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
#ifdef JSMN_STRICT
/* In strict mode primitive must be followed by a comma/object/array */
parser->pos = start;
return JSMN_ERROR_PART;
#endif
found:
if (tokens == NULL) {
parser->pos--;
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
parser->pos--;
return 0;
}
/**
* Fills next token with JSON string.
*/
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start = parser->pos;
parser->pos++;
/* Skip starting quote */
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;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
return 0;
}
/* Backslash: Quoted symbol expected */
if (c == '\\' && parser->pos + 1 < len) {
int i;
parser->pos++;
switch (js[parser->pos]) {
/* Allowed escaped symbols */
case '\"':
case '/':
case '\\':
case 'b':
case 'f':
case 'r':
case 'n':
case 't':
break;
/* Allows escaped symbol \uXXXX */
case 'u':
parser->pos++;
for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
i++) {
/* If it isn't a hex character we have an error */
if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
parser->pos = start;
return JSMN_ERROR_INVAL;
}
parser->pos++;
}
parser->pos--;
break;
/* Unexpected symbol */
default:
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
}
parser->pos = start;
return JSMN_ERROR_PART;
}
/**
* Parse JSON string and fill tokens.
*/
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens) {
int r;
int i;
jsmntok_t *token;
int count = parser->toknext;
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;
}
if (parser->toksuper != -1) {
jsmntok_t *t = &tokens[parser->toksuper];
#ifdef JSMN_STRICT
/* In strict mode an object or array can't become a key */
if (t->type == JSMN_OBJECT) {
return JSMN_ERROR_INVAL;
}
#endif
t->size++;
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
}
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = parser->pos;
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) {
return JSMN_ERROR_INVAL;
}
token = &tokens[parser->toknext - 1];
for (;;) {
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
}
if (token->parent == -1) {
if (token->type != type || parser->toksuper == -1) {
return JSMN_ERROR_INVAL;
}
break;
}
token = &tokens[token->parent];
}
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
parser->toksuper = -1;
token->end = parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1) {
return JSMN_ERROR_INVAL;
}
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
parser->toksuper = i;
break;
}
}
#endif
break;
case '\"':
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
case '\t':
case '\r':
case '\n':
case ' ':
break;
case ':':
parser->toksuper = parser->toknext - 1;
break;
case ',':
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
parser->toksuper = tokens[parser->toksuper].parent;
#else
for (i = parser->toknext - 1; i >= 0; i--) {
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = i;
break;
}
}
}
#endif
}
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 't':
case 'f':
case 'n':
/* And they must not be keys of the object */
if (tokens != NULL && parser->toksuper != -1) {
const jsmntok_t *t = &tokens[parser->toksuper];
if (t->type == JSMN_OBJECT ||
(t->type == JSMN_STRING && t->size != 0)) {
return JSMN_ERROR_INVAL;
}
}
#else
/* In non-strict mode every unquoted value is a primitive */
default:
#endif
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
#ifdef JSMN_STRICT
/* Unexpected char in strict mode */
default:
return JSMN_ERROR_INVAL;
#endif
}
}
if (tokens != NULL) {
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
if (tokens[i].start != -1 && tokens[i].end == -1) {
return JSMN_ERROR_PART;
}
}
}
return count;
}
/**
* Creates a new parser based over a given buffer with an array of tokens
* available.
*/
JSMN_API void jsmn_init(jsmn_parser *parser) {
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
}
#endif /* JSMN_HEADER */
#ifdef __cplusplus
}
#endif
#endif /* __JSMN_H_ */
#endif /* JSMN_H */

@ -11,7 +11,11 @@ static int test_failed = 0;
#define done() return 0
/* Check single condition */
#define check(cond) do { if (!(cond)) fail(); } while (0)
#define check(cond) \
do { \
if (!(cond)) \
fail(); \
} while (0)
/* Test runner */
static void test(int (*func)(void), const char *name) {

@ -1,51 +1,33 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "test.h"
#include "testutil.h"
int test_empty(void) {
check(parse("{}", 1, 1,
JSMN_OBJECT, 0, 2, 0));
check(parse("[]", 1, 1,
JSMN_ARRAY, 0, 2, 0));
check(parse("[{},{}]", 3, 3,
JSMN_ARRAY, 0, 7, 2,
JSMN_OBJECT, 1, 3, 0,
check(parse("{}", 1, 1, JSMN_OBJECT, 0, 2, 0));
check(parse("[]", 1, 1, JSMN_ARRAY, 0, 2, 0));
check(parse("[{},{}]", 3, 3, JSMN_ARRAY, 0, 7, 2, JSMN_OBJECT, 1, 3, 0,
JSMN_OBJECT, 4, 6, 0));
return 0;
}
int test_object(void) {
check(parse("{\"a\":0}", 3, 3,
JSMN_OBJECT, 0, 7, 1,
JSMN_STRING, "a", 1,
check(parse("{\"a\":0}", 3, 3, JSMN_OBJECT, 0, 7, 1, JSMN_STRING, "a", 1,
JSMN_PRIMITIVE, "0"));
check(parse("{\"a\":[]}", 3, 3,
JSMN_OBJECT, 0, 8, 1,
JSMN_STRING, "a", 1,
check(parse("{\"a\":[]}", 3, 3, JSMN_OBJECT, 0, 8, 1, JSMN_STRING, "a", 1,
JSMN_ARRAY, 5, 7, 0));
check(parse("{\"a\":{},\"b\":{}}", 5, 5,
JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "a", 1,
JSMN_OBJECT, -1, -1, 0,
JSMN_STRING, "b", 1,
JSMN_OBJECT, -1, -1, 0));
check(parse("{\"a\":{},\"b\":{}}", 5, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING,
"a", 1, JSMN_OBJECT, -1, -1, 0, JSMN_STRING, "b", 1, JSMN_OBJECT,
-1, -1, 0));
check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7,
JSMN_OBJECT, -1, -1, 3,
JSMN_STRING, "Day", 1,
JSMN_PRIMITIVE, "26",
JSMN_STRING, "Month", 1,
JSMN_PRIMITIVE, "9",
JSMN_STRING, "Year", 1,
JSMN_PRIMITIVE, "12"));
check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5,
JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "a", 1,
JSMN_PRIMITIVE, "0",
JSMN_STRING, "b", 1,
JSMN_OBJECT, -1, -1, 3, JSMN_STRING, "Day", 1, JSMN_PRIMITIVE,
"26", JSMN_STRING, "Month", 1, JSMN_PRIMITIVE, "9", JSMN_STRING,
"Year", 1, JSMN_PRIMITIVE, "12"));
check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5, JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "a", 1, JSMN_PRIMITIVE, "0", JSMN_STRING, "b", 1,
JSMN_STRING, "c", 0));
#ifdef JSMN_STRICT
@ -54,13 +36,13 @@ int test_object(void) {
check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5));
/* FIXME */
/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/
/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/
/* FIXME */
/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/
/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/
#endif
return 0;
}
@ -69,9 +51,7 @@ int test_array(void) {
/* FIXME */
/*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/
/*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/
check(parse("[10]", 2, 2,
JSMN_ARRAY, -1, -1, 1,
JSMN_PRIMITIVE, "10"));
check(parse("[10]", 2, 2, JSMN_ARRAY, -1, -1, 1, JSMN_PRIMITIVE, "10"));
check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3));
/* FIXME */
/*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/
@ -79,59 +59,36 @@ int test_array(void) {
}
int test_primitive(void) {
check(parse("{\"boolVar\" : true }", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "boolVar", 1,
JSMN_PRIMITIVE, "true"));
check(parse("{\"boolVar\" : false }", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "boolVar", 1,
JSMN_PRIMITIVE, "false"));
check(parse("{\"nullVar\" : null }", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "nullVar", 1,
JSMN_PRIMITIVE, "null"));
check(parse("{\"intVar\" : 12}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "intVar", 1,
JSMN_PRIMITIVE, "12"));
check(parse("{\"floatVar\" : 12.345}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "floatVar", 1,
JSMN_PRIMITIVE, "12.345"));
check(parse("{\"boolVar\" : true }", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "true"));
check(parse("{\"boolVar\" : false }", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "false"));
check(parse("{\"nullVar\" : null }", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "nullVar", 1, JSMN_PRIMITIVE, "null"));
check(parse("{\"intVar\" : 12}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
"intVar", 1, JSMN_PRIMITIVE, "12"));
check(parse("{\"floatVar\" : 12.345}", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "floatVar", 1, JSMN_PRIMITIVE, "12.345"));
return 0;
}
int test_string(void) {
check(parse("{\"strVar\" : \"hello world\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1,
JSMN_STRING, "hello world", 0));
check(parse("{\"strVar\" : \"hello world\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1, JSMN_STRING, "hello world", 0));
check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1,
JSMN_STRING, "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0));
check(parse("{\"strVar\": \"\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1,
JSMN_STRING, "", 0));
check(parse("{\"a\":\"\\uAbcD\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_STRING, "\\uAbcD", 0));
check(parse("{\"a\":\"str\\u0000\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_STRING, "str\\u0000", 0));
check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_STRING, "\\uFFFFstr", 0));
check(parse("{\"a\":[\"\\u0280\"]}", 4, 4,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
JSMN_ARRAY, -1, -1, 1,
JSMN_STRING, "\\u0280", 0));
JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "strVar", 1, JSMN_STRING,
"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0));
check(parse("{\"strVar\": \"\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
"strVar", 1, JSMN_STRING, "", 0));
check(parse("{\"a\":\"\\uAbcD\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
"a", 1, JSMN_STRING, "\\uAbcD", 0));
check(parse("{\"a\":\"str\\u0000\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1, JSMN_STRING, "str\\u0000", 0));
check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1, JSMN_STRING, "\\uFFFFstr", 0));
check(parse("{\"a\":[\"\\u0280\"]}", 4, 4, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1, JSMN_ARRAY, -1, -1, 1, JSMN_STRING,
"\\u0280", 0));
check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3));
@ -140,23 +97,20 @@ int test_string(void) {
}
int test_partial_string(void) {
int i;
int r;
unsigned long i;
jsmn_parser p;
jsmntok_t tok[5];
const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}";
jsmn_init(&p);
for (i = 1; i <= strlen(js); i++) {
r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0]));
r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0]));
if (i == strlen(js)) {
check(r == 5);
check(tokeq(js, tok, 5,
JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "x", 1,
JSMN_STRING, "va\\\\ue", 0,
JSMN_STRING, "y", 1,
JSMN_STRING, "value y", 0));
check(tokeq(js, tok, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, "x", 1,
JSMN_STRING, "va\\\\ue", 0, JSMN_STRING, "y", 1, JSMN_STRING,
"value y", 0));
} else {
check(r == JSMN_ERROR_PART);
}
@ -167,23 +121,19 @@ int test_partial_string(void) {
int test_partial_array(void) {
#ifdef JSMN_STRICT
int r;
int i;
unsigned long i;
jsmn_parser p;
jsmntok_t tok[10];
const char *js = "[ 1, true, [123, \"hello\"]]";
jsmn_init(&p);
for (i = 1; i <= strlen(js); i++) {
r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0]));
r = jsmn_parse(&p, js, i, tok, sizeof(tok) / sizeof(tok[0]));
if (i == strlen(js)) {
check(r == 6);
check(tokeq(js, tok, 6,
JSMN_ARRAY, -1, -1, 3,
JSMN_PRIMITIVE, "1",
JSMN_PRIMITIVE, "true",
JSMN_ARRAY, -1, -1, 2,
JSMN_PRIMITIVE, "123",
JSMN_STRING, "hello", 0));
check(tokeq(js, tok, 6, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1",
JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE,
"123", JSMN_STRING, "hello", 0));
} else {
check(r == JSMN_ERROR_PART);
}
@ -212,13 +162,9 @@ int test_array_nomem(void) {
r = jsmn_parse(&p, js, strlen(js), toklarge, 10);
check(r >= 0);
check(tokeq(js, toklarge, 4,
JSMN_ARRAY, -1, -1, 3,
JSMN_PRIMITIVE, "1",
JSMN_PRIMITIVE, "true",
JSMN_ARRAY, -1, -1, 2,
JSMN_PRIMITIVE, "123",
JSMN_STRING, "hello", 0));
check(tokeq(js, toklarge, 4, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1",
JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE,
"123", JSMN_STRING, "hello", 0));
}
return 0;
}
@ -235,11 +181,8 @@ int test_unquoted_keys(void) {
r = jsmn_parse(&p, js, strlen(js), tok, 10);
check(r >= 0);
check(tokeq(js, tok, 4,
JSMN_PRIMITIVE, "key1",
JSMN_STRING, "value", 0,
JSMN_PRIMITIVE, "key2",
JSMN_PRIMITIVE, "123"));
check(tokeq(js, tok, 4, JSMN_PRIMITIVE, "key1", JSMN_STRING, "value", 0,
JSMN_PRIMITIVE, "key2", JSMN_PRIMITIVE, "123"));
#endif
return 0;
}
@ -250,13 +193,16 @@ int test_issue_22(void) {
jsmntok_t tokens[128];
const char *js;
js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, "
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 }], "
"\"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);
@ -282,9 +228,7 @@ int test_input_length(void) {
jsmn_init(&p);
r = jsmn_parse(&p, js, 8, tokens, 10);
check(r == 3);
check(tokeq(js, tokens, 3,
JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1,
check(tokeq(js, tokens, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "a", 1,
JSMN_PRIMITIVE, "0"));
return 0;
}
@ -336,30 +280,20 @@ int test_count(void) {
return 0;
}
int test_nonstrict(void) {
#ifndef JSMN_STRICT
const char *js;
js = "a: 0garbage";
check(parse(js, 2, 2,
JSMN_PRIMITIVE, "a",
JSMN_PRIMITIVE, "0garbage"));
check(parse(js, 2, 2, JSMN_PRIMITIVE, "a", JSMN_PRIMITIVE, "0garbage"));
js = "Day : 26\nMonth : Sep\n\nYear: 12";
check(parse(js, 6, 6,
JSMN_PRIMITIVE, "Day",
JSMN_PRIMITIVE, "26",
JSMN_PRIMITIVE, "Month",
JSMN_PRIMITIVE, "Sep",
JSMN_PRIMITIVE, "Year",
JSMN_PRIMITIVE, "12"));
//nested {s don't cause a parse error.
js = "\"key {1\": 1234";
check(parse(js, 2, 2,
JSMN_STRING, "key {1", 1,
JSMN_PRIMITIVE, "1234"));
check(parse(js, 6, 6, JSMN_PRIMITIVE, "Day", JSMN_PRIMITIVE, "26",
JSMN_PRIMITIVE, "Month", JSMN_PRIMITIVE, "Sep", JSMN_PRIMITIVE,
"Year", JSMN_PRIMITIVE, "12"));
/* nested {s don't cause a parse error. */
js = "\"key {1\": 1234";
check(parse(js, 2, 2, JSMN_STRING, "key {1", 1, JSMN_PRIMITIVE, "1234"));
#endif
return 0;
@ -376,12 +310,29 @@ int test_unmatched_brackets(void) {
js = "\"key 1\"}: 1234";
check(parse(js, JSMN_ERROR_INVAL, 3));
js = "{\"key {1\": 1234}";
check(parse(js, 3, 3,
JSMN_OBJECT, 0, 16, 1,
JSMN_STRING, "key {1", 1,
check(parse(js, 3, 3, JSMN_OBJECT, 0, 16, 1, JSMN_STRING, "key {1", 1,
JSMN_PRIMITIVE, "1234"));
js = "{{\"key 1\": 1234}";
check(parse(js, JSMN_ERROR_PART, 4));
js = "{\"key 1\":{\"key 2\": 1234}";
check(parse(js, JSMN_ERROR_PART, 5));
return 0;
}
int test_object_key(void) {
const char *js;
js = "{\"key\": 1}";
check(parse(js, 3, 3, JSMN_OBJECT, 0, 10, 1, JSMN_STRING, "key", 1,
JSMN_PRIMITIVE, "1"));
#ifdef JSMN_STRICT
js = "{true: 1}";
check(parse(js, JSMN_ERROR_INVAL, 3));
js = "{1: 1}";
check(parse(js, JSMN_ERROR_INVAL, 3));
js = "{{\"key\": 1}: 2}";
check(parse(js, JSMN_ERROR_INVAL, 5));
js = "{[1,2]: 2}";
check(parse(js, JSMN_ERROR_INVAL, 5));
#endif
return 0;
}
@ -402,6 +353,7 @@ int main(void) {
test(test_count, "test tokens count estimation");
test(test_nonstrict, "test for non-strict mode");
test(test_unmatched_brackets, "test for unmatched brackets");
test(test_object_key, "test for key type");
printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed);
return (test_failed > 0);
}

@ -1,11 +1,13 @@
#ifndef __TEST_UTIL_H__
#define __TEST_UTIL_H__
#include "../jsmn.c"
#include "../jsmn.h"
static int vtokeq(const char *s, jsmntok_t *t, int numtok, va_list ap) {
static int vtokeq(const char *s, jsmntok_t *t, unsigned long numtok,
va_list ap) {
if (numtok > 0) {
int i, start, end, size;
unsigned long i;
int start, end, size;
int type;
char *value;
@ -27,30 +29,30 @@ static int vtokeq(const char *s, jsmntok_t *t, int numtok, va_list ap) {
value = NULL;
}
if (t[i].type != type) {
printf("token %d type is %d, not %d\n", i, t[i].type, type);
printf("token %lu type is %d, not %d\n", i, t[i].type, type);
return 0;
}
if (start != -1 && end != -1) {
if (t[i].start != start) {
printf("token %d start is %d, not %d\n", i, t[i].start, start);
printf("token %lu start is %d, not %d\n", i, t[i].start, start);
return 0;
}
if (t[i].end != end ) {
printf("token %d end is %d, not %d\n", i, t[i].end, end);
if (t[i].end != end) {
printf("token %lu end is %d, not %d\n", i, t[i].end, end);
return 0;
}
}
if (size != -1 && t[i].size != size) {
printf("token %d size is %d, not %d\n", i, t[i].size, size);
printf("token %lu size is %d, not %d\n", i, t[i].size, size);
return 0;
}
if (s != NULL && value != NULL) {
const char *p = s + t[i].start;
if (strlen(value) != t[i].end - t[i].start ||
if (strlen(value) != (unsigned long)(t[i].end - t[i].start) ||
strncmp(p, value, t[i].end - t[i].start) != 0) {
printf("token %d value is %.*s, not %s\n", i, t[i].end-t[i].start,
s+t[i].start, value);
printf("token %lu value is %.*s, not %s\n", i, t[i].end - t[i].start,
s + t[i].start, value);
return 0;
}
}

Loading…
Cancel
Save