jsmn.h (12822B)
1 /* 2 * MIT License 3 * 4 * Copyright (c) 2010 Serge Zaitsev 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 #ifndef JSMN_H 25 #define JSMN_H 26 27 #include <stddef.h> 28 29 #define JSMN_PARENT_LINKS 30 #define JSMN_STRICT 31 32 #ifdef __cplusplus 33 extern "C" { 34 #endif 35 36 #ifdef JSMN_STATIC 37 #define JSMN_API static 38 #else 39 #define JSMN_API extern 40 #endif 41 42 /** 43 * JSON type identifier. Basic types are: 44 * o Object 45 * o Array 46 * o String 47 * o Other primitive: number, boolean (true/false) or null 48 */ 49 typedef enum { 50 JSMN_UNDEFINED = 0, 51 JSMN_OBJECT = 1 << 0, 52 JSMN_ARRAY = 1 << 1, 53 JSMN_STRING = 1 << 2, 54 JSMN_PRIMITIVE = 1 << 3 55 } jsmntype_t; 56 57 enum jsmnerr { 58 /* Not enough tokens were provided */ 59 JSMN_ERROR_NOMEM = -1, 60 /* Invalid character inside JSON string */ 61 JSMN_ERROR_INVAL = -2, 62 /* The string is not a full JSON packet, more bytes expected */ 63 JSMN_ERROR_PART = -3 64 }; 65 66 /** 67 * JSON token description. 68 * type type (object, array, string etc.) 69 * start start position in JSON data string 70 * end end position in JSON data string 71 */ 72 typedef struct jsmntok { 73 jsmntype_t type; 74 int start; 75 int end; 76 int size; 77 #ifdef JSMN_PARENT_LINKS 78 int parent; 79 #endif 80 } jsmntok_t; 81 82 /** 83 * JSON parser. Contains an array of token blocks available. Also stores 84 * the string being parsed now and current position in that string. 85 */ 86 typedef struct jsmn_parser { 87 unsigned int pos; /* offset in the JSON string */ 88 unsigned int toknext; /* next token to allocate */ 89 int toksuper; /* superior token node, e.g. parent object or array */ 90 } jsmn_parser; 91 92 /** 93 * Create JSON parser over an array of tokens 94 */ 95 JSMN_API void jsmn_init(jsmn_parser *parser); 96 97 /** 98 * Run JSON parser. It parses a JSON data string into and array of tokens, each 99 * describing 100 * a single JSON object. 101 */ 102 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, 103 jsmntok_t *tokens, const unsigned int num_tokens, int stop_at_id); 104 105 #ifndef JSMN_HEADER 106 /** 107 * Allocates a fresh unused token from the token pool. 108 */ 109 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, 110 const size_t num_tokens) { 111 jsmntok_t *tok; 112 if (parser->toknext >= num_tokens) { 113 return NULL; 114 } 115 tok = &tokens[parser->toknext++]; 116 tok->start = tok->end = -1; 117 tok->size = 0; 118 #ifdef JSMN_PARENT_LINKS 119 tok->parent = -1; 120 #endif 121 return tok; 122 } 123 124 /** 125 * Fills token type and boundaries. 126 */ 127 static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, 128 const int start, const int end) { 129 token->type = type; 130 token->start = start; 131 token->end = end; 132 token->size = 0; 133 } 134 135 /** 136 * Fills next available token with JSON primitive. 137 */ 138 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, 139 const size_t len, jsmntok_t *tokens, 140 const size_t num_tokens) { 141 jsmntok_t *token; 142 int start; 143 144 start = parser->pos; 145 146 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 147 switch (js[parser->pos]) { 148 #ifndef JSMN_STRICT 149 /* In strict mode primitive must be followed by "," or "}" or "]" */ 150 case ':': 151 #endif 152 case '\t': 153 case '\r': 154 case '\n': 155 case ' ': 156 case ',': 157 case ']': 158 case '}': 159 goto found; 160 default: 161 /* to quiet a warning from gcc*/ 162 break; 163 } 164 if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 165 parser->pos = start; 166 return JSMN_ERROR_INVAL; 167 } 168 } 169 #ifdef JSMN_STRICT 170 /* In strict mode primitive must be followed by a comma/object/array */ 171 parser->pos = start; 172 return JSMN_ERROR_PART; 173 #endif 174 175 found: 176 if (tokens == NULL) { 177 parser->pos--; 178 return 0; 179 } 180 token = jsmn_alloc_token(parser, tokens, num_tokens); 181 if (token == NULL) { 182 parser->pos = start; 183 return JSMN_ERROR_NOMEM; 184 } 185 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 186 #ifdef JSMN_PARENT_LINKS 187 token->parent = parser->toksuper; 188 #endif 189 parser->pos--; 190 return 0; 191 } 192 193 /** 194 * Fills next token with JSON string. 195 */ 196 static int jsmn_parse_string(jsmn_parser *parser, const char *js, 197 const size_t len, jsmntok_t *tokens, 198 const size_t num_tokens) { 199 jsmntok_t *token; 200 201 int start = parser->pos; 202 203 /* Skip starting quote */ 204 parser->pos++; 205 206 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 207 char c = js[parser->pos]; 208 209 /* Quote: end of string */ 210 if (c == '\"') { 211 if (tokens == NULL) { 212 return 0; 213 } 214 token = jsmn_alloc_token(parser, tokens, num_tokens); 215 if (token == NULL) { 216 parser->pos = start; 217 return JSMN_ERROR_NOMEM; 218 } 219 jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); 220 #ifdef JSMN_PARENT_LINKS 221 token->parent = parser->toksuper; 222 #endif 223 return 0; 224 } 225 226 /* Backslash: Quoted symbol expected */ 227 if (c == '\\' && parser->pos + 1 < len) { 228 int i; 229 parser->pos++; 230 switch (js[parser->pos]) { 231 /* Allowed escaped symbols */ 232 case '\"': 233 case '/': 234 case '\\': 235 case 'b': 236 case 'f': 237 case 'r': 238 case 'n': 239 case 't': 240 break; 241 /* Allows escaped symbol \uXXXX */ 242 case 'u': 243 parser->pos++; 244 for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; 245 i++) { 246 /* If it isn't a hex character we have an error */ 247 if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 248 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 249 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 250 parser->pos = start; 251 return JSMN_ERROR_INVAL; 252 } 253 parser->pos++; 254 } 255 parser->pos--; 256 break; 257 /* Unexpected symbol */ 258 default: 259 parser->pos = start; 260 return JSMN_ERROR_INVAL; 261 } 262 } 263 } 264 parser->pos = start; 265 return JSMN_ERROR_PART; 266 } 267 268 /** 269 * Parse JSON string and fill tokens. 270 */ 271 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, 272 jsmntok_t *tokens, const unsigned int num_tokens, int stop_at_id) { 273 int r, i, idkey; 274 jsmntok_t *token; 275 int count = parser->toknext; 276 277 idkey = 0; 278 279 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 280 char c; 281 jsmntype_t type; 282 283 c = js[parser->pos]; 284 switch (c) { 285 case '{': 286 case '[': 287 count++; 288 if (tokens == NULL) { 289 break; 290 } 291 token = jsmn_alloc_token(parser, tokens, num_tokens); 292 if (token == NULL) { 293 return JSMN_ERROR_NOMEM; 294 } 295 if (parser->toksuper != -1) { 296 jsmntok_t *t = &tokens[parser->toksuper]; 297 #ifdef JSMN_STRICT 298 /* In strict mode an object or array can't become a key */ 299 if (t->type == JSMN_OBJECT) { 300 return JSMN_ERROR_INVAL; 301 } 302 #endif 303 t->size++; 304 #ifdef JSMN_PARENT_LINKS 305 token->parent = parser->toksuper; 306 #endif 307 } 308 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 309 token->start = parser->pos; 310 parser->toksuper = parser->toknext - 1; 311 break; 312 case '}': 313 case ']': 314 if (tokens == NULL) { 315 break; 316 } 317 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 318 #ifdef JSMN_PARENT_LINKS 319 if (parser->toknext < 1) { 320 return JSMN_ERROR_INVAL; 321 } 322 token = &tokens[parser->toknext - 1]; 323 for (;;) { 324 if (token->start != -1 && token->end == -1) { 325 if (token->type != type) { 326 return JSMN_ERROR_INVAL; 327 } 328 token->end = parser->pos + 1; 329 parser->toksuper = token->parent; 330 break; 331 } 332 if (token->parent == -1) { 333 if (token->type != type || parser->toksuper == -1) { 334 return JSMN_ERROR_INVAL; 335 } 336 break; 337 } 338 token = &tokens[token->parent]; 339 } 340 #else 341 for (i = parser->toknext - 1; i >= 0; i--) { 342 token = &tokens[i]; 343 if (token->start != -1 && token->end == -1) { 344 if (token->type != type) { 345 return JSMN_ERROR_INVAL; 346 } 347 parser->toksuper = -1; 348 token->end = parser->pos + 1; 349 break; 350 } 351 } 352 /* Error if unmatched closing bracket */ 353 if (i == -1) { 354 return JSMN_ERROR_INVAL; 355 } 356 for (; i >= 0; i--) { 357 token = &tokens[i]; 358 if (token->start != -1 && token->end == -1) { 359 parser->toksuper = i; 360 break; 361 } 362 } 363 #endif 364 break; 365 case '\"': 366 r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 367 if (r < 0) { 368 return r; 369 } 370 count++; 371 if (parser->toksuper != -1 && tokens != NULL) { 372 tokens[parser->toksuper].size++; 373 } 374 375 // big hack. resumable parsing when encountering the id field 376 if (stop_at_id) { 377 token = &tokens[parser->toknext-1]; 378 if (idkey == 1 && (token->end - token->start) == 64) { 379 //printf("jsmn: found id '%.*s'\n", token->end - token->start, js + token->start); 380 parser->pos++; 381 return -42; 382 } else if (idkey == 0 && (token->end - token->start) == 2 && 383 (js + token->start)[0] == 'i' && 384 (js + token->start)[1] == 'd') { 385 //printf("jsmn: found id key\n"); 386 idkey = 1; 387 } 388 } 389 390 break; 391 case '\t': 392 case '\r': 393 case '\n': 394 case ' ': 395 break; 396 case ':': 397 parser->toksuper = parser->toknext - 1; 398 break; 399 case ',': 400 if (tokens != NULL && parser->toksuper != -1 && 401 tokens[parser->toksuper].type != JSMN_ARRAY && 402 tokens[parser->toksuper].type != JSMN_OBJECT) { 403 #ifdef JSMN_PARENT_LINKS 404 parser->toksuper = tokens[parser->toksuper].parent; 405 #else 406 for (i = parser->toknext - 1; i >= 0; i--) { 407 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { 408 if (tokens[i].start != -1 && tokens[i].end == -1) { 409 parser->toksuper = i; 410 break; 411 } 412 } 413 } 414 #endif 415 } 416 break; 417 #ifdef JSMN_STRICT 418 /* In strict mode primitives are: numbers and booleans */ 419 case '-': 420 case '0': 421 case '1': 422 case '2': 423 case '3': 424 case '4': 425 case '5': 426 case '6': 427 case '7': 428 case '8': 429 case '9': 430 case 't': 431 case 'f': 432 case 'n': 433 /* And they must not be keys of the object */ 434 if (tokens != NULL && parser->toksuper != -1) { 435 const jsmntok_t *t = &tokens[parser->toksuper]; 436 if (t->type == JSMN_OBJECT || 437 (t->type == JSMN_STRING && t->size != 0)) { 438 return JSMN_ERROR_INVAL; 439 } 440 } 441 #else 442 /* In non-strict mode every unquoted value is a primitive */ 443 default: 444 #endif 445 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 446 if (r < 0) { 447 return r; 448 } 449 count++; 450 if (parser->toksuper != -1 && tokens != NULL) { 451 tokens[parser->toksuper].size++; 452 } 453 break; 454 455 #ifdef JSMN_STRICT 456 /* Unexpected char in strict mode */ 457 default: 458 return JSMN_ERROR_INVAL; 459 #endif 460 } 461 } 462 463 if (tokens != NULL) { 464 for (i = parser->toknext - 1; i >= 0; i--) { 465 /* Unmatched opened object or array */ 466 if (tokens[i].start != -1 && tokens[i].end == -1) { 467 return JSMN_ERROR_PART; 468 } 469 } 470 } 471 472 return count; 473 } 474 475 /** 476 * Creates a new parser based over a given buffer with an array of tokens 477 * available. 478 */ 479 JSMN_API void jsmn_init(jsmn_parser *parser) { 480 parser->pos = 0; 481 parser->toknext = 0; 482 parser->toksuper = -1; 483 } 484 485 #endif /* JSMN_HEADER */ 486 487 #ifdef __cplusplus 488 } 489 #endif 490 491 #endif /* JSMN_H */