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