Line data Source code
1 : /** @file jsmn.c https://github.com/zserge/jsmn.
2 : Copyright (c) 2010 Serge A. Zaitsev
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : */
22 :
23 : #include "jsmn.h"
24 :
25 : /**
26 : * Allocates a fresh unused token from the token pull.
27 : */
28 0 : static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
29 : jsmntok_t *tokens, size_t num_tokens) {
30 : jsmntok_t *tok;
31 0 : if (parser->toknext >= num_tokens) {
32 0 : return NULL;
33 : }
34 0 : tok = &tokens[parser->toknext++];
35 0 : tok->start = tok->end = -1;
36 0 : tok->size = 0;
37 : #ifdef JSMN_PARENT_LINKS
38 : tok->parent = -1;
39 : #endif
40 0 : return tok;
41 : }
42 :
43 : /**
44 : * Fills token type and boundaries.
45 : */
46 0 : static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
47 : int start, int end) {
48 0 : token->type = type;
49 0 : token->start = start;
50 0 : token->end = end;
51 0 : token->size = 0;
52 0 : }
53 :
54 : /**
55 : * Fills next available token with JSON primitive.
56 : */
57 0 : static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
58 : size_t len, jsmntok_t *tokens, size_t num_tokens) {
59 : jsmntok_t *token;
60 : int start;
61 :
62 0 : start = parser->pos;
63 :
64 0 : for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
65 0 : switch (js[parser->pos]) {
66 : #ifndef JSMN_STRICT
67 : /* In strict mode primitive must be followed by "," or "}" or "]" */
68 : case ':':
69 : #endif
70 : case '\t' : case '\r' : case '\n' : case ' ' :
71 : case ',' : case ']' : case '}' :
72 0 : goto found;
73 : }
74 0 : if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
75 0 : parser->pos = start;
76 0 : return JSMN_ERROR_INVAL;
77 : }
78 : }
79 : #ifdef JSMN_STRICT
80 : /* In strict mode primitive must be followed by a comma/object/array */
81 : parser->pos = start;
82 : return JSMN_ERROR_PART;
83 : #endif
84 :
85 : found:
86 0 : if (tokens == NULL) {
87 0 : parser->pos--;
88 0 : return 0;
89 : }
90 0 : token = jsmn_alloc_token(parser, tokens, num_tokens);
91 0 : if (token == NULL) {
92 0 : parser->pos = start;
93 0 : return JSMN_ERROR_NOMEM;
94 : }
95 0 : jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
96 : #ifdef JSMN_PARENT_LINKS
97 : token->parent = parser->toksuper;
98 : #endif
99 0 : parser->pos--;
100 0 : return 0;
101 : }
102 :
103 : /**
104 : * Fills next token with JSON string.
105 : */
106 0 : static int jsmn_parse_string(jsmn_parser *parser, const char *js,
107 : size_t len, jsmntok_t *tokens, size_t num_tokens) {
108 : jsmntok_t *token;
109 :
110 0 : int start = parser->pos;
111 :
112 0 : parser->pos++;
113 :
114 : /* Skip starting quote */
115 0 : for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
116 0 : char c = js[parser->pos];
117 :
118 : /* Quote: end of string */
119 0 : if (c == '\"') {
120 0 : if (tokens == NULL) {
121 0 : return 0;
122 : }
123 0 : token = jsmn_alloc_token(parser, tokens, num_tokens);
124 0 : if (token == NULL) {
125 0 : parser->pos = start;
126 0 : return JSMN_ERROR_NOMEM;
127 : }
128 0 : jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
129 : #ifdef JSMN_PARENT_LINKS
130 : token->parent = parser->toksuper;
131 : #endif
132 0 : return 0;
133 : }
134 :
135 : /* Backslash: Quoted symbol expected */
136 0 : if (c == '\\' && parser->pos + 1 < len) {
137 : int i;
138 0 : parser->pos++;
139 0 : switch (js[parser->pos]) {
140 : /* Allowed escaped symbols */
141 : case '\"': case '/' : case '\\' : case 'b' :
142 : case 'f' : case 'r' : case 'n' : case 't' :
143 0 : break;
144 : /* Allows escaped symbol \uXXXX */
145 : case 'u':
146 0 : parser->pos++;
147 0 : for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
148 : /* If it isn't a hex character we have an error */
149 0 : if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
150 0 : (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
151 0 : (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
152 0 : parser->pos = start;
153 0 : return JSMN_ERROR_INVAL;
154 : }
155 0 : parser->pos++;
156 : }
157 0 : parser->pos--;
158 0 : break;
159 : /* Unexpected symbol */
160 : default:
161 0 : parser->pos = start;
162 0 : return JSMN_ERROR_INVAL;
163 : }
164 : }
165 : }
166 0 : parser->pos = start;
167 0 : return JSMN_ERROR_PART;
168 : }
169 :
170 : /**
171 : * Parse JSON string and fill tokens.
172 : */
173 0 : int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
174 : jsmntok_t *tokens, unsigned int num_tokens) {
175 : int r;
176 : int i;
177 : jsmntok_t *token;
178 0 : int count = parser->toknext;
179 :
180 0 : for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
181 : char c;
182 : jsmntype_t type;
183 :
184 0 : c = js[parser->pos];
185 0 : switch (c) {
186 : case '{': case '[':
187 0 : count++;
188 0 : if (tokens == NULL) {
189 0 : break;
190 : }
191 0 : token = jsmn_alloc_token(parser, tokens, num_tokens);
192 0 : if (token == NULL)
193 0 : return JSMN_ERROR_NOMEM;
194 0 : if (parser->toksuper != -1) {
195 0 : tokens[parser->toksuper].size++;
196 : #ifdef JSMN_PARENT_LINKS
197 : token->parent = parser->toksuper;
198 : #endif
199 : }
200 0 : token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
201 0 : token->start = parser->pos;
202 0 : parser->toksuper = parser->toknext - 1;
203 0 : break;
204 : case '}': case ']':
205 0 : if (tokens == NULL)
206 0 : break;
207 0 : type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
208 : #ifdef JSMN_PARENT_LINKS
209 : if (parser->toknext < 1) {
210 : return JSMN_ERROR_INVAL;
211 : }
212 : token = &tokens[parser->toknext - 1];
213 : for (;;) {
214 : if (token->start != -1 && token->end == -1) {
215 : if (token->type != type) {
216 : return JSMN_ERROR_INVAL;
217 : }
218 : token->end = parser->pos + 1;
219 : parser->toksuper = token->parent;
220 : break;
221 : }
222 : if (token->parent == -1) {
223 : if(token->type != type || parser->toksuper == -1) {
224 : return JSMN_ERROR_INVAL;
225 : }
226 : break;
227 : }
228 : token = &tokens[token->parent];
229 : }
230 : #else
231 0 : for (i = parser->toknext - 1; i >= 0; i--) {
232 0 : token = &tokens[i];
233 0 : if (token->start != -1 && token->end == -1) {
234 0 : if (token->type != type) {
235 0 : return JSMN_ERROR_INVAL;
236 : }
237 0 : parser->toksuper = -1;
238 0 : token->end = parser->pos + 1;
239 0 : break;
240 : }
241 : }
242 : /* Error if unmatched closing bracket */
243 0 : if (i == -1) return JSMN_ERROR_INVAL;
244 0 : for (; i >= 0; i--) {
245 0 : token = &tokens[i];
246 0 : if (token->start != -1 && token->end == -1) {
247 0 : parser->toksuper = i;
248 0 : break;
249 : }
250 : }
251 : #endif
252 0 : break;
253 : case '\"':
254 0 : r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
255 0 : if (r < 0) return r;
256 0 : count++;
257 0 : if (parser->toksuper != -1 && tokens != NULL)
258 0 : tokens[parser->toksuper].size++;
259 0 : break;
260 : case '\t' : case '\r' : case '\n' : case ' ':
261 0 : break;
262 : case ':':
263 0 : parser->toksuper = parser->toknext - 1;
264 0 : break;
265 : case ',':
266 0 : if (tokens != NULL && parser->toksuper != -1 &&
267 0 : tokens[parser->toksuper].type != JSMN_ARRAY &&
268 0 : tokens[parser->toksuper].type != JSMN_OBJECT) {
269 : #ifdef JSMN_PARENT_LINKS
270 : parser->toksuper = tokens[parser->toksuper].parent;
271 : #else
272 0 : for (i = parser->toknext - 1; i >= 0; i--) {
273 0 : if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
274 0 : if (tokens[i].start != -1 && tokens[i].end == -1) {
275 0 : parser->toksuper = i;
276 0 : break;
277 : }
278 : }
279 : }
280 : #endif
281 : }
282 0 : break;
283 : #ifdef JSMN_STRICT
284 : /* In strict mode primitives are: numbers and booleans */
285 : case '-': case '0': case '1' : case '2': case '3' : case '4':
286 : case '5': case '6': case '7' : case '8': case '9':
287 : case 't': case 'f': case 'n' :
288 : /* And they must not be keys of the object */
289 : if (tokens != NULL && parser->toksuper != -1) {
290 : jsmntok_t *t = &tokens[parser->toksuper];
291 : if (t->type == JSMN_OBJECT ||
292 : (t->type == JSMN_STRING && t->size != 0)) {
293 : return JSMN_ERROR_INVAL;
294 : }
295 : }
296 : #else
297 : /* In non-strict mode every unquoted value is a primitive */
298 : default:
299 : #endif
300 0 : r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
301 0 : if (r < 0) return r;
302 0 : count++;
303 0 : if (parser->toksuper != -1 && tokens != NULL)
304 0 : tokens[parser->toksuper].size++;
305 0 : break;
306 :
307 : #ifdef JSMN_STRICT
308 : /* Unexpected char in strict mode */
309 : default:
310 : return JSMN_ERROR_INVAL;
311 : #endif
312 : }
313 : }
314 :
315 0 : if (tokens != NULL) {
316 0 : for (i = parser->toknext - 1; i >= 0; i--) {
317 : /* Unmatched opened object or array */
318 0 : if (tokens[i].start != -1 && tokens[i].end == -1) {
319 0 : return JSMN_ERROR_PART;
320 : }
321 : }
322 : }
323 :
324 0 : return count;
325 : }
326 :
327 : /**
328 : * Creates a new parser based over a given buffer with an array of tokens
329 : * available.
330 : */
331 0 : void jsmn_init(jsmn_parser *parser) {
332 0 : parser->pos = 0;
333 0 : parser->toknext = 0;
334 0 : parser->toksuper = -1;
335 0 : }
336 :
|