Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/libjsmn/jsmn.c
Line
Count
Source (jump to first uncovered line)
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
0
    jsmntok_t *tokens, size_t num_tokens) {
10
0
  jsmntok_t *tok;
11
0
  if (parser->toknext >= num_tokens) {
12
0
    return NULL;
13
0
  }
14
0
  tok = &tokens[parser->toknext++];
15
0
  tok->start = tok->end = -1;
16
0
  tok->size = 0;
17
0
#ifdef JSMN_PARENT_LINKS
18
0
  tok->parent = -1;
19
0
#endif
20
0
  return tok;
21
0
}
22
23
/**
24
 * Fills token type and boundaries.
25
 */
26
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
27
0
                            int start, int end) {
28
0
  token->type = type;
29
0
  token->start = start;
30
0
  token->end = end;
31
0
  token->size = 0;
32
0
}
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
0
    size_t len, jsmntok_t *tokens, size_t num_tokens) {
39
0
  jsmntok_t *token;
40
0
  int start;
41
42
0
  start = parser->pos;
43
44
0
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
45
0
    switch (js[parser->pos]) {
46
0
#ifndef JSMN_STRICT
47
      /* In strict mode primitive must be followed by "," or "}" or "]" */
48
0
      case ':':
49
0
#endif
50
0
      case '\t' : case '\r' : case '\n' : case ' ' :
51
0
      case ','  : case ']'  : case '}' :
52
0
        goto found;
53
0
    }
54
0
    if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
55
0
      parser->pos = start;
56
0
      return JSMN_ERROR_INVAL;
57
0
    }
58
0
  }
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
0
found:
66
0
  if (tokens == NULL) {
67
0
    parser->pos--;
68
0
    return 0;
69
0
  }
70
0
  token = jsmn_alloc_token(parser, tokens, num_tokens);
71
0
  if (token == NULL) {
72
0
    parser->pos = start;
73
0
    return JSMN_ERROR_NOMEM;
74
0
  }
75
0
  jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
76
0
#ifdef JSMN_PARENT_LINKS
77
0
  token->parent = parser->toksuper;
78
0
#endif
79
0
  parser->pos--;
80
0
  return 0;
81
0
}
82
83
/**
84
 * Filsl next token with JSON string.
85
 */
86
static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
87
0
    size_t len, jsmntok_t *tokens, size_t num_tokens) {
88
0
  jsmntok_t *token;
89
90
0
  int start = parser->pos;
91
92
0
  parser->pos++;
93
94
  /* Skip starting quote */
95
0
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
96
0
    char c = js[parser->pos];
97
98
    /* Quote: end of string */
99
0
    if (c == '\"') {
100
0
      if (tokens == NULL) {
101
0
        return 0;
102
0
      }
103
0
      token = jsmn_alloc_token(parser, tokens, num_tokens);
104
0
      if (token == NULL) {
105
0
        parser->pos = start;
106
0
        return JSMN_ERROR_NOMEM;
107
0
      }
108
0
      jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
109
0
#ifdef JSMN_PARENT_LINKS
110
0
      token->parent = parser->toksuper;
111
0
#endif
112
0
      return 0;
113
0
    }
114
115
    /* Backslash: Quoted symbol expected */
116
0
    if (c == '\\' && parser->pos + 1 < len) {
117
0
      int i;
118
0
      parser->pos++;
119
0
      switch (js[parser->pos]) {
120
        /* Allowed escaped symbols */
121
0
        case '\"': case '/' : case '\\' : case 'b' :
122
0
        case 'f' : case 'r' : case 'n'  : case 't' :
123
0
          break;
124
        /* Allows escaped symbol \uXXXX */
125
0
        case 'u':
126
0
          parser->pos++;
127
0
          for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
128
            /* If it isn't a hex character we have an error */
129
0
            if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
130
0
                  (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
131
0
                  (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
132
0
              parser->pos = start;
133
0
              return JSMN_ERROR_INVAL;
134
0
            }
135
0
            parser->pos++;
136
0
          }
137
0
          parser->pos--;
138
0
          break;
139
        /* Unexpected symbol */
140
0
        default:
141
0
          parser->pos = start;
142
0
          return JSMN_ERROR_INVAL;
143
0
      }
144
0
    }
145
0
  }
146
0
  parser->pos = start;
147
0
  return JSMN_ERROR_PART;
