sgdk
|
00001 /* 00002 * MIT License 00003 * 00004 * Copyright (c) 2010 Serge Zaitsev 00005 * 00006 * Permission is hereby granted, free of charge, to any person obtaining a copy 00007 * of this software and associated documentation files (the "Software"), to deal 00008 * in the Software without restriction, including without limitation the rights 00009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00010 * copies of the Software, and to permit persons to whom the Software is 00011 * furnished to do so, subject to the following conditions: 00012 * 00013 * The above copyright notice and this permission notice shall be included in 00014 * all copies or substantial portions of the Software. 00015 * 00016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00022 * SOFTWARE. 00023 */ 00024 #ifndef JSMN_H 00025 #define JSMN_H 00026 00027 #include "types.h" 00028 00029 #if (MODULE_MEGAWIFI != 0) 00030 00031 #ifdef __cplusplus 00032 extern "C" { 00033 #endif 00034 00035 #ifdef JSMN_STATIC 00036 #define JSMN_API static 00037 #else 00038 #define JSMN_API extern 00039 #endif 00040 00048 typedef enum { 00049 JSMN_UNDEFINED = 0, 00050 JSMN_OBJECT = 1, 00051 JSMN_ARRAY = 2, 00052 JSMN_STRING = 3, 00053 JSMN_PRIMITIVE = 4 00054 } jsmntype_t; 00055 00056 enum jsmnerr { 00057 /* Not enough tokens were provided */ 00058 JSMN_ERROR_NOMEM = -1, 00059 /* Invalid character inside JSON string */ 00060 JSMN_ERROR_INVAL = -2, 00061 /* The string is not a full JSON packet, more bytes expected */ 00062 JSMN_ERROR_PART = -3 00063 }; 00064 00071 typedef struct { 00072 jsmntype_t type; 00073 int start; 00074 int end; 00075 int size; 00076 #ifdef JSMN_PARENT_LINKS 00077 int parent; 00078 #endif 00079 } jsmntok_t; 00080 00085 typedef struct { 00086 unsigned int pos; /* offset in the JSON string */ 00087 unsigned int toknext; /* next token to allocate */ 00088 int toksuper; /* superior token node, e.g. parent object or array */ 00089 } jsmn_parser; 00090 00094 JSMN_API void jsmn_init(jsmn_parser *parser); 00095 00101 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const unsigned int len, 00102 jsmntok_t *tokens, const unsigned int num_tokens); 00103 00104 #ifndef JSMN_HEADER 00105 00108 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, 00109 const unsigned int num_tokens) { 00110 jsmntok_t *tok; 00111 if (parser->toknext >= num_tokens) { 00112 return NULL; 00113 } 00114 tok = &tokens[parser->toknext++]; 00115 tok->start = tok->end = -1; 00116 tok->size = 0; 00117 #ifdef JSMN_PARENT_LINKS 00118 tok->parent = -1; 00119 #endif 00120 return tok; 00121 } 00122 00126 static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, 00127 const int start, const int end) { 00128 token->type = type; 00129 token->start = start; 00130 token->end = end; 00131 token->size = 0; 00132 } 00133 00137 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, 00138 const unsigned int len, jsmntok_t *tokens, 00139 const unsigned int num_tokens) { 00140 jsmntok_t *token; 00141 int start; 00142 00143 start = parser->pos; 00144 00145 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 00146 switch (js[parser->pos]) { 00147 #ifndef JSMN_STRICT 00148 /* In strict mode primitive must be followed by "," or "}" or "]" */ 00149 case ':': 00150 #endif 00151 case '\t': 00152 case '\r': 00153 case '\n': 00154 case ' ': 00155 case ',': 00156 case ']': 00157 case '}': 00158 goto found; 00159 default: 00160 /* to quiet a warning from gcc*/ 00161 break; 00162 } 00163 if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 00164 parser->pos = start; 00165 return JSMN_ERROR_INVAL; 00166 } 00167 } 00168 #ifdef JSMN_STRICT 00169 /* In strict mode primitive must be followed by a comma/object/array */ 00170 parser->pos = start; 00171 return JSMN_ERROR_PART; 00172 #endif 00173 00174 found: 00175 if (tokens == NULL) { 00176 parser->pos--; 00177 return 0; 00178 } 00179 token = jsmn_alloc_token(parser, tokens, num_tokens); 00180 if (token == NULL) { 00181 parser->pos = start; 00182 return JSMN_ERROR_NOMEM; 00183 } 00184 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 00185 #ifdef JSMN_PARENT_LINKS 00186 token->parent = parser->toksuper; 00187 #endif 00188 parser->pos--; 00189 return 0; 00190 } 00191 00195 static int jsmn_parse_string(jsmn_parser *parser, const char *js, 00196 const unsigned int len, jsmntok_t *tokens, 00197 const unsigned int num_tokens) { 00198 jsmntok_t *token; 00199 00200 int start = parser->pos; 00201 00202 parser->pos++; 00203 00204 /* Skip starting quote */ 00205 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 00206 char c = js[parser->pos]; 00207 00208 /* Quote: end of string */ 00209 if (c == '\"') { 00210 if (tokens == NULL) { 00211 return 0; 00212 } 00213 token = jsmn_alloc_token(parser, tokens, num_tokens); 00214 if (token == NULL) { 00215 parser->pos = start; 00216 return JSMN_ERROR_NOMEM; 00217 } 00218 jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); 00219 #ifdef JSMN_PARENT_LINKS 00220 token->parent = parser->toksuper; 00221 #endif 00222 return 0; 00223 } 00224 00225 /* Backslash: Quoted symbol expected */ 00226 if (c == '\\' && parser->pos + 1 < len) { 00227 int i; 00228 parser->pos++; 00229 switch (js[parser->pos]) { 00230 /* Allowed escaped symbols */ 00231 case '\"': 00232 case '/': 00233 case '\\': 00234 case 'b': 00235 case 'f': 00236 case 'r': 00237 case 'n': 00238 case 't': 00239 break; 00240 /* Allows escaped symbol \uXXXX */ 00241 case 'u': 00242 parser->pos++; 00243 for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; 00244 i++) { 00245 /* If it isn't a hex character we have an error */ 00246 if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 00247 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 00248 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 00249 parser->pos = start; 00250 return JSMN_ERROR_INVAL; 00251 } 00252 parser->pos++; 00253 } 00254 parser->pos--; 00255 break; 00256 /* Unexpected symbol */ 00257 default: 00258 parser->pos = start; 00259 return JSMN_ERROR_INVAL; 00260 } 00261 } 00262 } 00263 parser->pos = start; 00264 return JSMN_ERROR_PART; 00265 } 00266 00270 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const unsigned int len, 00271 jsmntok_t *tokens, const unsigned int num_tokens) { 00272 int r; 00273 int i; 00274 jsmntok_t *token; 00275 int count = parser->toknext; 00276 00277 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 00278 char c; 00279 jsmntype_t type; 00280 00281 c = js[parser->pos]; 00282 switch (c) { 00283 case '{': 00284 case '[': 00285 count++; 00286 if (tokens == NULL) { 00287 break; 00288 } 00289 token = jsmn_alloc_token(parser, tokens, num_tokens); 00290 if (token == NULL) { 00291 return JSMN_ERROR_NOMEM; 00292 } 00293 if (parser->toksuper != -1) { 00294 jsmntok_t *t = &tokens[parser->toksuper]; 00295 #ifdef JSMN_STRICT 00296 /* In strict mode an object or array can't become a key */ 00297 if (t->type == JSMN_OBJECT) { 00298 return JSMN_ERROR_INVAL; 00299 } 00300 #endif 00301 t->size++; 00302 #ifdef JSMN_PARENT_LINKS 00303 token->parent = parser->toksuper; 00304 #endif 00305 } 00306 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 00307 token->start = parser->pos; 00308 parser->toksuper = parser->toknext - 1; 00309 break; 00310 case '}': 00311 case ']': 00312 if (tokens == NULL) { 00313 break; 00314 } 00315 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 00316 #ifdef JSMN_PARENT_LINKS 00317 if (parser->toknext < 1) { 00318 return JSMN_ERROR_INVAL; 00319 } 00320 token = &tokens[parser->toknext - 1]; 00321 for (;;) { 00322 if (token->start != -1 && token->end == -1) { 00323 if (token->type != type) { 00324 return JSMN_ERROR_INVAL; 00325 } 00326 token->end = parser->pos + 1; 00327 parser->toksuper = token->parent; 00328 break; 00329 } 00330 if (token->parent == -1) { 00331 if (token->type != type || parser->toksuper == -1) { 00332 return JSMN_ERROR_INVAL; 00333 } 00334 break; 00335 } 00336 token = &tokens[token->parent]; 00337 } 00338 #else 00339 for (i = parser->toknext - 1; i >= 0; i--) { 00340 token = &tokens[i]; 00341 if (token->start != -1 && token->end == -1) { 00342 if (token->type != type) { 00343 return JSMN_ERROR_INVAL; 00344 } 00345 parser->toksuper = -1; 00346 token->end = parser->pos + 1; 00347 break; 00348 } 00349 } 00350 /* Error if unmatched closing bracket */ 00351 if (i == -1) { 00352 return JSMN_ERROR_INVAL; 00353 } 00354 for (; i >= 0; i--) { 00355 token = &tokens[i]; 00356 if (token->start != -1 && token->end == -1) { 00357 parser->toksuper = i; 00358 break; 00359 } 00360 } 00361 #endif 00362 break; 00363 case '\"': 00364 r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 00365 if (r < 0) { 00366 return r; 00367 } 00368 count++; 00369 if (parser->toksuper != -1 && tokens != NULL) { 00370 tokens[parser->toksuper].size++; 00371 } 00372 break; 00373 case '\t': 00374 case '\r': 00375 case '\n': 00376 case ' ': 00377 break; 00378 case ':': 00379 parser->toksuper = parser->toknext - 1; 00380 break; 00381 case ',': 00382 if (tokens != NULL && parser->toksuper != -1 && 00383 tokens[parser->toksuper].type != JSMN_ARRAY && 00384 tokens[parser->toksuper].type != JSMN_OBJECT) { 00385 #ifdef JSMN_PARENT_LINKS 00386 parser->toksuper = tokens[parser->toksuper].parent; 00387 #else 00388 for (i = parser->toknext - 1; i >= 0; i--) { 00389 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { 00390 if (tokens[i].start != -1 && tokens[i].end == -1) { 00391 parser->toksuper = i; 00392 break; 00393 } 00394 } 00395 } 00396 #endif 00397 } 00398 break; 00399 #ifdef JSMN_STRICT 00400 /* In strict mode primitives are: numbers and booleans */ 00401 case '-': 00402 case '0': 00403 case '1': 00404 case '2': 00405 case '3': 00406 case '4': 00407 case '5': 00408 case '6': 00409 case '7': 00410 case '8': 00411 case '9': 00412 case 't': 00413 case 'f': 00414 case 'n': 00415 /* And they must not be keys of the object */ 00416 if (tokens != NULL && parser->toksuper != -1) { 00417 const jsmntok_t *t = &tokens[parser->toksuper]; 00418 if (t->type == JSMN_OBJECT || 00419 (t->type == JSMN_STRING && t->size != 0)) { 00420 return JSMN_ERROR_INVAL; 00421 } 00422 } 00423 #else 00424 /* In non-strict mode every unquoted value is a primitive */ 00425 default: 00426 #endif 00427 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 00428 if (r < 0) { 00429 return r; 00430 } 00431 count++; 00432 if (parser->toksuper != -1 && tokens != NULL) { 00433 tokens[parser->toksuper].size++; 00434 } 00435 break; 00436 00437 #ifdef JSMN_STRICT 00438 /* Unexpected char in strict mode */ 00439 default: 00440 return JSMN_ERROR_INVAL; 00441 #endif 00442 } 00443 } 00444 00445 if (tokens != NULL) { 00446 for (i = parser->toknext - 1; i >= 0; i--) { 00447 /* Unmatched opened object or array */ 00448 if (tokens[i].start != -1 && tokens[i].end == -1) { 00449 return JSMN_ERROR_PART; 00450 } 00451 } 00452 } 00453 00454 return count; 00455 } 00456 00461 JSMN_API void jsmn_init(jsmn_parser *parser) { 00462 parser->pos = 0; 00463 parser->toknext = 0; 00464 parser->toksuper = -1; 00465 } 00466 00467 #endif /* JSMN_HEADER */ 00468 00469 #ifdef __cplusplus 00470 } 00471 #endif 00472 00473 #endif // MODULE_MEGAWIFI 00474 00475 #endif /* JSMN_H */