blob: 1387b684a17c6b4c13d3b91844250c5563c8c95f [file] [log] [blame]
Haibo Huang9d58f792019-05-02 22:35:15 -07001/*
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 */
Haibo Huang00b4ac42019-07-15 13:58:05 -070024// ANDROID CHANGE: Default to header only.
25#ifndef JSMN_IMPL
26#define JSMN_HEADER
27#endif
28// END ANDROID CHANGE
29
Haibo Huang9d58f792019-05-02 22:35:15 -070030#ifndef JSMN_H
31#define JSMN_H
Edwin Wong27b5a352014-05-28 15:36:44 -070032
Haibo Huang8e498432018-09-06 14:52:29 -070033#include <stddef.h>
34
Edwin Wong27b5a352014-05-28 15:36:44 -070035#ifdef __cplusplus
36extern "C" {
37#endif
38
Haibo Huang9d58f792019-05-02 22:35:15 -070039#ifdef JSMN_STATIC
40#define JSMN_API static
41#else
42#define JSMN_API extern
43#endif
44
Edwin Wong27b5a352014-05-28 15:36:44 -070045/**
46 * JSON type identifier. Basic types are:
Haibo Huang8e498432018-09-06 14:52:29 -070047 * o Object
48 * o Array
49 * o String
50 * o Other primitive: number, boolean (true/false) or null
Edwin Wong27b5a352014-05-28 15:36:44 -070051 */
52typedef enum {
Haibo Huang9d58f792019-05-02 22:35:15 -070053 JSMN_UNDEFINED = 0,
54 JSMN_OBJECT = 1,
55 JSMN_ARRAY = 2,
56 JSMN_STRING = 3,
57 JSMN_PRIMITIVE = 4
Edwin Wong27b5a352014-05-28 15:36:44 -070058} jsmntype_t;
59
Haibo Huang8e498432018-09-06 14:52:29 -070060enum jsmnerr {
Haibo Huang9d58f792019-05-02 22:35:15 -070061 /* Not enough tokens were provided */
62 JSMN_ERROR_NOMEM = -1,
63 /* Invalid character inside JSON string */
64 JSMN_ERROR_INVAL = -2,
65 /* The string is not a full JSON packet, more bytes expected */
66 JSMN_ERROR_PART = -3
Haibo Huang8e498432018-09-06 14:52:29 -070067};
Edwin Wong27b5a352014-05-28 15:36:44 -070068
69/**
70 * JSON token description.
Haibo Huang8e498432018-09-06 14:52:29 -070071 * type type (object, array, string etc.)
72 * start start position in JSON data string
73 * end end position in JSON data string
Edwin Wong27b5a352014-05-28 15:36:44 -070074 */
75typedef struct {
Haibo Huang9d58f792019-05-02 22:35:15 -070076 jsmntype_t type;
77 int start;
78 int end;
79 int size;
Edwin Wong27b5a352014-05-28 15:36:44 -070080#ifdef JSMN_PARENT_LINKS
Haibo Huang9d58f792019-05-02 22:35:15 -070081 int parent;
Edwin Wong27b5a352014-05-28 15:36:44 -070082#endif
83} jsmntok_t;
84
85/**
86 * JSON parser. Contains an array of token blocks available. Also stores
Haibo Huang9d58f792019-05-02 22:35:15 -070087 * the string being parsed now and current position in that string.
Edwin Wong27b5a352014-05-28 15:36:44 -070088 */
89typedef struct {
Haibo Huang9d58f792019-05-02 22:35:15 -070090 unsigned int pos; /* offset in the JSON string */
91 unsigned int toknext; /* next token to allocate */
92 int toksuper; /* superior token node, e.g. parent object or array */
Edwin Wong27b5a352014-05-28 15:36:44 -070093} jsmn_parser;
94
95/**
96 * Create JSON parser over an array of tokens
97 */
Haibo Huang9d58f792019-05-02 22:35:15 -070098JSMN_API void jsmn_init(jsmn_parser *parser);
Edwin Wong27b5a352014-05-28 15:36:44 -070099
100/**
Haibo Huang9d58f792019-05-02 22:35:15 -0700101 * Run JSON parser. It parses a JSON data string into and array of tokens, each
102 * describing
Edwin Wong27b5a352014-05-28 15:36:44 -0700103 * a single JSON object.
104 */
Haibo Huang9d58f792019-05-02 22:35:15 -0700105JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
106 jsmntok_t *tokens, const unsigned int num_tokens);
107
Haibo Huang00b4ac42019-07-15 13:58:05 -0700108#ifndef JSMN_HEADER
Haibo Huang9d58f792019-05-02 22:35:15 -0700109/**
110 * Allocates a fresh unused token from the token pool.
111 */
112static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
113 const size_t num_tokens) {
114 jsmntok_t *tok;
115 if (parser->toknext >= num_tokens) {
116 return NULL;
117 }
118 tok = &tokens[parser->toknext++];
119 tok->start = tok->end = -1;
120 tok->size = 0;
121#ifdef JSMN_PARENT_LINKS
122 tok->parent = -1;
123#endif
124 return tok;
125}
126
127/**
128 * Fills token type and boundaries.
129 */
130static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
131 const int start, const int end) {
132 token->type = type;
133 token->start = start;
134 token->end = end;
135 token->size = 0;
136}
137
138/**
139 * Fills next available token with JSON primitive.
140 */
141static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
142 const size_t len, jsmntok_t *tokens,
143 const size_t num_tokens) {
144 jsmntok_t *token;
145 int start;
146
147 start = parser->pos;
148
149 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
150 switch (js[parser->pos]) {
151#ifndef JSMN_STRICT
152 /* In strict mode primitive must be followed by "," or "}" or "]" */
153 case ':':
154#endif
155 case '\t':
156 case '\r':
157 case '\n':
158 case ' ':
159 case ',':
160 case ']':
161 case '}':
162 goto found;
163 }
164 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
165 parser->pos = start;
166 return JSMN_ERROR_INVAL;
167 }
168 }
169#ifdef JSMN_STRICT
170 /* In strict mode primitive must be followed by a comma/object/array */
171 parser->pos = start;
172 return JSMN_ERROR_PART;
173#endif
174
175found:
176 if (tokens == NULL) {
177 parser->pos--;
178 return 0;
179 }
180 token = jsmn_alloc_token(parser, tokens, num_tokens);
181 if (token == NULL) {
182 parser->pos = start;
183 return JSMN_ERROR_NOMEM;
184 }
185 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
186#ifdef JSMN_PARENT_LINKS
187 token->parent = parser->toksuper;
188#endif
189 parser->pos--;
190 return 0;
191}
192
193/**
194 * Fills next token with JSON string.
195 */
196static int jsmn_parse_string(jsmn_parser *parser, const char *js,
197 const size_t len, jsmntok_t *tokens,
198 const size_t num_tokens) {
199 jsmntok_t *token;
200
201 int start = parser->pos;
202
203 parser->pos++;
204
205 /* Skip starting quote */
206 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
207 char c = js[parser->pos];
208
209 /* Quote: end of string */
210 if (c == '\"') {
211 if (tokens == NULL) {
212 return 0;
213 }
214 token = jsmn_alloc_token(parser, tokens, num_tokens);
215 if (token == NULL) {
216 parser->pos = start;
217 return JSMN_ERROR_NOMEM;
218 }
219 jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
220#ifdef JSMN_PARENT_LINKS
221 token->parent = parser->toksuper;
222#endif
223 return 0;
224 }
225
226 /* Backslash: Quoted symbol expected */
227 if (c == '\\' && parser->pos + 1 < len) {
228 int i;
229 parser->pos++;
230 switch (js[parser->pos]) {
231 /* Allowed escaped symbols */
232 case '\"':
233 case '/':
234 case '\\':
235 case 'b':
236 case 'f':
237 case 'r':
238 case 'n':
239 case 't':
240 break;
241 /* Allows escaped symbol \uXXXX */
242 case 'u':
243 parser->pos++;
244 for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
245 i++) {
246 /* If it isn't a hex character we have an error */
247 if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
248 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
249 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
250 parser->pos = start;
251 return JSMN_ERROR_INVAL;
252 }
253 parser->pos++;
254 }
255 parser->pos--;
256 break;
257 /* Unexpected symbol */
258 default:
259 parser->pos = start;
260 return JSMN_ERROR_INVAL;
261 }
262 }
263 }
264 parser->pos = start;
265 return JSMN_ERROR_PART;
266}
267
268/**
269 * Parse JSON string and fill tokens.
270 */
271JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
272 jsmntok_t *tokens, const unsigned int num_tokens) {
273 int r;
274 int i;
275 jsmntok_t *token;
276 int count = parser->toknext;
277
278 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
279 char c;
280 jsmntype_t type;
281
282 c = js[parser->pos];
283 switch (c) {
284 case '{':
285 case '[':
286 count++;
287 if (tokens == NULL) {
288 break;
289 }
290 token = jsmn_alloc_token(parser, tokens, num_tokens);
291 if (token == NULL) {
292 return JSMN_ERROR_NOMEM;
293 }
294 if (parser->toksuper != -1) {
295 jsmntok_t *t = &tokens[parser->toksuper];
296#ifdef JSMN_STRICT
297 /* In strict mode an object or array can't become a key */
298 if (t->type == JSMN_OBJECT) {
299 return JSMN_ERROR_INVAL;
300 }
301#endif
302 t->size++;
303#ifdef JSMN_PARENT_LINKS
304 token->parent = parser->toksuper;
305#endif
306 }
307 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
308 token->start = parser->pos;
309 parser->toksuper = parser->toknext - 1;
310 break;
311 case '}':
312 case ']':
313 if (tokens == NULL) {
314 break;
315 }
316 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
317#ifdef JSMN_PARENT_LINKS
318 if (parser->toknext < 1) {
319 return JSMN_ERROR_INVAL;
320 }
321 token = &tokens[parser->toknext - 1];
322 for (;;) {
323 if (token->start != -1 && token->end == -1) {
324 if (token->type != type) {
325 return JSMN_ERROR_INVAL;
326 }
327 token->end = parser->pos + 1;
328 parser->toksuper = token->parent;
329 break;
330 }
331 if (token->parent == -1) {
332 if (token->type != type || parser->toksuper == -1) {
333 return JSMN_ERROR_INVAL;
334 }
335 break;
336 }
337 token = &tokens[token->parent];
338 }
339#else
340 for (i = parser->toknext - 1; i >= 0; i--) {
341 token = &tokens[i];
342 if (token->start != -1 && token->end == -1) {
343 if (token->type != type) {
344 return JSMN_ERROR_INVAL;
345 }
346 parser->toksuper = -1;
347 token->end = parser->pos + 1;
348 break;
349 }
350 }
351 /* Error if unmatched closing bracket */
352 if (i == -1) {
353 return JSMN_ERROR_INVAL;
354 }
355 for (; i >= 0; i--) {
356 token = &tokens[i];
357 if (token->start != -1 && token->end == -1) {
358 parser->toksuper = i;
359 break;
360 }
361 }
362#endif
363 break;
364 case '\"':
365 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
366 if (r < 0) {
367 return r;
368 }
369 count++;
370 if (parser->toksuper != -1 && tokens != NULL) {
371 tokens[parser->toksuper].size++;
372 }
373 break;
374 case '\t':
375 case '\r':
376 case '\n':
377 case ' ':
378 break;
379 case ':':
380 parser->toksuper = parser->toknext - 1;
381 break;
382 case ',':
383 if (tokens != NULL && parser->toksuper != -1 &&
384 tokens[parser->toksuper].type != JSMN_ARRAY &&
385 tokens[parser->toksuper].type != JSMN_OBJECT) {
386#ifdef JSMN_PARENT_LINKS
387 parser->toksuper = tokens[parser->toksuper].parent;
388#else
389 for (i = parser->toknext - 1; i >= 0; i--) {
390 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
391 if (tokens[i].start != -1 && tokens[i].end == -1) {
392 parser->toksuper = i;
393 break;
394 }
395 }
396 }
397#endif
398 }
399 break;
400#ifdef JSMN_STRICT
401 /* In strict mode primitives are: numbers and booleans */
402 case '-':
403 case '0':
404 case '1':
405 case '2':
406 case '3':
407 case '4':
408 case '5':
409 case '6':
410 case '7':
411 case '8':
412 case '9':
413 case 't':
414 case 'f':
415 case 'n':
416 /* And they must not be keys of the object */
417 if (tokens != NULL && parser->toksuper != -1) {
418 const jsmntok_t *t = &tokens[parser->toksuper];
419 if (t->type == JSMN_OBJECT ||
420 (t->type == JSMN_STRING && t->size != 0)) {
421 return JSMN_ERROR_INVAL;
422 }
423 }
424#else
425 /* In non-strict mode every unquoted value is a primitive */
426 default:
427#endif
428 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
429 if (r < 0) {
430 return r;
431 }
432 count++;
433 if (parser->toksuper != -1 && tokens != NULL) {
434 tokens[parser->toksuper].size++;
435 }
436 break;
437
438#ifdef JSMN_STRICT
439 /* Unexpected char in strict mode */
440 default:
441 return JSMN_ERROR_INVAL;
442#endif
443 }
444 }
445
446 if (tokens != NULL) {
447 for (i = parser->toknext - 1; i >= 0; i--) {
448 /* Unmatched opened object or array */
449 if (tokens[i].start != -1 && tokens[i].end == -1) {
450 return JSMN_ERROR_PART;
451 }
452 }
453 }
454
455 return count;
456}
457
458/**
459 * Creates a new parser based over a given buffer with an array of tokens
460 * available.
461 */
462JSMN_API void jsmn_init(jsmn_parser *parser) {
463 parser->pos = 0;
464 parser->toknext = 0;
465 parser->toksuper = -1;
466}
467
Haibo Huang00b4ac42019-07-15 13:58:05 -0700468#endif /* JSMN_HEADER */
Edwin Wong27b5a352014-05-28 15:36:44 -0700469
470#ifdef __cplusplus
471}
472#endif
473
Haibo Huang9d58f792019-05-02 22:35:15 -0700474#endif /* JSMN_H */