jsmn.c revision 275970
10SN/A#include <stdlib.h>
217408Salanb#include <string.h>
30SN/A
40SN/A#include "jsmn.h"
50SN/A
60SN/A/**
72362SN/A * Allocates a fresh unused token from the token pull.
80SN/A */
92362SN/Astatic jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
100SN/A		jsmntok_t *tokens, size_t num_tokens) {
110SN/A	jsmntok_t *tok;
120SN/A	if (parser->toknext >= num_tokens) {
130SN/A		return NULL;
140SN/A	}
150SN/A	tok = &tokens[parser->toknext++];
160SN/A	tok->start = tok->end = -1;
170SN/A	tok->size = 0;
180SN/A#ifdef JSMN_PARENT_LINKS
190SN/A	tok->parent = -1;
200SN/A#endif
212362SN/A	return tok;
222362SN/A}
232362SN/A
240SN/A/**
250SN/A * Fills token type and boundaries.
260SN/A */
270SN/Astatic void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
280SN/A                            int start, int end) {
290SN/A	token->type = type;
3017408Salanb	token->start = start;
3117408Salanb	token->end = end;
320SN/A	token->size = 0;
330SN/A}
340SN/A
3517408Salanb/**
360SN/A * Fills next available token with JSON primitive.
370SN/A */
380SN/Astatic jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
390SN/A		jsmntok_t *tokens, size_t num_tokens) {
400SN/A	jsmntok_t *token;
410SN/A	int start;
420SN/A
430SN/A	start = parser->pos;
440SN/A
450SN/A	for (; js[parser->pos] != '\0'; parser->pos++) {
460SN/A		switch (js[parser->pos]) {
4712546Savstepan#ifndef JSMN_STRICT
480SN/A			/* In strict mode primitive must be followed by "," or "}" or "]" */
490SN/A			case ':':
500SN/A#endif
510SN/A			case '\t' : case '\r' : case '\n' : case ' ' :
520SN/A			case ','  : case ']'  : case '}' :
530SN/A				goto found;
540SN/A		}
550SN/A		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
560SN/A			parser->pos = start;
5712546Savstepan			return JSMN_ERROR_INVAL;
580SN/A		}
5912546Savstepan	}
600SN/A#ifdef JSMN_STRICT
610SN/A	/* In strict mode primitive must be followed by a comma/object/array */
620SN/A	parser->pos = start;
630SN/A	return JSMN_ERROR_PART;
640SN/A#endif
650SN/A
660SN/Afound:
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			parser->pos++;
112			switch (js[parser->pos]) {
113				/* Allowed escaped symbols */
114				case '\"': case '/' : case '\\' : case 'b' :
115				case 'f' : case 'r' : case 'n'  : case 't' :
116					break;
117				/* Allows escaped symbol \uXXXX */
118				case 'u':
119					parser->pos++;
120					int i = 0;
121					for(; i < 4 && js[parser->pos] != '\0'; i++) {
122						/* If it isn't a hex character we have an error */
123						if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
124									(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
125									(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
126							parser->pos = start;
127							return JSMN_ERROR_INVAL;
128						}
129						parser->pos++;
130					}
131					parser->pos--;
132					break;
133				/* Unexpected symbol */
134				default:
135					parser->pos = start;
136					return JSMN_ERROR_INVAL;
137			}
138		}
139	}
140	parser->pos = start;
141	return JSMN_ERROR_PART;
142}
143
144/**
145 * Parse JSON string and fill tokens.
146 */
147jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens,
148		unsigned int num_tokens) {
149	jsmnerr_t r;
150	int i;
151	jsmntok_t *token;
152
153	for (; js[parser->pos] != '\0'; parser->pos++) {
154		char c;
155		jsmntype_t type;
156
157		c = js[parser->pos];
158		switch (c) {
159			case '{': case '[':
160				token = jsmn_alloc_token(parser, tokens, num_tokens);
161				if (token == NULL)
162					return JSMN_ERROR_NOMEM;
163				if (parser->toksuper != -1) {
164					tokens[parser->toksuper].size++;
165#ifdef JSMN_PARENT_LINKS
166					token->parent = parser->toksuper;
167#endif
168				}
169				token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
170				token->start = parser->pos;
171				parser->toksuper = parser->toknext - 1;
172				break;
173			case '}': case ']':
174				type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
175#ifdef JSMN_PARENT_LINKS
176				if (parser->toknext < 1) {
177					return JSMN_ERROR_INVAL;
178				}
179				token = &tokens[parser->toknext - 1];
180				for (;;) {
181					if (token->start != -1 && token->end == -1) {
182						if (token->type != type) {
183							return JSMN_ERROR_INVAL;
184						}
185						token->end = parser->pos + 1;
186						parser->toksuper = token->parent;
187						break;
188					}
189					if (token->parent == -1) {
190						break;
191					}
192					token = &tokens[token->parent];
193				}
194#else
195				for (i = parser->toknext - 1; i >= 0; i--) {
196					token = &tokens[i];
197					if (token->start != -1 && token->end == -1) {
198						if (token->type != type) {
199							return JSMN_ERROR_INVAL;
200						}
201						parser->toksuper = -1;
202						token->end = parser->pos + 1;
203						break;
204					}
205				}
206				/* Error if unmatched closing bracket */
207				if (i == -1) return JSMN_ERROR_INVAL;
208				for (; i >= 0; i--) {
209					token = &tokens[i];
210					if (token->start != -1 && token->end == -1) {
211						parser->toksuper = i;
212						break;
213					}
214				}
215#endif
216				break;
217			case '\"':
218				r = jsmn_parse_string(parser, js, tokens, num_tokens);
219				if (r < 0) return r;
220				if (parser->toksuper != -1)
221					tokens[parser->toksuper].size++;
222				break;
223			case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
224				break;
225#ifdef JSMN_STRICT
226			/* In strict mode primitives are: numbers and booleans */
227			case '-': case '0': case '1' : case '2': case '3' : case '4':
228			case '5': case '6': case '7' : case '8': case '9':
229			case 't': case 'f': case 'n' :
230#else
231			/* In non-strict mode every unquoted value is a primitive */
232			default:
233#endif
234				r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
235				if (r < 0) return r;
236				if (parser->toksuper != -1)
237					tokens[parser->toksuper].size++;
238				break;
239
240#ifdef JSMN_STRICT
241			/* Unexpected char in strict mode */
242			default:
243				return JSMN_ERROR_INVAL;
244#endif
245
246		}
247	}
248
249	for (i = parser->toknext - 1; i >= 0; i--) {
250		/* Unmatched opened object or array */
251		if (tokens[i].start != -1 && tokens[i].end == -1) {
252			return JSMN_ERROR_PART;
253		}
254	}
255
256	return JSMN_SUCCESS;
257}
258
259/**
260 * Creates a new parser based over a given  buffer with an array of tokens
261 * available.
262 */
263void jsmn_init(jsmn_parser *parser) {
264	parser->pos = 0;
265	parser->toknext = 0;
266	parser->toksuper = -1;
267}
268
269