Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2014-2019 Damien P. George
7 : : *
8 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : * of this software and associated documentation files (the "Software"), to deal
10 : : * in the Software without restriction, including without limitation the rights
11 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : * copies of the Software, and to permit persons to whom the Software is
13 : : * furnished to do so, subject to the following conditions:
14 : : *
15 : : * The above copyright notice and this permission notice shall be included in
16 : : * all copies or substantial portions of the Software.
17 : : *
18 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 : : * THE SOFTWARE.
25 : : */
26 : :
27 : : #include <stdio.h>
28 : :
29 : : #include "py/objlist.h"
30 : : #include "py/objstringio.h"
31 : : #include "py/parsenum.h"
32 : : #include "py/runtime.h"
33 : : #include "py/stream.h"
34 : :
35 : : #if MICROPY_PY_JSON
36 : :
37 : : #if MICROPY_PY_JSON_SEPARATORS
38 : :
39 : : enum {
40 : : DUMP_MODE_TO_STRING = 1,
41 : : DUMP_MODE_TO_STREAM = 2,
42 : : };
43 : :
44 : 470 : static mp_obj_t mod_json_dump_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, unsigned int mode) {
45 : 470 : enum { ARG_separators };
46 : 470 : static const mp_arg_t allowed_args[] = {
47 : : { MP_QSTR_separators, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
48 : : };
49 : :
50 : 470 : mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
51 : 470 : mp_arg_parse_all(n_args - mode, pos_args + mode, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
52 : :
53 : 470 : mp_print_ext_t print_ext;
54 : :
55 [ + + ]: 470 : if (args[ARG_separators].u_obj == mp_const_none) {
56 : 118 : print_ext.item_separator = ", ";
57 : 118 : print_ext.key_separator = ": ";
58 : : } else {
59 : 352 : mp_obj_t *items;
60 : 352 : mp_obj_get_array_fixed_n(args[ARG_separators].u_obj, 2, &items);
61 : 324 : print_ext.item_separator = mp_obj_str_get_str(items[0]);
62 : 324 : print_ext.key_separator = mp_obj_str_get_str(items[1]);
63 : : }
64 : :
65 [ + + ]: 442 : if (mode == DUMP_MODE_TO_STRING) {
66 : : // dumps(obj)
67 : 376 : vstr_t vstr;
68 : 376 : vstr_init_print(&vstr, 8, &print_ext.base);
69 : 376 : mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON);
70 : 376 : return mp_obj_new_str_from_utf8_vstr(&vstr);
71 : : } else {
72 : : // dump(obj, stream)
73 : 66 : print_ext.base.data = MP_OBJ_TO_PTR(pos_args[1]);
74 : 66 : print_ext.base.print_strn = mp_stream_write_adaptor;
75 : 66 : mp_get_stream_raise(pos_args[1], MP_STREAM_OP_WRITE);
76 : 34 : mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON);
77 : 34 : return mp_const_none;
78 : : }
79 : : }
80 : :
81 : 80 : static mp_obj_t mod_json_dump(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
82 : 80 : return mod_json_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STREAM);
83 : : }
84 : : static MP_DEFINE_CONST_FUN_OBJ_KW(mod_json_dump_obj, 2, mod_json_dump);
85 : :
86 : 390 : static mp_obj_t mod_json_dumps(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
87 : 390 : return mod_json_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STRING);
88 : : }
89 : : static MP_DEFINE_CONST_FUN_OBJ_KW(mod_json_dumps_obj, 1, mod_json_dumps);
90 : :
91 : : #else
92 : :
93 : : static mp_obj_t mod_json_dump(mp_obj_t obj, mp_obj_t stream) {
94 : : mp_get_stream_raise(stream, MP_STREAM_OP_WRITE);
95 : : mp_print_t print = {MP_OBJ_TO_PTR(stream), mp_stream_write_adaptor};
96 : : mp_obj_print_helper(&print, obj, PRINT_JSON);
97 : : return mp_const_none;
98 : : }
99 : : static MP_DEFINE_CONST_FUN_OBJ_2(mod_json_dump_obj, mod_json_dump);
100 : :
101 : : static mp_obj_t mod_json_dumps(mp_obj_t obj) {
102 : : vstr_t vstr;
103 : : mp_print_t print;
104 : : vstr_init_print(&vstr, 8, &print);
105 : : mp_obj_print_helper(&print, obj, PRINT_JSON);
106 : : return mp_obj_new_str_from_utf8_vstr(&vstr);
107 : : }
108 : : static MP_DEFINE_CONST_FUN_OBJ_1(mod_json_dumps_obj, mod_json_dumps);
109 : :
110 : : #endif
111 : :
112 : : // The function below implements a simple non-recursive JSON parser.
113 : : //
114 : : // The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt
115 : : // The parser here will parse any valid JSON and return the correct
116 : : // corresponding Python object. It allows through a superset of JSON, since
117 : : // it treats commas and colons as "whitespace", and doesn't care if
118 : : // brackets/braces are correctly paired. It will raise a ValueError if the
119 : : // input is outside it's specs.
120 : : //
121 : : // Most of the work is parsing the primitives (null, false, true, numbers,
122 : : // strings). It does 1 pass over the input stream. It tries to be fast and
123 : : // small in code size, while not using more RAM than necessary.
124 : :
125 : : typedef struct _json_stream_t {
126 : : mp_obj_t stream_obj;
127 : : mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
128 : : int errcode;
129 : : byte cur;
130 : : } json_stream_t;
131 : :
132 : : #define S_EOF (0) // null is not allowed in json stream so is ok as EOF marker
133 : : #define S_END(s) ((s).cur == S_EOF)
134 : : #define S_CUR(s) ((s).cur)
135 : : #define S_NEXT(s) (json_stream_next(&(s)))
136 : :
137 : 790 : static byte json_stream_next(json_stream_t *s) {
138 : 790 : mp_uint_t ret = s->read(s->stream_obj, &s->cur, 1, &s->errcode);
139 [ - + ]: 790 : if (s->errcode != 0) {
140 : 0 : mp_raise_OSError(s->errcode);
141 : : }
142 [ + + ]: 790 : if (ret == 0) {
143 : 74 : s->cur = S_EOF;
144 : : }
145 : 790 : return s->cur;
146 : : }
147 : :
148 : 78 : static mp_obj_t mod_json_load(mp_obj_t stream_obj) {
149 : 78 : const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ);
150 : 78 : json_stream_t s = {stream_obj, stream_p->read, 0, 0};
151 : 78 : vstr_t vstr;
152 : 78 : vstr_init(&vstr, 8);
153 : 78 : mp_obj_list_t stack; // we use a list as a simple stack for nested JSON
154 : 78 : stack.len = 0;
155 : 78 : stack.items = NULL;
156 : 78 : mp_obj_t stack_top = MP_OBJ_NULL;
157 : 78 : const mp_obj_type_t *stack_top_type = NULL;
158 : 78 : mp_obj_t stack_key = MP_OBJ_NULL;
159 : 78 : S_NEXT(s);
160 : 324 : for (;;) {
161 : 246 : cont:
162 [ + + ]: 324 : if (S_END(s)) {
163 : : break;
164 : : }
165 : 322 : mp_obj_t next = MP_OBJ_NULL;
166 : 322 : bool enter = false;
167 : 322 : byte cur = S_CUR(s);
168 : 322 : S_NEXT(s);
169 [ + + + + : 322 : switch (cur) {
+ + + + +
+ ]
170 : 118 : case ',':
171 : : case ':':
172 : : case ' ':
173 : : case '\t':
174 : : case '\n':
175 : : case '\r':
176 : 118 : goto cont;
177 : 16 : case 'n':
178 [ + - + - : 16 : if (S_CUR(s) == 'u' && S_NEXT(s) == 'l' && S_NEXT(s) == 'l') {
+ - ]
179 : 16 : S_NEXT(s);
180 : 16 : next = mp_const_none;
181 : : } else {
182 : 0 : goto fail;
183 : : }
184 : 16 : break;
185 : 10 : case 'f':
186 [ + - + - : 10 : if (S_CUR(s) == 'a' && S_NEXT(s) == 'l' && S_NEXT(s) == 's' && S_NEXT(s) == 'e') {
+ - + - ]
187 : 10 : S_NEXT(s);
188 : 10 : next = mp_const_false;
189 : : } else {
190 : 0 : goto fail;
191 : : }
192 : 10 : break;
193 : 14 : case 't':
194 [ + - + - : 14 : if (S_CUR(s) == 'r' && S_NEXT(s) == 'u' && S_NEXT(s) == 'e') {
+ - ]
195 : 14 : S_NEXT(s);
196 : 14 : next = mp_const_true;
197 : : } else {
198 : 0 : goto fail;
199 : : }
200 : 14 : break;
201 : : case '"':
202 : 44 : vstr_reset(&vstr);
203 [ + + ]: 174 : for (; !S_END(s) && S_CUR(s) != '"';) {
204 : 130 : byte c = S_CUR(s);
205 [ + + ]: 130 : if (c == '\\') {
206 : 16 : c = S_NEXT(s);
207 [ + + + + : 16 : switch (c) {
+ + - ]
208 : 2 : case 'b':
209 : 2 : c = 0x08;
210 : 2 : break;
211 : 2 : case 'f':
212 : 2 : c = 0x0c;
213 : 2 : break;
214 : 2 : case 'n':
215 : 2 : c = 0x0a;
216 : 2 : break;
217 : 2 : case 'r':
218 : 2 : c = 0x0d;
219 : 2 : break;
220 : 2 : case 't':
221 : 2 : c = 0x09;
222 : 2 : break;
223 : : case 'u': {
224 : : mp_uint_t num = 0;
225 [ + + ]: 30 : for (int i = 0; i < 4; i++) {
226 : 24 : c = (S_NEXT(s) | 0x20) - '0';
227 [ + + ]: 24 : if (c > 9) {
228 : 8 : c -= ('a' - ('9' + 1));
229 : : }
230 : 24 : num = (num << 4) | c;
231 : : }
232 : 6 : vstr_add_char(&vstr, num);
233 : 6 : goto str_cont;
234 : : }
235 : : }
236 : : }
237 : 124 : vstr_add_byte(&vstr, c);
238 : 130 : str_cont:
239 : 130 : S_NEXT(s);
240 : : }
241 [ + + ]: 44 : if (S_END(s)) {
242 : 2 : goto fail;
243 : : }
244 : 42 : S_NEXT(s);
245 : 42 : next = mp_obj_new_str(vstr.buf, vstr.len);
246 : 42 : break;
247 : 32 : case '-':
248 : : case '0':
249 : : case '1':
250 : : case '2':
251 : : case '3':
252 : : case '4':
253 : : case '5':
254 : : case '6':
255 : : case '7':
256 : : case '8':
257 : : case '9': {
258 : 32 : bool flt = false;
259 : 32 : vstr_reset(&vstr);
260 : 112 : for (;;) {
261 : 72 : vstr_add_byte(&vstr, cur);
262 : 72 : cur = S_CUR(s);
263 [ + + + ]: 72 : if (cur == '.' || cur == 'E' || cur == 'e') {
264 : : flt = true;
265 [ + + ]: 56 : } else if (cur == '+' || cur == '-' || unichar_isdigit(cur)) {
266 : : // pass
267 : 40 : } else {
268 : : break;
269 : : }
270 : 40 : S_NEXT(s);
271 : : }
272 [ + + ]: 32 : if (flt) {
273 : 12 : next = mp_parse_num_float(vstr.buf, vstr.len, false, NULL);
274 : : } else {
275 : 20 : next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
276 : : }
277 : : break;
278 : : }
279 : 24 : case '[':
280 : 24 : next = mp_obj_new_list(0, NULL);
281 : 24 : enter = true;
282 : 24 : break;
283 : 20 : case '{':
284 : 20 : next = mp_obj_new_dict(0);
285 : 20 : enter = true;
286 : 20 : break;
287 : 42 : case '}':
288 : : case ']': {
289 [ + + ]: 42 : if (stack_top == MP_OBJ_NULL) {
290 : : // no object at all
291 : 2 : goto fail;
292 : : }
293 [ + + ]: 40 : if (stack.len == 0) {
294 : : // finished; compound object
295 : 28 : goto success;
296 : : }
297 : 12 : stack.len -= 1;
298 : 12 : stack_top = stack.items[stack.len];
299 : 12 : stack_top_type = mp_obj_get_type(stack_top);
300 : 12 : goto cont;
301 : : }
302 : 2 : default:
303 : 2 : goto fail;
304 : : }
305 [ + + ]: 158 : if (stack_top == MP_OBJ_NULL) {
306 : 70 : stack_top = next;
307 : 70 : stack_top_type = mp_obj_get_type(stack_top);
308 [ + + ]: 70 : if (!enter) {
309 : : // finished; single primitive only
310 : 40 : goto success;
311 : : }
312 : : } else {
313 : : // append to list or dict
314 [ + + ]: 88 : if (stack_top_type == &mp_type_list) {
315 : 34 : mp_obj_list_append(stack_top, next);
316 : : } else {
317 [ + + ]: 54 : if (stack_key == MP_OBJ_NULL) {
318 : 28 : stack_key = next;
319 [ + + ]: 28 : if (enter) {
320 : 2 : goto fail;
321 : : }
322 : : } else {
323 : 26 : mp_obj_dict_store(stack_top, stack_key, next);
324 : 26 : stack_key = MP_OBJ_NULL;
325 : : }
326 : : }
327 [ + + ]: 86 : if (enter) {
328 [ + + ]: 12 : if (stack.items == NULL) {
329 : 4 : mp_obj_list_init(&stack, 1);
330 : 4 : stack.items[0] = stack_top;
331 : : } else {
332 : 8 : mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top);
333 : : }
334 : 12 : stack_top = next;
335 : 12 : stack_top_type = mp_obj_get_type(stack_top);
336 : : }
337 : : }
338 : : }
339 : 70 : success:
340 : : // eat trailing whitespace
341 [ + + ]: 78 : while (unichar_isspace(S_CUR(s))) {
342 : 8 : S_NEXT(s);
343 : : }
344 [ + + ]: 70 : if (!S_END(s)) {
345 : : // unexpected chars
346 : 2 : goto fail;
347 : : }
348 [ + + - + ]: 68 : if (stack_top == MP_OBJ_NULL || stack.len != 0) {
349 : : // not exactly 1 object
350 : 2 : goto fail;
351 : : }
352 : 66 : vstr_clear(&vstr);
353 : 66 : return stack_top;
354 : :
355 : 12 : fail:
356 : 12 : mp_raise_ValueError(MP_ERROR_TEXT("syntax error in JSON"));
357 : : }
358 : : static MP_DEFINE_CONST_FUN_OBJ_1(mod_json_load_obj, mod_json_load);
359 : :
360 : 70 : static mp_obj_t mod_json_loads(mp_obj_t obj) {
361 : 70 : mp_buffer_info_t bufinfo;
362 : 70 : mp_get_buffer_raise(obj, &bufinfo, MP_BUFFER_READ);
363 : 70 : vstr_t vstr = {bufinfo.len, bufinfo.len, (char *)bufinfo.buf, true};
364 : 70 : mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0, MP_OBJ_NULL};
365 : 70 : return mod_json_load(MP_OBJ_FROM_PTR(&sio));
366 : : }
367 : : static MP_DEFINE_CONST_FUN_OBJ_1(mod_json_loads_obj, mod_json_loads);
368 : :
369 : : static const mp_rom_map_elem_t mp_module_json_globals_table[] = {
370 : : { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_json) },
371 : : { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_json_dump_obj) },
372 : : { MP_ROM_QSTR(MP_QSTR_dumps), MP_ROM_PTR(&mod_json_dumps_obj) },
373 : : { MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&mod_json_load_obj) },
374 : : { MP_ROM_QSTR(MP_QSTR_loads), MP_ROM_PTR(&mod_json_loads_obj) },
375 : : };
376 : :
377 : : static MP_DEFINE_CONST_DICT(mp_module_json_globals, mp_module_json_globals_table);
378 : :
379 : : const mp_obj_module_t mp_module_json = {
380 : : .base = { &mp_type_module },
381 : : .globals = (mp_obj_dict_t *)&mp_module_json_globals,
382 : : };
383 : :
384 : : MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_json, mp_module_json);
385 : :
386 : : #endif // MICROPY_PY_JSON
|