jsmn: Fix a parser bug where object keys could be non-strings
[jimtcl.git] / jsmn / jsmn.c
blob2de5ec27f2c6c3b330e2691d93268fe8d32708f8
1 #include "jsmn.h"
3 /**
4 * Allocates a fresh unused token from the token pool.
5 */
6 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
7 jsmntok_t *tokens, size_t num_tokens) {
8 jsmntok_t *tok;
9 if (parser->toknext >= num_tokens) {
10 return NULL;
12 tok = &tokens[parser->toknext++];
13 tok->start = tok->end = -1;
14 tok->size = 0;
15 #ifdef JSMN_PARENT_LINKS
16 tok->parent = -1;
17 #endif
18 return tok;
21 /**
22 * Fills token type and boundaries.
24 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
25 int start, int end) {
26 token->type = type;
27 token->start = start;
28 token->end = end;
29 token->size = 0;
32 /**
33 * Fills next available token with JSON primitive.
35 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
36 size_t len, jsmntok_t *tokens, size_t num_tokens) {
37 jsmntok_t *token;
38 int start;
40 start = parser->pos;
42 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
43 switch (js[parser->pos]) {
44 #ifndef JSMN_STRICT
45 /* In strict mode primitive must be followed by "," or "}" or "]" */
46 case ':':
47 #endif
48 case '\t' : case '\r' : case '\n' : case ' ' :
49 case ',' : case ']' : case '}' :
50 goto found;
52 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
53 parser->pos = start;
54 return JSMN_ERROR_INVAL;
57 #ifdef JSMN_STRICT
58 /* In strict mode primitive must be followed by a comma/object/array */
59 parser->pos = start;
60 return JSMN_ERROR_PART;
61 #endif
63 found:
64 if (tokens == NULL) {
65 parser->pos--;
66 return 0;
68 token = jsmn_alloc_token(parser, tokens, num_tokens);
69 if (token == NULL) {
70 parser->pos = start;
71 return JSMN_ERROR_NOMEM;
73 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
74 #ifdef JSMN_PARENT_LINKS
75 token->parent = parser->toksuper;
76 #endif
77 parser->pos--;
78 return 0;
81 /**
82 * Fills next token with JSON string.
84 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
85 size_t len, jsmntok_t *tokens, size_t num_tokens) {
86 jsmntok_t *token;
88 int start = parser->pos;
90 parser->pos++;
92 /* Skip starting quote */
93 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
94 char c = js[parser->pos];
96 /* Quote: end of string */
97 if (c == '\"') {
98 if (tokens == NULL) {
99 return 0;
101 token = jsmn_alloc_token(parser, tokens, num_tokens);
102 if (token == NULL) {
103 parser->pos = start;
104 return JSMN_ERROR_NOMEM;
106 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
107 #ifdef JSMN_PARENT_LINKS
108 token->parent = parser->toksuper;
109 #endif
110 return 0;
113 /* Backslash: Quoted symbol expected */
114 if (c == '\\' && parser->pos + 1 < len) {
115 int i;
116 parser->pos++;
117 switch (js[parser->pos]) {
118 /* Allowed escaped symbols */
119 case '\"': case '/' : case '\\' : case 'b' :
120 case 'f' : case 'r' : case 'n' : case 't' :
121 break;
122 /* Allows escaped symbol \uXXXX */
123 case 'u':
124 parser->pos++;
125 for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
126 /* If it isn't a hex character we have an error */
127 if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
128 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
129 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
130 parser->pos = start;
131 return JSMN_ERROR_INVAL;
133 parser->pos++;
135 parser->pos--;
136 break;
137 /* Unexpected symbol */
138 default:
139 parser->pos = start;
140 return JSMN_ERROR_INVAL;
144 parser->pos = start;
145 return JSMN_ERROR_PART;
149 * Parse JSON string and fill tokens.
151 int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
152 jsmntok_t *tokens, unsigned int num_tokens) {
153 int r;
154 int i;
155 jsmntok_t *token;
156 int count = parser->toknext;
158 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
159 char c;
160 jsmntype_t type;
162 c = js[parser->pos];
163 switch (c) {
164 case '{': case '[':
165 count++;
166 if (tokens == NULL) {
167 break;
169 token = jsmn_alloc_token(parser, tokens, num_tokens);
170 if (token == NULL)
171 return JSMN_ERROR_NOMEM;
172 if (parser->toksuper != -1) {
173 if (tokens[parser->toksuper].type == JSMN_OBJECT) {
174 /* Object keys must be strings, not objects or arrays */
175 return JSMN_ERROR_INVAL;
177 tokens[parser->toksuper].size++;
178 #ifdef JSMN_PARENT_LINKS
179 token->parent = parser->toksuper;
180 #endif
182 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
183 token->start = parser->pos;
184 parser->toksuper = parser->toknext - 1;
185 break;
186 case '}': case ']':
187 if (tokens == NULL)
188 break;
189 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
190 #ifdef JSMN_PARENT_LINKS
191 if (parser->toknext < 1) {
192 return JSMN_ERROR_INVAL;
194 token = &tokens[parser->toknext - 1];
195 for (;;) {
196 if (token->start != -1 && token->end == -1) {
197 if (token->type != type) {
198 return JSMN_ERROR_INVAL;
200 token->end = parser->pos + 1;
201 parser->toksuper = token->parent;
202 break;
204 if (token->parent == -1) {
205 if(token->type != type || parser->toksuper == -1) {
206 return JSMN_ERROR_INVAL;
208 break;
210 token = &tokens[token->parent];
212 #else
213 for (i = parser->toknext - 1; i >= 0; i--) {
214 token = &tokens[i];
215 if (token->start != -1 && token->end == -1) {
216 if (token->type != type) {
217 return JSMN_ERROR_INVAL;
219 parser->toksuper = -1;
220 token->end = parser->pos + 1;
221 break;
224 /* Error if unmatched closing bracket */
225 if (i == -1) return JSMN_ERROR_INVAL;
226 for (; i >= 0; i--) {
227 token = &tokens[i];
228 if (token->start != -1 && token->end == -1) {
229 parser->toksuper = i;
230 break;
233 #endif
234 break;
235 case '\"':
236 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
237 if (r < 0) return r;
238 count++;
239 if (parser->toksuper != -1 && tokens != NULL)
240 tokens[parser->toksuper].size++;
241 break;
242 case '\t' : case '\r' : case '\n' : case ' ':
243 break;
244 case ':':
245 parser->toksuper = parser->toknext - 1;
246 break;
247 case ',':
248 if (tokens != NULL && parser->toksuper != -1 &&
249 tokens[parser->toksuper].type != JSMN_ARRAY &&
250 tokens[parser->toksuper].type != JSMN_OBJECT) {
251 #ifdef JSMN_PARENT_LINKS
252 parser->toksuper = tokens[parser->toksuper].parent;
253 #else
254 for (i = parser->toknext - 1; i >= 0; i--) {
255 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
256 if (tokens[i].start != -1 && tokens[i].end == -1) {
257 parser->toksuper = i;
258 break;
262 #endif
264 break;
265 #ifdef JSMN_STRICT
266 /* In strict mode primitives are: numbers and booleans */
267 case '-': case '0': case '1' : case '2': case '3' : case '4':
268 case '5': case '6': case '7' : case '8': case '9':
269 case 't': case 'f': case 'n' :
270 /* And they must not be keys of the object */
271 if (tokens != NULL && parser->toksuper != -1) {
272 jsmntok_t *t = &tokens[parser->toksuper];
273 if (t->type == JSMN_OBJECT ||
274 (t->type == JSMN_STRING && t->size != 0)) {
275 return JSMN_ERROR_INVAL;
278 #else
279 /* In non-strict mode every unquoted value is a primitive */
280 default:
281 #endif
282 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
283 if (r < 0) return r;
284 count++;
285 if (parser->toksuper != -1 && tokens != NULL)
286 tokens[parser->toksuper].size++;
287 break;
289 #ifdef JSMN_STRICT
290 /* Unexpected char in strict mode */
291 default:
292 return JSMN_ERROR_INVAL;
293 #endif
297 if (tokens != NULL) {
298 for (i = parser->toknext - 1; i >= 0; i--) {
299 /* Unmatched opened object or array */
300 if (tokens[i].start != -1 && tokens[i].end == -1) {
301 return JSMN_ERROR_PART;
306 return count;
310 * Creates a new parser based over a given buffer with an array of tokens
311 * available.
313 void jsmn_init(jsmn_parser *parser) {
314 parser->pos = 0;
315 parser->toknext = 0;
316 parser->toksuper = -1;