git ssb

8+

cel / sbotc



Tree: cec6448afd2f0c42739c510e602283f56518510c

Files: cec6448afd2f0c42739c510e602283f56518510c / jsmn.c

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

Built with git-ssb-web