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