1#include <stdlib.h>
2
3#include "jsmn.h"
4
5/**
6 * Allocates a fresh unused token from the token pull.
7 */
8static 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 */
26static 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 */
37static 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
65found:
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 */
86static 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 */
153jsmnerr_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 */
277void jsmn_init(jsmn_parser *parser) {
278  parser->pos = 0;
279  parser->toknext = 0;
280  parser->toksuper = -1;
281}
282