1 /*
2 * MIT License
3 *
4 * Copyright (c) 2010 Serge Zaitsev
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24 // ANDROID CHANGE: Default to header only.
25 #ifndef JSMN_IMPL
26 #define JSMN_HEADER
27 #endif
28 // END ANDROID CHANGE
29
30 #ifndef JSMN_H
31 #define JSMN_H
32
33 #include <stddef.h>
34
35 #ifdef __cplusplus
36 extern "C" {
37 #endif
38
39 #ifdef JSMN_STATIC
40 #define JSMN_API static
41 #else
42 #define JSMN_API extern
43 #endif
44
45 /**
46 * JSON type identifier. Basic types are:
47 * o Object
48 * o Array
49 * o String
50 * o Other primitive: number, boolean (true/false) or null
51 */
52 typedef enum {
53 JSMN_UNDEFINED = 0,
54 JSMN_OBJECT = 1,
55 JSMN_ARRAY = 2,
56 JSMN_STRING = 3,
57 JSMN_PRIMITIVE = 4
58 } jsmntype_t;
59
60 enum jsmnerr {
61 /* Not enough tokens were provided */
62 JSMN_ERROR_NOMEM = -1,
63 /* Invalid character inside JSON string */
64 JSMN_ERROR_INVAL = -2,
65 /* The string is not a full JSON packet, more bytes expected */
66 JSMN_ERROR_PART = -3
67 };
68
69 /**
70 * JSON token description.
71 * type type (object, array, string etc.)
72 * start start position in JSON data string
73 * end end position in JSON data string
74 */
75 typedef struct {
76 jsmntype_t type;
77 int start;
78 int end;
79 int size;
80 #ifdef JSMN_PARENT_LINKS
81 int parent;
82 #endif
83 } jsmntok_t;
84
85 /**
86 * JSON parser. Contains an array of token blocks available. Also stores
87 * the string being parsed now and current position in that string.
88 */
89 typedef struct {
90 unsigned int pos; /* offset in the JSON string */
91 unsigned int toknext; /* next token to allocate */
92 int toksuper; /* superior token node, e.g. parent object or array */
93 } jsmn_parser;
94
95 /**
96 * Create JSON parser over an array of tokens
97 */
98 JSMN_API void jsmn_init(jsmn_parser *parser);
99
100 /**
101 * Run JSON parser. It parses a JSON data string into and array of tokens, each
102 * describing
103 * a single JSON object.
104 */
105 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
106 jsmntok_t *tokens, const unsigned int num_tokens);
107
108 #ifndef JSMN_HEADER
109 /**
110 * Allocates a fresh unused token from the token pool.
111 */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,const size_t num_tokens)112 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
113 const size_t num_tokens) {
114 jsmntok_t *tok;
115 if (parser->toknext >= num_tokens) {
116 return NULL;
117 }
118 tok = &tokens[parser->toknext++];
119 tok->start = tok->end = -1;
120 tok->size = 0;
121 #ifdef JSMN_PARENT_LINKS
122 tok->parent = -1;
123 #endif
124 return tok;
125 }
126
127 /**
128 * Fills token type and boundaries.
129 */
jsmn_fill_token(jsmntok_t * token,const jsmntype_t type,const int start,const int end)130 static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
131 const int start, const int end) {
132 token->type = type;
133 token->start = start;
134 token->end = end;
135 token->size = 0;
136 }
137
138 /**
139 * Fills next available token with JSON primitive.
140 */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,const size_t len,jsmntok_t * tokens,const size_t num_tokens)141 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
142 const size_t len, jsmntok_t *tokens,
143 const size_t num_tokens) {
144 jsmntok_t *token;
145 int start;
146
147 start = parser->pos;
148
149 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
150 switch (js[parser->pos]) {
151 #ifndef JSMN_STRICT
152 /* In strict mode primitive must be followed by "," or "}" or "]" */
153 case ':':
154 #endif
155 case '\t':
156 case '\r':
157 case '\n':
158 case ' ':
159 case ',':
160 case ']':
161 case '}':
162 goto found;
163 }
164 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
165 parser->pos = start;
166 return JSMN_ERROR_INVAL;
167 }
168 }
169 #ifdef JSMN_STRICT
170 /* In strict mode primitive must be followed by a comma/object/array */
171 parser->pos = start;
172 return JSMN_ERROR_PART;
173 #endif
174
175 found:
176 if (tokens == NULL) {
177 parser->pos--;
178 return 0;
179 }
180 token = jsmn_alloc_token(parser, tokens, num_tokens);
181 if (token == NULL) {
182 parser->pos = start;
183 return JSMN_ERROR_NOMEM;
184 }
185 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
186 #ifdef JSMN_PARENT_LINKS
187 token->parent = parser->toksuper;
188 #endif
189 parser->pos--;
190 return 0;
191 }
192
193 /**
194 * Fills next token with JSON string.
195 */
jsmn_parse_string(jsmn_parser * parser,const char * js,const size_t len,jsmntok_t * tokens,const size_t num_tokens)196 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
197 const size_t len, jsmntok_t *tokens,
198 const size_t num_tokens) {
199 jsmntok_t *token;
200
201 int start = parser->pos;
202
203 parser->pos++;
204
205 /* Skip starting quote */
206 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
207 char c = js[parser->pos];
208
209 /* Quote: end of string */
210 if (c == '\"') {
211 if (tokens == NULL) {
212 return 0;
213 }
214 token = jsmn_alloc_token(parser, tokens, num_tokens);
215 if (token == NULL) {
216 parser->pos = start;
217 return JSMN_ERROR_NOMEM;
218 }
219 jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
220 #ifdef JSMN_PARENT_LINKS
221 token->parent = parser->toksuper;
222 #endif
223 return 0;
224 }
225
226 /* Backslash: Quoted symbol expected */
227 if (c == '\\' && parser->pos + 1 < len) {
228 int i;
229 parser->pos++;
230 switch (js[parser->pos]) {
231 /* Allowed escaped symbols */
232 case '\"':
233 case '/':
234 case '\\':
235 case 'b':
236 case 'f':
237 case 'r':
238 case 'n':
239 case 't':
240 break;
241 /* Allows escaped symbol \uXXXX */
242 case 'u':
243 parser->pos++;
244 for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
245 i++) {
246 /* If it isn't a hex character we have an error */
247 if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
248 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
249 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
250 parser->pos = start;
251 return JSMN_ERROR_INVAL;
252 }
253 parser->pos++;
254 }
255 parser->pos--;
256 break;
257 /* Unexpected symbol */
258 default:
259 parser->pos = start;
260 return JSMN_ERROR_INVAL;
261 }
262 }
263 }
264 parser->pos = start;
265 return JSMN_ERROR_PART;
266 }
267
268 /**
269 * Parse JSON string and fill tokens.
270 */
jsmn_parse(jsmn_parser * parser,const char * js,const size_t len,jsmntok_t * tokens,const unsigned int num_tokens)271 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
272 jsmntok_t *tokens, const unsigned int num_tokens) {
273 int r;
274 int i;
275 jsmntok_t *token;
276 int count = parser->toknext;
277
278 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
279 char c;
280 jsmntype_t type;
281
282 c = js[parser->pos];
283 switch (c) {
284 case '{':
285 case '[':
286 count++;
287 if (tokens == NULL) {
288 break;
289 }
290 token = jsmn_alloc_token(parser, tokens, num_tokens);
291 if (token == NULL) {
292 return JSMN_ERROR_NOMEM;
293 }
294 if (parser->toksuper != -1) {
295 jsmntok_t *t = &tokens[parser->toksuper];
296 #ifdef JSMN_STRICT
297 /* In strict mode an object or array can't become a key */
298 if (t->type == JSMN_OBJECT) {
299 return JSMN_ERROR_INVAL;
300 }
301 #endif
302 t->size++;
303 #ifdef JSMN_PARENT_LINKS
304 token->parent = parser->toksuper;
305 #endif
306 }
307 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
308 token->start = parser->pos;
309 parser->toksuper = parser->toknext - 1;
310 break;
311 case '}':
312 case ']':
313 if (tokens == NULL) {
314 break;
315 }
316 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
317 #ifdef JSMN_PARENT_LINKS
318 if (parser->toknext < 1) {
319 return JSMN_ERROR_INVAL;
320 }
321 token = &tokens[parser->toknext - 1];
322 for (;;) {
323 if (token->start != -1 && token->end == -1) {
324 if (token->type != type) {
325 return JSMN_ERROR_INVAL;
326 }
327 token->end = parser->pos + 1;
328 parser->toksuper = token->parent;
329 break;
330 }
331 if (token->parent == -1) {
332 if (token->type != type || parser->toksuper == -1) {
333 return JSMN_ERROR_INVAL;
334 }
335 break;
336 }
337 token = &tokens[token->parent];
338 }
339 #else
340 for (i = parser->toknext - 1; i >= 0; i--) {
341 token = &tokens[i];
342 if (token->start != -1 && token->end == -1) {
343 if (token->type != type) {
344 return JSMN_ERROR_INVAL;
345 }
346 parser->toksuper = -1;
347 token->end = parser->pos + 1;
348 break;
349 }
350 }
351 /* Error if unmatched closing bracket */
352 if (i == -1) {
353 return JSMN_ERROR_INVAL;
354 }
355 for (; i >= 0; i--) {
356 token = &tokens[i];
357 if (token->start != -1 && token->end == -1) {
358 parser->toksuper = i;
359 break;
360 }
361 }
362 #endif
363 break;
364 case '\"':
365 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
366 if (r < 0) {
367 return r;
368 }
369 count++;
370 if (parser->toksuper != -1 && tokens != NULL) {
371 tokens[parser->toksuper].size++;
372 }
373 break;
374 case '\t':
375 case '\r':
376 case '\n':
377 case ' ':
378 break;
379 case ':':
380 parser->toksuper = parser->toknext - 1;
381 break;
382 case ',':
383 if (tokens != NULL && parser->toksuper != -1 &&
384 tokens[parser->toksuper].type != JSMN_ARRAY &&
385 tokens[parser->toksuper].type != JSMN_OBJECT) {
386 #ifdef JSMN_PARENT_LINKS
387 parser->toksuper = tokens[parser->toksuper].parent;
388 #else
389 for (i = parser->toknext - 1; i >= 0; i--) {
390 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
391 if (tokens[i].start != -1 && tokens[i].end == -1) {
392 parser->toksuper = i;
393 break;
394 }
395 }
396 }
397 #endif
398 }
399 break;
400 #ifdef JSMN_STRICT
401 /* In strict mode primitives are: numbers and booleans */
402 case '-':
403 case '0':
404 case '1':
405 case '2':
406 case '3':
407 case '4':
408 case '5':
409 case '6':
410 case '7':
411 case '8':
412 case '9':
413 case 't':
414 case 'f':
415 case 'n':
416 /* And they must not be keys of the object */
417 if (tokens != NULL && parser->toksuper != -1) {
418 const jsmntok_t *t = &tokens[parser->toksuper];
419 if (t->type == JSMN_OBJECT ||
420 (t->type == JSMN_STRING && t->size != 0)) {
421 return JSMN_ERROR_INVAL;
422 }
423 }
424 #else
425 /* In non-strict mode every unquoted value is a primitive */
426 default:
427 #endif
428 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
429 if (r < 0) {
430 return r;
431 }
432 count++;
433 if (parser->toksuper != -1 && tokens != NULL) {
434 tokens[parser->toksuper].size++;
435 }
436 break;
437
438 #ifdef JSMN_STRICT
439 /* Unexpected char in strict mode */
440 default:
441 return JSMN_ERROR_INVAL;
442 #endif
443 }
444 }
445
446 if (tokens != NULL) {
447 for (i = parser->toknext - 1; i >= 0; i--) {
448 /* Unmatched opened object or array */
449 if (tokens[i].start != -1 && tokens[i].end == -1) {
450 return JSMN_ERROR_PART;
451 }
452 }
453 }
454
455 return count;
456 }
457
458 /**
459 * Creates a new parser based over a given buffer with an array of tokens
460 * available.
461 */
jsmn_init(jsmn_parser * parser)462 JSMN_API void jsmn_init(jsmn_parser *parser) {
463 parser->pos = 0;
464 parser->toknext = 0;
465 parser->toksuper = -1;
466 }
467
468 #endif /* JSMN_HEADER */
469
470 #ifdef __cplusplus
471 }
472 #endif
473
474 #endif /* JSMN_H */
475