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