jsmn.c revision 290001
1#include <stdlib.h>
2
3#include "jsmn.h"
4
5/**
6 * Allocates a fresh unused token from the token pull.
7 */
8static 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 */
26static 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 */
37static 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
65found:
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 */
86static 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 == '\\' && parser->pos + 1 < len) {
117			int i;
118			parser->pos++;
119			switch (js[parser->pos]) {
120				/* Allowed escaped symbols */
121				case '\"': case '/' : case '\\' : case 'b' :
122				case 'f' : case 'r' : case 'n'  : case 't' :
123					break;
124				/* Allows escaped symbol \uXXXX */
125				case 'u':
126					parser->pos++;
127					for(i = 0; i < 4 && parser->pos < len && 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 */
153jsmnerr_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 ' ':
238				break;
239			case ':':
240				parser->toksuper = parser->toknext - 1;
241				break;
242			case ',':
243				if (tokens != NULL &&
244						tokens[parser->toksuper].type != JSMN_ARRAY &&
245						tokens[parser->toksuper].type != JSMN_OBJECT) {
246#ifdef JSMN_PARENT_LINKS
247					parser->toksuper = tokens[parser->toksuper].parent;
248#else
249					for (i = parser->toknext - 1; i >= 0; i--) {
250						if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
251							if (tokens[i].start != -1 && tokens[i].end == -1) {
252								parser->toksuper = i;
253								break;
254							}
255						}
256					}
257#endif
258				}
259				break;
260#ifdef JSMN_STRICT
261			/* In strict mode primitives are: numbers and booleans */
262			case '-': case '0': case '1' : case '2': case '3' : case '4':
263			case '5': case '6': case '7' : case '8': case '9':
264			case 't': case 'f': case 'n' :
265				/* And they must not be keys of the object */
266				if (tokens != NULL) {
267					jsmntok_t *t = &tokens[parser->toksuper];
268					if (t->type == JSMN_OBJECT ||
269							(t->type == JSMN_STRING && t->size != 0)) {
270						return JSMN_ERROR_INVAL;
271					}
272				}
273#else
274			/* In non-strict mode every unquoted value is a primitive */
275			default:
276#endif
277				r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
278				if (r < 0) return r;
279				count++;
280				if (parser->toksuper != -1 && tokens != NULL)
281					tokens[parser->toksuper].size++;
282				break;
283
284#ifdef JSMN_STRICT
285			/* Unexpected char in strict mode */
286			default:
287				return JSMN_ERROR_INVAL;
288#endif
289		}
290	}
291	if (tokens != NULL) {
292		for (i = parser->toknext - 1; i >= 0; i--) {
293			/* Unmatched opened object or array */
294			if (tokens[i].start != -1 && tokens[i].end == -1) {
295				return JSMN_ERROR_PART;
296			}
297		}
298	}
299
300	return count;
301}
302
303/**
304 * Creates a new parser based over a given  buffer with an array of tokens
305 * available.
306 */
307void jsmn_init(jsmn_parser *parser) {
308	parser->pos = 0;
309	parser->toknext = 0;
310	parser->toksuper = -1;
311}
312
313