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