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