damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

jsmn.h (12192B)


      1 /*
      2  * MIT License
      3  *
      4  * Copyright (c) 2010 Serge Zaitsev
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  */
     24 #ifndef JSMN_H
     25 #define JSMN_H
     26 
     27 #include <stddef.h>
     28 
     29 #define JSMN_PARENT_LINKS
     30 #define JSMN_STRICT
     31 
     32 #ifdef __cplusplus
     33 extern "C" {
     34 #endif
     35 
     36 #ifdef JSMN_STATIC
     37 #define JSMN_API static
     38 #else
     39 #define JSMN_API extern
     40 #endif
     41 
     42 /**
     43  * JSON type identifier. Basic types are:
     44  * 	o Object
     45  * 	o Array
     46  * 	o String
     47  * 	o Other primitive: number, boolean (true/false) or null
     48  */
     49 typedef enum {
     50   JSMN_UNDEFINED = 0,
     51   JSMN_OBJECT = 1 << 0,
     52   JSMN_ARRAY = 1 << 1,
     53   JSMN_STRING = 1 << 2,
     54   JSMN_PRIMITIVE = 1 << 3
     55 } jsmntype_t;
     56 
     57 enum jsmnerr {
     58   /* Not enough tokens were provided */
     59   JSMN_ERROR_NOMEM = -1,
     60   /* Invalid character inside JSON string */
     61   JSMN_ERROR_INVAL = -2,
     62   /* The string is not a full JSON packet, more bytes expected */
     63   JSMN_ERROR_PART = -3
     64 };
     65 
     66 /**
     67  * JSON token description.
     68  * type		type (object, array, string etc.)
     69  * start	start position in JSON data string
     70  * end		end position in JSON data string
     71  */
     72 typedef struct jsmntok {
     73   jsmntype_t type;
     74   int start;
     75   int end;
     76   int size;
     77 #ifdef JSMN_PARENT_LINKS
     78   int parent;
     79 #endif
     80 } jsmntok_t;
     81 
     82 /**
     83  * JSON parser. Contains an array of token blocks available. Also stores
     84  * the string being parsed now and current position in that string.
     85  */
     86 typedef struct jsmn_parser {
     87   unsigned int pos;     /* offset in the JSON string */
     88   unsigned int toknext; /* next token to allocate */
     89   int toksuper;         /* superior token node, e.g. parent object or array */
     90 } jsmn_parser;
     91 
     92 /**
     93  * Create JSON parser over an array of tokens
     94  */
     95 JSMN_API void jsmn_init(jsmn_parser *parser);
     96 
     97 /**
     98  * Run JSON parser. It parses a JSON data string into and array of tokens, each
     99  * describing
    100  * a single JSON object.
    101  */
    102 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
    103                         jsmntok_t *tokens, const unsigned int num_tokens);
    104 
    105 #ifndef JSMN_HEADER
    106 /**
    107  * Allocates a fresh unused token from the token pool.
    108  */
    109 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
    110                                    const size_t num_tokens) {
    111   jsmntok_t *tok;
    112   if (parser->toknext >= num_tokens) {
    113     return NULL;
    114   }
    115   tok = &tokens[parser->toknext++];
    116   tok->start = tok->end = -1;
    117   tok->size = 0;
    118 #ifdef JSMN_PARENT_LINKS
    119   tok->parent = -1;
    120 #endif
    121   return tok;
    122 }
    123 
    124 /**
    125  * Fills token type and boundaries.
    126  */
    127 static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
    128                             const int start, const int end) {
    129   token->type = type;
    130   token->start = start;
    131   token->end = end;
    132   token->size = 0;
    133 }
    134 
    135 /**
    136  * Fills next available token with JSON primitive.
    137  */
    138 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
    139                                 const size_t len, jsmntok_t *tokens,
    140                                 const size_t num_tokens) {
    141   jsmntok_t *token;
    142   int start;
    143 
    144   start = parser->pos;
    145 
    146   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
    147     switch (js[parser->pos]) {
    148 #ifndef JSMN_STRICT
    149     /* In strict mode primitive must be followed by "," or "}" or "]" */
    150     case ':':
    151 #endif
    152     case '\t':
    153     case '\r':
    154     case '\n':
    155     case ' ':
    156     case ',':
    157     case ']':
    158     case '}':
    159       goto found;
    160     default:
    161                    /* to quiet a warning from gcc*/
    162       break;
    163     }
    164     if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
    165       parser->pos = start;
    166       return JSMN_ERROR_INVAL;
    167     }
    168   }
    169 #ifdef JSMN_STRICT
    170   /* In strict mode primitive must be followed by a comma/object/array */
    171   parser->pos = start;
    172   return JSMN_ERROR_PART;
    173 #endif
    174 
    175 found:
    176   if (tokens == NULL) {
    177     parser->pos--;
    178     return 0;
    179   }
    180   token = jsmn_alloc_token(parser, tokens, num_tokens);
    181   if (token == NULL) {
    182     parser->pos = start;
    183     return JSMN_ERROR_NOMEM;
    184   }
    185   jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
    186 #ifdef JSMN_PARENT_LINKS
    187   token->parent = parser->toksuper;
    188 #endif
    189   parser->pos--;
    190   return 0;
    191 }
    192 
    193 /**
    194  * Fills next token with JSON string.
    195  */
    196 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
    197                              const size_t len, jsmntok_t *tokens,
    198                              const size_t num_tokens) {
    199   jsmntok_t *token;
    200 
    201   int start = parser->pos;
    202   
    203   /* Skip starting quote */
    204   parser->pos++;
    205   
    206   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
    207     char c = js[parser->pos];
    208 
    209     /* Quote: end of string */
    210     if (c == '\"') {
    211       if (tokens == NULL) {
    212         return 0;
    213       }
    214       token = jsmn_alloc_token(parser, tokens, num_tokens);
    215       if (token == NULL) {
    216         parser->pos = start;
    217         return JSMN_ERROR_NOMEM;
    218       }
    219       jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
    220 #ifdef JSMN_PARENT_LINKS
    221       token->parent = parser->toksuper;
    222 #endif
    223       return 0;
    224     }
    225 
    226     /* Backslash: Quoted symbol expected */
    227     if (c == '\\' && parser->pos + 1 < len) {
    228       int i;
    229       parser->pos++;
    230       switch (js[parser->pos]) {
    231       /* Allowed escaped symbols */
    232       case '\"':
    233       case '/':
    234       case '\\':
    235       case 'b':
    236       case 'f':
    237       case 'r':
    238       case 'n':
    239       case 't':
    240         break;
    241       /* Allows escaped symbol \uXXXX */
    242       case 'u':
    243         parser->pos++;
    244         for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
    245              i++) {
    246           /* If it isn't a hex character we have an error */
    247           if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) ||   /* 0-9 */
    248                 (js[parser->pos] >= 65 && js[parser->pos] <= 70) ||   /* A-F */
    249                 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
    250             parser->pos = start;
    251             return JSMN_ERROR_INVAL;
    252           }
    253           parser->pos++;
    254         }
    255         parser->pos--;
    256         break;
    257       /* Unexpected symbol */
    258       default:
    259         parser->pos = start;
    260         return JSMN_ERROR_INVAL;
    261       }
    262     }
    263   }
    264   parser->pos = start;
    265   return JSMN_ERROR_PART;
    266 }
    267 
    268 /**
    269  * Parse JSON string and fill tokens.
    270  */
    271 JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
    272                         jsmntok_t *tokens, const unsigned int num_tokens) {
    273   int r;
    274   int i;
    275   jsmntok_t *token;
    276   int count = parser->toknext;
    277 
    278   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
    279     char c;
    280     jsmntype_t type;
    281 
    282     c = js[parser->pos];
    283     switch (c) {
    284     case '{':
    285     case '[':
    286       count++;
    287       if (tokens == NULL) {
    288         break;
    289       }
    290       token = jsmn_alloc_token(parser, tokens, num_tokens);
    291       if (token == NULL) {
    292         return JSMN_ERROR_NOMEM;
    293       }
    294       if (parser->toksuper != -1) {
    295         jsmntok_t *t = &tokens[parser->toksuper];
    296 #ifdef JSMN_STRICT
    297         /* In strict mode an object or array can't become a key */
    298         if (t->type == JSMN_OBJECT) {
    299           return JSMN_ERROR_INVAL;
    300         }
    301 #endif
    302         t->size++;
    303 #ifdef JSMN_PARENT_LINKS
    304         token->parent = parser->toksuper;
    305 #endif
    306       }
    307       token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
    308       token->start = parser->pos;
    309       parser->toksuper = parser->toknext - 1;
    310       break;
    311     case '}':
    312     case ']':
    313       if (tokens == NULL) {
    314         break;
    315       }
    316       type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
    317 #ifdef JSMN_PARENT_LINKS
    318       if (parser->toknext < 1) {
    319         return JSMN_ERROR_INVAL;
    320       }
    321       token = &tokens[parser->toknext - 1];
    322       for (;;) {
    323         if (token->start != -1 && token->end == -1) {
    324           if (token->type != type) {
    325             return JSMN_ERROR_INVAL;
    326           }
    327           token->end = parser->pos + 1;
    328           parser->toksuper = token->parent;
    329           break;
    330         }
    331         if (token->parent == -1) {
    332           if (token->type != type || parser->toksuper == -1) {
    333             return JSMN_ERROR_INVAL;
    334           }
    335           break;
    336         }
    337         token = &tokens[token->parent];
    338       }
    339 #else
    340       for (i = parser->toknext - 1; i >= 0; i--) {
    341         token = &tokens[i];
    342         if (token->start != -1 && token->end == -1) {
    343           if (token->type != type) {
    344             return JSMN_ERROR_INVAL;
    345           }
    346           parser->toksuper = -1;
    347           token->end = parser->pos + 1;
    348           break;
    349         }
    350       }
    351       /* Error if unmatched closing bracket */
    352       if (i == -1) {
    353         return JSMN_ERROR_INVAL;
    354       }
    355       for (; i >= 0; i--) {
    356         token = &tokens[i];
    357         if (token->start != -1 && token->end == -1) {
    358           parser->toksuper = i;
    359           break;
    360         }
    361       }
    362 #endif
    363       break;
    364     case '\"':
    365       r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
    366       if (r < 0) {
    367         return r;
    368       }
    369       count++;
    370       if (parser->toksuper != -1 && tokens != NULL) {
    371         tokens[parser->toksuper].size++;
    372       }
    373       break;
    374     case '\t':
    375     case '\r':
    376     case '\n':
    377     case ' ':
    378       break;
    379     case ':':
    380       parser->toksuper = parser->toknext - 1;
    381       break;
    382     case ',':
    383       if (tokens != NULL && parser->toksuper != -1 &&
    384           tokens[parser->toksuper].type != JSMN_ARRAY &&
    385           tokens[parser->toksuper].type != JSMN_OBJECT) {
    386 #ifdef JSMN_PARENT_LINKS
    387         parser->toksuper = tokens[parser->toksuper].parent;
    388 #else
    389         for (i = parser->toknext - 1; i >= 0; i--) {
    390           if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
    391             if (tokens[i].start != -1 && tokens[i].end == -1) {
    392               parser->toksuper = i;
    393               break;
    394             }
    395           }
    396         }
    397 #endif
    398       }
    399       break;
    400 #ifdef JSMN_STRICT
    401     /* In strict mode primitives are: numbers and booleans */
    402     case '-':
    403     case '0':
    404     case '1':
    405     case '2':
    406     case '3':
    407     case '4':
    408     case '5':
    409     case '6':
    410     case '7':
    411     case '8':
    412     case '9':
    413     case 't':
    414     case 'f':
    415     case 'n':
    416       /* And they must not be keys of the object */
    417       if (tokens != NULL && parser->toksuper != -1) {
    418         const jsmntok_t *t = &tokens[parser->toksuper];
    419         if (t->type == JSMN_OBJECT ||
    420             (t->type == JSMN_STRING && t->size != 0)) {
    421           return JSMN_ERROR_INVAL;
    422         }
    423       }
    424 #else
    425     /* In non-strict mode every unquoted value is a primitive */
    426     default:
    427 #endif
    428       r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
    429       if (r < 0) {
    430         return r;
    431       }
    432       count++;
    433       if (parser->toksuper != -1 && tokens != NULL) {
    434         tokens[parser->toksuper].size++;
    435       }
    436       break;
    437 
    438 #ifdef JSMN_STRICT
    439     /* Unexpected char in strict mode */
    440     default:
    441       return JSMN_ERROR_INVAL;
    442 #endif
    443     }
    444   }
    445 
    446   if (tokens != NULL) {
    447     for (i = parser->toknext - 1; i >= 0; i--) {
    448       /* Unmatched opened object or array */
    449       if (tokens[i].start != -1 && tokens[i].end == -1) {
    450         return JSMN_ERROR_PART;
    451       }
    452     }
    453   }
    454 
    455   return count;
    456 }
    457 
    458 /**
    459  * Creates a new parser based over a given buffer with an array of tokens
    460  * available.
    461  */
    462 JSMN_API void jsmn_init(jsmn_parser *parser) {
    463   parser->pos = 0;
    464   parser->toknext = 0;
    465   parser->toksuper = -1;
    466 }
    467 
    468 #endif /* JSMN_HEADER */
    469 
    470 #ifdef __cplusplus
    471 }
    472 #endif
    473 
    474 #endif /* JSMN_H */