Coverage Report

Created: 2026-02-09 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/lib/jsmn/jsmn.h
Line
Count
Source
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,
49
  JSMN_ARRAY = 2,
50
  JSMN_STRING = 3,
51
  JSMN_PRIMITIVE = 4
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
12.5M
                                   const size_t num_tokens) {
108
12.5M
  jsmntok_t *tok;
109
12.5M
  if (parser->toknext >= num_tokens) {
110
41.6k
    return NULL;
111
41.6k
  }
112
12.5M
  tok = &tokens[parser->toknext++];
113
12.5M
  tok->start = tok->end = -1;
114
12.5M
  tok->size = 0;
115
12.5M
#ifdef JSMN_PARENT_LINKS
116
12.5M
  tok->parent = -1;
117
12.5M
#endif
118
12.5M
  return tok;
119
12.5M
}
120
121
/**
122
 * Fills token type and boundaries.
123
 */
124
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
125
7.63M
                            const int start, const int end) {
126
7.63M
  token->type = type;
127
7.63M
  token->start = start;
128
7.63M
  token->end = end;
129
7.63M
  token->size = 0;
130
7.63M
}
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
7.17M
                                const size_t num_tokens) {
138
7.17M
  jsmntok_t *token;
139
7.17M
  int start;
140
141
7.17M
  start = parser->pos;
142
143
569M
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
144
568M
    switch (js[parser->pos]) {
145
#ifndef JSMN_STRICT
146
    /* In strict mode primitive must be followed by "," or "}" or "]" */
147
    case ':':
148
#endif
149
99.1k
    case '\t':
150
540k
    case '\r':
151
2.06M
    case '\n':
152
2.33M
    case ' ':
153
5.69M
    case ',':
154
6.65M
    case ']':
155
6.68M
    case '}':
156
6.68M
      goto found;
157
562M
    default:
158
                   /* to quiet a warning from gcc*/
159
562M
      break;
160
568M
    }
161
562M
    if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
162
3.56k
      parser->pos = start;
163
3.56k
      return JSMN_ERROR_INVAL;
164
3.56k
    }
165
562M
  }
166
490k
#ifdef JSMN_STRICT
167
  /* In strict mode primitive must be followed by a comma/object/array */
168
490k
  parser->pos = start;
169
490k
  return JSMN_ERROR_PART;
170
0
#endif
171
172
6.68M
found:
173
6.68M
  if (tokens == NULL) {
174
144k
    parser->pos--;
175
144k
    return 0;
176
144k
  }
177
6.53M
  token = jsmn_alloc_token(parser, tokens, num_tokens);
178
6.53M
  if (token == NULL) {
179
22.7k
    parser->pos = start;
180
22.7k
    return JSMN_ERROR_NOMEM;
181
22.7k
  }
182
6.51M
  jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
183
6.51M
#ifdef JSMN_PARENT_LINKS
184
6.51M
  token->parent = parser->toksuper;
185
6.51M
#endif
186
6.51M
  parser->pos--;
187
6.51M
  return 0;
