1 /*************************************************************************//**
3 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
5 * Unless otherwise specified, all software contained herein is
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 ****************************************************************************/
22 * Allocates a fresh unused token from the token pull.
24 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
25 jsmntok_t *tokens, size_t num_tokens) {
27 if (parser->toknext >= num_tokens) {
30 tok = &tokens[parser->toknext++];
31 tok->start = tok->end = -1;
33 #ifdef JSMN_PARENT_LINKS
40 * Fills token type and boundaries.
42 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
51 * Fills next available token with JSON primitive.
53 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
54 size_t len, jsmntok_t *tokens, size_t num_tokens) {
60 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
61 switch (js[parser->pos]) {
63 /* In strict mode primitive must be followed by "," or "}" or "]" */
66 case '\t' : case '\r' : case '\n' : case ' ' :
67 case ',' : case ']' : case '}' :
70 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
72 return JSMN_ERROR_INVAL;
76 /* In strict mode primitive must be followed by a comma/object/array */
78 return JSMN_ERROR_PART;
86 token = jsmn_alloc_token(parser, tokens, num_tokens);
89 return JSMN_ERROR_NOMEM;
91 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
92 #ifdef JSMN_PARENT_LINKS
93 token->parent = parser->toksuper;
100 * Fills next token with JSON string.
102 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
103 size_t len, jsmntok_t *tokens, size_t num_tokens) {
106 int start = parser->pos;
110 /* Skip starting quote */
111 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
112 char c = js[parser->pos];
114 /* Quote: end of string */
116 if (tokens == NULL) {
119 token = jsmn_alloc_token(parser, tokens, num_tokens);
122 return JSMN_ERROR_NOMEM;
124 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
125 #ifdef JSMN_PARENT_LINKS
126 token->parent = parser->toksuper;
131 /* Backslash: Quoted symbol expected */
132 if (c == '\\' && parser->pos + 1 < len) {
135 switch (js[parser->pos]) {
136 /* Allowed escaped symbols */
137 case '\"': case '/' : case '\\' : case 'b' :
138 case 'f' : case 'r' : case 'n' : case 't' :
140 /* Allows escaped symbol \uXXXX */
143 for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
144 /* If it isn't a hex character we have an error */
145 if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
146 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
147 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
149 return JSMN_ERROR_INVAL;
155 /* Unexpected symbol */
158 return JSMN_ERROR_INVAL;
163 return JSMN_ERROR_PART;
167 * Parse JSON string and fill tokens.
169 int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
170 jsmntok_t *tokens, unsigned int num_tokens) {
174 int count = parser->toknext;
176 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
184 if (tokens == NULL) {
187 token = jsmn_alloc_token(parser, tokens, num_tokens);
189 return JSMN_ERROR_NOMEM;
190 if (parser->toksuper != -1) {
191 tokens[parser->toksuper].size++;
192 #ifdef JSMN_PARENT_LINKS
193 token->parent = parser->toksuper;
196 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
197 token->start = parser->pos;
198 parser->toksuper = parser->toknext - 1;
203 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
204 #ifdef JSMN_PARENT_LINKS
205 if (parser->toknext < 1) {
206 return JSMN_ERROR_INVAL;
208 token = &tokens[parser->toknext - 1];
210 if (token->start != -1 && token->end == -1) {
211 if (token->type != type) {
212 return JSMN_ERROR_INVAL;
214 token->end = parser->pos + 1;
215 parser->toksuper = token->parent;
218 if (token->parent == -1) {
221 token = &tokens[token->parent];
224 for (i = parser->toknext - 1; i >= 0; i--) {
226 if (token->start != -1 && token->end == -1) {
227 if (token->type != type) {
228 return JSMN_ERROR_INVAL;
230 parser->toksuper = -1;
231 token->end = parser->pos + 1;
235 /* Error if unmatched closing bracket */
236 if (i == -1) return JSMN_ERROR_INVAL;
237 for (; i >= 0; i--) {
239 if (token->start != -1 && token->end == -1) {
240 parser->toksuper = i;
247 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
250 if (parser->toksuper != -1 && tokens != NULL)
251 tokens[parser->toksuper].size++;
253 case '\t' : case '\r' : case '\n' : case ' ':
256 parser->toksuper = parser->toknext - 1;
259 if (tokens != NULL && parser->toksuper != -1 &&
260 tokens[parser->toksuper].type != JSMN_ARRAY &&
261 tokens[parser->toksuper].type != JSMN_OBJECT) {
262 #ifdef JSMN_PARENT_LINKS
263 parser->toksuper = tokens[parser->toksuper].parent;
265 for (i = parser->toknext - 1; i >= 0; i--) {
266 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
267 if (tokens[i].start != -1 && tokens[i].end == -1) {
268 parser->toksuper = i;
277 /* In strict mode primitives are: numbers and booleans */
278 case '-': case '0': case '1' : case '2': case '3' : case '4':
279 case '5': case '6': case '7' : case '8': case '9':
280 case 't': case 'f': case 'n' :
281 /* And they must not be keys of the object */
282 if (tokens != NULL && parser->toksuper != -1) {
283 jsmntok_t *t = &tokens[parser->toksuper];
284 if (t->type == JSMN_OBJECT ||
285 (t->type == JSMN_STRING && t->size != 0)) {
286 return JSMN_ERROR_INVAL;
290 /* In non-strict mode every unquoted value is a primitive */
293 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
296 if (parser->toksuper != -1 && tokens != NULL)
297 tokens[parser->toksuper].size++;
301 /* Unexpected char in strict mode */
303 return JSMN_ERROR_INVAL;
308 if (tokens != NULL) {
309 for (i = parser->toknext - 1; i >= 0; i--) {
310 /* Unmatched opened object or array */
311 if (tokens[i].start != -1 && tokens[i].end == -1) {
312 return JSMN_ERROR_PART;
321 * Creates a new parser based over a given buffer with an array of tokens
324 void jsmn_init(jsmn_parser *parser) {
327 parser->toksuper = -1;