Home | History | Annotate | Download | only in jsmn
      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 == '\\') {
    117       parser->pos++;
    118       switch (js[parser->pos]) {
    119         /* Allowed escaped symbols */
    120         case '\"': case '/' : case '\\' : case 'b' :
    121         case 'f' : case 'r' : case 'n'  : case 't' :
    122           break;
    123         /* Allows escaped symbol \uXXXX */
    124         case 'u':
    125           parser->pos++;
    126           int i = 0;
    127           for(; i < 4 && 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 ':' : case ',': case ' ':
    238         break;
    239 #ifdef JSMN_STRICT
    240       /* In strict mode primitives are: numbers and booleans */
    241       case '-': case '0': case '1' : case '2': case '3' : case '4':
    242       case '5': case '6': case '7' : case '8': case '9':
    243       case 't': case 'f': case 'n' :
    244 #else
    245       /* In non-strict mode every unquoted value is a primitive */
    246       default:
    247 #endif
    248         r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
    249         if (r < 0) return r;
    250         count++;
    251         if (parser->toksuper != -1 && tokens != NULL)
    252           tokens[parser->toksuper].size++;
    253         break;
    254 
    255 #ifdef JSMN_STRICT
    256       /* Unexpected char in strict mode */
    257       default:
    258         return JSMN_ERROR_INVAL;
    259 #endif
    260     }
    261   }
    262 
    263   for (i = parser->toknext - 1; i >= 0; i--) {
    264     /* Unmatched opened object or array */
    265     if (tokens[i].start != -1 && tokens[i].end == -1) {
    266       return JSMN_ERROR_PART;
    267     }
    268   }
    269 
    270   return count;
    271 }
    272 
    273 /**
    274  * Creates a new parser based over a given  buffer with an array of tokens
    275  * available.
    276  */
    277 void jsmn_init(jsmn_parser *parser) {
    278   parser->pos = 0;
    279   parser->toknext = 0;
    280   parser->toksuper = -1;
    281 }
    282