188
6.53M
}
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
2.58M
                             const size_t num_tokens) {
196
2.58M
  jsmntok_t *token;
197
198
2.58M
  int start = parser->pos;
199
200
2.58M
  parser->pos++;
201
202
  /* Skip starting quote */
203
9.01G
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
204
9.00G
    char c = js[parser->pos];
205
206
    /* Quote: end of string */
207
9.00G
    if (c == '\"') {
208
1.13M
      if (tokens == NULL) {
209
7.36k
        return 0;
210
7.36k
      }
211
1.12M
      token = jsmn_alloc_token(parser, tokens, num_tokens);
212
1.12M
      if (token == NULL) {
213
3.23k
        parser->pos = start;
214
3.23k
        return JSMN_ERROR_NOMEM;
215
3.23k
      }
216
1.12M
      jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
217
1.12M
#ifdef JSMN_PARENT_LINKS
218
1.12M
      token->parent = parser->toksuper;
219
1.12M
#endif
220
1.12M
      return 0;
221
1.12M
    }
222
223
    /* Backslash: Quoted symbol expected */
224
9.00G
    if (c == '\\' && parser->pos + 1 < len) {
225
55.2M
      int i;
226
55.2M
      parser->pos++;
227
55.2M
      switch (js[parser->pos]) {
228
      /* Allowed escaped symbols */
229
10.0M
      case '\"':
230
11.0M
      case '/':
231
12.2M
      case '\\':
232
18.8M
      case 'b':
233
19.0M
      case 'f':
234
19.6M
      case 'r':
235
19.6M
      case 'n':
236
21.9M
      case 't':
237
21.9M
        break;
238
      /* Allows escaped symbol \uXXXX */
239
33.3M
      case 'u':
240
33.3M
        parser->pos++;
241
166M
        for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
242
133M
             i++) {
243
          /* If it isn't a hex character we have an error */
244
133M
          if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) ||   /* 0-9 */
245
86.6M
                (js[parser->pos] >= 65 && js[parser->pos] <= 70) ||   /* A-F */
246
19.1M
                (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
247
1.31k
            parser->pos = start;
248
1.31k
            return JSMN_ERROR_INVAL;
249
1.31k
          }
250
133M
          parser->pos++;
251
133M
        }
252
33.3M
        parser->pos--;
253
33.3M
        break;
254
      /* Unexpected symbol */
255
1.24k
      default:
256
1.24k
        parser->pos = start;
257
1.24k
        return JSMN_ERROR_INVAL;
258
55.2M
      }
259
55.2M
    }
260
9.00G
  }
261
1.45M
  parser->pos = start;
262
1.45M
  return JSMN_ERROR_PART;
