automatic code formatting

modernize
Serge A. Zaitsev 6 years ago
parent b3ebee88e8
commit bcf450dbc7

@ -1,9 +1,9 @@
#include "../jsmn.h"
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <unistd.h>
#include "../jsmn.h"
/* Function realloc_it() is a wrapper function for standard realloc() /* Function realloc_it() is a wrapper function for standard realloc()
* with one difference - it frees old memory pointer in case of realloc * with one difference - it frees old memory pointer in case of realloc
@ -12,12 +12,12 @@
* memory can't be re-allocated - use standard realloc() instead. * memory can't be re-allocated - use standard realloc() instead.
*/ */
static inline void *realloc_it(void *ptrmem, size_t size) { static inline void *realloc_it(void *ptrmem, size_t size) {
void *p = realloc(ptrmem, size); void *p = realloc(ptrmem, size);
if (!p) { if (!p) {
free (ptrmem); free(ptrmem);
fprintf(stderr, "realloc(): errno=%d\n", errno); fprintf(stderr, "realloc(): errno=%d\n", errno);
} }
return p; return p;
} }
/* /*
@ -26,101 +26,103 @@ static inline void *realloc_it(void *ptrmem, size_t size) {
*/ */
static int dump(const char *js, jsmntok_t *t, size_t count, int indent) { static int dump(const char *js, jsmntok_t *t, size_t count, int indent) {
int i, j, k; int i, j, k;
if (count == 0) { if (count == 0) {
return 0; return 0;
} }
if (t->type == JSMN_PRIMITIVE) { 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; return 1;
} else if (t->type == JSMN_STRING) { } 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; return 1;
} else if (t->type == JSMN_OBJECT) { } else if (t->type == JSMN_OBJECT) {
printf("\n"); printf("\n");
j = 0; j = 0;
for (i = 0; i < t->size; i++) { for (i = 0; i < t->size; i++) {
for (k = 0; k < indent; k++) printf(" "); for (k = 0; k < indent; k++)
j += dump(js, t+1+j, count-j, indent+1); printf(" ");
printf(": "); j += dump(js, t + 1 + j, count - j, indent + 1);
j += dump(js, t+1+j, count-j, indent+1); printf(": ");
printf("\n"); j += dump(js, t + 1 + j, count - j, indent + 1);
} printf("\n");
return j+1; }
} else if (t->type == JSMN_ARRAY) { return j + 1;
j = 0; } else if (t->type == JSMN_ARRAY) {
printf("\n"); j = 0;
for (i = 0; i < t->size; i++) { printf("\n");
for (k = 0; k < indent-1; k++) printf(" "); for (i = 0; i < t->size; i++) {
printf(" - "); for (k = 0; k < indent - 1; k++)
j += dump(js, t+1+j, count-j, indent+1); printf(" ");
printf("\n"); printf(" - ");
} j += dump(js, t + 1 + j, count - j, indent + 1);
return j+1; printf("\n");
} }
return 0; return j + 1;
}
return 0;
} }
int main() { int main() {
int r; int r;
int eof_expected = 0; int eof_expected = 0;
char *js = NULL; char *js = NULL;
size_t jslen = 0; size_t jslen = 0;
char buf[BUFSIZ]; char buf[BUFSIZ];
jsmn_parser p; jsmn_parser p;
jsmntok_t *tok; jsmntok_t *tok;
size_t tokcount = 2; size_t tokcount = 2;
/* Prepare parser */ /* Prepare parser */
jsmn_init(&p); jsmn_init(&p);
/* Allocate some tokens as a start */ /* Allocate some tokens as a start */
tok = malloc(sizeof(*tok) * tokcount); tok = malloc(sizeof(*tok) * tokcount);
if (tok == NULL) { if (tok == NULL) {
fprintf(stderr, "malloc(): errno=%d\n", errno); fprintf(stderr, "malloc(): errno=%d\n", errno);
return 3; return 3;
} }
for (;;) { for (;;) {
/* Read another chunk */ /* Read another chunk */
r = fread(buf, 1, sizeof(buf), stdin); r = fread(buf, 1, sizeof(buf), stdin);
if (r < 0) { if (r < 0) {
fprintf(stderr, "fread(): %d, errno=%d\n", r, errno); fprintf(stderr, "fread(): %d, errno=%d\n", r, errno);
return 1; return 1;
} }
if (r == 0) { if (r == 0) {
if (eof_expected != 0) { if (eof_expected != 0) {
return 0; return 0;
} else { } else {
fprintf(stderr, "fread(): unexpected EOF\n"); fprintf(stderr, "fread(): unexpected EOF\n");
return 2; return 2;
} }
} }
js = realloc_it(js, jslen + r + 1); js = realloc_it(js, jslen + r + 1);
if (js == NULL) { if (js == NULL) {
return 3; return 3;
} }
strncpy(js + jslen, buf, r); strncpy(js + jslen, buf, r);
jslen = jslen + r; jslen = jslen + r;
again: again:
r = jsmn_parse(&p, js, jslen, tok, tokcount); r = jsmn_parse(&p, js, jslen, tok, tokcount);
if (r < 0) { if (r < 0) {
if (r == JSMN_ERROR_NOMEM) { if (r == JSMN_ERROR_NOMEM) {
tokcount = tokcount * 2; tokcount = tokcount * 2;
tok = realloc_it(tok, sizeof(*tok) * tokcount); tok = realloc_it(tok, sizeof(*tok) * tokcount);
if (tok == NULL) { if (tok == NULL) {
return 3; return 3;
} }
goto again; goto again;
} }
} else { } else {
dump(js, tok, p.toknext, 0); dump(js, tok, p.toknext, 0);
eof_expected = 1; eof_expected = 1;
} }
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

@ -1,7 +1,7 @@
#include "../jsmn.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "../jsmn.h"
/* /*
* A small example of jsmn parsing when JSON structure is known and number of * A small example of jsmn parsing when JSON structure is known and number of
@ -9,68 +9,69 @@
*/ */
static const char *JSON_STRING = static const char *JSON_STRING =
"{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n "
"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}"; "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { 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) { strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
return 0; return 0;
} }
return -1; return -1;
} }
int main() { int main() {
int i; int i;
int r; int r;
jsmn_parser p; jsmn_parser p;
jsmntok_t t[128]; /* We expect no more than 128 tokens */ jsmntok_t t[128]; /* We expect no more than 128 tokens */
jsmn_init(&p); 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,
if (r < 0) { sizeof(t) / sizeof(t[0]));
printf("Failed to parse JSON: %d\n", r); if (r < 0) {
return 1; printf("Failed to parse JSON: %d\n", r);
} return 1;
}
/* Assume the top-level element is an object */ /* Assume the top-level element is an object */
if (r < 1 || t[0].type != JSMN_OBJECT) { if (r < 1 || t[0].type != JSMN_OBJECT) {
printf("Object expected\n"); printf("Object expected\n");
return 1; return 1;
} }
/* Loop over all keys of the root object */ /* Loop over all keys of the root object */
for (i = 1; i < r; i++) { for (i = 1; i < r; i++) {
if (jsoneq(JSON_STRING, &t[i], "user") == 0) { if (jsoneq(JSON_STRING, &t[i], "user") == 0) {
/* We may use strndup() to fetch string value */ /* We may use strndup() to fetch string value */
printf("- User: %.*s\n", t[i+1].end-t[i+1].start, printf("- User: %.*s\n", t[i + 1].end - t[i + 1].start,
JSON_STRING + t[i+1].start); JSON_STRING + t[i + 1].start);
i++; i++;
} else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) { } else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) {
/* We may additionally check if the value is either "true" or "false" */ /* We may additionally check if the value is either "true" or "false" */
printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start, printf("- Admin: %.*s\n", t[i + 1].end - t[i + 1].start,
JSON_STRING + t[i+1].start); JSON_STRING + t[i + 1].start);
i++; i++;
} else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) { } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) {
/* We may want to do strtol() here to get numeric value */ /* We may want to do strtol() here to get numeric value */
printf("- UID: %.*s\n", t[i+1].end-t[i+1].start, printf("- UID: %.*s\n", t[i + 1].end - t[i + 1].start,
JSON_STRING + t[i+1].start); JSON_STRING + t[i + 1].start);
i++; i++;
} else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) { } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) {
int j; int j;
printf("- Groups:\n"); 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 */ continue; /* We expect groups to be an array of strings */
} }
for (j = 0; j < t[i+1].size; j++) { for (j = 0; j < t[i + 1].size; j++) {
jsmntok_t *g = &t[i+j+2]; jsmntok_t *g = &t[i + j + 2];
printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start); printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start);
} }
i += t[i+1].size + 1; i += t[i + 1].size + 1;
} else { } 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); JSON_STRING + t[i].start);
} }
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

