LCOV - code coverage report
Current view: top level - extmod - modjson.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-161-gc73204128.info Lines: 175 179 97.8 %
Date: 2024-12-23 07:29:41 Functions: 6 6 100.0 %
Branches: 77 90 85.6 %

           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

Generated by: LCOV version 1.15-5-g462f71d