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