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