1 /*************************************************************************//**
3 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 ****************************************************************************/
21 * Allocates a fresh unused token from the token pull.
23 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
24 jsmntok_t *tokens, size_t num_tokens) {
26 if (parser->toknext >= num_tokens) {
29 tok = &tokens[parser->toknext++];
30 tok->start = tok->end = -1;
32 #ifdef JSMN_PARENT_LINKS
39 * Fills token type and boundaries.
41 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
50 * Fills next available token with JSON primitive.
52 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
53 size_t len, jsmntok_t *tokens, size_t num_tokens) {
59 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
60 switch (js[parser->pos]) {
62 /* In strict mode primitive must be followed by "," or "}" or "]" */
65 case '\t' : case '\r' : case '\n' : case ' ' :
66 case ',' : case ']' : case '}' :
69 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
71 return JSMN_ERROR_INVAL;
75 /* In strict mode primitive must be followed by a comma/object/array */
77 return JSMN_ERROR_PART;
85 token = jsmn_alloc_token(parser, tokens, num_tokens);
88 return JSMN_ERROR_NOMEM;
90 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
91 #ifdef JSMN_PARENT_LINKS
92 token->parent = parser->toksuper;
99 * Fills next token with JSON string.
101 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
102 size_t len, jsmntok_t *tokens, size_t num_tokens) {
105 int start = parser->pos;
109 /* Skip starting quote */
110 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
111 char c = js[parser->pos];
113 /* Quote: end of string */
115 if (tokens == NULL) {
118 token = jsmn_alloc_token(parser, tokens, num_tokens);
121 return JSMN_ERROR_NOMEM;
123 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
124 #ifdef JSMN_PARENT_LINKS
125 token->parent = parser->toksuper;
130 /* Backslash: Quoted symbol expected */
131 if (c == '\\' && parser->pos + 1 < len) {
134 switch (js[parser->pos]) {
135 /* Allowed escaped symbols */
136 case '\"': case '/' : case '\\' : case 'b' :
137 case 'f' : case 'r' : case 'n' : case 't' :
139 /* Allows escaped symbol \uXXXX */
142 for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
143 /* If it isn't a hex character we have an error */
144 if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
145 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
146 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
148 return JSMN_ERROR_INVAL;
154 /* Unexpected symbol */
157 return JSMN_ERROR_INVAL;
162 return JSMN_ERROR_PART;
166 * Parse JSON string and fill tokens.
168 int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
169 jsmntok_t *tokens, unsigned int num_tokens) {
173 int count = parser->toknext;
175 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
183 if (tokens == NULL) {
186 token = jsmn_alloc_token(parser, tokens, num_tokens);
188 return JSMN_ERROR_NOMEM;
189 if (parser->toksuper != -1) {
190 tokens[parser->toksuper].size++;
191 #ifdef JSMN_PARENT_LINKS
192 token->parent = parser->toksuper;
195 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
196 token->start = parser->pos;
197 parser->toksuper = parser->toknext - 1;
202 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
203 #ifdef JSMN_PARENT_LINKS
204 if (parser->toknext < 1) {
205 return JSMN_ERROR_INVAL;
207 token = &tokens[parser->toknext - 1];
209 if (token->start != -1 && token->end == -1) {
210 if (token->type != type) {
211 return JSMN_ERROR_INVAL;
213 token->end = parser->pos + 1;
214 parser->toksuper = token->parent;
217 if (token->parent == -1) {
220 token = &tokens[token->parent];
223 for (i = parser->toknext - 1; i >= 0; i--) {
225 if (token->start != -1 && token->end == -1) {
226 if (token->type != type) {
227 return JSMN_ERROR_INVAL;
229 parser->toksuper = -1;
230 token->end = parser->pos + 1;
234 /* Error if unmatched closing bracket */
235 if (i == -1) return JSMN_ERROR_INVAL;
236 for (; i >= 0; i--) {
238 if (token->start != -1 && token->end == -1) {
239 parser->toksuper = i;
246 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
249 if (parser->toksuper != -1 && tokens != NULL)
250 tokens[parser->toksuper].size++;
252 case '\t' : case '\r' : case '\n' : case ' ':
255 parser->toksuper = parser->toknext - 1;
258 if (tokens != NULL && parser->toksuper != -1 &&
259 tokens[parser->toksuper].type != JSMN_ARRAY &&
260 tokens[parser->toksuper].type != JSMN_OBJECT) {
261 #ifdef JSMN_PARENT_LINKS
262 parser->toksuper = tokens[parser->toksuper].parent;
264 for (i = parser->toknext - 1; i >= 0; i--) {
265 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
266 if (tokens[i].start != -1 && tokens[i].end == -1) {
267 parser->toksuper = i;
276 /* In strict mode primitives are: numbers and booleans */
277 case '-': case '0': case '1' : case '2': case '3' : case '4':
278 case '5': case '6': case '7' : case '8': case '9':
279 case 't': case 'f': case 'n' :
280 /* And they must not be keys of the object */
281 if (tokens != NULL && parser->toksuper != -1) {
282 jsmntok_t *t = &tokens[parser->toksuper];
283 if (t->type == JSMN_OBJECT ||
284 (t->type == JSMN_STRING && t->size != 0)) {
285 return JSMN_ERROR_INVAL;
289 /* In non-strict mode every unquoted value is a primitive */
292 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
295 if (parser->toksuper != -1 && tokens != NULL)
296 tokens[parser->toksuper].size++;
300 /* Unexpected char in strict mode */
302 return JSMN_ERROR_INVAL;
307 if (tokens != NULL) {
308 for (i = parser->toknext - 1; i >= 0; i--) {
309 /* Unmatched opened object or array */
310 if (tokens[i].start != -1 && tokens[i].end == -1) {
311 return JSMN_ERROR_PART;
320 * Creates a new parser based over a given buffer with an array of tokens
323 void jsmn_init(jsmn_parser *parser) {
326 parser->toksuper = -1;