516
jsmn.c

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

@ -15,20 +15,20 @@ extern "C" {
* o Other primitive: number, boolean (true/false) or null * o Other primitive: number, boolean (true/false) or null
*/ */
typedef enum { typedef enum {
JSMN_UNDEFINED = 0, JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1, JSMN_OBJECT = 1,
JSMN_ARRAY = 2, JSMN_ARRAY = 2,
JSMN_STRING = 3, JSMN_STRING = 3,
JSMN_PRIMITIVE = 4 JSMN_PRIMITIVE = 4
} jsmntype_t; } jsmntype_t;
enum jsmnerr { enum jsmnerr {
/* Not enough tokens were provided */ /* Not enough tokens were provided */
JSMN_ERROR_NOMEM = -1, JSMN_ERROR_NOMEM = -1,
/* Invalid character inside JSON string */ /* Invalid character inside JSON string */
JSMN_ERROR_INVAL = -2, JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */ /* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3 JSMN_ERROR_PART = -3
}; };
/** /**
@ -38,12 +38,12 @@ enum jsmnerr {
* end end position in JSON data string * end end position in JSON data string
*/ */
typedef struct { typedef struct {
jsmntype_t type; jsmntype_t type;
int start; int start;
int end; int end;
int size; int size;
#ifdef JSMN_PARENT_LINKS #ifdef JSMN_PARENT_LINKS
int parent; int parent;
#endif #endif
} jsmntok_t; } jsmntok_t;
@ -52,9 +52,9 @@ typedef struct {
* the string being parsed now and current position in that string * the string being parsed now and current position in that string
*/ */
typedef struct { typedef struct {
unsigned int pos; /* offset in the JSON string */ unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */ 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; } jsmn_parser;
/** /**
@ -63,11 +63,12 @@ typedef struct {
void jsmn_init(jsmn_parser *parser); 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. * a single JSON object.
*/ */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens); jsmntok_t *tokens, unsigned int num_tokens);
#ifdef __cplusplus #ifdef __cplusplus
} }

@ -5,23 +5,27 @@ static int test_passed = 0;
static int test_failed = 0; static int test_failed = 0;
/* Terminate current test with error */ /* Terminate current test with error */
#define fail() return __LINE__ #define fail() return __LINE__
/* Successful end of the test case */ /* Successful end of the test case */
#define done() return 0 #define done() return 0
/* Check single condition */ /* Check single condition */
#define check(cond) do { if (!(cond)) fail(); } while (0) #define check(cond) \
do { \
if (!(cond)) \
fail(); \
} while (0)
/* Test runner */ /* Test runner */
static void test(int (*func)(void), const char *name) { static void test(int (*func)(void), const char *name) {
int r = func(); int r = func();
if (r == 0) { if (r == 0) {
test_passed++; test_passed++;
} else { } else {
test_failed++; test_failed++;
printf("FAILED: %s (at line %d)\n", name, r); printf("FAILED: %s (at line %d)\n", name, r);
} }
} }
#endif /* __TEST_H__ */ #endif /* __TEST_H__ */

@ -1,407 +1,339 @@
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include "test.h" #include "test.h"
#include "testutil.h" #include "testutil.h"
int test_empty(void) { int test_empty(void) {
check(parse("{}", 1, 1, check(parse("{}", 1, 1, JSMN_OBJECT, 0, 2, 0));
JSMN_OBJECT, 0, 2, 0)); check(parse("[]", 1, 1, JSMN_ARRAY, 0, 2, 0));
check(parse("[]", 1, 1, check(parse("[{},{}]", 3, 3, JSMN_ARRAY, 0, 7, 2, JSMN_OBJECT, 1, 3, 0,
JSMN_ARRAY, 0, 2, 0)); JSMN_OBJECT, 4, 6, 0));
check(parse("[{},{}]", 3, 3, return 0;
JSMN_ARRAY, 0, 7, 2,
JSMN_OBJECT, 1, 3, 0,
JSMN_OBJECT, 4, 6, 0));
return 0;
} }
int test_object(void) { int test_object(void) {
check(parse("{\"a\":0}", 3, 3, check(parse("{\"a\":0}", 3, 3, JSMN_OBJECT, 0, 7, 1, JSMN_STRING, "a", 1,
JSMN_OBJECT, 0, 7, 1, JSMN_PRIMITIVE, "0"));
JSMN_STRING, "a", 1, check(parse("{\"a\":[]}", 3, 3, JSMN_OBJECT, 0, 8, 1, JSMN_STRING, "a", 1,
JSMN_PRIMITIVE, "0")); JSMN_ARRAY, 5, 7, 0));
check(parse("{\"a\":[]}", 3, 3, check(parse("{\"a\":{},\"b\":{}}", 5, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING,
JSMN_OBJECT, 0, 8, 1, "a", 1, JSMN_OBJECT, -1, -1, 0, JSMN_STRING, "b", 1, JSMN_OBJECT,
JSMN_STRING, "a", 1, -1, -1, 0));
JSMN_ARRAY, 5, 7, 0)); check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7,
check(parse("{\"a\":{},\"b\":{}}", 5, 5, JSMN_OBJECT, -1, -1, 3, JSMN_STRING, "Day", 1, JSMN_PRIMITIVE,
JSMN_OBJECT, -1, -1, 2, "26", JSMN_STRING, "Month", 1, JSMN_PRIMITIVE, "9", JSMN_STRING,
JSMN_STRING, "a", 1, "Year", 1, JSMN_PRIMITIVE, "12"));
JSMN_OBJECT, -1, -1, 0, check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5, JSMN_OBJECT, -1, -1, 2,
JSMN_STRING, "b", 1, JSMN_STRING, "a", 1, JSMN_PRIMITIVE, "0", JSMN_STRING, "b", 1,
JSMN_OBJECT, -1, -1, 0)); JSMN_STRING, "c", 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_STRING, "c", 0));
#ifdef JSMN_STRICT #ifdef JSMN_STRICT
check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3)); check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3)); check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3)); check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3)); check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3));
check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5)); check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5));
/* FIXME */ /* FIXME */
/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/ /*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/
/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/ /*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/ /*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/ /*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/ /*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/
/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/ /*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/
#endif #endif
return 0; return 0;
} }
int test_array(void) { int test_array(void) {
/* FIXME */ /* FIXME */
/*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/ /*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/
/*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/ /*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/
check(parse("[10]", 2, 2, check(parse("[10]", 2, 2, JSMN_ARRAY, -1, -1, 1, JSMN_PRIMITIVE, "10"));
JSMN_ARRAY, -1, -1, 1, check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3));
JSMN_PRIMITIVE, "10")); /* FIXME */
check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3)); /*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/
/* FIXME */ return 0;
/*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/
return 0;
} }
int test_primitive(void) { int test_primitive(void) {
check(parse("{\"boolVar\" : true }", 3, 3, check(parse("{\"boolVar\" : true }", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "true"));
JSMN_STRING, "boolVar", 1, check(parse("{\"boolVar\" : false }", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_PRIMITIVE, "true")); JSMN_STRING, "boolVar", 1, JSMN_PRIMITIVE, "false"));
check(parse("{\"boolVar\" : false }", 3, 3, check(parse("{\"nullVar\" : null }", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "nullVar", 1, JSMN_PRIMITIVE, "null"));
JSMN_STRING, "boolVar", 1, check(parse("{\"intVar\" : 12}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
JSMN_PRIMITIVE, "false")); "intVar", 1, JSMN_PRIMITIVE, "12"));
check(parse("{\"nullVar\" : null }", 3, 3, check(parse("{\"floatVar\" : 12.345}", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "floatVar", 1, JSMN_PRIMITIVE, "12.345"));
JSMN_STRING, "nullVar", 1, return 0;
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) { int test_string(void) {
check(parse("{\"strVar\" : \"hello world\"}", 3, 3, check(parse("{\"strVar\" : \"hello world\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "strVar", 1, JSMN_STRING, "hello world", 0));
JSMN_STRING, "strVar", 1, check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3,
JSMN_STRING, "hello world", 0)); JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "strVar", 1, JSMN_STRING,
check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3, "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0));
JSMN_OBJECT, -1, -1, 1, check(parse("{\"strVar\": \"\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
JSMN_STRING, "strVar", 1, "strVar", 1, JSMN_STRING, "", 0));
JSMN_STRING, "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0)); check(parse("{\"a\":\"\\uAbcD\"}", 3, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING,
check(parse("{\"strVar\": \"\"}", 3, 3, "a", 1, JSMN_STRING, "\\uAbcD", 0));
JSMN_OBJECT, -1, -1, 1, check(parse("{\"a\":\"str\\u0000\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "strVar", 1, JSMN_STRING, "a", 1, JSMN_STRING, "str\\u0000", 0));
JSMN_STRING, "", 0)); check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3, JSMN_OBJECT, -1, -1, 1,
check(parse("{\"a\":\"\\uAbcD\"}", 3, 3, JSMN_STRING, "a", 1, JSMN_STRING, "\\uFFFFstr", 0));
JSMN_OBJECT, -1, -1, 1, check(parse("{\"a\":[\"\\u0280\"]}", 4, 4, JSMN_OBJECT, -1, -1, 1,
JSMN_STRING, "a", 1, JSMN_STRING, "a", 1, JSMN_ARRAY, -1, -1, 1, JSMN_STRING,
JSMN_STRING, "\\uAbcD", 0)); "\\u0280", 0));
check(parse("{\"a\":\"str\\u0000\"}", 3, 3,
JSMN_OBJECT, -1, -1, 1, check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3));
JSMN_STRING, "a", 1, check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3));
JSMN_STRING, "str\\u0000", 0)); check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4));
check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3, return 0;
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));
check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4));
return 0;
} }
int test_partial_string(void) { int test_partial_string(void) {
int i; int i;
int r; int r;
jsmn_parser p; jsmn_parser p;
jsmntok_t tok[5]; jsmntok_t tok[5];
const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}"; const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}";
jsmn_init(&p); jsmn_init(&p);
for (i = 1; i <= strlen(js); i++) { 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)) { if (i == strlen(js)) {
check(r == 5); check(r == 5);
check(tokeq(js, tok, 5, check(tokeq(js, tok, 5, JSMN_OBJECT, -1, -1, 2, JSMN_STRING, "x", 1,
JSMN_OBJECT, -1, -1, 2, JSMN_STRING, "va\\\\ue", 0, JSMN_STRING, "y", 1, JSMN_STRING,
JSMN_STRING, "x", 1, "value y", 0));
JSMN_STRING, "va\\\\ue", 0, } else {
JSMN_STRING, "y", 1, check(r == JSMN_ERROR_PART);
JSMN_STRING, "value y", 0)); }
} else { }
check(r == JSMN_ERROR_PART); return 0;
}
}
return 0;
} }
int test_partial_array(void) { int test_partial_array(void) {
#ifdef JSMN_STRICT #ifdef JSMN_STRICT
int r; int r;
int i; int i;
jsmn_parser p; jsmn_parser p;
jsmntok_t tok[10]; jsmntok_t tok[10];
const char *js = "[ 1, true, [123, \"hello\"]]"; const char *js = "[ 1, true, [123, \"hello\"]]";
jsmn_init(&p); jsmn_init(&p);
for (i = 1; i <= strlen(js); i++) { 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)) { if (i == strlen(js)) {
check(r == 6); check(r == 6);
check(tokeq(js, tok, 6, check(tokeq(js, tok, 6, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1",
JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE,
JSMN_PRIMITIVE, "1", "123", JSMN_STRING, "hello", 0));
JSMN_PRIMITIVE, "true", } else {
JSMN_ARRAY, -1, -1, 2, check(r == JSMN_ERROR_PART);
JSMN_PRIMITIVE, "123", }
JSMN_STRING, "hello", 0)); }
} else {
check(r == JSMN_ERROR_PART);
}
}
#endif #endif
return 0; return 0;
} }
int test_array_nomem(void) { int test_array_nomem(void) {
int i; int i;
int r; int r;
jsmn_parser p; jsmn_parser p;
jsmntok_t toksmall[10], toklarge[10]; jsmntok_t toksmall[10], toklarge[10];
const char *js; const char *js;
js = " [ 1, true, [123, \"hello\"]]"; js = " [ 1, true, [123, \"hello\"]]";
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
jsmn_init(&p); jsmn_init(&p);
memset(toksmall, 0, sizeof(toksmall)); memset(toksmall, 0, sizeof(toksmall));
memset(toklarge, 0, sizeof(toklarge)); memset(toklarge, 0, sizeof(toklarge));
r = jsmn_parse(&p, js, strlen(js), toksmall, i); r = jsmn_parse(&p, js, strlen(js), toksmall, i);
check(r == JSMN_ERROR_NOMEM); check(r == JSMN_ERROR_NOMEM);
memcpy(toklarge, toksmall, sizeof(toksmall)); memcpy(toklarge, toksmall, sizeof(toksmall));
r = jsmn_parse(&p, js, strlen(js), toklarge, 10); r = jsmn_parse(&p, js, strlen(js), toklarge, 10);
check(r >= 0); check(r >= 0);
check(tokeq(js, toklarge, 4, check(tokeq(js, toklarge, 4, JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "1",
JSMN_ARRAY, -1, -1, 3, JSMN_PRIMITIVE, "true", JSMN_ARRAY, -1, -1, 2, JSMN_PRIMITIVE,
JSMN_PRIMITIVE, "1", "123", JSMN_STRING, "hello", 0));
JSMN_PRIMITIVE, "true", }
JSMN_ARRAY, -1, -1, 2, return 0;
JSMN_PRIMITIVE, "123",
JSMN_STRING, "hello", 0));
}
return 0;
} }
int test_unquoted_keys(void) { int test_unquoted_keys(void) {
#ifndef JSMN_STRICT #ifndef JSMN_STRICT
int r; int r;
jsmn_parser p; jsmn_parser p;
jsmntok_t tok[10]; jsmntok_t tok[10];
const char *js; const char *js;
jsmn_init(&p); jsmn_init(&p);
js = "key1: \"value\"\nkey2 : 123"; js = "key1: \"value\"\nkey2 : 123";
r = jsmn_parse(&p, js, strlen(js), tok, 10); r = jsmn_parse(&p, js, strlen(js), tok, 10);
check(r >= 0); check(r >= 0);
check(tokeq(js, tok, 4, check(tokeq(js, tok, 4, JSMN_PRIMITIVE, "key1", JSMN_STRING, "value", 0,
JSMN_PRIMITIVE, "key1", JSMN_PRIMITIVE, "key2", JSMN_PRIMITIVE, "123"));
JSMN_STRING, "value", 0,
JSMN_PRIMITIVE, "key2",
JSMN_PRIMITIVE, "123"));
#endif #endif
return 0; return 0;
} }
int test_issue_22(void) { int test_issue_22(void) {
int r; int r;
jsmn_parser p; jsmn_parser p;
jsmntok_t tokens[128]; jsmntok_t tokens[128];
const char *js; const char *js;
js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, " js =
"\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", " "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, "
"\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], " "\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", "
"\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, " "\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], "
"\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", " "\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, "
"\"imageheight\":64, \"imagewidth\":160, \"margin\":0, \"name\":\"Tiles\", " "\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", "
"\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], " "\"imageheight\":64, \"imagewidth\":160, \"margin\":0, "
"\"tilewidth\":32, \"version\":1, \"width\":10 }"; "\"name\":\"Tiles\", "
jsmn_init(&p); "\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 "
r = jsmn_parse(&p, js, strlen(js), tokens, 128); "}], "
check(r >= 0); "\"tilewidth\":32, \"version\":1, \"width\":10 }";
return 0; jsmn_init(&p);
r = jsmn_parse(&p, js, strlen(js), tokens, 128);
check(r >= 0);
return 0;
} }
int test_issue_27(void) { int test_issue_27(void) {
const char *js = const char *js =
"{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", "; "{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", ";
check(parse(js, JSMN_ERROR_PART, 8)); check(parse(js, JSMN_ERROR_PART, 8));
return 0; return 0;
} }
int test_input_length(void) { int test_input_length(void) {
const char *js; const char *js;
int r; int r;
jsmn_parser p; jsmn_parser p;
jsmntok_t tokens[10]; jsmntok_t tokens[10];
js = "{\"a\": 0}garbage"; js = "{\"a\": 0}garbage";
jsmn_init(&p); jsmn_init(&p);
r = jsmn_parse(&p, js, 8, tokens, 10); r = jsmn_parse(&p, js, 8, tokens, 10);
check(r == 3); check(r == 3);
check(tokeq(js, tokens, 3, check(tokeq(js, tokens, 3, JSMN_OBJECT, -1, -1, 1, JSMN_STRING, "a", 1,
JSMN_OBJECT, -1, -1, 1, JSMN_PRIMITIVE, "0"));
JSMN_STRING, "a", 1, return 0;
JSMN_PRIMITIVE, "0"));
return 0;
} }
int test_count(void) { int test_count(void) {
jsmn_parser p; jsmn_parser p;
const char *js; const char *js;
js = "{}"; js = "{}";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
js = "[]"; js = "[]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1);
js = "[[]]"; js = "[[]]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2);
js = "[[], []]"; js = "[[], []]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
js = "[[], []]"; js = "[[], []]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3);
js = "[[], [[]], [[], []]]"; js = "[[], [[]], [[], []]]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
js = "[\"a\", [[], []]]"; js = "[\"a\", [[], []]]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
js = "[[], \"[], [[]]\", [[]]]"; js = "[[], \"[], [[]]\", [[]]]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5);
js = "[1, 2, 3]"; js = "[1, 2, 3]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4);
js = "[1, 2, [3, \"a\"], null]"; js = "[1, 2, [3, \"a\"], null]";
jsmn_init(&p); jsmn_init(&p);
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7);
return 0; return 0;
} }
int test_nonstrict(void) { int test_nonstrict(void) {
#ifndef JSMN_STRICT #ifndef JSMN_STRICT
const char *js; const char *js;
js = "a: 0garbage"; js = "a: 0garbage";
check(parse(js, 2, 2, check(parse(js, 2, 2, JSMN_PRIMITIVE, "a", JSMN_PRIMITIVE, "0garbage"));
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",
js = "Day : 26\nMonth : Sep\n\nYear: 12"; JSMN_PRIMITIVE, "Month", JSMN_PRIMITIVE, "Sep", JSMN_PRIMITIVE,
check(parse(js, 6, 6, "Year", JSMN_PRIMITIVE, "12"));
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"));
// 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 #endif
return 0; return 0;
} }
int test_unmatched_brackets(void) { int test_unmatched_brackets(void) {
const char *js; const char *js;
js = "\"key 1\": 1234}"; js = "\"key 1\": 1234}";
check(parse(js, JSMN_ERROR_INVAL, 2)); check(parse(js, JSMN_ERROR_INVAL, 2));
js = "{\"key 1\": 1234"; js = "{\"key 1\": 1234";
check(parse(js, JSMN_ERROR_PART, 3)); check(parse(js, JSMN_ERROR_PART, 3));
js = "{\"key 1\": 1234}}"; js = "{\"key 1\": 1234}}";
check(parse(js, JSMN_ERROR_INVAL, 3)); check(parse(js, JSMN_ERROR_INVAL, 3));
js = "\"key 1\"}: 1234"; js = "\"key 1\"}: 1234";
check(parse(js, JSMN_ERROR_INVAL, 3)); check(parse(js, JSMN_ERROR_INVAL, 3));
js = "{\"key {1\": 1234}"; js = "{\"key {1\": 1234}";
check(parse(js, 3, 3, check(parse(js, 3, 3, JSMN_OBJECT, 0, 16, 1, JSMN_STRING, "key {1", 1,
JSMN_OBJECT, 0, 16, 1, JSMN_PRIMITIVE, "1234"));
JSMN_STRING, "key {1", 1, js = "{{\"key 1\": 1234}";
JSMN_PRIMITIVE, "1234")); check(parse(js, JSMN_ERROR_PART, 4));
js = "{{\"key 1\": 1234}"; return 0;
check(parse(js, JSMN_ERROR_PART, 4));
return 0;
} }
int main(void) { int main(void) {
test(test_empty, "test for a empty JSON objects/arrays"); test(test_empty, "test for a empty JSON objects/arrays");
test(test_object, "test for a JSON objects"); test(test_object, "test for a JSON objects");
test(test_array, "test for a JSON arrays"); test(test_array, "test for a JSON arrays");
test(test_primitive, "test primitive JSON data types"); test(test_primitive, "test primitive JSON data types");
test(test_string, "test string JSON data types"); test(test_string, "test string JSON data types");
test(test_partial_string, "test partial JSON string parsing"); test(test_partial_string, "test partial JSON string parsing");
test(test_partial_array, "test partial array reading"); test(test_partial_array, "test partial array reading");
test(test_array_nomem, "test array reading with a smaller number of tokens"); test(test_array_nomem, "test array reading with a smaller number of tokens");
test(test_unquoted_keys, "test unquoted keys (like in JavaScript)"); test(test_unquoted_keys, "test unquoted keys (like in JavaScript)");
test(test_input_length, "test strings that are not null-terminated"); test(test_input_length, "test strings that are not null-terminated");
test(test_issue_22, "test issue #22"); test(test_issue_22, "test issue #22");
test(test_issue_27, "test issue #27"); test(test_issue_27, "test issue #27");
test(test_count, "test tokens count estimation"); test(test_count, "test tokens count estimation");
test(test_nonstrict, "test for non-strict mode"); test(test_nonstrict, "test for non-strict mode");
test(test_unmatched_brackets, "test for unmatched brackets"); test(test_unmatched_brackets, "test for unmatched brackets");
printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed); printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed);
return (test_failed > 0); return (test_failed > 0);
} }

@ -4,91 +4,91 @@
#include "../jsmn.c" #include "../jsmn.c"
static int vtokeq(const char *s, jsmntok_t *t, int numtok, va_list ap) { static int vtokeq(const char *s, jsmntok_t *t, int numtok, va_list ap) {
if (numtok > 0) { if (numtok > 0) {
int i, start, end, size; int i, start, end, size;
int type; int type;
char *value; char *value;
size = -1; size = -1;
value = NULL; value = NULL;
for (i = 0; i < numtok; i++) { for (i = 0; i < numtok; i++) {
type = va_arg(ap, int); type = va_arg(ap, int);
if (type == JSMN_STRING) { if (type == JSMN_STRING) {
value = va_arg(ap, char *); value = va_arg(ap, char *);
size = va_arg(ap, int); size = va_arg(ap, int);
start = end = -1; start = end = -1;
} else if (type == JSMN_PRIMITIVE) { } else if (type == JSMN_PRIMITIVE) {
value = va_arg(ap, char *); value = va_arg(ap, char *);
start = end = size = -1; start = end = size = -1;
} else { } else {
start = va_arg(ap, int); start = va_arg(ap, int);
end = va_arg(ap, int); end = va_arg(ap, int);
size = va_arg(ap, int); size = va_arg(ap, int);
value = NULL; value = NULL;
} }
if (t[i].type != type) { if (t[i].type != type) {
printf("token %d type is %d, not %d\n", i, t[i].type, type); printf("token %d type is %d, not %d\n", i, t[i].type, type);
return 0; return 0;
} }
if (start != -1 && end != -1) { if (start != -1 && end != -1) {
if (t[i].start != start) { if (t[i].start != start) {
printf("token %d start is %d, not %d\n", i, t[i].start, start); printf("token %d start is %d, not %d\n", i, t[i].start, start);
return 0; return 0;
} }
if (t[i].end != end ) { if (t[i].end != end) {
printf("token %d end is %d, not %d\n", i, t[i].end, end); printf("token %d end is %d, not %d\n", i, t[i].end, end);
return 0; return 0;
} }
} }
if (size != -1 && t[i].size != size) { if (size != -1 && t[i].size != size) {
printf("token %d size is %d, not %d\n", i, t[i].size, size); printf("token %d size is %d, not %d\n", i, t[i].size, size);
return 0; return 0;
} }
if (s != NULL && value != NULL) { if (s != NULL && value != NULL) {
const char *p = s + t[i].start; const char *p = s + t[i].start;
if (strlen(value) != t[i].end - t[i].start || if (strlen(value) != t[i].end - t[i].start ||
strncmp(p, value, t[i].end - t[i].start) != 0) { 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, printf("token %d value is %.*s, not %s\n", i, t[i].end - t[i].start,
s+t[i].start, value); s + t[i].start, value);
return 0; return 0;
} }
} }
} }
} }
return 1; return 1;
} }
static int tokeq(const char *s, jsmntok_t *tokens, int numtok, ...) { static int tokeq(const char *s, jsmntok_t *tokens, int numtok, ...) {
int ok; int ok;
va_list args; va_list args;
va_start(args, numtok); va_start(args, numtok);
ok = vtokeq(s, tokens, numtok, args); ok = vtokeq(s, tokens, numtok, args);
va_end(args); va_end(args);
return ok; return ok;
} }
static int parse(const char *s, int status, int numtok, ...) { static int parse(const char *s, int status, int numtok, ...) {
int r; int r;
int ok = 1; int ok = 1;
va_list args; va_list args;
jsmn_parser p; jsmn_parser p;
jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t)); jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t));
jsmn_init(&p); jsmn_init(&p);
r = jsmn_parse(&p, s, strlen(s), t, numtok); r = jsmn_parse(&p, s, strlen(s), t, numtok);
if (r != status) { if (r != status) {
printf("status is %d, not %d\n", r, status); printf("status is %d, not %d\n", r, status);
return 0; return 0;
} }
if (status >= 0) { if (status >= 0) {
va_start(args, numtok); va_start(args, numtok);
ok = vtokeq(s, t, numtok, args); ok = vtokeq(s, t, numtok, args);
va_end(args); va_end(args);
} }
free(t); free(t);
return ok; return ok;
} }
#endif /* __TEST_UTIL_H__ */ #endif /* __TEST_UTIL_H__ */

Loading…
Cancel
Save