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