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