LCOV - code coverage report
Current view: top level - py - objstringio.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.23.0-410-g82e69df33.info Lines: 100 108 92.6 %
Date: 2024-10-10 13:49:24 Functions: 9 9 100.0 %
Branches: 45 53 84.9 %

           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) 2013, 2014 Damien P. George
       7                 :            :  * Copyright (c) 2014-2017 Paul Sokolovsky
       8                 :            :  *
       9                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a copy
      10                 :            :  * of this software and associated documentation files (the "Software"), to deal
      11                 :            :  * in the Software without restriction, including without limitation the rights
      12                 :            :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      13                 :            :  * copies of the Software, and to permit persons to whom the Software is
      14                 :            :  * furnished to do so, subject to the following conditions:
      15                 :            :  *
      16                 :            :  * The above copyright notice and this permission notice shall be included in
      17                 :            :  * all copies or substantial portions of the Software.
      18                 :            :  *
      19                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      20                 :            :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      22                 :            :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      24                 :            :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      25                 :            :  * THE SOFTWARE.
      26                 :            :  */
      27                 :            : 
      28                 :            : #include <stdio.h>
      29                 :            : #include <string.h>
      30                 :            : 
      31                 :            : #include "py/objstr.h"
      32                 :            : #include "py/objstringio.h"
      33                 :            : #include "py/runtime.h"
      34                 :            : #include "py/stream.h"
      35                 :            : 
      36                 :            : #if MICROPY_PY_IO
      37                 :            : 
      38                 :            : #if MICROPY_CPYTHON_COMPAT
      39                 :      36626 : static void check_stringio_is_open(const mp_obj_stringio_t *o) {
      40         [ +  + ]:      36626 :     if (o->vstr == NULL) {
      41                 :         12 :         mp_raise_ValueError(MP_ERROR_TEXT("I/O operation on closed file"));
      42                 :            :     }
      43                 :      36614 : }
      44                 :            : #else
      45                 :            : #define check_stringio_is_open(o)
      46                 :            : #endif
      47                 :            : 
      48                 :          4 : static void stringio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
      49                 :          4 :     (void)kind;
      50                 :          4 :     mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
      51         [ -  + ]:          4 :     mp_printf(print, self->base.type == &mp_type_stringio ? "<io.StringIO 0x%x>" : "<io.BytesIO 0x%x>", self);
      52                 :          4 : }
      53                 :            : 
      54                 :      11578 : static mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
      55                 :      11578 :     (void)errcode;
      56                 :      11578 :     mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
      57                 :      11578 :     check_stringio_is_open(o);
      58         [ +  + ]:      11572 :     if (o->vstr->len <= o->pos) {  // read to EOF, or seeked to EOF or beyond
      59                 :            :         return 0;
      60                 :            :     }
      61                 :      11460 :     mp_uint_t remaining = o->vstr->len - o->pos;
      62         [ +  + ]:      11460 :     if (size > remaining) {
      63                 :          6 :         size = remaining;
      64                 :            :     }
      65                 :      11460 :     memcpy(buf, o->vstr->buf + o->pos, size);
      66                 :      11460 :     o->pos += size;
      67                 :      11460 :     return size;
      68                 :            : }
      69                 :            : 
      70                 :         18 : static void stringio_copy_on_write(mp_obj_stringio_t *o) {
      71                 :         18 :     const void *buf = o->vstr->buf;
      72                 :         18 :     o->vstr->buf = m_new(char, o->vstr->len);
      73                 :         18 :     o->vstr->fixed_buf = false;
      74                 :         18 :     o->ref_obj = MP_OBJ_NULL;
      75                 :         18 :     memcpy(o->vstr->buf, buf, o->vstr->len);
      76                 :         18 : }
      77                 :            : 
      78                 :      24832 : static mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
      79                 :      24832 :     (void)errcode;
      80                 :      24832 :     mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
      81                 :      24832 :     check_stringio_is_open(o);
      82                 :            : 
      83         [ +  + ]:      24830 :     if (o->vstr->fixed_buf) {
      84                 :         18 :         stringio_copy_on_write(o);
      85                 :            :     }
      86                 :            : 
      87                 :      24830 :     mp_uint_t new_pos = o->pos + size;
      88         [ -  + ]:      24830 :     if (new_pos < size) {
      89                 :            :         // Writing <size> bytes will overflow o->pos beyond limit of mp_uint_t.
      90                 :          0 :         *errcode = MP_EFBIG;
      91                 :          0 :         return MP_STREAM_ERROR;
      92                 :            :     }
      93                 :      24830 :     mp_uint_t org_len = o->vstr->len;
      94         [ +  + ]:      24830 :     if (new_pos > o->vstr->alloc) {
      95                 :            :         // Take all what's already allocated...
      96                 :       1409 :         o->vstr->len = o->vstr->alloc;
      97                 :            :         // ... and add more
      98                 :       1409 :         vstr_add_len(o->vstr, new_pos - o->vstr->alloc);
      99                 :            :     }
     100                 :            :     // If there was a seek past EOF, clear the hole
     101         [ +  + ]:      24830 :     if (o->pos > org_len) {
     102                 :         12 :         memset(o->vstr->buf + org_len, 0, o->pos - org_len);
     103                 :            :     }
     104                 :      24830 :     memcpy(o->vstr->buf + o->pos, buf, size);
     105                 :      24830 :     o->pos = new_pos;
     106         [ +  + ]:      24830 :     if (new_pos > o->vstr->len) {
     107                 :      23391 :         o->vstr->len = new_pos;
     108                 :            :     }
     109                 :            :     return size;
     110                 :            : }
     111                 :            : 
     112                 :        124 : static mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
     113                 :        124 :     (void)errcode;
     114                 :        124 :     mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
     115   [ +  +  -  + ]:        124 :     switch (request) {
     116                 :         96 :         case MP_STREAM_SEEK: {
     117                 :         96 :             struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg;
     118                 :         96 :             mp_uint_t ref = 0;
     119      [ +  +  + ]:         96 :             switch (s->whence) {
     120                 :         24 :                 case MP_SEEK_CUR:
     121                 :         24 :                     ref = o->pos;
     122                 :         24 :                     break;
     123                 :         10 :                 case MP_SEEK_END:
     124                 :         10 :                     ref = o->vstr->len;
     125                 :         10 :                     break;
     126                 :            :             }
     127                 :         96 :             mp_uint_t new_pos = ref + s->offset;
     128                 :            : 
     129                 :            :             // For MP_SEEK_SET, offset is unsigned
     130   [ +  +  +  + ]:         96 :             if (s->whence != MP_SEEK_SET && s->offset < 0) {
     131         [ -  + ]:          4 :                 if (new_pos > ref) {
     132                 :            :                     // Negative offset from SEEK_CUR or SEEK_END went past 0.
     133                 :            :                     // CPython sets position to 0, POSIX returns an EINVAL error
     134                 :          0 :                     new_pos = 0;
     135                 :            :                 }
     136         [ -  + ]:         92 :             } else if (new_pos < ref) {
     137                 :            :                 // positive offset went beyond the limit of mp_uint_t
     138                 :          0 :                 *errcode = MP_EINVAL;  // replace with MP_EOVERFLOW when defined
     139                 :          0 :                 return MP_STREAM_ERROR;
     140                 :            :             }
     141                 :         96 :             s->offset = o->pos = new_pos;
     142                 :         96 :             return 0;
     143                 :            :         }
     144                 :            :         case MP_STREAM_FLUSH:
     145                 :            :             return 0;
     146                 :         18 :         case MP_STREAM_CLOSE:
     147                 :            :             #if MICROPY_CPYTHON_COMPAT
     148                 :         18 :             vstr_free(o->vstr);
     149                 :         18 :             o->vstr = NULL;
     150                 :            :             #else
     151                 :            :             vstr_clear(o->vstr);
     152                 :            :             o->vstr->alloc = 0;
     153                 :            :             o->vstr->len = 0;
     154                 :            :             o->pos = 0;
     155                 :            :             #endif
     156                 :         18 :             return 0;
     157                 :          0 :         default:
     158                 :          0 :             *errcode = MP_EINVAL;
     159                 :          0 :             return MP_STREAM_ERROR;
     160                 :            :     }
     161                 :            : }
     162                 :            : 
     163                 :            : #define STREAM_TO_CONTENT_TYPE(o) (((o)->base.type == &mp_type_stringio) ? &mp_type_str : &mp_type_bytes)
     164                 :            : 
     165                 :        216 : static mp_obj_t stringio_getvalue(mp_obj_t self_in) {
     166                 :        216 :     mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
     167                 :        216 :     check_stringio_is_open(self);
     168                 :            :     // TODO: Try to avoid copying string
     169         [ +  + ]:        340 :     return mp_obj_new_str_of_type(STREAM_TO_CONTENT_TYPE(self), (byte *)self->vstr->buf, self->vstr->len);
     170                 :            : }
     171                 :            : static MP_DEFINE_CONST_FUN_OBJ_1(stringio_getvalue_obj, stringio_getvalue);
     172                 :            : 
     173                 :        440 : static mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) {
     174                 :        440 :     mp_obj_stringio_t *o = mp_obj_malloc(mp_obj_stringio_t, type);
     175                 :        440 :     o->pos = 0;
     176                 :        440 :     o->ref_obj = MP_OBJ_NULL;
     177                 :        440 :     return o;
     178                 :            : }
     179                 :            : 
     180                 :        440 : static mp_obj_t stringio_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     181                 :        440 :     (void)n_kw; // TODO check n_kw==0
     182                 :            : 
     183                 :        440 :     mp_uint_t sz = 16;
     184                 :        440 :     bool initdata = false;
     185                 :        440 :     mp_buffer_info_t bufinfo;
     186                 :            : 
     187                 :        440 :     mp_obj_stringio_t *o = stringio_new(type_in);
     188                 :            : 
     189         [ +  + ]:        440 :     if (n_args > 0) {
     190   [ +  +  +  +  :        214 :         if (mp_obj_is_int(args[0])) {
                   -  + ]
     191                 :          4 :             sz = mp_obj_get_int(args[0]);
     192                 :            :         } else {
     193                 :        210 :             mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
     194                 :            : 
     195   [ +  +  +  -  :        210 :             if (mp_obj_is_str_or_bytes(args[0])) {
             +  -  +  + ]
     196                 :        206 :                 o->vstr = m_new_obj(vstr_t);
     197                 :        206 :                 vstr_init_fixed_buf(o->vstr, bufinfo.len, bufinfo.buf);
     198                 :        206 :                 o->vstr->len = bufinfo.len;
     199                 :        206 :                 o->ref_obj = args[0];
     200                 :        206 :                 return MP_OBJ_FROM_PTR(o);
     201                 :            :             }
     202                 :            : 
     203                 :          4 :             sz = bufinfo.len;
     204                 :          4 :             initdata = true;
     205                 :            :         }
     206                 :            :     }
     207                 :            : 
     208                 :        234 :     o->vstr = vstr_new(sz);
     209                 :            : 
     210         [ +  + ]:        234 :     if (initdata) {
     211                 :          4 :         stringio_write(MP_OBJ_FROM_PTR(o), bufinfo.buf, bufinfo.len, NULL);
     212                 :            :         // Cur ptr is always at the beginning of buffer at the construction
     213                 :          4 :         o->pos = 0;
     214                 :            :     }
     215                 :            :     return MP_OBJ_FROM_PTR(o);
     216                 :            : }
     217                 :            : 
     218                 :            : static const mp_rom_map_elem_t stringio_locals_dict_table[] = {
     219                 :            :     { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
     220                 :            :     { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
     221                 :            :     { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
     222                 :            :     { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
     223                 :            :     { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
     224                 :            :     { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
     225                 :            :     { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
     226                 :            :     { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
     227                 :            :     { MP_ROM_QSTR(MP_QSTR_getvalue), MP_ROM_PTR(&stringio_getvalue_obj) },
     228                 :            :     { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
     229                 :            :     { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) },
     230                 :            : };
     231                 :            : 
     232                 :            : static MP_DEFINE_CONST_DICT(stringio_locals_dict, stringio_locals_dict_table);
     233                 :            : 
     234                 :            : static const mp_stream_p_t stringio_stream_p = {
     235                 :            :     .read = stringio_read,
     236                 :            :     .write = stringio_write,
     237                 :            :     .ioctl = stringio_ioctl,
     238                 :            :     .is_text = true,
     239                 :            : };
     240                 :            : 
     241                 :            : MP_DEFINE_CONST_OBJ_TYPE(
     242                 :            :     mp_type_stringio,
     243                 :            :     MP_QSTR_StringIO,
     244                 :            :     MP_TYPE_FLAG_ITER_IS_STREAM,
     245                 :            :     make_new, stringio_make_new,
     246                 :            :     print, stringio_print,
     247                 :            :     protocol, &stringio_stream_p,
     248                 :            :     locals_dict, &stringio_locals_dict
     249                 :            :     );
     250                 :            : 
     251                 :            : #if MICROPY_PY_IO_BYTESIO
     252                 :            : static const mp_stream_p_t bytesio_stream_p = {
     253                 :            :     .read = stringio_read,
     254                 :            :     .write = stringio_write,
     255                 :            :     .ioctl = stringio_ioctl,
     256                 :            : };
     257                 :            : 
     258                 :            : MP_DEFINE_CONST_OBJ_TYPE(
     259                 :            :     mp_type_bytesio,
     260                 :            :     MP_QSTR_BytesIO,
     261                 :            :     MP_TYPE_FLAG_ITER_IS_STREAM,
     262                 :            :     make_new, stringio_make_new,
     263                 :            :     print, stringio_print,
     264                 :            :     protocol, &bytesio_stream_p,
     265                 :            :     locals_dict, &stringio_locals_dict
     266                 :            :     );
     267                 :            : #endif
     268                 :            : 
     269                 :            : #endif

Generated by: LCOV version 1.15-5-g462f71d