Fork of my original MQTTGateway

Dependencies:   mbed-http

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers jsmn.c Source File

jsmn.c

00001 #include "jsmn.h"
00002 
00003 /**
00004  * Allocates a fresh unused token from the token pull.
00005  */
00006 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
00007         jsmntok_t *tokens, size_t num_tokens) {
00008     jsmntok_t *tok;
00009     if (parser->toknext >= num_tokens) {
00010         return NULL;
00011     }
00012     tok = &tokens[parser->toknext++];
00013     tok->start = tok->end = -1;
00014     tok->size = 0;
00015 #ifdef JSMN_PARENT_LINKS
00016     tok->parent = -1;
00017 #endif
00018     return tok;
00019 }
00020 
00021 /**
00022  * Fills token type and boundaries.
00023  */
00024 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
00025                             int start, int end) {
00026     token->type = type;
00027     token->start = start;
00028     token->end = end;
00029     token->size = 0;
00030 }
00031 
00032 /**
00033  * Fills next available token with JSON primitive.
00034  */
00035 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
00036         size_t len, jsmntok_t *tokens, size_t num_tokens) {
00037     jsmntok_t *token;
00038     int start;
00039 
00040     start = parser->pos;
00041 
00042     for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
00043         switch (js[parser->pos]) {
00044 #ifndef JSMN_STRICT
00045             /* In strict mode primitive must be followed by "," or "}" or "]" */
00046             case ':':
00047 #endif
00048             case '\t' : case '\r' : case '\n' : case ' ' :
00049             case ','  : case ']'  : case '}' :
00050                 goto found;
00051         }
00052         if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
00053             parser->pos = start;
00054             return JSMN_ERROR_INVAL;
00055         }
00056     }
00057 #ifdef JSMN_STRICT
00058     /* In strict mode primitive must be followed by a comma/object/array */
00059     parser->pos = start;
00060     return JSMN_ERROR_PART;
00061 #endif
00062 
00063 found:
00064     if (tokens == NULL) {
00065         parser->pos--;
00066         return 0;
00067     }
00068     token = jsmn_alloc_token(parser, tokens, num_tokens);
00069     if (token == NULL) {
00070         parser->pos = start;
00071         return JSMN_ERROR_NOMEM;
00072     }
00073     jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
00074 #ifdef JSMN_PARENT_LINKS
00075     token->parent = parser->toksuper;
00076 #endif
00077     parser->pos--;
00078     return 0;
00079 }
00080 
00081 /**
00082  * Fills next token with JSON string.
00083  */
00084 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
00085         size_t len, jsmntok_t *tokens, size_t num_tokens) {
00086     jsmntok_t *token;
00087 
00088     int start = parser->pos;
00089 
00090     parser->pos++;
00091 
00092     /* Skip starting quote */
00093     for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
00094         char c = js[parser->pos];
00095 
00096         /* Quote: end of string */
00097         if (c == '\"') {
00098             if (tokens == NULL) {
00099                 return 0;
00100             }
00101             token = jsmn_alloc_token(parser, tokens, num_tokens);
00102             if (token == NULL) {
00103                 parser->pos = start;
00104                 return JSMN_ERROR_NOMEM;
00105             }
00106             jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
00107 #ifdef JSMN_PARENT_LINKS
00108             token->parent = parser->toksuper;
00109 #endif
00110             return 0;
00111         }
00112 
00113         /* Backslash: Quoted symbol expected */
00114         if (c == '\\' && parser->pos + 1 < len) {
00115             int i;
00116             parser->pos++;
00117             switch (js[parser->pos]) {
00118                 /* Allowed escaped symbols */
00119                 case '\"': case '/' : case '\\' : case 'b' :
00120                 case 'f' : case 'r' : case 'n'  : case 't' :
00121                     break;
00122                 /* Allows escaped symbol \uXXXX */
00123                 case 'u':
00124                     parser->pos++;
00125                     for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
00126                         /* If it isn't a hex character we have an error */
00127                         if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
00128                                     (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
00129                                     (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
00130                             parser->pos = start;
00131                             return JSMN_ERROR_INVAL;
00132                         }
00133                         parser->pos++;
00134                     }
00135                     parser->pos--;
00136                     break;
00137                 /* Unexpected symbol */
00138                 default:
00139                     parser->pos = start;
00140                     return JSMN_ERROR_INVAL;
00141             }
00142         }
00143     }
00144     parser->pos = start;
00145     return JSMN_ERROR_PART;
00146 }
00147 
00148 /**
00149  * Parse JSON string and fill tokens.
00150  */
00151 int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
00152         jsmntok_t *tokens, unsigned int num_tokens) {
00153     int r;
00154     int i;
00155     jsmntok_t *token;
00156     int count = parser->toknext;
00157 
00158     for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
00159         char c;
00160         jsmntype_t type;
00161 
00162         c = js[parser->pos];
00163         switch (c) {
00164             case '{': case '[':
00165                 count++;
00166                 if (tokens == NULL) {
00167                     break;
00168                 }
00169                 token = jsmn_alloc_token(parser, tokens, num_tokens);
00170                 if (token == NULL)
00171                     return JSMN_ERROR_NOMEM;
00172                 if (parser->toksuper != -1) {
00173                     tokens[parser->toksuper].size++;
00174 #ifdef JSMN_PARENT_LINKS
00175                     token->parent = parser->toksuper;
00176 #endif
00177                 }
00178                 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
00179                 token->start = parser->pos;
00180                 parser->toksuper = parser->toknext - 1;
00181                 break;
00182             case '}': case ']':
00183                 if (tokens == NULL)
00184                     break;
00185                 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
00186 #ifdef JSMN_PARENT_LINKS
00187                 if (parser->toknext < 1) {
00188                     return JSMN_ERROR_INVAL;
00189                 }
00190                 token = &tokens[parser->toknext - 1];
00191                 for (;;) {
00192                     if (token->start != -1 && token->end == -1) {
00193                         if (token->type != type) {
00194                             return JSMN_ERROR_INVAL;
00195                         }
00196                         token->end = parser->pos + 1;
00197                         parser->toksuper = token->parent;
00198                         break;
00199                     }
00200                     if (token->parent == -1) {
00201                         if(token->type != type || parser->toksuper == -1) {
00202                             return JSMN_ERROR_INVAL;
00203                         }
00204                         break;
00205                     }
00206                     token = &tokens[token->parent];
00207                 }
00208 #else
00209                 for (i = parser->toknext - 1; i >= 0; i--) {
00210                     token = &tokens[i];
00211                     if (token->start != -1 && token->end == -1) {
00212                         if (token->type != type) {
00213                             return JSMN_ERROR_INVAL;
00214                         }
00215                         parser->toksuper = -1;
00216                         token->end = parser->pos + 1;
00217                         break;
00218                     }
00219                 }
00220                 /* Error if unmatched closing bracket */
00221                 if (i == -1) return JSMN_ERROR_INVAL;
00222                 for (; i >= 0; i--) {
00223                     token = &tokens[i];
00224                     if (token->start != -1 && token->end == -1) {
00225                         parser->toksuper = i;
00226                         break;
00227                     }
00228                 }
00229 #endif
00230                 break;
00231             case '\"':
00232                 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
00233                 if (r < 0) return r;
00234                 count++;
00235                 if (parser->toksuper != -1 && tokens != NULL)
00236                     tokens[parser->toksuper].size++;
00237                 break;
00238             case '\t' : case '\r' : case '\n' : case ' ':
00239                 break;
00240             case ':':
00241                 parser->toksuper = parser->toknext - 1;
00242                 break;
00243             case ',':
00244                 if (tokens != NULL && parser->toksuper != -1 &&
00245                         tokens[parser->toksuper].type != JSMN_ARRAY &&
00246                         tokens[parser->toksuper].type != JSMN_OBJECT) {
00247 #ifdef JSMN_PARENT_LINKS
00248                     parser->toksuper = tokens[parser->toksuper].parent;
00249 #else
00250                     for (i = parser->toknext - 1; i >= 0; i--) {
00251                         if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
00252                             if (tokens[i].start != -1 && tokens[i].end == -1) {
00253                                 parser->toksuper = i;
00254                                 break;
00255                             }
00256                         }
00257                     }
00258 #endif
00259                 }
00260                 break;
00261 #ifdef JSMN_STRICT
00262             /* In strict mode primitives are: numbers and booleans */
00263             case '-': case '0': case '1' : case '2': case '3' : case '4':
00264             case '5': case '6': case '7' : case '8': case '9':
00265             case 't': case 'f': case 'n' :
00266                 /* And they must not be keys of the object */
00267                 if (tokens != NULL && parser->toksuper != -1) {
00268                     jsmntok_t *t = &tokens[parser->toksuper];
00269                     if (t->type == JSMN_OBJECT ||
00270                             (t->type == JSMN_STRING && t->size != 0)) {
00271                         return JSMN_ERROR_INVAL;
00272                     }
00273                 }
00274 #else
00275             /* In non-strict mode every unquoted value is a primitive */
00276             default:
00277 #endif
00278                 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
00279                 if (r < 0) return r;
00280                 count++;
00281                 if (parser->toksuper != -1 && tokens != NULL)
00282                     tokens[parser->toksuper].size++;
00283                 break;
00284 
00285 #ifdef JSMN_STRICT
00286             /* Unexpected char in strict mode */
00287             default:
00288                 return JSMN_ERROR_INVAL;
00289 #endif
00290         }
00291     }
00292 
00293     if (tokens != NULL) {
00294         for (i = parser->toknext - 1; i >= 0; i--) {
00295             /* Unmatched opened object or array */
00296             if (tokens[i].start != -1 && tokens[i].end == -1) {
00297                 return JSMN_ERROR_PART;
00298             }
00299         }
00300     }
00301 
00302     return count;
00303 }
00304 
00305 /**
00306  * Creates a new parser based over a given  buffer with an array of tokens
00307  * available.
00308  */
00309 void jsmn_init(jsmn_parser *parser) {
00310     parser->pos = 0;
00311     parser->toknext = 0;
00312     parser->toksuper = -1;
00313 }
00314