1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (c) 2010 Serge A. Zaitsev
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a copy
5*4882a593Smuzhiyun * of this software and associated documentation files (the "Software"), to deal
6*4882a593Smuzhiyun * in the Software without restriction, including without limitation the rights
7*4882a593Smuzhiyun * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8*4882a593Smuzhiyun * copies of the Software, and to permit persons to whom the Software is
9*4882a593Smuzhiyun * furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * The above copyright notice and this permission notice shall be included in
12*4882a593Smuzhiyun * all copies or substantial portions of the Software.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17*4882a593Smuzhiyun * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18*4882a593Smuzhiyun * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19*4882a593Smuzhiyun * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20*4882a593Smuzhiyun * THE SOFTWARE.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * Slightly modified by AK to not assume 0 terminated input.
23*4882a593Smuzhiyun */
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <stdlib.h>
26*4882a593Smuzhiyun #include "jsmn.h"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun * Allocates a fresh unused token from the token pool.
30*4882a593Smuzhiyun */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)31*4882a593Smuzhiyun static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
32*4882a593Smuzhiyun jsmntok_t *tokens, size_t num_tokens)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun jsmntok_t *tok;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun if ((unsigned)parser->toknext >= num_tokens)
37*4882a593Smuzhiyun return NULL;
38*4882a593Smuzhiyun tok = &tokens[parser->toknext++];
39*4882a593Smuzhiyun tok->start = tok->end = -1;
40*4882a593Smuzhiyun tok->size = 0;
41*4882a593Smuzhiyun return tok;
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun /*
45*4882a593Smuzhiyun * Fills token type and boundaries.
46*4882a593Smuzhiyun */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)47*4882a593Smuzhiyun static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
48*4882a593Smuzhiyun int start, int end)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun token->type = type;
51*4882a593Smuzhiyun token->start = start;
52*4882a593Smuzhiyun token->end = end;
53*4882a593Smuzhiyun token->size = 0;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun /*
57*4882a593Smuzhiyun * Fills next available token with JSON primitive.
58*4882a593Smuzhiyun */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)59*4882a593Smuzhiyun static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
60*4882a593Smuzhiyun size_t len,
61*4882a593Smuzhiyun jsmntok_t *tokens, size_t num_tokens)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun jsmntok_t *token;
64*4882a593Smuzhiyun int start;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun start = parser->pos;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun for (; parser->pos < len; parser->pos++) {
69*4882a593Smuzhiyun switch (js[parser->pos]) {
70*4882a593Smuzhiyun #ifndef JSMN_STRICT
71*4882a593Smuzhiyun /*
72*4882a593Smuzhiyun * In strict mode primitive must be followed by ","
73*4882a593Smuzhiyun * or "}" or "]"
74*4882a593Smuzhiyun */
75*4882a593Smuzhiyun case ':':
76*4882a593Smuzhiyun #endif
77*4882a593Smuzhiyun case '\t':
78*4882a593Smuzhiyun case '\r':
79*4882a593Smuzhiyun case '\n':
80*4882a593Smuzhiyun case ' ':
81*4882a593Smuzhiyun case ',':
82*4882a593Smuzhiyun case ']':
83*4882a593Smuzhiyun case '}':
84*4882a593Smuzhiyun goto found;
85*4882a593Smuzhiyun default:
86*4882a593Smuzhiyun break;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
89*4882a593Smuzhiyun parser->pos = start;
90*4882a593Smuzhiyun return JSMN_ERROR_INVAL;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun #ifdef JSMN_STRICT
94*4882a593Smuzhiyun /*
95*4882a593Smuzhiyun * In strict mode primitive must be followed by a
96*4882a593Smuzhiyun * comma/object/array.
97*4882a593Smuzhiyun */
98*4882a593Smuzhiyun parser->pos = start;
99*4882a593Smuzhiyun return JSMN_ERROR_PART;
100*4882a593Smuzhiyun #endif
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun found:
103*4882a593Smuzhiyun token = jsmn_alloc_token(parser, tokens, num_tokens);
104*4882a593Smuzhiyun if (token == NULL) {
105*4882a593Smuzhiyun parser->pos = start;
106*4882a593Smuzhiyun return JSMN_ERROR_NOMEM;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
109*4882a593Smuzhiyun parser->pos--; /* parent sees closing brackets */
110*4882a593Smuzhiyun return JSMN_SUCCESS;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun /*
114*4882a593Smuzhiyun * Fills next token with JSON string.
115*4882a593Smuzhiyun */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)116*4882a593Smuzhiyun static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
117*4882a593Smuzhiyun size_t len,
118*4882a593Smuzhiyun jsmntok_t *tokens, size_t num_tokens)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun jsmntok_t *token;
121*4882a593Smuzhiyun int start = parser->pos;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /* Skip starting quote */
124*4882a593Smuzhiyun parser->pos++;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun for (; parser->pos < len; parser->pos++) {
127*4882a593Smuzhiyun char c = js[parser->pos];
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun /* Quote: end of string */
130*4882a593Smuzhiyun if (c == '\"') {
131*4882a593Smuzhiyun token = jsmn_alloc_token(parser, tokens, num_tokens);
132*4882a593Smuzhiyun if (token == NULL) {
133*4882a593Smuzhiyun parser->pos = start;
134*4882a593Smuzhiyun return JSMN_ERROR_NOMEM;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun jsmn_fill_token(token, JSMN_STRING, start+1,
137*4882a593Smuzhiyun parser->pos);
138*4882a593Smuzhiyun return JSMN_SUCCESS;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun /* Backslash: Quoted symbol expected */
142*4882a593Smuzhiyun if (c == '\\') {
143*4882a593Smuzhiyun parser->pos++;
144*4882a593Smuzhiyun switch (js[parser->pos]) {
145*4882a593Smuzhiyun /* Allowed escaped symbols */
146*4882a593Smuzhiyun case '\"':
147*4882a593Smuzhiyun case '/':
148*4882a593Smuzhiyun case '\\':
149*4882a593Smuzhiyun case 'b':
150*4882a593Smuzhiyun case 'f':
151*4882a593Smuzhiyun case 'r':
152*4882a593Smuzhiyun case 'n':
153*4882a593Smuzhiyun case 't':
154*4882a593Smuzhiyun break;
155*4882a593Smuzhiyun /* Allows escaped symbol \uXXXX */
156*4882a593Smuzhiyun case 'u':
157*4882a593Smuzhiyun /* TODO */
158*4882a593Smuzhiyun break;
159*4882a593Smuzhiyun /* Unexpected symbol */
160*4882a593Smuzhiyun default:
161*4882a593Smuzhiyun parser->pos = start;
162*4882a593Smuzhiyun return JSMN_ERROR_INVAL;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun parser->pos = start;
167*4882a593Smuzhiyun return JSMN_ERROR_PART;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun /*
171*4882a593Smuzhiyun * Parse JSON string and fill tokens.
172*4882a593Smuzhiyun */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)173*4882a593Smuzhiyun jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
174*4882a593Smuzhiyun jsmntok_t *tokens, unsigned int num_tokens)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun jsmnerr_t r;
177*4882a593Smuzhiyun int i;
178*4882a593Smuzhiyun jsmntok_t *token;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun for (; parser->pos < len; parser->pos++) {
181*4882a593Smuzhiyun char c;
182*4882a593Smuzhiyun jsmntype_t type;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun c = js[parser->pos];
185*4882a593Smuzhiyun switch (c) {
186*4882a593Smuzhiyun case '{':
187*4882a593Smuzhiyun case '[':
188*4882a593Smuzhiyun token = jsmn_alloc_token(parser, tokens, num_tokens);
189*4882a593Smuzhiyun if (token == NULL)
190*4882a593Smuzhiyun return JSMN_ERROR_NOMEM;
191*4882a593Smuzhiyun if (parser->toksuper != -1)
192*4882a593Smuzhiyun tokens[parser->toksuper].size++;
193*4882a593Smuzhiyun token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
194*4882a593Smuzhiyun token->start = parser->pos;
195*4882a593Smuzhiyun parser->toksuper = parser->toknext - 1;
196*4882a593Smuzhiyun break;
197*4882a593Smuzhiyun case '}':
198*4882a593Smuzhiyun case ']':
199*4882a593Smuzhiyun type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
200*4882a593Smuzhiyun for (i = parser->toknext - 1; i >= 0; i--) {
201*4882a593Smuzhiyun token = &tokens[i];
202*4882a593Smuzhiyun if (token->start != -1 && token->end == -1) {
203*4882a593Smuzhiyun if (token->type != type)
204*4882a593Smuzhiyun return JSMN_ERROR_INVAL;
205*4882a593Smuzhiyun parser->toksuper = -1;
206*4882a593Smuzhiyun token->end = parser->pos + 1;
207*4882a593Smuzhiyun break;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun /* Error if unmatched closing bracket */
211*4882a593Smuzhiyun if (i == -1)
212*4882a593Smuzhiyun return JSMN_ERROR_INVAL;
213*4882a593Smuzhiyun for (; i >= 0; i--) {
214*4882a593Smuzhiyun token = &tokens[i];
215*4882a593Smuzhiyun if (token->start != -1 && token->end == -1) {
216*4882a593Smuzhiyun parser->toksuper = i;
217*4882a593Smuzhiyun break;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun break;
221*4882a593Smuzhiyun case '\"':
222*4882a593Smuzhiyun r = jsmn_parse_string(parser, js, len, tokens,
223*4882a593Smuzhiyun num_tokens);
224*4882a593Smuzhiyun if (r < 0)
225*4882a593Smuzhiyun return r;
226*4882a593Smuzhiyun if (parser->toksuper != -1)
227*4882a593Smuzhiyun tokens[parser->toksuper].size++;
228*4882a593Smuzhiyun break;
229*4882a593Smuzhiyun case '\t':
230*4882a593Smuzhiyun case '\r':
231*4882a593Smuzhiyun case '\n':
232*4882a593Smuzhiyun case ':':
233*4882a593Smuzhiyun case ',':
234*4882a593Smuzhiyun case ' ':
235*4882a593Smuzhiyun break;
236*4882a593Smuzhiyun #ifdef JSMN_STRICT
237*4882a593Smuzhiyun /*
238*4882a593Smuzhiyun * In strict mode primitives are:
239*4882a593Smuzhiyun * numbers and booleans.
240*4882a593Smuzhiyun */
241*4882a593Smuzhiyun case '-':
242*4882a593Smuzhiyun case '0':
243*4882a593Smuzhiyun case '1':
244*4882a593Smuzhiyun case '2':
245*4882a593Smuzhiyun case '3':
246*4882a593Smuzhiyun case '4':
247*4882a593Smuzhiyun case '5':
248*4882a593Smuzhiyun case '6':
249*4882a593Smuzhiyun case '7':
250*4882a593Smuzhiyun case '8':
251*4882a593Smuzhiyun case '9':
252*4882a593Smuzhiyun case 't':
253*4882a593Smuzhiyun case 'f':
254*4882a593Smuzhiyun case 'n':
255*4882a593Smuzhiyun #else
256*4882a593Smuzhiyun /*
257*4882a593Smuzhiyun * In non-strict mode every unquoted value
258*4882a593Smuzhiyun * is a primitive.
259*4882a593Smuzhiyun */
260*4882a593Smuzhiyun /*FALL THROUGH */
261*4882a593Smuzhiyun default:
262*4882a593Smuzhiyun #endif
263*4882a593Smuzhiyun r = jsmn_parse_primitive(parser, js, len, tokens,
264*4882a593Smuzhiyun num_tokens);
265*4882a593Smuzhiyun if (r < 0)
266*4882a593Smuzhiyun return r;
267*4882a593Smuzhiyun if (parser->toksuper != -1)
268*4882a593Smuzhiyun tokens[parser->toksuper].size++;
269*4882a593Smuzhiyun break;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun #ifdef JSMN_STRICT
272*4882a593Smuzhiyun /* Unexpected char in strict mode */
273*4882a593Smuzhiyun default:
274*4882a593Smuzhiyun return JSMN_ERROR_INVAL;
275*4882a593Smuzhiyun #endif
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun for (i = parser->toknext - 1; i >= 0; i--) {
280*4882a593Smuzhiyun /* Unmatched opened object or array */
281*4882a593Smuzhiyun if (tokens[i].start != -1 && tokens[i].end == -1)
282*4882a593Smuzhiyun return JSMN_ERROR_PART;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun return JSMN_SUCCESS;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun /*
289*4882a593Smuzhiyun * Creates a new parser based over a given buffer with an array of tokens
290*4882a593Smuzhiyun * available.
291*4882a593Smuzhiyun */
jsmn_init(jsmn_parser * parser)292*4882a593Smuzhiyun void jsmn_init(jsmn_parser *parser)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun parser->pos = 0;
295*4882a593Smuzhiyun parser->toknext = 0;
296*4882a593Smuzhiyun parser->toksuper = -1;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
jsmn_strerror(jsmnerr_t err)299*4882a593Smuzhiyun const char *jsmn_strerror(jsmnerr_t err)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun switch (err) {
302*4882a593Smuzhiyun case JSMN_ERROR_NOMEM:
303*4882a593Smuzhiyun return "No enough tokens";
304*4882a593Smuzhiyun case JSMN_ERROR_INVAL:
305*4882a593Smuzhiyun return "Invalid character inside JSON string";
306*4882a593Smuzhiyun case JSMN_ERROR_PART:
307*4882a593Smuzhiyun return "The string is not a full JSON packet, more bytes expected";
308*4882a593Smuzhiyun case JSMN_SUCCESS:
309*4882a593Smuzhiyun return "Success";
310*4882a593Smuzhiyun default:
311*4882a593Smuzhiyun return "Unknown json error";
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun }
314