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