blob: a4dc8cfc9151b1b2d2c41ef9d6f533dff6b53ac9 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
Alexander Steffen55f33242017-06-30 09:22:17 +02002 * This file is part of the MicroPython project, http://micropython.org/
Damien George04b91472014-05-03 23:27:38 +01003 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
Paul Sokolovsky8fea8332019-01-31 11:55:21 +03007 * Copyright (c) 2014-2017 Paul Sokolovsky
Damien George04b91472014-05-03 23:27:38 +01008 *
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
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030028#include <stdio.h>
29#include <string.h>
30
Damien George51dfcb42015-01-01 20:27:54 +000031#include "py/objstr.h"
Damien Georgee93c1ca2016-10-13 11:43:28 +110032#include "py/objstringio.h"
Damien George51dfcb42015-01-01 20:27:54 +000033#include "py/runtime.h"
34#include "py/stream.h"
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030035
Damien Georgeee3fd462014-05-24 23:03:12 +010036#if MICROPY_PY_IO
Paul Sokolovsky100cd362014-04-26 20:59:39 +030037
stijnbf195412015-01-16 13:36:18 +010038#if MICROPY_CPYTHON_COMPAT
Angus Grattondecf8e62024-02-27 15:32:29 +110039static void check_stringio_is_open(const mp_obj_stringio_t *o) {
stijnbf195412015-01-16 13:36:18 +010040 if (o->vstr == NULL) {
Jim Mussareddef76fe2020-03-02 22:35:22 +110041 mp_raise_ValueError(MP_ERROR_TEXT("I/O operation on closed file"));
stijnbf195412015-01-16 13:36:18 +010042 }
43}
44#else
45#define check_stringio_is_open(o)
46#endif
47
Angus Grattondecf8e62024-02-27 15:32:29 +110048static void stringio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
Damien Georgeff8dd3f2015-01-20 12:47:20 +000049 (void)kind;
Damien George999cedb2015-11-27 17:01:44 +000050 mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
Damien George7f9d1d62015-04-09 23:56:15 +010051 mp_printf(print, self->base.type == &mp_type_stringio ? "<io.StringIO 0x%x>" : "<io.BytesIO 0x%x>", self);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030052}
53
Angus Grattondecf8e62024-02-27 15:32:29 +110054static mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
Damien Georgeff8dd3f2015-01-20 12:47:20 +000055 (void)errcode;
Damien George999cedb2015-11-27 17:01:44 +000056 mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
stijnbf195412015-01-16 13:36:18 +010057 check_stringio_is_open(o);
Tom Collins53461de2017-05-12 13:30:12 -070058 if (o->vstr->len <= o->pos) { // read to EOF, or seeked to EOF or beyond
59 return 0;
60 }
Damien George40f3c022014-07-03 13:25:24 +010061 mp_uint_t remaining = o->vstr->len - o->pos;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030062 if (size > remaining) {
63 size = remaining;
64 }
65 memcpy(buf, o->vstr->buf + o->pos, size);
66 o->pos += size;
67 return size;
68}
69
Angus Grattondecf8e62024-02-27 15:32:29 +110070static void stringio_copy_on_write(mp_obj_stringio_t *o) {
Paul Sokolovsky07241cd2017-06-05 23:54:21 +030071 const void *buf = o->vstr->buf;
72 o->vstr->buf = m_new(char, o->vstr->len);
Paul Sokolovsky07241cd2017-06-05 23:54:21 +030073 o->vstr->fixed_buf = false;
74 o->ref_obj = MP_OBJ_NULL;
Yonatan Goldschmidt4318a6d2019-11-25 17:21:54 +020075 memcpy(o->vstr->buf, buf, o->vstr->len);
Paul Sokolovsky07241cd2017-06-05 23:54:21 +030076}
77
Angus Grattondecf8e62024-02-27 15:32:29 +110078static mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
Damien Georgeff8dd3f2015-01-20 12:47:20 +000079 (void)errcode;
Damien George999cedb2015-11-27 17:01:44 +000080 mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
stijnbf195412015-01-16 13:36:18 +010081 check_stringio_is_open(o);
Paul Sokolovsky07241cd2017-06-05 23:54:21 +030082
83 if (o->vstr->fixed_buf) {
84 stringio_copy_on_write(o);
85 }
86
Tom Collinse26fb3a2017-05-25 13:41:59 -070087 mp_uint_t new_pos = o->pos + size;
88 if (new_pos < size) {
89 // Writing <size> bytes will overflow o->pos beyond limit of mp_uint_t.
90 *errcode = MP_EFBIG;
91 return MP_STREAM_ERROR;
92 }
Paul Sokolovsky3990b172016-07-28 01:53:44 +030093 mp_uint_t org_len = o->vstr->len;
Tom Collinse26fb3a2017-05-25 13:41:59 -070094 if (new_pos > o->vstr->alloc) {
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030095 // Take all what's already allocated...
96 o->vstr->len = o->vstr->alloc;
97 // ... and add more
Tom Collinse26fb3a2017-05-25 13:41:59 -070098 vstr_add_len(o->vstr, new_pos - o->vstr->alloc);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030099 }
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300100 // If there was a seek past EOF, clear the hole
101 if (o->pos > org_len) {
102 memset(o->vstr->buf + org_len, 0, o->pos - org_len);
103 }
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300104 memcpy(o->vstr->buf + o->pos, buf, size);
Tom Collinse26fb3a2017-05-25 13:41:59 -0700105 o->pos = new_pos;
106 if (new_pos > o->vstr->len) {
107 o->vstr->len = new_pos;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300108 }
109 return size;
110}
111
Angus Grattondecf8e62024-02-27 15:32:29 +1100112static mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300113 (void)errcode;
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300114 mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300115 switch (request) {
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300116 case MP_STREAM_SEEK: {
Damien George69661f32020-02-27 15:36:53 +1100117 struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg;
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300118 mp_uint_t ref = 0;
119 switch (s->whence) {
Paul Sokolovsky0cd9ab72017-08-20 21:32:17 +0300120 case MP_SEEK_CUR:
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300121 ref = o->pos;
122 break;
Paul Sokolovsky0cd9ab72017-08-20 21:32:17 +0300123 case MP_SEEK_END:
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300124 ref = o->vstr->len;
125 break;
126 }
Tom Collins168350c2017-05-25 13:53:49 -0700127 mp_uint_t new_pos = ref + s->offset;
Paul Sokolovsky0cd9ab72017-08-20 21:32:17 +0300128
129 // For MP_SEEK_SET, offset is unsigned
130 if (s->whence != MP_SEEK_SET && s->offset < 0) {
Tom Collins168350c2017-05-25 13:53:49 -0700131 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 new_pos = 0;
135 }
136 } else if (new_pos < ref) {
137 // positive offset went beyond the limit of mp_uint_t
138 *errcode = MP_EINVAL; // replace with MP_EOVERFLOW when defined
139 return MP_STREAM_ERROR;
140 }
141 s->offset = o->pos = new_pos;
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300142 return 0;
143 }
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300144 case MP_STREAM_FLUSH:
145 return 0;
Damien Georgecf31d382018-03-07 17:48:53 +1100146 case MP_STREAM_CLOSE:
147 #if MICROPY_CPYTHON_COMPAT
148 vstr_free(o->vstr);
149 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 return 0;
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300157 default:
158 *errcode = MP_EINVAL;
159 return MP_STREAM_ERROR;
160 }
161}
162
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300163#define STREAM_TO_CONTENT_TYPE(o) (((o)->base.type == &mp_type_stringio) ? &mp_type_str : &mp_type_bytes)
164
Angus Grattondecf8e62024-02-27 15:32:29 +1100165static mp_obj_t stringio_getvalue(mp_obj_t self_in) {
Damien George999cedb2015-11-27 17:01:44 +0000166 mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
stijnbf195412015-01-16 13:36:18 +0100167 check_stringio_is_open(self);
Paul Sokolovsky2ae66972016-05-13 01:35:52 +0300168 // TODO: Try to avoid copying string
Damien George69661f32020-02-27 15:36:53 +1100169 return mp_obj_new_str_of_type(STREAM_TO_CONTENT_TYPE(self), (byte *)self->vstr->buf, self->vstr->len);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300170}
Angus Grattondecf8e62024-02-27 15:32:29 +1100171static MP_DEFINE_CONST_FUN_OBJ_1(stringio_getvalue_obj, stringio_getvalue);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300172
Angus Grattondecf8e62024-02-27 15:32:29 +1100173static mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) {
Jim Mussared0e7bfc82022-04-22 17:09:15 +1000174 mp_obj_stringio_t *o = mp_obj_malloc(mp_obj_stringio_t, type);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300175 o->pos = 0;
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300176 o->ref_obj = MP_OBJ_NULL;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300177 return o;
178}
179
Angus Grattondecf8e62024-02-27 15:32:29 +1100180static 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) {
Damien Georgeff8dd3f2015-01-20 12:47:20 +0000181 (void)n_kw; // TODO check n_kw==0
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300182
183 mp_uint_t sz = 16;
184 bool initdata = false;
185 mp_buffer_info_t bufinfo;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300186
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300187 mp_obj_stringio_t *o = stringio_new(type_in);
188
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300189 if (n_args > 0) {
Damien Georgeeee1e882019-01-30 18:49:52 +1100190 if (mp_obj_is_int(args[0])) {
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300191 sz = mp_obj_get_int(args[0]);
192 } else {
193 mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300194
Damien Georgeeee1e882019-01-30 18:49:52 +1100195 if (mp_obj_is_str_or_bytes(args[0])) {
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300196 o->vstr = m_new_obj(vstr_t);
197 vstr_init_fixed_buf(o->vstr, bufinfo.len, bufinfo.buf);
198 o->vstr->len = bufinfo.len;
199 o->ref_obj = args[0];
200 return MP_OBJ_FROM_PTR(o);
201 }
202
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300203 sz = bufinfo.len;
204 initdata = true;
205 }
206 }
207
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300208 o->vstr = vstr_new(sz);
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300209
210 if (initdata) {
Damien George999cedb2015-11-27 17:01:44 +0000211 stringio_write(MP_OBJ_FROM_PTR(o), bufinfo.buf, bufinfo.len, NULL);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300212 // Cur ptr is always at the beginning of buffer at the construction
213 o->pos = 0;
214 }
Damien George999cedb2015-11-27 17:01:44 +0000215 return MP_OBJ_FROM_PTR(o);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300216}
217
Angus Grattondecf8e62024-02-27 15:32:29 +1100218static const mp_rom_map_elem_t stringio_locals_dict_table[] = {
Damien Georgecbf76742015-11-27 13:38:15 +0000219 { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
Paul Sokolovskyd22a04d2016-10-09 11:56:11 +0300220 { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
Damien Georgecbf76742015-11-27 13:38:15 +0000221 { 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) },
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300223 { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
Andrew Leeched937782020-03-10 15:14:35 +1100224 { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300225 { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
Damien Georgecf31d382018-03-07 17:48:53 +1100226 { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
Damien Georgecbf76742015-11-27 13:38:15 +0000227 { 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) },
Jim Mussared198311c2023-06-26 23:53:12 +1000229 { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) },
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300230};
231
Angus Grattondecf8e62024-02-27 15:32:29 +1100232static MP_DEFINE_CONST_DICT(stringio_locals_dict, stringio_locals_dict_table);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300233
Angus Grattondecf8e62024-02-27 15:32:29 +1100234static const mp_stream_p_t stringio_stream_p = {
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300235 .read = stringio_read,
236 .write = stringio_write,
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300237 .ioctl = stringio_ioctl,
Damien Georgeadf0f2a2014-07-27 22:38:58 +0100238 .is_text = true,
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300239};
240
Jim Mussared662b9762021-07-14 14:38:38 +1000241MP_DEFINE_CONST_OBJ_TYPE(
242 mp_type_stringio,
243 MP_QSTR_StringIO,
Jim Mussared6da41b52022-09-16 23:57:38 +1000244 MP_TYPE_FLAG_ITER_IS_STREAM,
Jim Mussared94beeab2022-09-17 00:31:23 +1000245 make_new, stringio_make_new,
Jim Mussared662b9762021-07-14 14:38:38 +1000246 print, stringio_print,
Jim Mussared662b9762021-07-14 14:38:38 +1000247 protocol, &stringio_stream_p,
Jim Mussared9dce8272022-06-24 16:27:46 +1000248 locals_dict, &stringio_locals_dict
Jim Mussared662b9762021-07-14 14:38:38 +1000249 );
Paul Sokolovsky100cd362014-04-26 20:59:39 +0300250
Damien Georgeee3fd462014-05-24 23:03:12 +0100251#if MICROPY_PY_IO_BYTESIO
Angus Grattondecf8e62024-02-27 15:32:29 +1100252static const mp_stream_p_t bytesio_stream_p = {
Paul m. p. P3b3a4742019-07-16 06:00:36 +0200253 .read = stringio_read,
254 .write = stringio_write,
255 .ioctl = stringio_ioctl,
256};
257
Jim Mussared662b9762021-07-14 14:38:38 +1000258MP_DEFINE_CONST_OBJ_TYPE(
259 mp_type_bytesio,
260 MP_QSTR_BytesIO,
Jim Mussared6da41b52022-09-16 23:57:38 +1000261 MP_TYPE_FLAG_ITER_IS_STREAM,
Jim Mussared94beeab2022-09-17 00:31:23 +1000262 make_new, stringio_make_new,
Jim Mussared662b9762021-07-14 14:38:38 +1000263 print, stringio_print,
Jim Mussared662b9762021-07-14 14:38:38 +1000264 protocol, &bytesio_stream_p,
Jim Mussared9dce8272022-06-24 16:27:46 +1000265 locals_dict, &stringio_locals_dict
Jim Mussared662b9762021-07-14 14:38:38 +1000266 );
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300267#endif
268
Paul Sokolovsky100cd362014-04-26 20:59:39 +0300269#endif