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