1 /* $NetBSD: jsmn.c,v 1.6 2020/05/25 20:47:24 christos Exp $ */
2
3 #include <stdlib.h>
4
5 #include "jsmn.h"
6
7 /**
8 * Allocates a fresh unused token from the token pull.
9 */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)10 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
11 jsmntok_t *tokens, size_t num_tokens) {
12 jsmntok_t *tok;
13 if (parser->toknext >= num_tokens) {
14 return NULL;
15 }
16 tok = &tokens[parser->toknext++];
17 tok->start = tok->end = -1;
18 tok->size = 0;
19 #ifdef JSMN_PARENT_LINKS
20 tok->parent = -1;
21 #endif
22 return tok;
23 }
24
25 /**
26 * Fills token type and boundaries.
27 */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)28 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
29 int start, int end) {
30 token->type = type;
31 token->start = start;
32 token->end = end;
33 token->size = 0;
34 }
35
36 /**
37 * Fills next available token with JSON primitive.
38 */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)39 static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
40 size_t len, jsmntok_t *tokens, size_t num_tokens) {
41 jsmntok_t *token;
42 int start;
43
44 start = parser->pos;
45
46 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
47 switch (js[parser->pos]) {
48 #ifndef JSMN_STRICT
49 /* In strict mode primitive must be followed by "," or "}" or "]" */
50 case ':':
51 #endif
52 case '\t' : case '\r' : case '\n' : case ' ' :
53 case ',' : case ']' : case '}' :
54 goto found;
55 }
56 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
57 parser->pos = start;
58 return JSMN_ERROR_INVAL;
59 }
60 }
61 #ifdef JSMN_STRICT
62 /* In strict mode primitive must be followed by a comma/object/array */
63 parser->pos = start;
64 return JSMN_ERROR_PART;
65 #endif
66
67 found:
68 if (tokens == NULL) {
69 parser->pos--;
70 return 0;
71 }
72 token = jsmn_alloc_token(parser, tokens, num_tokens);
73 if (token == NULL) {
74 parser->pos = start;
75 return JSMN_ERROR_NOMEM;
76 }
77 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
78 #ifdef JSMN_PARENT_LINKS
79 token->parent = parser->toksuper;
80 #endif
81 parser->pos--;
82 return 0;
83 }
84
85 /**
86 * Filsl next token with JSON string.
87 */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)88 static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
89 size_t len, jsmntok_t *tokens, size_t num_tokens) {
90 jsmntok_t *token;
91
92 int start = parser->pos;
93
94 parser->pos++;
95
96 /* Skip starting quote */
97 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
98 char c = js[parser->pos];
99
100 /* Quote: end of string */
101 if (c == '\"') {
102 if (tokens == NULL) {
103 return 0;
104 }
105 token = jsmn_alloc_token(parser, tokens, num_tokens);
106 if (token == NULL) {
107 parser->pos = start;
108 return JSMN_ERROR_NOMEM;
109 }
110 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
111 #ifdef JSMN_PARENT_LINKS
112 token->parent = parser->toksuper;
113 #endif
114 return 0;
115 }
116
117 /* Backslash: Quoted symbol expected */
118 if (c == '\\' && parser->pos + 1 < len) {
119 int i;
120 parser->pos++;
121 switch (js[parser->pos]) {
122 /* Allowed escaped symbols */
123 case '\"': case '/' : case '\\' : case 'b' :
124 case 'f' : case 'r' : case 'n' : case 't' :
125 break;
126 /* Allows escaped symbol \uXXXX */
127 case 'u':
128 parser->pos++;
129 for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
130 /* If it isn't a hex character we have an error */
131 if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
132 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
133 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
134 parser->pos = start;
135 return JSMN_ERROR_INVAL;
136 }
137 parser->pos++;
138 }
139 parser->pos--;
140 break;
141 /* Unexpected symbol */
142 default:
143 parser->pos = start;
144 return JSMN_ERROR_INVAL;
145 }
146 }
147 }
148 parser->pos = start;
149 return JSMN_ERROR_PART;
150 }
151
152 /**
153 * Parse JSON string and fill tokens.
154 */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)155 jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
156 jsmntok_t *tokens, unsigned int num_tokens) {
157 jsmnerr_t r;
158 int i;
159 jsmntok_t *token;
160 int count = 0;
161
162 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
163 char c;
164 jsmntype_t type;
165
166 c = js[parser->pos];
167 switch (c) {
168 case '{': case '[':
169 count++;
170 if (tokens == NULL) {
171 break;
172 }
173 token = jsmn_alloc_token(parser, tokens, num_tokens);
174 if (token == NULL)
175 return JSMN_ERROR_NOMEM;
176 if (parser->toksuper != -1) {
177 tokens[parser->toksuper].size++;
178 #ifdef JSMN_PARENT_LINKS
179 token->parent = parser->toksuper;
180 #endif
181 }
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;
193 }
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;
199 }
200 token->end = parser->pos + 1;
201 parser->toksuper = token->parent;
202 break;
203 }
204 if (token->parent == -1) {
205 break;
206 }
207 token = &tokens[token->parent];
208 }
209 #else
210 for (i = parser->toknext - 1; i >= 0; i--) {
211 token = &tokens[i];
212 if (token->start != -1 && token->end == -1) {
213 if (token->type != type) {
214 return JSMN_ERROR_INVAL;
215 }
216 parser->toksuper = -1;
217 token->end = parser->pos + 1;
218 break;
219 }
220 }
221 /* Error if unmatched closing bracket */
222 if (i == -1) return JSMN_ERROR_INVAL;
223 for (; i >= 0; i--) {
224 token = &tokens[i];
225 if (token->start != -1 && token->end == -1) {
226 parser->toksuper = i;
227 break;
228 }
229 }
230 #endif
231 break;
232 case '\"':
233 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
234 if (r < 0) return r;
235 count++;
236 if (parser->toksuper != -1 && tokens != NULL)
237 tokens[parser->toksuper].size++;
238 break;
239 case '\t' : case '\r' : case '\n' : case ' ':
240 break;
241 case ':':
242 parser->toksuper = parser->toknext - 1;
243 break;
244 case ',':
245 if (tokens != NULL &&
246 tokens[parser->toksuper].type != JSMN_ARRAY &&
247 tokens[parser->toksuper].type != JSMN_OBJECT) {
248 #ifdef JSMN_PARENT_LINKS
249 parser->toksuper = tokens[parser->toksuper].parent;
250 #else
251 for (i = parser->toknext - 1; i >= 0; i--) {
252 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
253 if (tokens[i].start != -1 && tokens[i].end == -1) {
254 parser->toksuper = i;
255 break;
256 }
257 }
258 }
259 #endif
260 }
261 break;
262 #ifdef JSMN_STRICT
263 /* In strict mode primitives are: numbers and booleans */
264 case '-': case '0': case '1' : case '2': case '3' : case '4':
265 case '5': case '6': case '7' : case '8': case '9':
266 case 't': case 'f': case 'n' :
267 /* And they must not be keys of the object */
268 if (tokens != NULL) {
269 jsmntok_t *t = &tokens[parser->toksuper];
270 if (t->type == JSMN_OBJECT ||
271 (t->type == JSMN_STRING && t->size != 0)) {
272 return JSMN_ERROR_INVAL;
273 }
274 }
275 #else
276 /* In non-strict mode every unquoted value is a primitive */
277 default:
278 #endif
279 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
280 if (r < 0) return r;
281 count++;
282 if (parser->toksuper != -1 && tokens != NULL)
283 tokens[parser->toksuper].size++;
284 break;
285
286 #ifdef JSMN_STRICT
287 /* Unexpected char in strict mode */
288 default:
289 return JSMN_ERROR_INVAL;
290 #endif
291 }
292 }
293 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 }
301
302 return count;
303 }
304
305 /**
306 * Creates a new parser based over a given buffer with an array of tokens
307 * available.
308 */
jsmn_init(jsmn_parser * parser)309 void jsmn_init(jsmn_parser *parser) {
310 parser->pos = 0;
311 parser->toknext = 0;
312 parser->toksuper = -1;
313 }
314
315