/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 | | |