PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
jsmn.h
Go to the documentation of this file.
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#ifndef JSMN_H
25#define JSMN_H
26
27#include <stddef.h>
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33#ifdef JSMN_STATIC
34#define JSMN_API static
35#else
36#define JSMN_API extern
37#endif
38
53
54enum jsmnerr {
55 /* Not enough tokens were provided */
57 /* Invalid character inside JSON string */
59 /* The string is not a full JSON packet, more bytes expected */
61};
62
69typedef struct {
71 int start;
72 int end;
73 int size;
74#ifdef JSMN_PARENT_LINKS
75 int parent;
76#endif
77} jsmntok_t;
78
83typedef struct {
84 unsigned int pos; /* offset in the JSON string */
85 unsigned int toknext; /* next token to allocate */
86 int toksuper; /* superior token node, e.g. parent object or array */
88
92JSMN_API void jsmn_init(jsmn_parser *parser);
93
99JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
100 jsmntok_t *tokens, const unsigned int num_tokens);
101
102#ifndef JSMN_HEADER
106static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
107 const size_t num_tokens) {
108 jsmntok_t *tok;
109 if (parser->toknext >= num_tokens) {
110 return NULL;
111 }
112 tok = &tokens[parser->toknext++];
113 tok->start = tok->end = -1;
114 tok->size = 0;
115#ifdef JSMN_PARENT_LINKS
116 tok->parent = -1;
117#endif
118 return tok;
119}
120
124static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
125 const int start, const int end) {
126 token->type = type;
127 token->start = start;
128 token->end = end;
129 token->size = 0;
130}
131
135static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
136 const size_t len, jsmntok_t *tokens,
137 const size_t num_tokens) {
138 jsmntok_t *token;
139 int start;
140
141 start = parser->pos;
142
143 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
144 switch (js[parser->pos]) {
145#ifndef JSMN_STRICT
146 /* In strict mode primitive must be followed by "," or "}" or "]" */
147 case ':':
148#endif
149 case '\t':
150 case '\r':
151 case '\n':
152 case ' ':
153 case ',':
154 case ']':
155 case '}':
156 goto found;
157 }
158 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
159 parser->pos = start;
160 return JSMN_ERROR_INVAL;
161 }
162 }
163#ifdef JSMN_STRICT
164 /* In strict mode primitive must be followed by a comma/object/array */
165 parser->pos = start;
166 return JSMN_ERROR_PART;
167#endif
168
169found:
170 if (tokens == NULL) {
171 parser->pos--;
172 return 0;
173 }
174 token = jsmn_alloc_token(parser, tokens, num_tokens);
175 if (token == NULL) {
176 parser->pos = start;
177 return JSMN_ERROR_NOMEM;
178 }
179 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
180#ifdef JSMN_PARENT_LINKS
181 token->parent = parser->toksuper;
182#endif
183 parser->pos--;
184 return 0;
185}
186
190static int jsmn_parse_string(jsmn_parser *parser, const char *js,
191 const size_t len, jsmntok_t *tokens,
192 const size_t num_tokens) {
193 jsmntok_t *token;
194
195 int start = parser->pos;
196
197 parser->pos++;
198
199 /* Skip starting quote */
200 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
201 char c = js[parser->pos];
202
203 /* Quote: end of string */
204 if (c == '\"') {
205 if (tokens == NULL) {
206 return 0;
207 }
208 token = jsmn_alloc_token(parser, tokens, num_tokens);
209 if (token == NULL) {
210 parser->pos = start;
211 return JSMN_ERROR_NOMEM;
212 }
213 jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
214#ifdef JSMN_PARENT_LINKS
215 token->parent = parser->toksuper;
216#endif
217 return 0;
218 }
219
220 /* Backslash: Quoted symbol expected */
221 if (c == '\\' && parser->pos + 1 < len) {
222 int i;
223 parser->pos++;
224 switch (js[parser->pos]) {
225 /* Allowed escaped symbols */
226 case '\"':
227 case '/':
228 case '\\':
229 case 'b':
230 case 'f':
231 case 'r':
232 case 'n':
233 case 't':
234 break;
235 /* Allows escaped symbol \uXXXX */
236 case 'u':
237 parser->pos++;
238 for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
239 i++) {
240 /* If it isn't a hex character we have an error */
241 if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
242 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
243 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
244 parser->pos = start;
245 return JSMN_ERROR_INVAL;
246 }
247 parser->pos++;
248 }
249 parser->pos--;
250 break;
251 /* Unexpected symbol */
252 default:
253 parser->pos = start;
254 return JSMN_ERROR_INVAL;
255 }
256 }
257 }
258 parser->pos = start;
259 return JSMN_ERROR_PART;
260}
261
265JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
266 jsmntok_t *tokens, const unsigned int num_tokens) {
267 int r;
268 int i;
269 jsmntok_t *token;
270 int count = parser->toknext;
271
272 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
273 char c;
274 jsmntype_t type;
275
276 c = js[parser->pos];
277 switch (c) {
278 case '{':
279 case '[':
280 count++;
281 if (tokens == NULL) {
282 break;
283 }
284 token = jsmn_alloc_token(parser, tokens, num_tokens);
285 if (token == NULL) {
286 return JSMN_ERROR_NOMEM;
287 }
288 if (parser->toksuper != -1) {
289 jsmntok_t *t = &tokens[parser->toksuper];
290#ifdef JSMN_STRICT
291 /* In strict mode an object or array can't become a key */
292 if (t->type == JSMN_OBJECT) {
293 return JSMN_ERROR_INVAL;
294 }
295#endif
296 t->size++;
297#ifdef JSMN_PARENT_LINKS
298 token->parent = parser->toksuper;
299#endif
300 }
301 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
302 token->start = parser->pos;
303 parser->toksuper = parser->toknext - 1;
304 break;
305 case '}':
306 case ']':
307 if (tokens == NULL) {
308 break;
309 }
310 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
311#ifdef JSMN_PARENT_LINKS
312 if (parser->toknext < 1) {
313 return JSMN_ERROR_INVAL;
314 }
315 token = &tokens[parser->toknext - 1];
316 for (;;) {
317 if (token->start != -1 && token->end == -1) {
318 if (token->type != type) {
319 return JSMN_ERROR_INVAL;
320 }
321 token->end = parser->pos + 1;
322 parser->toksuper = token->parent;
323 break;
324 }
325 if (token->parent == -1) {
326 if (token->type != type || parser->toksuper == -1) {
327 return JSMN_ERROR_INVAL;
328 }
329 break;
330 }
331 token = &tokens[token->parent];
332 }
333#else
334 for (i = parser->toknext - 1; i >= 0; i--) {
335 token = &tokens[i];
336 if (token->start != -1 && token->end == -1) {
337 if (token->type != type) {
338 return JSMN_ERROR_INVAL;
339 }
340 parser->toksuper = -1;
341 token->end = parser->pos + 1;
342 break;
343 }
344 }
345 /* Error if unmatched closing bracket */
346 if (i == -1) {
347 return JSMN_ERROR_INVAL;
348 }
349 for (; i >= 0; i--) {
350 token = &tokens[i];
351 if (token->start != -1 && token->end == -1) {
352 parser->toksuper = i;
353 break;
354 }
355 }
356#endif
357 break;
358 case '\"':
359 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
360 if (r < 0) {
361 return r;
362 }
363 count++;
364 if (parser->toksuper != -1 && tokens != NULL) {
365 tokens[parser->toksuper].size++;
366 }
367 break;
368 case '\t':
369 case '\r':
370 case '\n':
371 case ' ':
372 break;
373 case ':':
374 parser->toksuper = parser->toknext - 1;
375 break;
376 case ',':
377 if (tokens != NULL && parser->toksuper != -1 &&
378 tokens[parser->toksuper].type != JSMN_ARRAY &&
379 tokens[parser->toksuper].type != JSMN_OBJECT) {
380#ifdef JSMN_PARENT_LINKS
381 parser->toksuper = tokens[parser->toksuper].parent;
382#else
383 for (i = parser->toknext - 1; i >= 0; i--) {
384 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
385 if (tokens[i].start != -1 && tokens[i].end == -1) {
386 parser->toksuper = i;
387 break;
388 }
389 }
390 }
391#endif
392 }
393 break;
394#ifdef JSMN_STRICT
395 /* In strict mode primitives are: numbers and booleans */
396 case '-':
397 case '0':
398 case '1':
399 case '2':
400 case '3':
401 case '4':
402 case '5':
403 case '6':
404 case '7':
405 case '8':
406 case '9':
407 case 't':
408 case 'f':
409 case 'n':
410 /* And they must not be keys of the object */
411 if (tokens != NULL && parser->toksuper != -1) {
412 const jsmntok_t *t = &tokens[parser->toksuper];
413 if (t->type == JSMN_OBJECT ||
414 (t->type == JSMN_STRING && t->size != 0)) {
415 return JSMN_ERROR_INVAL;
416 }
417 }
418#else
419 /* In non-strict mode every unquoted value is a primitive */
420 default:
421#endif
422 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
423 if (r < 0) {
424 return r;
425 }
426 count++;
427 if (parser->toksuper != -1 && tokens != NULL) {
428 tokens[parser->toksuper].size++;
429 }
430 break;
431
432#ifdef JSMN_STRICT
433 /* Unexpected char in strict mode */
434 default:
435 return JSMN_ERROR_INVAL;
436#endif
437 }
438 }
439
440 if (tokens != NULL) {
441 for (i = parser->toknext - 1; i >= 0; i--) {
442 /* Unmatched opened object or array */
443 if (tokens[i].start != -1 && tokens[i].end == -1) {
444 return JSMN_ERROR_PART;
445 }
446 }
447 }
448
449 return count;
450}
451
457 parser->pos = 0;
458 parser->toknext = 0;
459 parser->toksuper = -1;
460}
461
462#endif /* JSMN_HEADER */
463
464#ifdef __cplusplus
465}
466#endif
467
468#endif /* JSMN_H */
#define NULL
Definition BinReloc.cpp:317
jsmntype_t
Definition jsmn.h:46
@ JSMN_PRIMITIVE
Definition jsmn.h:51
@ JSMN_OBJECT
Definition jsmn.h:48
@ JSMN_UNDEFINED
Definition jsmn.h:47
@ JSMN_ARRAY
Definition jsmn.h:49
@ JSMN_STRING
Definition jsmn.h:50
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, jsmntok_t *tokens, const unsigned int num_tokens)
Definition jsmn.h:265
JSMN_API void jsmn_init(jsmn_parser *parser)
Definition jsmn.h:456
#define JSMN_API
Definition jsmn.h:36
jsmnerr
Definition jsmn.h:54
@ JSMN_ERROR_INVAL
Definition jsmn.h:58
@ JSMN_ERROR_PART
Definition jsmn.h:60
@ JSMN_ERROR_NOMEM
Definition jsmn.h:56
unsigned int pos
Definition jsmn.h:84
int toksuper
Definition jsmn.h:86
unsigned int toknext
Definition jsmn.h:85
int start
Definition jsmn.h:71
int size
Definition jsmn.h:73
int end
Definition jsmn.h:72
jsmntype_t type
Definition jsmn.h:70
int count
Definition test.cpp:12