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