148
0
}
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
0
    jsmntok_t *tokens, unsigned int num_tokens) {
155
0
  jsmnerr_t r;
156
0
  int i;
157
0
  jsmntok_t *token;
158
0
  int count = 0;
159
160
0
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
161
0
    char c;
162
0
    jsmntype_t type;
163
164
0
    c = js[parser->pos];
165
0
    switch (c) {
166
0
      case '{': case '[':
167
0
        count++;
168
0
        if (tokens == NULL) {
169
0
          break;
170
0
        }
171
0
        token = jsmn_alloc_token(parser, tokens, num_tokens);
172
0
        if (token == NULL)
173
0
          return JSMN_ERROR_NOMEM;
174
0
        if (parser->toksuper != -1) {
175
0
          tokens[parser->toksuper].size++;
176
0
#ifdef JSMN_PARENT_LINKS
177
0
          token->parent = parser->toksuper;
178
0
#endif
179
0
        }
180
0
        token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
181
0
        token->start = parser->pos;
182
0
        parser->toksuper = parser->toknext - 1;
183
0
        break;
184
0
      case '}': case ']':
185
0
        if (tokens == NULL)
186
0
          break;
187
0
        type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
188
0
#ifdef JSMN_PARENT_LINKS
189
0
        if (parser->toknext < 1) {
190
0
          return JSMN_ERROR_INVAL;
191
0
        }
192
0
        token = &tokens[parser->toknext - 1];
193
0
        for (;;) {
194
0
          if (token->start != -1 && token->end == -1) {
195
0
            if (token->type != type) {
196
0
              return JSMN_ERROR_INVAL;
197
0
            }
198
0
            token->end = parser->pos + 1;
199
0
            parser->toksuper = token->parent;
200
0
            break;
201
0
          }
202
0
          if (token->parent == -1) {
203
0
            break;
204
0
          }
205
0
          token = &tokens[token->parent];
206
0
        }
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
0
        break;
230
0
      case '\"':
231
0
        r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
232
0
        if (r < 0) return r;
233
0
        count++;
234
0
        if (parser->toksuper != -1 && tokens != NULL)
235
0
          tokens[parser->toksuper].size++;
236
0
        break;
237
0
      case '\t' : case '\r' : case '\n' : case ' ':
238
0
        break;
239
0
      case ':':
240
0
        parser->toksuper = parser->toknext - 1;
241
0
        break;
242
0
      case ',':
243
0
        if (tokens != NULL &&
244
0
            tokens[parser->toksuper].type != JSMN_ARRAY &&
245
0
            tokens[parser->toksuper].type != JSMN_OBJECT) {
246
0
#ifdef JSMN_PARENT_LINKS
247
0
          parser->toksuper = tokens[parser->toksuper].parent;
248
#else
249
          for (i = parser->toknext - 1; i >= 0; i--) {
250
            if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
251
              if (tokens[i].start != -1 && tokens[i].end == -1) {
252
                parser->toksuper = i;
253
                break;
254
              }
255
            }
256
          }
257
#endif
258
0
        }
259
0
        break;
260
#ifdef JSMN_STRICT
261
      /* In strict mode primitives are: numbers and booleans */
262
      case '-': case '0': case '1' : case '2': case '3' : case '4':
263
      case '5': case '6': case '7' : case '8': case '9':
264
      case 't': case 'f': case 'n' :
265
        /* And they must not be keys of the object */
266
        if (tokens != NULL) {
267
          jsmntok_t *t = &tokens[parser->toksuper];
268
          if (t->type == JSMN_OBJECT ||
269
              (t->type == JSMN_STRING && t->size != 0)) {
270
            return JSMN_ERROR_INVAL;
271
          }
272
        }
273
#else
274
      /* In non-strict mode every unquoted value is a primitive */
275
0
      default:
276
0
#endif
277
0
        r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
278
0
        if (r < 0) return r;
279
0
        count++;
280
0
        if (parser->toksuper != -1 && tokens != NULL)
281
0
          tokens[parser->toksuper].size++;
282
0
        break;
283
284
#ifdef JSMN_STRICT
285
      /* Unexpected char in strict mode */
286
      default:
287
        return JSMN_ERROR_INVAL;
288
#endif
289
0
    }
290
0
  }
291
0
  if (tokens != NULL) {
292
0
    for (i = parser->toknext - 1; i >= 0; i--) {
293
      /* Unmatched opened object or array */
294
0
      if (tokens[i].start != -1 && tokens[i].end == -1) {
295
0
        return JSMN_ERROR_PART;
296
0
      }
297
0
    }
298
0
  }
299
300
0
  return count;
301
0
}
302
303
/**
304
 * Creates a new parser based over a given  buffer with an array of tokens
305
 * available.
306
 */
307
0
void jsmn_init(jsmn_parser *parser) {
308
0
  parser->pos = 0;
309
0
  parser->toknext = 0;
310
0
  parser->toksuper = -1;
311
0
}
312