Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/include/jsmn.h
2065 views
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 */
472
473