263
2.58M
}
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
4.54M
                        jsmntok_t *tokens, const unsigned int num_tokens) {
270
4.54M
  int r;
271
4.54M
  int i;
272
4.54M
  jsmntok_t *token;
273
4.54M
  int count = parser->toknext;
274
275
39.6M
  for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
276
38.8M
    char c;
277
38.8M
    jsmntype_t type;
278
279
38.8M
    c = js[parser->pos];
280
38.8M
    switch (c) {
281
445k
    case '{':
282
4.89M
    case '[':
283
4.89M
      count++;
284
4.89M
      if (tokens == NULL) {
285
4.93k
        break;
286
4.93k
      }
287
4.89M
      token = jsmn_alloc_token(parser, tokens, num_tokens);
288
4.89M
      if (token == NULL) {
289
15.6k
        return JSMN_ERROR_NOMEM;
290
15.6k
      }
291
4.87M
      if (parser->toksuper != -1) {
292
4.51M
        jsmntok_t *t = &tokens[parser->toksuper];
293
4.51M
#ifdef JSMN_STRICT
294
        /* In strict mode an object or array can't become a key */
295
4.51M
        if (t->type == JSMN_OBJECT) {
296
242k
          return JSMN_ERROR_INVAL;
297
242k
        }
298
4.27M
#endif
299
4.27M
        t->size++;
300
4.27M
#ifdef JSMN_PARENT_LINKS
301
4.27M
        token->parent = parser->toksuper;
302
4.27M
#endif
303
4.27M
      }
304
4.63M
      token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
305
4.63M
      token->start = parser->pos;
306
4.63M
      parser->toksuper = parser->toknext - 1;
307
4.63M
      break;
308
202k
    case '}':
309
1.77M
    case ']':
310
1.77M
      if (tokens == NULL) {
311
4.80k
        break;
312
4.80k
      }
313
1.77M
      type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
314
1.77M
#ifdef JSMN_PARENT_LINKS
315
1.77M
      if (parser->toknext < 1) {
316
377k
        return JSMN_ERROR_INVAL;
317
377k
      }
318
1.39M
      token = &tokens[parser->toknext - 1];
319
5.05M
      for (;;) {
320
5.05M
        if (token->start != -1 && token->end == -1) {
321
667k
          if (token->type != type) {
322
486
            return JSMN_ERROR_INVAL;
323
486
          }
324
666k
          token->end = parser->pos + 1;
325
666k
          parser->toksuper = token->parent;
326
666k
          break;
327
667k
        }
328
4.38M
        if (token->parent == -1) {
329
729k
          if (token->type != type || parser->toksuper == -1) {
330
615k
            return JSMN_ERROR_INVAL;
331
615k
          }
332
113k
          break;
333
729k
        }
334
3.65M
        token = &tokens[token->parent];
335
3.65M
      }
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
780k
      break;
361
2.58M
    case '\"':
362
2.58M
      r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
363
2.58M
      if (r < 0) {
364
1.45M
        return r;
365
1.45M
      }
366
1.12M
      count++;
367
1.12M
      if (parser->toksuper != -1 && tokens != NULL) {
368
1.10M
        tokens[parser->toksuper].size++;
369
1.10M
      }
370
1.12M
      break;
371
110k
    case '\t':
372
559k
    case '\r':
373
2.10M
    case '\n':
374
16.4M
    case ' ':
375
16.4M
      break;
376
424k
    case ':':
377
424k
      parser->toksuper = parser->toknext - 1;
378
424k
      break;
379
5.07M
    case ',':
380
5.07M
      if (tokens != NULL && parser->toksuper != -1 &&
381
3.37M
          tokens[parser->toksuper].type != JSMN_ARRAY &&
382
365k
          tokens[parser->toksuper].type != JSMN_OBJECT) {
383
351k
#ifdef JSMN_PARENT_LINKS
384
351k
        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
351k
      }
396
5.07M
      break;
397
0
#ifdef JSMN_STRICT
398
    /* In strict mode primitives are: numbers and booleans */
399
1.59M
    case '-':
400
2.30M
    case '0':
401
2.77M
    case '1':
402
3.96M
    case '2':
403
4.55M
    case '3':
404
4.92M
    case '4':
405
5.07M
    case '5':
406
5.71M
    case '6':
407
5.81M
    case '7':
408
6.36M
    case '8':
409
7.00M
    case '9':
410
7.02M
    case 't':
411
7.12M
    case 'f':
412
7.17M
    case 'n':
413
      /* And they must not be keys of the object */
414
7.17M
      if (tokens != NULL && parser->toksuper != -1) {
415
5.90M
        const jsmntok_t *t = &tokens[parser->toksuper];
416
5.90M
        if (t->type == JSMN_OBJECT ||
417
5.90M
            (t->type == JSMN_STRING && t->size != 0)) {
418
673
          return JSMN_ERROR_INVAL;
419
673
        }
420
5.90M
      }
421
#else
422
    /* In non-strict mode every unquoted value is a primitive */
423
    default:
424
#endif
425
7.17M
      r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
426
7.17M
      if (r < 0) {
427
516k
        return r;
428
516k
      }
429
6.65M
      count++;
430
6.65M
      if (parser->toksuper != -1 && tokens != NULL) {
431
5.40M
        tokens[parser->toksuper].size++;
432
5.40M
      }
433
6.65M
      break;
434
435
0
#ifdef JSMN_STRICT
436
    /* Unexpected char in strict mode */
437
473k
    default:
438
473k
      return JSMN_ERROR_INVAL;
439
38.8M
#endif
440
38.8M
    }
441
38.8M
  }
442
443
844k
  if (tokens != NULL) {
444
7.86G
    for (i = parser->toknext - 1; i >= 0; i--) {
445
      /* Unmatched opened object or array */
446
7.86G
      if (tokens[i].start != -1 && tokens[i].end == -1) {
447
806k
        return JSMN_ERROR_PART;
448
806k
      }
449
7.86G
    }
450
844k
  }
451
452
37.5k
  return count;
453
844k
}
454
455
/**
456
 * Creates a new parser based over a given buffer with an array of tokens
457
 * available.
458
 */
459
1.76M
JSMN_API void jsmn_init(jsmn_parser *parser) {
460
1.76M
  parser->pos = 0;
461
1.76M
  parser->toknext = 0;
462
1.76M
  parser->toksuper = -1;
463
1.76M
}
464
465
#endif /* JSMN_HEADER */
466
467
#ifdef __cplusplus
468
}
469
#endif
470
471
#endif /* JSMN_H */