jsmn.c (7855B)
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 * Slightly modified by AK to not assume 0 terminated input. 23 */ 24 25#include <stdlib.h> 26#include "jsmn.h" 27#define JSMN_STRICT 28 29/* 30 * Allocates a fresh unused token from the token pool. 31 */ 32static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, 33 jsmntok_t *tokens, size_t num_tokens) 34{ 35 jsmntok_t *tok; 36 37 if ((unsigned)parser->toknext >= num_tokens) 38 return NULL; 39 tok = &tokens[parser->toknext++]; 40 tok->start = tok->end = -1; 41 tok->size = 0; 42 return tok; 43} 44 45/* 46 * Fills token type and boundaries. 47 */ 48static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, 49 int start, int end) 50{ 51 token->type = type; 52 token->start = start; 53 token->end = end; 54 token->size = 0; 55} 56 57/* 58 * Fills next available token with JSON primitive. 59 */ 60static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, 61 size_t len, 62 jsmntok_t *tokens, size_t num_tokens) 63{ 64 jsmntok_t *token; 65 int start; 66 67 start = parser->pos; 68 69 for (; parser->pos < len; parser->pos++) { 70 switch (js[parser->pos]) { 71#ifndef JSMN_STRICT 72 /* 73 * In strict mode primitive must be followed by "," 74 * or "}" or "]" 75 */ 76 case ':': 77#endif 78 case '\t': 79 case '\r': 80 case '\n': 81 case ' ': 82 case ',': 83 case ']': 84 case '}': 85 goto found; 86 default: 87 break; 88 } 89 if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 90 parser->pos = start; 91 return JSMN_ERROR_INVAL; 92 } 93 } 94#ifdef JSMN_STRICT 95 /* 96 * In strict mode primitive must be followed by a 97 * comma/object/array. 98 */ 99 parser->pos = start; 100 return JSMN_ERROR_PART; 101#endif 102 103found: 104 token = jsmn_alloc_token(parser, tokens, num_tokens); 105 if (token == NULL) { 106 parser->pos = start; 107 return JSMN_ERROR_NOMEM; 108 } 109 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 110 parser->pos--; /* parent sees closing brackets */ 111 return JSMN_SUCCESS; 112} 113 114/* 115 * Fills next token with JSON string. 116 */ 117static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, 118 size_t len, 119 jsmntok_t *tokens, size_t num_tokens) 120{ 121 jsmntok_t *token; 122 int start = parser->pos; 123 124 /* Skip starting quote */ 125 parser->pos++; 126 127 for (; parser->pos < len; parser->pos++) { 128 char c = js[parser->pos]; 129 130 /* Quote: end of string */ 131 if (c == '\"') { 132 token = jsmn_alloc_token(parser, tokens, num_tokens); 133 if (token == NULL) { 134 parser->pos = start; 135 return JSMN_ERROR_NOMEM; 136 } 137 jsmn_fill_token(token, JSMN_STRING, start+1, 138 parser->pos); 139 return JSMN_SUCCESS; 140 } 141 142 /* Backslash: Quoted symbol expected */ 143 if (c == '\\') { 144 parser->pos++; 145 switch (js[parser->pos]) { 146 /* Allowed escaped symbols */ 147 case '\"': 148 case '/': 149 case '\\': 150 case 'b': 151 case 'f': 152 case 'r': 153 case 'n': 154 case 't': 155 break; 156 /* Allows escaped symbol \uXXXX */ 157 case 'u': 158 /* TODO */ 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, size_t len, 175 jsmntok_t *tokens, unsigned int num_tokens) 176{ 177 jsmnerr_t r; 178 int i; 179 jsmntok_t *token; 180#ifdef JSMN_STRICT 181 /* 182 * Keeps track of whether a new object/list/primitive is expected. New items are only 183 * allowed after an opening brace, comma or colon. A closing brace after a comma is not 184 * valid JSON. 185 */ 186 int expecting_item = 1; 187#endif 188 189 for (; parser->pos < len; parser->pos++) { 190 char c; 191 jsmntype_t type; 192 193 c = js[parser->pos]; 194 switch (c) { 195 case '{': 196 case '[': 197#ifdef JSMN_STRICT 198 if (!expecting_item) 199 return JSMN_ERROR_INVAL; 200#endif 201 token = jsmn_alloc_token(parser, tokens, num_tokens); 202 if (token == NULL) 203 return JSMN_ERROR_NOMEM; 204 if (parser->toksuper != -1) 205 tokens[parser->toksuper].size++; 206 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 207 token->start = parser->pos; 208 parser->toksuper = parser->toknext - 1; 209 break; 210 case '}': 211 case ']': 212#ifdef JSMN_STRICT 213 if (expecting_item) 214 return JSMN_ERROR_INVAL; 215#endif 216 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 217 for (i = parser->toknext - 1; i >= 0; i--) { 218 token = &tokens[i]; 219 if (token->start != -1 && token->end == -1) { 220 if (token->type != type) 221 return JSMN_ERROR_INVAL; 222 parser->toksuper = -1; 223 token->end = parser->pos + 1; 224 break; 225 } 226 } 227 /* Error if unmatched closing bracket */ 228 if (i == -1) 229 return JSMN_ERROR_INVAL; 230 for (; i >= 0; i--) { 231 token = &tokens[i]; 232 if (token->start != -1 && token->end == -1) { 233 parser->toksuper = i; 234 break; 235 } 236 } 237 break; 238 case '\"': 239#ifdef JSMN_STRICT 240 if (!expecting_item) 241 return JSMN_ERROR_INVAL; 242 expecting_item = 0; 243#endif 244 r = jsmn_parse_string(parser, js, len, tokens, 245 num_tokens); 246 if (r < 0) 247 return r; 248 if (parser->toksuper != -1) 249 tokens[parser->toksuper].size++; 250 break; 251 case '\t': 252 case '\r': 253 case '\n': 254 case ' ': 255 break; 256#ifdef JSMN_STRICT 257 case ':': 258 case ',': 259 if (expecting_item) 260 return JSMN_ERROR_INVAL; 261 expecting_item = 1; 262 break; 263 /* 264 * In strict mode primitives are: 265 * numbers and booleans. 266 */ 267 case '-': 268 case '0': 269 case '1': 270 case '2': 271 case '3': 272 case '4': 273 case '5': 274 case '6': 275 case '7': 276 case '8': 277 case '9': 278 case 't': 279 case 'f': 280 case 'n': 281#else 282 case ':': 283 case ',': 284 break; 285 /* 286 * In non-strict mode every unquoted value 287 * is a primitive. 288 */ 289 /*FALL THROUGH */ 290 default: 291#endif 292 293#ifdef JSMN_STRICT 294 if (!expecting_item) 295 return JSMN_ERROR_INVAL; 296 expecting_item = 0; 297#endif 298 r = jsmn_parse_primitive(parser, js, len, tokens, 299 num_tokens); 300 if (r < 0) 301 return r; 302 if (parser->toksuper != -1) 303 tokens[parser->toksuper].size++; 304 break; 305 306#ifdef JSMN_STRICT 307 /* Unexpected char in strict mode */ 308 default: 309 return JSMN_ERROR_INVAL; 310#endif 311 } 312 } 313 314 for (i = parser->toknext - 1; i >= 0; i--) { 315 /* Unmatched opened object or array */ 316 if (tokens[i].start != -1 && tokens[i].end == -1) 317 return JSMN_ERROR_PART; 318 } 319 320#ifdef JSMN_STRICT 321 return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS; 322#else 323 return JSMN_SUCCESS; 324#endif 325} 326 327/* 328 * Creates a new parser based over a given buffer with an array of tokens 329 * available. 330 */ 331void jsmn_init(jsmn_parser *parser) 332{ 333 parser->pos = 0; 334 parser->toknext = 0; 335 parser->toksuper = -1; 336} 337 338const char *jsmn_strerror(jsmnerr_t err) 339{ 340 switch (err) { 341 case JSMN_ERROR_NOMEM: 342 return "No enough tokens"; 343 case JSMN_ERROR_INVAL: 344 return "Invalid character inside JSON string"; 345 case JSMN_ERROR_PART: 346 return "The string is not a full JSON packet, more bytes expected"; 347 case JSMN_SUCCESS: 348 return "Success"; 349 default: 350 return "Unknown json error"; 351 } 352}