blob: 828bcec464827c8cbcb46060dfbf88887936396e [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
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
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +020027#include <assert.h>
28#include <string.h>
29
30#include "py/runtime.h"
Damien George51dfcb42015-01-01 20:27:54 +000031#include "py/builtin.h"
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +020032#include "py/stream.h"
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +030033#include "py/objstringio.h"
34#include "py/frozenmod.h"
Paul Sokolovsky98a627d2014-04-03 14:57:53 +030035
Damien Georgeee3fd462014-05-24 23:03:12 +010036#if MICROPY_PY_IO
Paul Sokolovsky98a627d2014-04-03 14:57:53 +030037
Paul Sokolovsky9e296662014-05-19 20:59:13 +030038extern const mp_obj_type_t mp_type_fileio;
39extern const mp_obj_type_t mp_type_textio;
40
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +020041#if MICROPY_PY_IO_BUFFEREDWRITER
42typedef struct _mp_obj_bufwriter_t {
43 mp_obj_base_t base;
44 mp_obj_t stream;
45 size_t alloc;
46 size_t len;
47 byte buf[0];
48} mp_obj_bufwriter_t;
49
50STATIC 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) {
51 mp_arg_check_num(n_args, n_kw, 2, 2, false);
52 size_t alloc = mp_obj_get_int(args[1]);
53 mp_obj_bufwriter_t *o = m_new_obj_var(mp_obj_bufwriter_t, byte, alloc);
54 o->base.type = type;
55 o->stream = args[0];
56 o->alloc = alloc;
57 o->len = 0;
58 return o;
59}
60
61STATIC mp_uint_t bufwriter_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
62 mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in);
63
64 mp_uint_t org_size = size;
65
66 while (size > 0) {
67 mp_uint_t rem = self->alloc - self->len;
68 if (size < rem) {
69 memcpy(self->buf + self->len, buf, size);
70 self->len += size;
71 return org_size;
72 }
73
Paul Sokolovsky2c81b9b2016-03-25 14:59:30 +020074 // Buffer flushing policy here is to flush entire buffer all the time.
75 // This allows e.g. to have a block device as backing storage and write
76 // entire block to it. memcpy below is not ideal and could be optimized
77 // in some cases. But the way it is now it at least ensures that buffer
78 // is word-aligned, to guard against obscure cases when it matters, e.g.
79 // https://github.com/micropython/micropython/issues/1863
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +020080 memcpy(self->buf + self->len, buf, rem);
81 buf = (byte*)buf + rem;
82 size -= rem;
Paul Sokolovsky7f7c84b2016-05-18 02:40:03 +030083 mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->alloc, errcode);
84 if (*errcode != 0) {
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +020085 return MP_STREAM_ERROR;
86 }
Paul Sokolovsky7f7c84b2016-05-18 02:40:03 +030087 // TODO: try to recover from a case of non-blocking stream, e.g. move
88 // remaining chunk to the beginning of buffer.
89 assert(out_sz == self->alloc);
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +020090 self->len = 0;
91 }
92
93 return org_size;
94}
95
Paul Sokolovsky063e6e72016-03-25 14:33:38 +020096STATIC mp_obj_t bufwriter_flush(mp_obj_t self_in) {
97 mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in);
98
99 if (self->len != 0) {
100 int err;
Paul Sokolovsky7f7c84b2016-05-18 02:40:03 +0300101 mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->len, &err);
102 // TODO: try to recover from a case of non-blocking stream, e.g. move
103 // remaining chunk to the beginning of buffer.
104 assert(out_sz == self->len);
Paul Sokolovsky063e6e72016-03-25 14:33:38 +0200105 self->len = 0;
Paul Sokolovsky7f7c84b2016-05-18 02:40:03 +0300106 if (err != 0) {
Damien George3a0a7712016-10-07 13:31:59 +1100107 mp_raise_OSError(err);
Paul Sokolovsky063e6e72016-03-25 14:33:38 +0200108 }
109 }
110
111 return mp_const_none;
112}
113STATIC MP_DEFINE_CONST_FUN_OBJ_1(bufwriter_flush_obj, bufwriter_flush);
114
Paul Sokolovsky45645042017-07-28 21:41:42 +0300115STATIC const mp_rom_map_elem_t bufwriter_locals_dict_table[] = {
116 { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
117 { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&bufwriter_flush_obj) },
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +0200118};
119STATIC MP_DEFINE_CONST_DICT(bufwriter_locals_dict, bufwriter_locals_dict_table);
120
121STATIC const mp_stream_p_t bufwriter_stream_p = {
122 .write = bufwriter_write,
123};
124
125STATIC const mp_obj_type_t bufwriter_type = {
126 { &mp_type_type },
127 .name = MP_QSTR_BufferedWriter,
128 .make_new = bufwriter_make_new,
Paul Sokolovsky07209f82016-06-18 18:19:24 +0300129 .protocol = &bufwriter_stream_p,
Paul Sokolovsky45645042017-07-28 21:41:42 +0300130 .locals_dict = (mp_obj_dict_t*)&bufwriter_locals_dict,
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +0200131};
132#endif // MICROPY_PY_IO_BUFFEREDWRITER
133
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +0300134#if MICROPY_MODULE_FROZEN_STR
135STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) {
Paul Sokolovsky4a4490f2017-05-06 18:42:35 +0300136 VSTR_FIXED(path_buf, MICROPY_ALLOC_PATH_MAX);
137 size_t len;
138
139 // As an extension to pkg_resources.resource_stream(), we support
140 // package parameter being None, the path_in is interpreted as a
141 // raw path.
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +0300142 if (package_in != mp_const_none) {
Paul Sokolovsky4a4490f2017-05-06 18:42:35 +0300143 mp_obj_t args[5];
144 args[0] = package_in;
145 args[1] = mp_const_none; // TODO should be globals
146 args[2] = mp_const_none; // TODO should be locals
147 args[3] = mp_const_true; // Pass sentinel "non empty" value to force returning of leaf module
148 args[4] = MP_OBJ_NEW_SMALL_INT(0);
149
150 // TODO lookup __import__ and call that instead of going straight to builtin implementation
151 mp_obj_t pkg = mp_builtin___import__(5, args);
152
153 mp_obj_t dest[2];
154 mp_load_method_maybe(pkg, MP_QSTR___path__, dest);
155 if (dest[0] == MP_OBJ_NULL) {
156 mp_raise_TypeError(NULL);
157 }
158
159 const char *path = mp_obj_str_get_data(dest[0], &len);
160 vstr_add_strn(&path_buf, path, len);
161 vstr_add_byte(&path_buf, '/');
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +0300162 }
163
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +0300164 const char *path = mp_obj_str_get_data(path_in, &len);
Paul Sokolovsky4a4490f2017-05-06 18:42:35 +0300165 vstr_add_strn(&path_buf, path, len);
166
167 len = path_buf.len;
168 const char *data = mp_find_frozen_str(path_buf.buf, &len);
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +0300169 if (data != NULL) {
170 mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t);
171 o->base.type = &mp_type_bytesio;
172 o->vstr = m_new_obj(vstr_t);
173 vstr_init_fixed_buf(o->vstr, len + 1, (char*)data);
174 o->vstr->len = len;
175 o->pos = 0;
176 return MP_OBJ_FROM_PTR(o);
177 }
178
Damien George46017592017-11-16 13:17:51 +1100179 mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len);
Paul Sokolovsky4a4490f2017-05-06 18:42:35 +0300180 return mp_builtin_open(1, &path_out, (mp_map_t*)&mp_const_empty_map);
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +0300181}
182MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream);
183#endif
184
Damien Georgecbf76742015-11-27 13:38:15 +0000185STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = {
Paul Sokolovskyddb9dba2016-05-02 13:56:33 +0300186 { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uio) },
Paul Sokolovsky98a627d2014-04-03 14:57:53 +0300187 // Note: mp_builtin_open_obj should be defined by port, it's not
188 // part of the core.
Damien Georgecbf76742015-11-27 13:38:15 +0000189 { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
Paul Sokolovskyd7da2db2017-05-03 01:47:08 +0300190 #if MICROPY_PY_IO_RESOURCE_STREAM
191 { MP_ROM_QSTR(MP_QSTR_resource_stream), MP_ROM_PTR(&resource_stream_obj) },
192 #endif
Damien Georgeee3fd462014-05-24 23:03:12 +0100193 #if MICROPY_PY_IO_FILEIO
Damien Georgecbf76742015-11-27 13:38:15 +0000194 { MP_ROM_QSTR(MP_QSTR_FileIO), MP_ROM_PTR(&mp_type_fileio) },
Paul Sokolovsky9e296662014-05-19 20:59:13 +0300195 #if MICROPY_CPYTHON_COMPAT
Damien Georgecbf76742015-11-27 13:38:15 +0000196 { MP_ROM_QSTR(MP_QSTR_TextIOWrapper), MP_ROM_PTR(&mp_type_textio) },
Paul Sokolovsky9e296662014-05-19 20:59:13 +0300197 #endif
Damien Georgee5039c62015-02-15 13:17:11 +0000198 #endif
Damien Georgecbf76742015-11-27 13:38:15 +0000199 { MP_ROM_QSTR(MP_QSTR_StringIO), MP_ROM_PTR(&mp_type_stringio) },
Damien Georgeee3fd462014-05-24 23:03:12 +0100200 #if MICROPY_PY_IO_BYTESIO
Damien Georgecbf76742015-11-27 13:38:15 +0000201 { MP_ROM_QSTR(MP_QSTR_BytesIO), MP_ROM_PTR(&mp_type_bytesio) },
Paul Sokolovskya47b64a2014-05-15 07:28:19 +0300202 #endif
Paul Sokolovsky5d93dfb2016-03-25 01:10:49 +0200203 #if MICROPY_PY_IO_BUFFEREDWRITER
204 { MP_ROM_QSTR(MP_QSTR_BufferedWriter), MP_ROM_PTR(&bufwriter_type) },
205 #endif
Paul Sokolovsky98a627d2014-04-03 14:57:53 +0300206};
207
Damien George3b603f22014-11-29 14:39:27 +0000208STATIC MP_DEFINE_CONST_DICT(mp_module_io_globals, mp_module_io_globals_table);
Paul Sokolovsky98a627d2014-04-03 14:57:53 +0300209
210const mp_obj_module_t mp_module_io = {
211 .base = { &mp_type_module },
Damien George8b0535e2014-04-05 21:53:54 +0100212 .globals = (mp_obj_dict_t*)&mp_module_io_globals,
Paul Sokolovsky98a627d2014-04-03 14:57:53 +0300213};
214
215#endif