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