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