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