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 *
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 <assert.h>
28#include <string.h>
29
30#include "py/runtime.h"
31#include "py/builtin.h"
32#include "py/stream.h"
33#include "py/binary.h"
34#include "py/objarray.h"
35#include "py/objstringio.h"
36#include "py/frozenmod.h"
37
38#if MICROPY_PY_IO
39
40extern const mp_obj_type_t mp_type_fileio;
41extern const mp_obj_type_t mp_type_textio;
42
43#if MICROPY_PY_IO_IOBASE
44
45STATIC const mp_obj_type_t mp_type_iobase;
46
47STATIC const mp_obj_base_t iobase_singleton = {&mp_type_iobase};
48
49STATIC mp_obj_t iobase_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
50 (void)type;
51 (void)n_args;
52 (void)n_kw;
53 (void)args;
54 return MP_OBJ_FROM_PTR(&iobase_singleton);
55}
56
57STATIC mp_uint_t iobase_read_write(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode, qstr qst) {
58 mp_obj_t dest[3];
59 mp_load_method(obj, qst, dest);
60 mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, size, buf};
61 dest[2] = MP_OBJ_FROM_PTR(&ar);
62 mp_obj_t ret_obj = mp_call_method_n_kw(1, 0, dest);
63 if (ret_obj == mp_const_none) {
64 *errcode = MP_EAGAIN;
65 return MP_STREAM_ERROR;
66 }
67 mp_int_t ret = mp_obj_get_int(ret_obj);
68 if (ret >= 0) {
69 return ret;
70 } else {
71 *errcode = -ret;
72 return MP_STREAM_ERROR;
73 }
74}
75STATIC mp_uint_t iobase_read(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode) {
76 return iobase_read_write(obj, buf, size, errcode, MP_QSTR_readinto);
77}
78
79STATIC mp_uint_t iobase_write(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode) {
80 return iobase_read_write(obj, (void *)buf, size, errcode, MP_QSTR_write);
81}
82
83STATIC mp_uint_t iobase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) {
84 mp_obj_t dest[4];
85 mp_load_method(obj, MP_QSTR_ioctl, dest);
86 dest[2] = mp_obj_new_int_from_uint(request);
87 dest[3] = mp_obj_new_int_from_uint(arg);
88 mp_int_t ret = mp_obj_get_int(mp_call_method_n_kw(2, 0, dest));
89 if (ret >= 0) {
90 return ret;
91 } else {
92 *errcode = -ret;
93 return MP_STREAM_ERROR;
94 }
95}
96
97STATIC const mp_stream_p_t iobase_p = {
98 .read = iobase_read,
99 .write = iobase_write,
100 .ioctl = iobase_ioctl,
101};
102
103STATIC const mp_obj_type_t mp_type_iobase = {
104 { &mp_type_type },
105 .name = MP_QSTR_IOBase,
106 .make_new = iobase_make_new,
107 .protocol = &iobase_p,
108};
109
110#endif // MICROPY_PY_IO_IOBASE
111
112#if MICROPY_PY_IO_BUFFEREDWRITER
113typedef struct _mp_obj_bufwriter_t {
114 mp_obj_base_t base;
115 mp_obj_t stream;
116 size_t alloc;
117 size_t len;
118 byte buf[0];
119} mp_obj_bufwriter_t;
120
121STATIC mp_obj_t bufwriter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
122 mp_arg_check_num(n_args, n_kw, 2, 2, false);
123 size_t alloc = mp_obj_get_int(args[1]);
124 mp_obj_bufwriter_t *o = m_new_obj_var(mp_obj_bufwriter_t, byte, alloc);
125 o->base.type = type;
126 o->stream = args[0];
127 o->alloc = alloc;
128 o->len = 0;
129 return o;
130}
131
132STATIC mp_uint_t bufwriter_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
133 mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in);
134
135 mp_uint_t org_size = size;
136
137 while (size > 0) {
138 mp_uint_t rem = self->alloc - self->len;
139 if (size < rem) {
140 memcpy(self->buf + self->len, buf, size);
141 self->len += size;
142 return org_size;
143 }
144
145 // Buffer flushing policy here is to flush entire buffer all the time.
146 // This allows e.g. to have a block device as backing storage and write
147 // entire block to it. memcpy below is not ideal and could be optimized
148 // in some cases. But the way it is now it at least ensures that buffer
149 // is word-aligned, to guard against obscure cases when it matters, e.g.
150 // https://github.com/micropython/micropython/issues/1863
151 memcpy(self->buf + self->len, buf, rem);
152 buf = (byte *)buf + rem;
153 size -= rem;
154 mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->alloc, errcode);
155 (void)out_sz;
156 if (*errcode != 0) {
157 return MP_STREAM_ERROR;
158 }
159 // TODO: try to recover from a case of non-blocking stream, e.g. move
160 // remaining chunk to the beginning of buffer.
161 assert(out_sz == self->alloc);
162 self->len = 0;
163 }
164
165 return org_size;
166}
167
168STATIC mp_obj_t bufwriter_flush(mp_obj_t self_in) {
169 mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in);
170
171 if (self->len != 0) {
172 int err;
173 mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->len, &err);
174 (void)out_sz;
175 // TODO: try to recover from a case of non-blocking stream, e.g. move
176 // remaining chunk to the beginning of buffer.
177 assert(out_sz == self->len);
178 self->len = 0;
179 if (err != 0) {
180 mp_raise_OSError(err);
181 }
182 }
183
184 return mp_const_none;
185}
186STATIC MP_DEFINE_CONST_FUN_OBJ_1(bufwriter_flush_obj, bufwriter_flush);
187
188STATIC const mp_rom_map_elem_t bufwriter_locals_dict_table[] = {
189 { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
190 { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&bufwriter_flush_obj) },
191};
192STATIC MP_DEFINE_CONST_DICT(bufwriter_locals_dict, bufwriter_locals_dict_table);
193
194STATIC const mp_stream_p_t bufwriter_stream_p = {
195 .write = bufwriter_write,
196};
197
198STATIC const mp_obj_type_t mp_type_bufwriter = {
199 { &mp_type_type },
200 .name = MP_QSTR_BufferedWriter,
201 .make_new = bufwriter_make_new,
202 .protocol = &bufwriter_stream_p,
203 .locals_dict = (mp_obj_dict_t *)&bufwriter_locals_dict,
204};
205#endif // MICROPY_PY_IO_BUFFEREDWRITER
206
207#if MICROPY_PY_IO_RESOURCE_STREAM
208STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) {
209 VSTR_FIXED(path_buf, MICROPY_ALLOC_PATH_MAX);
210 size_t len;
211
212 // As an extension to pkg_resources.resource_stream(), we support
213 // package parameter being None, the path_in is interpreted as a
214 // raw path.
215 if (package_in != mp_const_none) {
216 // Pass "True" as sentinel value in fromlist to force returning of leaf module
217 mp_obj_t pkg = mp_import_name(mp_obj_str_get_qstr(package_in), mp_const_true, MP_OBJ_NEW_SMALL_INT(0));
218
219 mp_obj_t dest[2];
220 mp_load_method_maybe(pkg, MP_QSTR___path__, dest);
221 if (dest[0] == MP_OBJ_NULL) {
222 mp_raise_TypeError(NULL);
223 }
224
225 const char *path = mp_obj_str_get_data(dest[0], &len);
226 vstr_add_strn(&path_buf, path, len);
227 vstr_add_byte(&path_buf, '/');
228 }
229
230 const char *path = mp_obj_str_get_data(path_in, &len);
231 vstr_add_strn(&path_buf, path, len);
232
233 len = path_buf.len;
234 const char *data = mp_find_frozen_str(path_buf.buf, &len);
235 if (data != NULL) {
236 mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t);
237 o->base.type = &mp_type_bytesio;
238 o->vstr = m_new_obj(vstr_t);
239 vstr_init_fixed_buf(o->vstr, len + 1, (char *)data);
240 o->vstr->len = len;
241 o->pos = 0;
242 return MP_OBJ_FROM_PTR(o);
243 }
244
245 mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len);
246 return mp_builtin_open(1, &path_out, (mp_map_t *)&mp_const_empty_map);
247}
248STATIC MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream);
249#endif
250
251STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = {
252 { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uio) },
253 // Note: mp_builtin_open_obj should be defined by port, it's not
254 // part of the core.
255 { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
256 #if MICROPY_PY_IO_IOBASE
257 { MP_ROM_QSTR(MP_QSTR_IOBase), MP_ROM_PTR(&mp_type_iobase) },
258 #endif
259 #if MICROPY_PY_IO_RESOURCE_STREAM
260 { MP_ROM_QSTR(MP_QSTR_resource_stream), MP_ROM_PTR(&resource_stream_obj) },
261 #endif
262 #if MICROPY_PY_IO_FILEIO
263 { MP_ROM_QSTR(MP_QSTR_FileIO), MP_ROM_PTR(&mp_type_fileio) },
264 #if MICROPY_CPYTHON_COMPAT
265 { MP_ROM_QSTR(MP_QSTR_TextIOWrapper), MP_ROM_PTR(&mp_type_textio) },
266 #endif
267 #endif
268 { MP_ROM_QSTR(MP_QSTR_StringIO), MP_ROM_PTR(&mp_type_stringio) },
269 #if MICROPY_PY_IO_BYTESIO
270 { MP_ROM_QSTR(MP_QSTR_BytesIO), MP_ROM_PTR(&mp_type_bytesio) },
271 #endif
272 #if MICROPY_PY_IO_BUFFEREDWRITER
273 { MP_ROM_QSTR(MP_QSTR_BufferedWriter), MP_ROM_PTR(&mp_type_bufwriter) },
274 #endif
275};
276
277STATIC MP_DEFINE_CONST_DICT(mp_module_io_globals, mp_module_io_globals_table);
278
279const mp_obj_module_t mp_module_io = {
280 .base = { &mp_type_module },
281 .globals = (mp_obj_dict_t *)&mp_module_io_globals,
282};
283
284#endif
285