1 #include <stdlib.h>
2 
3 #include "jsmn.h"
4 
5 /**
6  * Allocates a fresh unused token from the token pull.
7  */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)8 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
9     jsmntok_t *tokens, size_t num_tokens) {
10   jsmntok_t *tok;
11   if (parser->toknext >= num_tokens) {
12     return NULL;
13   }
14   tok = &tokens[parser->toknext++];
15   tok->start = tok->end = -1;
16   tok->size = 0;
17 #ifdef JSMN_PARENT_LINKS
18   tok->parent = -1;
19 #endif
20   return tok;
21 }
22 
23 /**
24  * Fills token type and boundaries.
25  */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)26 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
27                             int start, int end) {
28   token->type = type;
29   token->start = start;
30   token->end = end;
31   token->size = 0;
32 }
33 
34 /**
35  * Fills next available token with JSON primitive.
36  */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)37 static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
38     size_t len, jsmntok_t *tokens, size_t num_tokens) {
39   jsmntok_t *token;
40   int start;
41 
42   start = parser->pos;
43 
44   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
45     switch (js[parser->pos]) {
46 #ifndef JSMN_STRICT
47       /* In strict mode primitive must be followed by "," or "}" or "]" */
48       case ':':
49 #endif
50       case '\t' : case '\r' : case '\n' : case ' ' :
51       case ','  : case ']'  : case '}' :
52         goto found;
53     }
54     if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
55       parser->pos = start;
56       return JSMN_ERROR_INVAL;
57     }
58   }
59 #ifdef JSMN_STRICT
60   /* In strict mode primitive must be followed by a comma/object/array */
61   parser->pos = start;
62   return JSMN_ERROR_PART;
63 #endif
64 
65 found:
66   if (tokens == NULL) {
67     parser->pos--;
68     return 0;
69   }
70   token = jsmn_alloc_token(parser, tokens, num_tokens);
71   if (token == NULL) {
72     parser->pos = start;
73     return JSMN_ERROR_NOMEM;
74   }
75   jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
76 #ifdef JSMN_PARENT_LINKS
77   token->parent = parser->toksuper;
78 #endif
79   parser->pos--;
80   return 0;
81 }
82 
83 /**
84  * Filsl next token with JSON string.
85  */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)86 static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
87     size_t len, jsmntok_t *tokens, size_t num_tokens) {
88   jsmntok_t *token;
89 
90   int start = parser->pos;
91 
92   parser->pos++;
93 
94   /* Skip starting quote */
95   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
96     char c = js[parser->pos];
97 
98     /* Quote: end of string */
99     if (c == '\"') {
100       if (tokens == NULL) {
101         return 0;
102       }
103       token = jsmn_alloc_token(parser, tokens, num_tokens);
104       if (token == NULL) {
105         parser->pos = start;
106         return JSMN_ERROR_NOMEM;
107       }
108       jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
109 #ifdef JSMN_PARENT_LINKS
110       token->parent = parser->toksuper;
111 #endif
112       return 0;
113     }
114 
115     /* Backslash: Quoted symbol expected */
116     if (c == '\\') {
117       parser->pos++;
118       switch (js[parser->pos]) {
119         /* Allowed escaped symbols */
120         case '\"': case '/' : case '\\' : case 'b' :
121         case 'f' : case 'r' : case 'n'  : case 't' :
122           break;
123         /* Allows escaped symbol \uXXXX */
124         case 'u':
125           parser->pos++;
126           int i = 0;
127           for(; i < 4 && js[parser->pos] != '\0'; i++) {
128             /* If it isn't a hex character we have an error */
129             if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
130                   (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
131                   (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
132               parser->pos = start;
133               return JSMN_ERROR_INVAL;
134             }
135             parser->pos++;
136           }
137           parser->pos--;
138           break;
139         /* Unexpected symbol */
140         default:
141           parser->pos = start;
142           return JSMN_ERROR_INVAL;
143       }
144     }
145   }
146   parser->pos = start;
147   return JSMN_ERROR_PART;
148 }
149 
150 /**
151  * Parse JSON string and fill tokens.
152  */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)153 jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
154     jsmntok_t *tokens, unsigned int num_tokens) {
155   jsmnerr_t r;
156   int i;
157   jsmntok_t *token;
158   int count = 0;
159 
160   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
161     char c;
162     jsmntype_t type;
163 
164     c = js[parser->pos];
165     switch (c) {
166       case '{': case '[':
167         count++;
168         if (tokens == NULL) {
169           break;
170         }
171         token = jsmn_alloc_token(parser, tokens, num_tokens);
172         if (token == NULL)
173           return JSMN_ERROR_NOMEM;
174         if (parser->toksuper != -1) {
175           tokens[parser->toksuper].size++;
176 #ifdef JSMN_PARENT_LINKS
177           token->parent = parser->toksuper;
178 #endif
179         }
180         token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
181         token->start = parser->pos;
182         parser->toksuper = parser->toknext - 1;
183         break;
184       case '}': case ']':
185         if (tokens == NULL)
186           break;
187         type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
188 #ifdef JSMN_PARENT_LINKS
189         if (parser->toknext < 1) {
190           return JSMN_ERROR_INVAL;
191         }
192         token = &tokens[parser->toknext - 1];
193         for (;;) {
194           if (token->start != -1 && token->end == -1) {
195             if (token->type != type) {
196               return JSMN_ERROR_INVAL;
197             }
198             token->end = parser->pos + 1;
199             parser->toksuper = token->parent;
200             break;
201           }
202           if (token->parent == -1) {
203             break;
204           }
205           token = &tokens[token->parent];
206         }
207 #else
208         for (i = parser->toknext - 1; i >= 0; i--) {
209           token = &tokens[i];
210           if (token->start != -1 && token->end == -1) {
211             if (token->type != type) {
212               return JSMN_ERROR_INVAL;
213             }
214             parser->toksuper = -1;
215             token->end = parser->pos + 1;
216             break;
217           }
218         }
219         /* Error if unmatched closing bracket */
220         if (i == -1) return JSMN_ERROR_INVAL;
221         for (; i >= 0; i--) {
222           token = &tokens[i];
223           if (token->start != -1 && token->end == -1) {
224             parser->toksuper = i;
225             break;
226           }
227         }
228 #endif
229         break;
230       case '\"':
231         r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
232         if (r < 0) return r;
233         count++;
234         if (parser->toksuper != -1 && tokens != NULL)
235           tokens[parser->toksuper].size++;
236         break;
237       case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
238         break;
239 #ifdef JSMN_STRICT
240       /* In strict mode primitives are: numbers and booleans */
241       case '-': case '0': case '1' : case '2': case '3' : case '4':
242       case '5': case '6': case '7' : case '8': case '9':
243       case 't': case 'f': case 'n' :
244 #else
245       /* In non-strict mode every unquoted value is a primitive */
246       default:
247 #endif
248         r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
249         if (r < 0) return r;
250         count++;
251         if (parser->toksuper != -1 && tokens != NULL)
252           tokens[parser->toksuper].size++;
253         break;
254 
255 #ifdef JSMN_STRICT
256       /* Unexpected char in strict mode */
257       default:
258         return JSMN_ERROR_INVAL;
259 #endif
260     }
261   }
262 
263   for (i = parser->toknext - 1; i >= 0; i--) {
264     /* Unmatched opened object or array */
265     if (tokens[i].start != -1 && tokens[i].end == -1) {
266       return JSMN_ERROR_PART;
267     }
268   }
269 
270   return count;
271 }
272 
273 /**
274  * Creates a new parser based over a given  buffer with an array of tokens
275  * available.
276  */
jsmn_init(jsmn_parser * parser)277 void jsmn_init(jsmn_parser *parser) {
278   parser->pos = 0;
279   parser->toknext = 0;
280   parser->toksuper = -1;
281 }
282