1959826caSMatt Macy /*
2959826caSMatt Macy * Copyright (c) 2010 Serge A. Zaitsev
3959826caSMatt Macy *
4959826caSMatt Macy * Permission is hereby granted, free of charge, to any person obtaining a copy
5959826caSMatt Macy * of this software and associated documentation files (the "Software"), to deal
6959826caSMatt Macy * in the Software without restriction, including without limitation the rights
7959826caSMatt Macy * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8959826caSMatt Macy * copies of the Software, and to permit persons to whom the Software is
9959826caSMatt Macy * furnished to do so, subject to the following conditions:
10959826caSMatt Macy *
11959826caSMatt Macy * The above copyright notice and this permission notice shall be included in
12959826caSMatt Macy * all copies or substantial portions of the Software.
13959826caSMatt Macy *
14959826caSMatt Macy * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15959826caSMatt Macy * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16959826caSMatt Macy * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17959826caSMatt Macy * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18959826caSMatt Macy * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19959826caSMatt Macy * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20959826caSMatt Macy * THE SOFTWARE.
21959826caSMatt Macy *
22959826caSMatt Macy * Slightly modified by AK to not assume 0 terminated input.
23959826caSMatt Macy *
24959826caSMatt Macy */
25959826caSMatt Macy
26959826caSMatt Macy #include <stdlib.h>
27959826caSMatt Macy #include "jsmn.h"
28*62ff619dSAlexander Motin #define JSMN_STRICT
29959826caSMatt Macy
30959826caSMatt Macy /*
31959826caSMatt Macy * Allocates a fresh unused token from the token pool.
32959826caSMatt Macy */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)33959826caSMatt Macy static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
34959826caSMatt Macy jsmntok_t *tokens, size_t num_tokens)
35959826caSMatt Macy {
36959826caSMatt Macy jsmntok_t *tok;
37959826caSMatt Macy
38959826caSMatt Macy if ((unsigned)parser->toknext >= num_tokens)
39959826caSMatt Macy return NULL;
40959826caSMatt Macy tok = &tokens[parser->toknext++];
41959826caSMatt Macy tok->start = tok->end = -1;
42959826caSMatt Macy tok->size = 0;
43959826caSMatt Macy return tok;
44959826caSMatt Macy }
45959826caSMatt Macy
46959826caSMatt Macy /*
47959826caSMatt Macy * Fills token type and boundaries.
48959826caSMatt Macy */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)49959826caSMatt Macy static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
50959826caSMatt Macy int start, int end)
51959826caSMatt Macy {
52959826caSMatt Macy token->type = type;
53959826caSMatt Macy token->start = start;
54959826caSMatt Macy token->end = end;
55959826caSMatt Macy token->size = 0;
56959826caSMatt Macy }
57959826caSMatt Macy
58959826caSMatt Macy /*
59959826caSMatt Macy * Fills next available token with JSON primitive.
60959826caSMatt Macy */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)61959826caSMatt Macy static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
62959826caSMatt Macy size_t len,
63959826caSMatt Macy jsmntok_t *tokens, size_t num_tokens)
64959826caSMatt Macy {
65959826caSMatt Macy jsmntok_t *token;
66959826caSMatt Macy int start;
67959826caSMatt Macy
68959826caSMatt Macy start = parser->pos;
69959826caSMatt Macy
70959826caSMatt Macy for (; parser->pos < len; parser->pos++) {
71959826caSMatt Macy switch (js[parser->pos]) {
72959826caSMatt Macy #ifndef JSMN_STRICT
73959826caSMatt Macy /*
74959826caSMatt Macy * In strict mode primitive must be followed by ","
75959826caSMatt Macy * or "}" or "]"
76959826caSMatt Macy */
77959826caSMatt Macy case ':':
78959826caSMatt Macy #endif
79959826caSMatt Macy case '\t':
80959826caSMatt Macy case '\r':
81959826caSMatt Macy case '\n':
82959826caSMatt Macy case ' ':
83959826caSMatt Macy case ',':
84959826caSMatt Macy case ']':
85959826caSMatt Macy case '}':
86959826caSMatt Macy goto found;
87959826caSMatt Macy default:
88959826caSMatt Macy break;
89959826caSMatt Macy }
90959826caSMatt Macy if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
91959826caSMatt Macy parser->pos = start;
92959826caSMatt Macy return JSMN_ERROR_INVAL;
93959826caSMatt Macy }
94959826caSMatt Macy }
95959826caSMatt Macy #ifdef JSMN_STRICT
96959826caSMatt Macy /*
97959826caSMatt Macy * In strict mode primitive must be followed by a
98959826caSMatt Macy * comma/object/array.
99959826caSMatt Macy */
100959826caSMatt Macy parser->pos = start;
101959826caSMatt Macy return JSMN_ERROR_PART;
102959826caSMatt Macy #endif
103959826caSMatt Macy
104959826caSMatt Macy found:
105959826caSMatt Macy token = jsmn_alloc_token(parser, tokens, num_tokens);
106959826caSMatt Macy if (token == NULL) {
107959826caSMatt Macy parser->pos = start;
108959826caSMatt Macy return JSMN_ERROR_NOMEM;
109959826caSMatt Macy }
110959826caSMatt Macy jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
111959826caSMatt Macy parser->pos--; /* parent sees closing brackets */
112959826caSMatt Macy return JSMN_SUCCESS;
113959826caSMatt Macy }
114959826caSMatt Macy
115959826caSMatt Macy /*
116959826caSMatt Macy * Fills next token with JSON string.
117959826caSMatt Macy */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)118959826caSMatt Macy static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
119959826caSMatt Macy size_t len,
120959826caSMatt Macy jsmntok_t *tokens, size_t num_tokens)
121959826caSMatt Macy {
122959826caSMatt Macy jsmntok_t *token;
123959826caSMatt Macy int start = parser->pos;
124959826caSMatt Macy
125959826caSMatt Macy /* Skip starting quote */
126959826caSMatt Macy parser->pos++;
127959826caSMatt Macy
128959826caSMatt Macy for (; parser->pos < len; parser->pos++) {
129959826caSMatt Macy char c = js[parser->pos];
130959826caSMatt Macy
131959826caSMatt Macy /* Quote: end of string */
132959826caSMatt Macy if (c == '\"') {
133959826caSMatt Macy token = jsmn_alloc_token(parser, tokens, num_tokens);
134959826caSMatt Macy if (token == NULL) {
135959826caSMatt Macy parser->pos = start;
136959826caSMatt Macy return JSMN_ERROR_NOMEM;
137959826caSMatt Macy }
138959826caSMatt Macy jsmn_fill_token(token, JSMN_STRING, start+1,
139959826caSMatt Macy parser->pos);
140959826caSMatt Macy return JSMN_SUCCESS;
141959826caSMatt Macy }
142959826caSMatt Macy
143959826caSMatt Macy /* Backslash: Quoted symbol expected */
144959826caSMatt Macy if (c == '\\') {
145959826caSMatt Macy parser->pos++;
146959826caSMatt Macy switch (js[parser->pos]) {
147959826caSMatt Macy /* Allowed escaped symbols */
148959826caSMatt Macy case '\"':
149959826caSMatt Macy case '/':
150959826caSMatt Macy case '\\':
151959826caSMatt Macy case 'b':
152959826caSMatt Macy case 'f':
153959826caSMatt Macy case 'r':
154959826caSMatt Macy case 'n':
155959826caSMatt Macy case 't':
156959826caSMatt Macy break;
157959826caSMatt Macy /* Allows escaped symbol \uXXXX */
158959826caSMatt Macy case 'u':
159959826caSMatt Macy /* TODO */
160959826caSMatt Macy break;
161959826caSMatt Macy /* Unexpected symbol */
162959826caSMatt Macy default:
163959826caSMatt Macy parser->pos = start;
164959826caSMatt Macy return JSMN_ERROR_INVAL;
165959826caSMatt Macy }
166959826caSMatt Macy }
167959826caSMatt Macy }
168959826caSMatt Macy parser->pos = start;
169959826caSMatt Macy return JSMN_ERROR_PART;
170959826caSMatt Macy }
171959826caSMatt Macy
172959826caSMatt Macy /*
173959826caSMatt Macy * Parse JSON string and fill tokens.
174959826caSMatt Macy */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)175959826caSMatt Macy jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
176959826caSMatt Macy jsmntok_t *tokens, unsigned int num_tokens)
177959826caSMatt Macy {
178959826caSMatt Macy jsmnerr_t r;
179959826caSMatt Macy int i;
180959826caSMatt Macy jsmntok_t *token;
181*62ff619dSAlexander Motin #ifdef JSMN_STRICT
182*62ff619dSAlexander Motin /*
183*62ff619dSAlexander Motin * Keeps track of whether a new object/list/primitive is expected. New items are only
184*62ff619dSAlexander Motin * allowed after an opening brace, comma or colon. A closing brace after a comma is not
185*62ff619dSAlexander Motin * valid JSON.
186*62ff619dSAlexander Motin */
187*62ff619dSAlexander Motin int expecting_item = 1;
188*62ff619dSAlexander Motin #endif
189959826caSMatt Macy
190959826caSMatt Macy for (; parser->pos < len; parser->pos++) {
191959826caSMatt Macy char c;
192959826caSMatt Macy jsmntype_t type;
193959826caSMatt Macy
194959826caSMatt Macy c = js[parser->pos];
195959826caSMatt Macy switch (c) {
196959826caSMatt Macy case '{':
197959826caSMatt Macy case '[':
198*62ff619dSAlexander Motin #ifdef JSMN_STRICT
199*62ff619dSAlexander Motin if (!expecting_item)
200*62ff619dSAlexander Motin return JSMN_ERROR_INVAL;
201*62ff619dSAlexander Motin #endif
202959826caSMatt Macy token = jsmn_alloc_token(parser, tokens, num_tokens);
203959826caSMatt Macy if (token == NULL)
204959826caSMatt Macy return JSMN_ERROR_NOMEM;
205959826caSMatt Macy if (parser->toksuper != -1)
206959826caSMatt Macy tokens[parser->toksuper].size++;
207959826caSMatt Macy token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
208959826caSMatt Macy token->start = parser->pos;
209959826caSMatt Macy parser->toksuper = parser->toknext - 1;
210959826caSMatt Macy break;
211959826caSMatt Macy case '}':
212959826caSMatt Macy case ']':
213*62ff619dSAlexander Motin #ifdef JSMN_STRICT
214*62ff619dSAlexander Motin if (expecting_item)
215*62ff619dSAlexander Motin return JSMN_ERROR_INVAL;
216*62ff619dSAlexander Motin #endif
217959826caSMatt Macy type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
218959826caSMatt Macy for (i = parser->toknext - 1; i >= 0; i--) {
219959826caSMatt Macy token = &tokens[i];
220959826caSMatt Macy if (token->start != -1 && token->end == -1) {
221959826caSMatt Macy if (token->type != type)
222959826caSMatt Macy return JSMN_ERROR_INVAL;
223959826caSMatt Macy parser->toksuper = -1;
224959826caSMatt Macy token->end = parser->pos + 1;
225959826caSMatt Macy break;
226959826caSMatt Macy }
227959826caSMatt Macy }
228959826caSMatt Macy /* Error if unmatched closing bracket */
229959826caSMatt Macy if (i == -1)
230959826caSMatt Macy return JSMN_ERROR_INVAL;
231959826caSMatt Macy for (; i >= 0; i--) {
232959826caSMatt Macy token = &tokens[i];
233959826caSMatt Macy if (token->start != -1 && token->end == -1) {
234959826caSMatt Macy parser->toksuper = i;
235959826caSMatt Macy break;
236959826caSMatt Macy }
237959826caSMatt Macy }
238959826caSMatt Macy break;
239959826caSMatt Macy case '\"':
240*62ff619dSAlexander Motin #ifdef JSMN_STRICT
241*62ff619dSAlexander Motin if (!expecting_item)
242*62ff619dSAlexander Motin return JSMN_ERROR_INVAL;
243*62ff619dSAlexander Motin expecting_item = 0;
244*62ff619dSAlexander Motin #endif
245959826caSMatt Macy r = jsmn_parse_string(parser, js, len, tokens,
246959826caSMatt Macy num_tokens);
247959826caSMatt Macy if (r < 0)
248959826caSMatt Macy return r;
249959826caSMatt Macy if (parser->toksuper != -1)
250959826caSMatt Macy tokens[parser->toksuper].size++;
251959826caSMatt Macy break;
252959826caSMatt Macy case '\t':
253959826caSMatt Macy case '\r':
254959826caSMatt Macy case '\n':
255959826caSMatt Macy case ' ':
256959826caSMatt Macy break;
257959826caSMatt Macy #ifdef JSMN_STRICT
258*62ff619dSAlexander Motin case ':':
259*62ff619dSAlexander Motin case ',':
260*62ff619dSAlexander Motin if (expecting_item)
261*62ff619dSAlexander Motin return JSMN_ERROR_INVAL;
262*62ff619dSAlexander Motin expecting_item = 1;
263*62ff619dSAlexander Motin break;
264959826caSMatt Macy /*
265959826caSMatt Macy * In strict mode primitives are:
266959826caSMatt Macy * numbers and booleans.
267959826caSMatt Macy */
268959826caSMatt Macy case '-':
269959826caSMatt Macy case '0':
270959826caSMatt Macy case '1':
271959826caSMatt Macy case '2':
272959826caSMatt Macy case '3':
273959826caSMatt Macy case '4':
274959826caSMatt Macy case '5':
275959826caSMatt Macy case '6':
276959826caSMatt Macy case '7':
277959826caSMatt Macy case '8':
278959826caSMatt Macy case '9':
279959826caSMatt Macy case 't':
280959826caSMatt Macy case 'f':
281959826caSMatt Macy case 'n':
282959826caSMatt Macy #else
283*62ff619dSAlexander Motin case ':':
284*62ff619dSAlexander Motin case ',':
285*62ff619dSAlexander Motin break;
286959826caSMatt Macy /*
287959826caSMatt Macy * In non-strict mode every unquoted value
288959826caSMatt Macy * is a primitive.
289959826caSMatt Macy */
290959826caSMatt Macy /*FALL THROUGH */
291959826caSMatt Macy default:
292959826caSMatt Macy #endif
293*62ff619dSAlexander Motin
294*62ff619dSAlexander Motin #ifdef JSMN_STRICT
295*62ff619dSAlexander Motin if (!expecting_item)
296*62ff619dSAlexander Motin return JSMN_ERROR_INVAL;
297*62ff619dSAlexander Motin expecting_item = 0;
298*62ff619dSAlexander Motin #endif
299959826caSMatt Macy r = jsmn_parse_primitive(parser, js, len, tokens,
300959826caSMatt Macy num_tokens);
301959826caSMatt Macy if (r < 0)
302959826caSMatt Macy return r;
303959826caSMatt Macy if (parser->toksuper != -1)
304959826caSMatt Macy tokens[parser->toksuper].size++;
305959826caSMatt Macy break;
306959826caSMatt Macy
307959826caSMatt Macy #ifdef JSMN_STRICT
308959826caSMatt Macy /* Unexpected char in strict mode */
309959826caSMatt Macy default:
310959826caSMatt Macy return JSMN_ERROR_INVAL;
311959826caSMatt Macy #endif
312959826caSMatt Macy }
313959826caSMatt Macy }
314959826caSMatt Macy
315959826caSMatt Macy for (i = parser->toknext - 1; i >= 0; i--) {
316959826caSMatt Macy /* Unmatched opened object or array */
317959826caSMatt Macy if (tokens[i].start != -1 && tokens[i].end == -1)
318959826caSMatt Macy return JSMN_ERROR_PART;
319959826caSMatt Macy }
320959826caSMatt Macy
321*62ff619dSAlexander Motin #ifdef JSMN_STRICT
322*62ff619dSAlexander Motin return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
323*62ff619dSAlexander Motin #else
324959826caSMatt Macy return JSMN_SUCCESS;
325*62ff619dSAlexander Motin #endif
326959826caSMatt Macy }
327959826caSMatt Macy
328959826caSMatt Macy /*
329959826caSMatt Macy * Creates a new parser based over a given buffer with an array of tokens
330959826caSMatt Macy * available.
331959826caSMatt Macy */
jsmn_init(jsmn_parser * parser)332959826caSMatt Macy void jsmn_init(jsmn_parser *parser)
333959826caSMatt Macy {
334959826caSMatt Macy parser->pos = 0;
335959826caSMatt Macy parser->toknext = 0;
336959826caSMatt Macy parser->toksuper = -1;
337959826caSMatt Macy }
338959826caSMatt Macy
jsmn_strerror(jsmnerr_t err)339959826caSMatt Macy const char *jsmn_strerror(jsmnerr_t err)
340959826caSMatt Macy {
341959826caSMatt Macy switch (err) {
342959826caSMatt Macy case JSMN_ERROR_NOMEM:
343959826caSMatt Macy return "No enough tokens";
344959826caSMatt Macy case JSMN_ERROR_INVAL:
345959826caSMatt Macy return "Invalid character inside JSON string";
346959826caSMatt Macy case JSMN_ERROR_PART:
347959826caSMatt Macy return "The string is not a full JSON packet, more bytes expected";
348959826caSMatt Macy case JSMN_SUCCESS:
349959826caSMatt Macy return "Success";
350959826caSMatt Macy default:
351959826caSMatt Macy return "Unknown json error";
352959826caSMatt Macy }
353959826caSMatt Macy }
354