1*de579d12Sflorian /*
2*de579d12Sflorian Copyright (c) 2010 Serge A. Zaitsev
3*de579d12Sflorian
4*de579d12Sflorian Permission is hereby granted, free of charge, to any person obtaining a copy
5*de579d12Sflorian of this software and associated documentation files (the "Software"), to deal
6*de579d12Sflorian in the Software without restriction, including without limitation the rights
7*de579d12Sflorian to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8*de579d12Sflorian copies of the Software, and to permit persons to whom the Software is
9*de579d12Sflorian furnished to do so, subject to the following conditions:
10*de579d12Sflorian
11*de579d12Sflorian The above copyright notice and this permission notice shall be included in
12*de579d12Sflorian all copies or substantial portions of the Software.
13*de579d12Sflorian
14*de579d12Sflorian THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*de579d12Sflorian IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*de579d12Sflorian FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17*de579d12Sflorian AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18*de579d12Sflorian LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19*de579d12Sflorian OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20*de579d12Sflorian THE SOFTWARE.*
21*de579d12Sflorian */
22*de579d12Sflorian #include "jsmn.h"
23*de579d12Sflorian
24*de579d12Sflorian /**
25*de579d12Sflorian * Allocates a fresh unused token from the token pull.
26*de579d12Sflorian */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)27*de579d12Sflorian static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
28*de579d12Sflorian jsmntok_t *tokens, size_t num_tokens) {
29*de579d12Sflorian jsmntok_t *tok;
30*de579d12Sflorian if (parser->toknext >= num_tokens) {
31*de579d12Sflorian return NULL;
32*de579d12Sflorian }
33*de579d12Sflorian tok = &tokens[parser->toknext++];
34*de579d12Sflorian tok->start = tok->end = -1;
35*de579d12Sflorian tok->size = 0;
36*de579d12Sflorian #ifdef JSMN_PARENT_LINKS
37*de579d12Sflorian tok->parent = -1;
38*de579d12Sflorian #endif
39*de579d12Sflorian return tok;
40*de579d12Sflorian }
41*de579d12Sflorian
42*de579d12Sflorian /**
43*de579d12Sflorian * Fills token type and boundaries.
44*de579d12Sflorian */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)45*de579d12Sflorian static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
46*de579d12Sflorian int start, int end) {
47*de579d12Sflorian token->type = type;
48*de579d12Sflorian token->start = start;
49*de579d12Sflorian token->end = end;
50*de579d12Sflorian token->size = 0;
51*de579d12Sflorian }
52*de579d12Sflorian
53*de579d12Sflorian /**
54*de579d12Sflorian * Fills next available token with JSON primitive.
55*de579d12Sflorian */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)56*de579d12Sflorian static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
57*de579d12Sflorian size_t len, jsmntok_t *tokens, size_t num_tokens) {
58*de579d12Sflorian jsmntok_t *token;
59*de579d12Sflorian int start;
60*de579d12Sflorian
61*de579d12Sflorian start = parser->pos;
62*de579d12Sflorian
63*de579d12Sflorian for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
64*de579d12Sflorian switch (js[parser->pos]) {
65*de579d12Sflorian #ifndef JSMN_STRICT
66*de579d12Sflorian /* In strict mode primitive must be followed by "," or "}" or "]" */
67*de579d12Sflorian case ':':
68*de579d12Sflorian #endif
69*de579d12Sflorian case '\t' : case '\r' : case '\n' : case ' ' :
70*de579d12Sflorian case ',' : case ']' : case '}' :
71*de579d12Sflorian goto found;
72*de579d12Sflorian }
73*de579d12Sflorian if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
74*de579d12Sflorian parser->pos = start;
75*de579d12Sflorian return JSMN_ERROR_INVAL;
76*de579d12Sflorian }
77*de579d12Sflorian }
78*de579d12Sflorian #ifdef JSMN_STRICT
79*de579d12Sflorian /* In strict mode primitive must be followed by a comma/object/array */
80*de579d12Sflorian parser->pos = start;
81*de579d12Sflorian return JSMN_ERROR_PART;
82*de579d12Sflorian #endif
83*de579d12Sflorian
84*de579d12Sflorian found:
85*de579d12Sflorian if (tokens == NULL) {
86*de579d12Sflorian parser->pos--;
87*de579d12Sflorian return 0;
88*de579d12Sflorian }
89*de579d12Sflorian token = jsmn_alloc_token(parser, tokens, num_tokens);
90*de579d12Sflorian if (token == NULL) {
91*de579d12Sflorian parser->pos = start;
92*de579d12Sflorian return JSMN_ERROR_NOMEM;
93*de579d12Sflorian }
94*de579d12Sflorian jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
95*de579d12Sflorian #ifdef JSMN_PARENT_LINKS
96*de579d12Sflorian token->parent = parser->toksuper;
97*de579d12Sflorian #endif
98*de579d12Sflorian parser->pos--;
99*de579d12Sflorian return 0;
100*de579d12Sflorian }
101*de579d12Sflorian
102*de579d12Sflorian /**
103*de579d12Sflorian * Fills next token with JSON string.
104*de579d12Sflorian */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)105*de579d12Sflorian static int jsmn_parse_string(jsmn_parser *parser, const char *js,
106*de579d12Sflorian size_t len, jsmntok_t *tokens, size_t num_tokens) {
107*de579d12Sflorian jsmntok_t *token;
108*de579d12Sflorian
109*de579d12Sflorian int start = parser->pos;
110*de579d12Sflorian
111*de579d12Sflorian parser->pos++;
112*de579d12Sflorian
113*de579d12Sflorian /* Skip starting quote */
114*de579d12Sflorian for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
115*de579d12Sflorian char c = js[parser->pos];
116*de579d12Sflorian
117*de579d12Sflorian /* Quote: end of string */
118*de579d12Sflorian if (c == '\"') {
119*de579d12Sflorian if (tokens == NULL) {
120*de579d12Sflorian return 0;
121*de579d12Sflorian }
122*de579d12Sflorian token = jsmn_alloc_token(parser, tokens, num_tokens);
123*de579d12Sflorian if (token == NULL) {
124*de579d12Sflorian parser->pos = start;
125*de579d12Sflorian return JSMN_ERROR_NOMEM;
126*de579d12Sflorian }
127*de579d12Sflorian jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
128*de579d12Sflorian #ifdef JSMN_PARENT_LINKS
129*de579d12Sflorian token->parent = parser->toksuper;
130*de579d12Sflorian #endif
131*de579d12Sflorian return 0;
132*de579d12Sflorian }
133*de579d12Sflorian
134*de579d12Sflorian /* Backslash: Quoted symbol expected */
135*de579d12Sflorian if (c == '\\' && parser->pos + 1 < len) {
136*de579d12Sflorian int i;
137*de579d12Sflorian parser->pos++;
138*de579d12Sflorian switch (js[parser->pos]) {
139*de579d12Sflorian /* Allowed escaped symbols */
140*de579d12Sflorian case '\"': case '/' : case '\\' : case 'b' :
141*de579d12Sflorian case 'f' : case 'r' : case 'n' : case 't' :
142*de579d12Sflorian break;
143*de579d12Sflorian /* Allows escaped symbol \uXXXX */
144*de579d12Sflorian case 'u':
145*de579d12Sflorian parser->pos++;
146*de579d12Sflorian for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
147*de579d12Sflorian /* If it isn't a hex character we have an error */
148*de579d12Sflorian if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
149*de579d12Sflorian (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
150*de579d12Sflorian (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
151*de579d12Sflorian parser->pos = start;
152*de579d12Sflorian return JSMN_ERROR_INVAL;
153*de579d12Sflorian }
154*de579d12Sflorian parser->pos++;
155*de579d12Sflorian }
156*de579d12Sflorian parser->pos--;
157*de579d12Sflorian break;
158*de579d12Sflorian /* Unexpected symbol */
159*de579d12Sflorian default:
160*de579d12Sflorian parser->pos = start;
161*de579d12Sflorian return JSMN_ERROR_INVAL;
162*de579d12Sflorian }
163*de579d12Sflorian }
164*de579d12Sflorian }
165*de579d12Sflorian parser->pos = start;
166*de579d12Sflorian return JSMN_ERROR_PART;
167*de579d12Sflorian }
168*de579d12Sflorian
169*de579d12Sflorian /**
170*de579d12Sflorian * Parse JSON string and fill tokens.
171*de579d12Sflorian */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)172*de579d12Sflorian int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
173*de579d12Sflorian jsmntok_t *tokens, unsigned int num_tokens) {
174*de579d12Sflorian int r;
175*de579d12Sflorian int i;
176*de579d12Sflorian jsmntok_t *token;
177*de579d12Sflorian int count = parser->toknext;
178*de579d12Sflorian
179*de579d12Sflorian for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
180*de579d12Sflorian char c;
181*de579d12Sflorian jsmntype_t type;
182*de579d12Sflorian
183*de579d12Sflorian c = js[parser->pos];
184*de579d12Sflorian switch (c) {
185*de579d12Sflorian case '{': case '[':
186*de579d12Sflorian count++;
187*de579d12Sflorian if (tokens == NULL) {
188*de579d12Sflorian break;
189*de579d12Sflorian }
190*de579d12Sflorian token = jsmn_alloc_token(parser, tokens, num_tokens);
191*de579d12Sflorian if (token == NULL)
192*de579d12Sflorian return JSMN_ERROR_NOMEM;
193*de579d12Sflorian if (parser->toksuper != -1) {
194*de579d12Sflorian tokens[parser->toksuper].size++;
195*de579d12Sflorian #ifdef JSMN_PARENT_LINKS
196*de579d12Sflorian token->parent = parser->toksuper;
197*de579d12Sflorian #endif
198*de579d12Sflorian }
199*de579d12Sflorian token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
200*de579d12Sflorian token->start = parser->pos;
201*de579d12Sflorian parser->toksuper = parser->toknext - 1;
202*de579d12Sflorian break;
203*de579d12Sflorian case '}': case ']':
204*de579d12Sflorian if (tokens == NULL)
205*de579d12Sflorian break;
206*de579d12Sflorian type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
207*de579d12Sflorian #ifdef JSMN_PARENT_LINKS
208*de579d12Sflorian if (parser->toknext < 1) {
209*de579d12Sflorian return JSMN_ERROR_INVAL;
210*de579d12Sflorian }
211*de579d12Sflorian token = &tokens[parser->toknext - 1];
212*de579d12Sflorian for (;;) {
213*de579d12Sflorian if (token->start != -1 && token->end == -1) {
214*de579d12Sflorian if (token->type != type) {
215*de579d12Sflorian return JSMN_ERROR_INVAL;
216*de579d12Sflorian }
217*de579d12Sflorian token->end = parser->pos + 1;
218*de579d12Sflorian parser->toksuper = token->parent;
219*de579d12Sflorian break;
220*de579d12Sflorian }
221*de579d12Sflorian if (token->parent == -1) {
222*de579d12Sflorian break;
223*de579d12Sflorian }
224*de579d12Sflorian token = &tokens[token->parent];
225*de579d12Sflorian }
226*de579d12Sflorian #else
227*de579d12Sflorian for (i = parser->toknext - 1; i >= 0; i--) {
228*de579d12Sflorian token = &tokens[i];
229*de579d12Sflorian if (token->start != -1 && token->end == -1) {
230*de579d12Sflorian if (token->type != type) {
231*de579d12Sflorian return JSMN_ERROR_INVAL;
232*de579d12Sflorian }
233*de579d12Sflorian parser->toksuper = -1;
234*de579d12Sflorian token->end = parser->pos + 1;
235*de579d12Sflorian break;
236*de579d12Sflorian }
237*de579d12Sflorian }
238*de579d12Sflorian /* Error if unmatched closing bracket */
239*de579d12Sflorian if (i == -1) return JSMN_ERROR_INVAL;
240*de579d12Sflorian for (; i >= 0; i--) {
241*de579d12Sflorian token = &tokens[i];
242*de579d12Sflorian if (token->start != -1 && token->end == -1) {
243*de579d12Sflorian parser->toksuper = i;
244*de579d12Sflorian break;
245*de579d12Sflorian }
246*de579d12Sflorian }
247*de579d12Sflorian #endif
248*de579d12Sflorian break;
249*de579d12Sflorian case '\"':
250*de579d12Sflorian r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
251*de579d12Sflorian if (r < 0) return r;
252*de579d12Sflorian count++;
253*de579d12Sflorian if (parser->toksuper != -1 && tokens != NULL)
254*de579d12Sflorian tokens[parser->toksuper].size++;
255*de579d12Sflorian break;
256*de579d12Sflorian case '\t' : case '\r' : case '\n' : case ' ':
257*de579d12Sflorian break;
258*de579d12Sflorian case ':':
259*de579d12Sflorian parser->toksuper = parser->toknext - 1;
260*de579d12Sflorian break;
261*de579d12Sflorian case ',':
262*de579d12Sflorian if (tokens != NULL && parser->toksuper != -1 &&
263*de579d12Sflorian tokens[parser->toksuper].type != JSMN_ARRAY &&
264*de579d12Sflorian tokens[parser->toksuper].type != JSMN_OBJECT) {
265*de579d12Sflorian #ifdef JSMN_PARENT_LINKS
266*de579d12Sflorian parser->toksuper = tokens[parser->toksuper].parent;
267*de579d12Sflorian #else
268*de579d12Sflorian for (i = parser->toknext - 1; i >= 0; i--) {
269*de579d12Sflorian if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
270*de579d12Sflorian if (tokens[i].start != -1 && tokens[i].end == -1) {
271*de579d12Sflorian parser->toksuper = i;
272*de579d12Sflorian break;
273*de579d12Sflorian }
274*de579d12Sflorian }
275*de579d12Sflorian }
276*de579d12Sflorian #endif
277*de579d12Sflorian }
278*de579d12Sflorian break;
279*de579d12Sflorian #ifdef JSMN_STRICT
280*de579d12Sflorian /* In strict mode primitives are: numbers and booleans */
281*de579d12Sflorian case '-': case '0': case '1' : case '2': case '3' : case '4':
282*de579d12Sflorian case '5': case '6': case '7' : case '8': case '9':
283*de579d12Sflorian case 't': case 'f': case 'n' :
284*de579d12Sflorian /* And they must not be keys of the object */
285*de579d12Sflorian if (tokens != NULL && parser->toksuper != -1) {
286*de579d12Sflorian jsmntok_t *t = &tokens[parser->toksuper];
287*de579d12Sflorian if (t->type == JSMN_OBJECT ||
288*de579d12Sflorian (t->type == JSMN_STRING && t->size != 0)) {
289*de579d12Sflorian return JSMN_ERROR_INVAL;
290*de579d12Sflorian }
291*de579d12Sflorian }
292*de579d12Sflorian #else
293*de579d12Sflorian /* In non-strict mode every unquoted value is a primitive */
294*de579d12Sflorian default:
295*de579d12Sflorian #endif
296*de579d12Sflorian r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
297*de579d12Sflorian if (r < 0) return r;
298*de579d12Sflorian count++;
299*de579d12Sflorian if (parser->toksuper != -1 && tokens != NULL)
300*de579d12Sflorian tokens[parser->toksuper].size++;
301*de579d12Sflorian break;
302*de579d12Sflorian
303*de579d12Sflorian #ifdef JSMN_STRICT
304*de579d12Sflorian /* Unexpected char in strict mode */
305*de579d12Sflorian default:
306*de579d12Sflorian return JSMN_ERROR_INVAL;
307*de579d12Sflorian #endif
308*de579d12Sflorian }
309*de579d12Sflorian }
310*de579d12Sflorian
311*de579d12Sflorian if (tokens != NULL) {
312*de579d12Sflorian for (i = parser->toknext - 1; i >= 0; i--) {
313*de579d12Sflorian /* Unmatched opened object or array */
314*de579d12Sflorian if (tokens[i].start != -1 && tokens[i].end == -1) {
315*de579d12Sflorian return JSMN_ERROR_PART;
316*de579d12Sflorian }
317*de579d12Sflorian }
318*de579d12Sflorian }
319*de579d12Sflorian
320*de579d12Sflorian return count;
321*de579d12Sflorian }
322*de579d12Sflorian
323*de579d12Sflorian /**
324*de579d12Sflorian * Creates a new parser based over a given buffer with an array of tokens
325*de579d12Sflorian * available.
326*de579d12Sflorian */
jsmn_init(jsmn_parser * parser)327*de579d12Sflorian void jsmn_init(jsmn_parser *parser) {
328*de579d12Sflorian parser->pos = 0;
329*de579d12Sflorian parser->toknext = 0;
330*de579d12Sflorian parser->toksuper = -1;
331*de579d12Sflorian }
332*de579d12Sflorian
333