jsmn.h (12192B)
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); 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) { 273 int r; 274 int i; 275 jsmntok_t *token; 276 int count = parser->toknext; 277 278 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 279 char c; 280 jsmntype_t type; 281 282 c = js[parser->pos]; 283 switch (c) { 284 case '{': 285 case '[': 286 count++; 287 if (tokens == NULL) { 288 break; 289 } 290 token = jsmn_alloc_token(parser, tokens, num_tokens); 291 if (token == NULL) { 292 return JSMN_ERROR_NOMEM; 293 } 294 if (parser->toksuper != -1) { 295 jsmntok_t *t = &tokens[parser->toksuper]; 296 #ifdef JSMN_STRICT 297 /* In strict mode an object or array can't become a key */ 298 if (t->type == JSMN_OBJECT) { 299 return JSMN_ERROR_INVAL; 300 } 301 #endif 302 t->size++; 303 #ifdef JSMN_PARENT_LINKS 304 token->parent = parser->toksuper; 305 #endif 306 } 307 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 308 token->start = parser->pos; 309 parser->toksuper = parser->toknext - 1; 310 break; 311 case '}': 312 case ']': 313 if (tokens == NULL) { 314 break; 315 } 316 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 317 #ifdef JSMN_PARENT_LINKS 318 if (parser->toknext < 1) { 319 return JSMN_ERROR_INVAL; 320 } 321 token = &tokens[parser->toknext - 1]; 322 for (;;) { 323 if (token->start != -1 && token->end == -1) { 324 if (token->type != type) { 325 return JSMN_ERROR_INVAL; 326 } 327 token->end = parser->pos + 1; 328 parser->toksuper = token->parent; 329 break; 330 } 331 if (token->parent == -1) { 332 if (token->type != type || parser->toksuper == -1) { 333 return JSMN_ERROR_INVAL; 334 } 335 break; 336 } 337 token = &tokens[token->parent]; 338 } 339 #else 340 for (i = parser->toknext - 1; i >= 0; i--) { 341 token = &tokens[i]; 342 if (token->start != -1 && token->end == -1) { 343 if (token->type != type) { 344 return JSMN_ERROR_INVAL; 345 } 346 parser->toksuper = -1; 347 token->end = parser->pos + 1; 348 break; 349 } 350 } 351 /* Error if unmatched closing bracket */ 352 if (i == -1) { 353 return JSMN_ERROR_INVAL; 354 } 355 for (; i >= 0; i--) { 356 token = &tokens[i]; 357 if (token->start != -1 && token->end == -1) { 358 parser->toksuper = i; 359 break; 360 } 361 } 362 #endif 363 break; 364 case '\"': 365 r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 366 if (r < 0) { 367 return r; 368 } 369 count++; 370 if (parser->toksuper != -1 && tokens != NULL) { 371 tokens[parser->toksuper].size++; 372 } 373 break; 374 case '\t': 375 case '\r': 376 case '\n': 377 case ' ': 378 break; 379 case ':': 380 parser->toksuper = parser->toknext - 1; 381 break; 382 case ',': 383 if (tokens != NULL && parser->toksuper != -1 && 384 tokens[parser->toksuper].type != JSMN_ARRAY && 385 tokens[parser->toksuper].type != JSMN_OBJECT) { 386 #ifdef JSMN_PARENT_LINKS 387 parser->toksuper = tokens[parser->toksuper].parent; 388 #else 389 for (i = parser->toknext - 1; i >= 0; i--) { 390 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { 391 if (tokens[i].start != -1 && tokens[i].end == -1) { 392 parser->toksuper = i; 393 break; 394 } 395 } 396 } 397 #endif 398 } 399 break; 400 #ifdef JSMN_STRICT 401 /* In strict mode primitives are: numbers and booleans */ 402 case '-': 403 case '0': 404 case '1': 405 case '2': 406 case '3': 407 case '4': 408 case '5': 409 case '6': 410 case '7': 411 case '8': 412 case '9': 413 case 't': 414 case 'f': 415 case 'n': 416 /* And they must not be keys of the object */ 417 if (tokens != NULL && parser->toksuper != -1) { 418 const jsmntok_t *t = &tokens[parser->toksuper]; 419 if (t->type == JSMN_OBJECT || 420 (t->type == JSMN_STRING && t->size != 0)) { 421 return JSMN_ERROR_INVAL; 422 } 423 } 424 #else 425 /* In non-strict mode every unquoted value is a primitive */ 426 default: 427 #endif 428 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 429 if (r < 0) { 430 return r; 431 } 432 count++; 433 if (parser->toksuper != -1 && tokens != NULL) { 434 tokens[parser->toksuper].size++; 435 } 436 break; 437 438 #ifdef JSMN_STRICT 439 /* Unexpected char in strict mode */ 440 default: 441 return JSMN_ERROR_INVAL; 442 #endif 443 } 444 } 445 446 if (tokens != NULL) { 447 for (i = parser->toknext - 1; i >= 0; i--) { 448 /* Unmatched opened object or array */ 449 if (tokens[i].start != -1 && tokens[i].end == -1) { 450 return JSMN_ERROR_PART; 451 } 452 } 453 } 454 455 return count; 456 } 457 458 /** 459 * Creates a new parser based over a given buffer with an array of tokens 460 * available. 461 */ 462 JSMN_API void jsmn_init(jsmn_parser *parser) { 463 parser->pos = 0; 464 parser->toknext = 0; 465 parser->toksuper = -1; 466 } 467 468 #endif /* JSMN_HEADER */ 469 470 #ifdef __cplusplus 471 } 472 #endif 473 474 #endif /* JSMN_H */