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