blob: 645c441cb2d6c98ea2b11713a4bb8ff04d9b3a2f [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
Paul Sokolovskya47b64a2014-05-15 07:28:19 +03007 * Copyright (c) 2014 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/nlr.h"
32#include "py/objstr.h"
Damien Georgee93c1ca2016-10-13 11:43:28 +110033#include "py/objstringio.h"
Damien George51dfcb42015-01-01 20:27:54 +000034#include "py/runtime.h"
35#include "py/stream.h"
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030036
Damien Georgeee3fd462014-05-24 23:03:12 +010037#if MICROPY_PY_IO
Paul Sokolovsky100cd362014-04-26 20:59:39 +030038
stijnbf195412015-01-16 13:36:18 +010039#if MICROPY_CPYTHON_COMPAT
40STATIC void check_stringio_is_open(const mp_obj_stringio_t *o) {
41 if (o->vstr == NULL) {
Damien George94c41bb2017-03-28 22:37:26 +110042 mp_raise_ValueError("I/O operation on closed file");
stijnbf195412015-01-16 13:36:18 +010043 }
44}
45#else
46#define check_stringio_is_open(o)
47#endif
48
Damien George7f9d1d62015-04-09 23:56:15 +010049STATIC 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 +000050 (void)kind;
Damien George999cedb2015-11-27 17:01:44 +000051 mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
Damien George7f9d1d62015-04-09 23:56:15 +010052 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 +030053}
54
Damien Georgeadf0f2a2014-07-27 22:38:58 +010055STATIC 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 +000056 (void)errcode;
Damien George999cedb2015-11-27 17:01:44 +000057 mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
stijnbf195412015-01-16 13:36:18 +010058 check_stringio_is_open(o);
Tom Collins53461de2017-05-12 13:30:12 -070059 if (o->vstr->len <= o->pos) { // read to EOF, or seeked to EOF or beyond
60 return 0;
61 }
Damien George40f3c022014-07-03 13:25:24 +010062 mp_uint_t remaining = o->vstr->len - o->pos;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030063 if (size > remaining) {
64 size = remaining;
65 }
66 memcpy(buf, o->vstr->buf + o->pos, size);
67 o->pos += size;
68 return size;
69}
70
Paul Sokolovsky07241cd2017-06-05 23:54:21 +030071STATIC void stringio_copy_on_write(mp_obj_stringio_t *o) {
72 const void *buf = o->vstr->buf;
73 o->vstr->buf = m_new(char, o->vstr->len);
74 memcpy(o->vstr->buf, buf, o->vstr->len);
75 o->vstr->fixed_buf = false;
76 o->ref_obj = MP_OBJ_NULL;
77}
78
Damien Georgeadf0f2a2014-07-27 22:38:58 +010079STATIC 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 +000080 (void)errcode;
Damien George999cedb2015-11-27 17:01:44 +000081 mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
stijnbf195412015-01-16 13:36:18 +010082 check_stringio_is_open(o);
Paul Sokolovsky07241cd2017-06-05 23:54:21 +030083
84 if (o->vstr->fixed_buf) {
85 stringio_copy_on_write(o);
86 }
87
Tom Collinse26fb3a2017-05-25 13:41:59 -070088 mp_uint_t new_pos = o->pos + size;
89 if (new_pos < size) {
90 // Writing <size> bytes will overflow o->pos beyond limit of mp_uint_t.
91 *errcode = MP_EFBIG;
92 return MP_STREAM_ERROR;
93 }
Paul Sokolovsky3990b172016-07-28 01:53:44 +030094 mp_uint_t org_len = o->vstr->len;
Tom Collinse26fb3a2017-05-25 13:41:59 -070095 if (new_pos > o->vstr->alloc) {
Paul Sokolovskycb9dc082014-04-26 20:26:14 +030096 // Take all what's already allocated...
97 o->vstr->len = o->vstr->alloc;
98 // ... and add more
Tom Collinse26fb3a2017-05-25 13:41:59 -070099 vstr_add_len(o->vstr, new_pos - o->vstr->alloc);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300100 }
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300101 // If there was a seek past EOF, clear the hole
102 if (o->pos > org_len) {
103 memset(o->vstr->buf + org_len, 0, o->pos - org_len);
104 }
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300105 memcpy(o->vstr->buf + o->pos, buf, size);
Tom Collinse26fb3a2017-05-25 13:41:59 -0700106 o->pos = new_pos;
107 if (new_pos > o->vstr->len) {
108 o->vstr->len = new_pos;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300109 }
110 return size;
111}
112
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300113STATIC mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
114 (void)errcode;
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300115 mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300116 switch (request) {
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300117 case MP_STREAM_SEEK: {
118 struct mp_stream_seek_t *s = (struct mp_stream_seek_t*)arg;
119 mp_uint_t ref = 0;
120 switch (s->whence) {
121 case 1: // SEEK_CUR
122 ref = o->pos;
123 break;
124 case 2: // SEEK_END
125 ref = o->vstr->len;
126 break;
127 }
128 o->pos = ref + s->offset;
129 s->offset = o->pos;
130 return 0;
131 }
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300132 case MP_STREAM_FLUSH:
133 return 0;
134 default:
135 *errcode = MP_EINVAL;
136 return MP_STREAM_ERROR;
137 }
138}
139
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300140#define STREAM_TO_CONTENT_TYPE(o) (((o)->base.type == &mp_type_stringio) ? &mp_type_str : &mp_type_bytes)
141
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300142STATIC mp_obj_t stringio_getvalue(mp_obj_t self_in) {
Damien George999cedb2015-11-27 17:01:44 +0000143 mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
stijnbf195412015-01-16 13:36:18 +0100144 check_stringio_is_open(self);
Paul Sokolovsky2ae66972016-05-13 01:35:52 +0300145 // TODO: Try to avoid copying string
Damien Georgef600a6a2014-05-25 22:34:34 +0100146 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 +0300147}
148STATIC MP_DEFINE_CONST_FUN_OBJ_1(stringio_getvalue_obj, stringio_getvalue);
149
150STATIC mp_obj_t stringio_close(mp_obj_t self_in) {
Damien George999cedb2015-11-27 17:01:44 +0000151 mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
stijnbf195412015-01-16 13:36:18 +0100152#if MICROPY_CPYTHON_COMPAT
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300153 vstr_free(self->vstr);
154 self->vstr = NULL;
stijnbf195412015-01-16 13:36:18 +0100155#else
156 vstr_clear(self->vstr);
157 self->vstr->alloc = 0;
158 self->vstr->len = 0;
159 self->pos = 0;
160#endif
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300161 return mp_const_none;
162}
163STATIC MP_DEFINE_CONST_FUN_OBJ_1(stringio_close_obj, stringio_close);
164
Damien George4b72b3a2016-01-03 14:21:40 +0000165STATIC mp_obj_t stringio___exit__(size_t n_args, const mp_obj_t *args) {
Damien Georgeff8dd3f2015-01-20 12:47:20 +0000166 (void)n_args;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300167 return stringio_close(args[0]);
168}
169STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stringio___exit___obj, 4, 4, stringio___exit__);
170
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300171STATIC mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) {
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300172 mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t);
Damien George5b3f0b72016-01-03 15:55:55 +0000173 o->base.type = type;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300174 o->pos = 0;
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300175 o->ref_obj = MP_OBJ_NULL;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300176 return o;
177}
178
Damien George5b3f0b72016-01-03 15:55:55 +0000179STATIC 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 +0000180 (void)n_kw; // TODO check n_kw==0
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300181
182 mp_uint_t sz = 16;
183 bool initdata = false;
184 mp_buffer_info_t bufinfo;
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300185
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300186 mp_obj_stringio_t *o = stringio_new(type_in);
187
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300188 if (n_args > 0) {
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300189 if (MP_OBJ_IS_INT(args[0])) {
190 sz = mp_obj_get_int(args[0]);
191 } else {
192 mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300193
194 if (MP_OBJ_IS_STR_OR_BYTES(args[0])) {
195 o->vstr = m_new_obj(vstr_t);
196 vstr_init_fixed_buf(o->vstr, bufinfo.len, bufinfo.buf);
197 o->vstr->len = bufinfo.len;
198 o->ref_obj = args[0];
199 return MP_OBJ_FROM_PTR(o);
200 }
201
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300202 sz = bufinfo.len;
203 initdata = true;
204 }
205 }
206
Paul Sokolovsky07241cd2017-06-05 23:54:21 +0300207 o->vstr = vstr_new(sz);
Paul Sokolovsky50d3a9d2017-02-02 00:33:43 +0300208
209 if (initdata) {
Damien George999cedb2015-11-27 17:01:44 +0000210 stringio_write(MP_OBJ_FROM_PTR(o), bufinfo.buf, bufinfo.len, NULL);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300211 // Cur ptr is always at the beginning of buffer at the construction
212 o->pos = 0;
213 }
Damien George999cedb2015-11-27 17:01:44 +0000214 return MP_OBJ_FROM_PTR(o);
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300215}
216
Damien Georgecbf76742015-11-27 13:38:15 +0000217STATIC const mp_rom_map_elem_t stringio_locals_dict_table[] = {
218 { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
Paul Sokolovskyd22a04d2016-10-09 11:56:11 +0300219 { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
Damien Georgecbf76742015-11-27 13:38:15 +0000220 { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
221 { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
Paul Sokolovsky3990b172016-07-28 01:53:44 +0300222 { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300223 { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
Damien Georgecbf76742015-11-27 13:38:15 +0000224 { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&stringio_close_obj) },
225 { MP_ROM_QSTR(MP_QSTR_getvalue), MP_ROM_PTR(&stringio_getvalue_obj) },
226 { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
227 { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&stringio___exit___obj) },
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300228};
229
230STATIC MP_DEFINE_CONST_DICT(stringio_locals_dict, stringio_locals_dict_table);
231
232STATIC const mp_stream_p_t stringio_stream_p = {
233 .read = stringio_read,
234 .write = stringio_write,
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300235 .ioctl = stringio_ioctl,
Damien Georgeadf0f2a2014-07-27 22:38:58 +0100236 .is_text = true,
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300237};
238
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300239STATIC const mp_stream_p_t bytesio_stream_p = {
240 .read = stringio_read,
241 .write = stringio_write,
Paul Sokolovskyf039ac52016-07-28 01:14:32 +0300242 .ioctl = stringio_ioctl,
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300243};
244
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300245const mp_obj_type_t mp_type_stringio = {
246 { &mp_type_type },
247 .name = MP_QSTR_StringIO,
248 .print = stringio_print,
249 .make_new = stringio_make_new,
Damien Georgeae8d8672016-01-09 23:14:54 +0000250 .getiter = mp_identity_getiter,
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300251 .iternext = mp_stream_unbuffered_iter,
Paul Sokolovsky07209f82016-06-18 18:19:24 +0300252 .protocol = &stringio_stream_p,
Damien George999cedb2015-11-27 17:01:44 +0000253 .locals_dict = (mp_obj_dict_t*)&stringio_locals_dict,
Paul Sokolovskycb9dc082014-04-26 20:26:14 +0300254};
Paul Sokolovsky100cd362014-04-26 20:59:39 +0300255
Damien Georgeee3fd462014-05-24 23:03:12 +0100256#if MICROPY_PY_IO_BYTESIO
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300257const mp_obj_type_t mp_type_bytesio = {
258 { &mp_type_type },
259 .name = MP_QSTR_BytesIO,
260 .print = stringio_print,
261 .make_new = stringio_make_new,
Damien Georgeae8d8672016-01-09 23:14:54 +0000262 .getiter = mp_identity_getiter,
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300263 .iternext = mp_stream_unbuffered_iter,
Paul Sokolovsky07209f82016-06-18 18:19:24 +0300264 .protocol = &bytesio_stream_p,
Damien George999cedb2015-11-27 17:01:44 +0000265 .locals_dict = (mp_obj_dict_t*)&stringio_locals_dict,
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300266};
267#endif
268
Paul Sokolovsky100cd362014-04-26 20:59:39 +0300269#endif