• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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