blob: 853c3f17ad7a923dc40d61bf435d943547c0ea63 [file] [log] [blame]
Edwin Wong27b5a352014-05-28 15:36:44 -07001#include "jsmn.h"
2
3/**
Haibo Huang983ec7f2019-02-01 17:05:26 -08004 * Allocates a fresh unused token from the token pool.
Edwin Wong27b5a352014-05-28 15:36:44 -07005 */
6static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
Haibo Huang8e498432018-09-06 14:52:29 -07007 jsmntok_t *tokens, size_t num_tokens) {
8 jsmntok_t *tok;
9 if (parser->toknext >= num_tokens) {
10 return NULL;
11 }
12 tok = &tokens[parser->toknext++];
13 tok->start = tok->end = -1;
14 tok->size = 0;
Edwin Wong27b5a352014-05-28 15:36:44 -070015#ifdef JSMN_PARENT_LINKS
Haibo Huang8e498432018-09-06 14:52:29 -070016 tok->parent = -1;
Edwin Wong27b5a352014-05-28 15:36:44 -070017#endif
Haibo Huang8e498432018-09-06 14:52:29 -070018 return tok;
Edwin Wong27b5a352014-05-28 15:36:44 -070019}
20
21/**
22 * Fills token type and boundaries.
23 */
24static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
25 int start, int end) {
Haibo Huang8e498432018-09-06 14:52:29 -070026 token->type = type;
27 token->start = start;
28 token->end = end;
29 token->size = 0;
Edwin Wong27b5a352014-05-28 15:36:44 -070030}
31
32/**
33 * Fills next available token with JSON primitive.
34 */
Haibo Huang8e498432018-09-06 14:52:29 -070035static 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;
Edwin Wong27b5a352014-05-28 15:36:44 -070039
Haibo Huang8e498432018-09-06 14:52:29 -070040 start = parser->pos;
Edwin Wong27b5a352014-05-28 15:36:44 -070041
Haibo Huang8e498432018-09-06 14:52:29 -070042 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
43 switch (js[parser->pos]) {
Edwin Wong27b5a352014-05-28 15:36:44 -070044#ifndef JSMN_STRICT
Haibo Huang8e498432018-09-06 14:52:29 -070045 /* In strict mode primitive must be followed by "," or "}" or "]" */
46 case ':':
Edwin Wong27b5a352014-05-28 15:36:44 -070047#endif
Haibo Huang8e498432018-09-06 14:52:29 -070048 case '\t' : case '\r' : case '\n' : case ' ' :
49 case ',' : case ']' : case '}' :
50 goto found;
51 }
52 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
53 parser->pos = start;
54 return JSMN_ERROR_INVAL;
55 }
56 }
Edwin Wong27b5a352014-05-28 15:36:44 -070057#ifdef JSMN_STRICT
Haibo Huang8e498432018-09-06 14:52:29 -070058 /* In strict mode primitive must be followed by a comma/object/array */
59 parser->pos = start;
60 return JSMN_ERROR_PART;
Edwin Wong27b5a352014-05-28 15:36:44 -070061#endif
62
63found:
Haibo Huang8e498432018-09-06 14:52:29 -070064 if (tokens == NULL) {
65 parser->pos--;
66 return 0;
67 }
68 token = jsmn_alloc_token(parser, tokens, num_tokens);
69 if (token == NULL) {
70 parser->pos = start;
71 return JSMN_ERROR_NOMEM;
72 }
73 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
Edwin Wong27b5a352014-05-28 15:36:44 -070074#ifdef JSMN_PARENT_LINKS
Haibo Huang8e498432018-09-06 14:52:29 -070075 token->parent = parser->toksuper;
Edwin Wong27b5a352014-05-28 15:36:44 -070076#endif
Haibo Huang8e498432018-09-06 14:52:29 -070077 parser->pos--;
78 return 0;
Edwin Wong27b5a352014-05-28 15:36:44 -070079}
80
81/**
Haibo Huang8e498432018-09-06 14:52:29 -070082 * Fills next token with JSON string.
Edwin Wong27b5a352014-05-28 15:36:44 -070083 */
Haibo Huang8e498432018-09-06 14:52:29 -070084static 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;
Edwin Wong27b5a352014-05-28 15:36:44 -070087
Haibo Huang8e498432018-09-06 14:52:29 -070088 int start = parser->pos;
Edwin Wong27b5a352014-05-28 15:36:44 -070089
Haibo Huang8e498432018-09-06 14:52:29 -070090 parser->pos++;
Edwin Wong27b5a352014-05-28 15:36:44 -070091
Haibo Huang8e498432018-09-06 14:52:29 -070092 /* Skip starting quote */
93 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
94 char c = js[parser->pos];
Edwin Wong27b5a352014-05-28 15:36:44 -070095
Haibo Huang8e498432018-09-06 14:52:29 -070096 /* Quote: end of string */
97 if (c == '\"') {
98 if (tokens == NULL) {
99 return 0;
100 }
101 token = jsmn_alloc_token(parser, tokens, num_tokens);
102 if (token == NULL) {
103 parser->pos = start;
104 return JSMN_ERROR_NOMEM;
105 }
106 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
Edwin Wong27b5a352014-05-28 15:36:44 -0700107#ifdef JSMN_PARENT_LINKS
Haibo Huang8e498432018-09-06 14:52:29 -0700108 token->parent = parser->toksuper;
Edwin Wong27b5a352014-05-28 15:36:44 -0700109#endif
Haibo Huang8e498432018-09-06 14:52:29 -0700110 return 0;
111 }
Edwin Wong27b5a352014-05-28 15:36:44 -0700112
Haibo Huang8e498432018-09-06 14:52:29 -0700113 /* 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;
132 }
133 parser->pos++;
134 }
135 parser->pos--;
136 break;
137 /* Unexpected symbol */
138 default:
139 parser->pos = start;
140 return JSMN_ERROR_INVAL;
141 }
142 }
143 }
144 parser->pos = start;
145 return JSMN_ERROR_PART;
Edwin Wong27b5a352014-05-28 15:36:44 -0700146}
147
148/**
149 * Parse JSON string and fill tokens.
150 */
Haibo Huang8e498432018-09-06 14:52:29 -0700151int 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;
Edwin Wong27b5a352014-05-28 15:36:44 -0700157
Haibo Huang8e498432018-09-06 14:52:29 -0700158 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
159 char c;
160 jsmntype_t type;
Edwin Wong27b5a352014-05-28 15:36:44 -0700161
Haibo Huang8e498432018-09-06 14:52:29 -0700162 c = js[parser->pos];
163 switch (c) {
164 case '{': case '[':
165 count++;
166 if (tokens == NULL) {
167 break;
168 }
169 token = jsmn_alloc_token(parser, tokens, num_tokens);
170 if (token == NULL)
171 return JSMN_ERROR_NOMEM;
172 if (parser->toksuper != -1) {
173 tokens[parser->toksuper].size++;
Edwin Wong27b5a352014-05-28 15:36:44 -0700174#ifdef JSMN_PARENT_LINKS
Haibo Huang8e498432018-09-06 14:52:29 -0700175 token->parent = parser->toksuper;
Edwin Wong27b5a352014-05-28 15:36:44 -0700176#endif
Haibo Huang8e498432018-09-06 14:52:29 -0700177 }
178 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
179 token->start = parser->pos;
180 parser->toksuper = parser->toknext - 1;
181 break;
182 case '}': case ']':
183 if (tokens == NULL)
184 break;
185 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
Edwin Wong27b5a352014-05-28 15:36:44 -0700186#ifdef JSMN_PARENT_LINKS
Haibo Huang8e498432018-09-06 14:52:29 -0700187 if (parser->toknext < 1) {
188 return JSMN_ERROR_INVAL;
189 }
190 token = &tokens[parser->toknext - 1];
191 for (;;) {
192 if (token->start != -1 && token->end == -1) {
193 if (token->type != type) {
194 return JSMN_ERROR_INVAL;
195 }
196 token->end = parser->pos + 1;
197 parser->toksuper = token->parent;
198 break;
199 }
200 if (token->parent == -1) {
201 if(token->type != type || parser->toksuper == -1) {
202 return JSMN_ERROR_INVAL;
203 }
204 break;
205 }
206 token = &tokens[token->parent];
207 }
Edwin Wong27b5a352014-05-28 15:36:44 -0700208#else
Haibo Huang8e498432018-09-06 14:52:29 -0700209 for (i = parser->toknext - 1; i >= 0; i--) {
210 token = &tokens[i];
211 if (token->start != -1 && token->end == -1) {
212 if (token->type != type) {
213 return JSMN_ERROR_INVAL;
214 }
215 parser->toksuper = -1;
216 token->end = parser->pos + 1;
217 break;
218 }
219 }
220 /* Error if unmatched closing bracket */
221 if (i == -1) return JSMN_ERROR_INVAL;
222 for (; i >= 0; i--) {
223 token = &tokens[i];
224 if (token->start != -1 && token->end == -1) {
225 parser->toksuper = i;
226 break;
227 }
228 }
Edwin Wong27b5a352014-05-28 15:36:44 -0700229#endif
Haibo Huang8e498432018-09-06 14:52:29 -0700230 break;
231 case '\"':
232 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
233 if (r < 0) return r;
234 count++;
235 if (parser->toksuper != -1 && tokens != NULL)
236 tokens[parser->toksuper].size++;
237 break;
238 case '\t' : case '\r' : case '\n' : case ' ':
239 break;
240 case ':':
241 parser->toksuper = parser->toknext - 1;
242 break;
243 case ',':
244 if (tokens != NULL && parser->toksuper != -1 &&
245 tokens[parser->toksuper].type != JSMN_ARRAY &&
246 tokens[parser->toksuper].type != JSMN_OBJECT) {
247#ifdef JSMN_PARENT_LINKS
248 parser->toksuper = tokens[parser->toksuper].parent;
249#else
250 for (i = parser->toknext - 1; i >= 0; i--) {
251 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
252 if (tokens[i].start != -1 && tokens[i].end == -1) {
253 parser->toksuper = i;
254 break;
255 }
256 }
257 }
258#endif
259 }
260 break;
Edwin Wong27b5a352014-05-28 15:36:44 -0700261#ifdef JSMN_STRICT
Haibo Huang8e498432018-09-06 14:52:29 -0700262 /* In strict mode primitives are: numbers and booleans */
263 case '-': case '0': case '1' : case '2': case '3' : case '4':
264 case '5': case '6': case '7' : case '8': case '9':
265 case 't': case 'f': case 'n' :
266 /* And they must not be keys of the object */
267 if (tokens != NULL && parser->toksuper != -1) {
268 jsmntok_t *t = &tokens[parser->toksuper];
269 if (t->type == JSMN_OBJECT ||
270 (t->type == JSMN_STRING && t->size != 0)) {
271 return JSMN_ERROR_INVAL;
272 }
273 }
Edwin Wong27b5a352014-05-28 15:36:44 -0700274#else
Haibo Huang8e498432018-09-06 14:52:29 -0700275 /* In non-strict mode every unquoted value is a primitive */
276 default:
Edwin Wong27b5a352014-05-28 15:36:44 -0700277#endif
Haibo Huang8e498432018-09-06 14:52:29 -0700278 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
279 if (r < 0) return r;
280 count++;
281 if (parser->toksuper != -1 && tokens != NULL)
282 tokens[parser->toksuper].size++;
283 break;
Edwin Wong27b5a352014-05-28 15:36:44 -0700284
285#ifdef JSMN_STRICT
Haibo Huang8e498432018-09-06 14:52:29 -0700286 /* Unexpected char in strict mode */
287 default:
288 return JSMN_ERROR_INVAL;
Edwin Wong27b5a352014-05-28 15:36:44 -0700289#endif
Haibo Huang8e498432018-09-06 14:52:29 -0700290 }
291 }
Edwin Wong27b5a352014-05-28 15:36:44 -0700292
Haibo Huang8e498432018-09-06 14:52:29 -0700293 if (tokens != NULL) {
294 for (i = parser->toknext - 1; i >= 0; i--) {
295 /* Unmatched opened object or array */
296 if (tokens[i].start != -1 && tokens[i].end == -1) {
297 return JSMN_ERROR_PART;
298 }
299 }
300 }
Edwin Wong27b5a352014-05-28 15:36:44 -0700301
Haibo Huang8e498432018-09-06 14:52:29 -0700302 return count;
Edwin Wong27b5a352014-05-28 15:36:44 -0700303}
304
305/**
306 * Creates a new parser based over a given buffer with an array of tokens
307 * available.
308 */
309void jsmn_init(jsmn_parser *parser) {
Haibo Huang8e498432018-09-06 14:52:29 -0700310 parser->pos = 0;
311 parser->toknext = 0;
312 parser->toksuper = -1;
Edwin Wong27b5a352014-05-28 15:36:44 -0700313}
Haibo Huang8e498432018-09-06 14:52:29 -0700314