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) 2014 Damien P. George
7 : : * Copyright (c) 2014-2016 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 <string.h>
29 : : #include <unistd.h>
30 : :
31 : : #include "py/objstr.h"
32 : : #include "py/stream.h"
33 : : #include "py/runtime.h"
34 : :
35 : : // This file defines generic Python stream read/write methods which
36 : : // dispatch to the underlying stream interface of an object.
37 : :
38 : : // TODO: should be in mpconfig.h
39 : : #define DEFAULT_BUFFER_SIZE 256
40 : :
41 : : static mp_obj_t stream_readall(mp_obj_t self_in);
42 : :
43 : : // Returns error condition in *errcode, if non-zero, return value is number of bytes written
44 : : // before error condition occurred. If *errcode == 0, returns total bytes written (which will
45 : : // be equal to input size).
46 : 817212 : mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode, byte flags) {
47 : 817212 : byte *buf = buf_;
48 : 817212 : typedef mp_uint_t (*io_func_t)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
49 : 817212 : io_func_t io_func;
50 [ + + ]: 817212 : const mp_stream_p_t *stream_p = mp_get_stream(stream);
51 [ + + ]: 817212 : if (flags & MP_STREAM_RW_WRITE) {
52 : 733066 : io_func = (io_func_t)stream_p->write;
53 : : } else {
54 : 84146 : io_func = stream_p->read;
55 : : }
56 : :
57 : 817212 : *errcode = 0;
58 : 817212 : mp_uint_t done = 0;
59 [ + + ]: 1464184 : while (size > 0) {
60 : 731078 : mp_uint_t out_sz = io_func(stream, buf, size, errcode);
61 : : // For read, out_sz == 0 means EOF. For write, it's unspecified
62 : : // what it means, but we don't make any progress, so returning
63 : : // is still the best option.
64 [ + + ]: 731070 : if (out_sz == 0) {
65 : : return done;
66 : : }
67 [ + + ]: 730914 : if (out_sz == MP_STREAM_ERROR) {
68 : : // If we read something before getting EAGAIN, don't leak it
69 [ + + + + ]: 76 : if (mp_is_nonblocking_error(*errcode) && done != 0) {
70 : 2 : *errcode = 0;
71 : : }
72 : 76 : return done;
73 : : }
74 [ + + ]: 730838 : if (flags & MP_STREAM_RW_ONCE) {
75 : : return out_sz;
76 : : }
77 : :
78 : 646972 : buf += out_sz;
79 : 646972 : size -= out_sz;
80 : 646972 : done += out_sz;
81 : : }
82 : : return done;
83 : : }
84 : :
85 : 156 : mp_off_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) {
86 : 156 : struct mp_stream_seek_t seek_s;
87 : 156 : seek_s.offset = offset;
88 : 156 : seek_s.whence = whence;
89 : 156 : const mp_stream_p_t *stream_p = mp_get_stream(stream);
90 : 156 : mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, errcode);
91 [ + + ]: 154 : if (res == MP_STREAM_ERROR) {
92 : : return (mp_off_t)-1;
93 : : }
94 : 148 : return seek_s.offset;
95 : : }
96 : :
97 : 184616 : const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) {
98 : 184616 : const mp_obj_type_t *type = mp_obj_get_type(self_in);
99 [ + + ]: 184616 : if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) {
100 : 184581 : const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(type, protocol);
101 [ + + + - ]: 184581 : if (!((flags & MP_STREAM_OP_READ) && stream_p->read == NULL)
102 [ + + + - ]: 184581 : && !((flags & MP_STREAM_OP_WRITE) && stream_p->write == NULL)
103 [ + + + - ]: 184581 : && !((flags & MP_STREAM_OP_IOCTL) && stream_p->ioctl == NULL)) {
104 : 184581 : return stream_p;
105 : : }
106 : : }
107 : : // CPython: io.UnsupportedOperation, OSError subclass
108 : 35 : mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported"));
109 : : }
110 : :
111 : 464 : static mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) {
112 : : // What to do if sz < -1? Python docs don't specify this case.
113 : : // CPython does a readall, but here we silently let negatives through,
114 : : // and they will cause a MemoryError.
115 : 464 : mp_int_t sz;
116 [ + + - + ]: 464 : if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) {
117 : 272 : return stream_readall(args[0]);
118 : : }
119 : :
120 [ + + ]: 192 : const mp_stream_p_t *stream_p = mp_get_stream(args[0]);
121 : :
122 : : #if MICROPY_PY_BUILTINS_STR_UNICODE
123 [ + + ]: 192 : if (stream_p->is_text) {
124 : : // We need to read sz number of unicode characters. Because we don't have any
125 : : // buffering, and because the stream API can only read bytes, we must read here
126 : : // in units of bytes and must never over read. If we want sz chars, then reading
127 : : // sz bytes will never over-read, so we follow this approach, in a loop to keep
128 : : // reading until we have exactly enough chars. This will be 1 read for text
129 : : // with ASCII-only chars, and about 2 reads for text with a couple of non-ASCII
130 : : // chars. For text with lots of non-ASCII chars, it'll be pretty inefficient
131 : : // in time and memory.
132 : :
133 : 54 : vstr_t vstr;
134 : 54 : vstr_init(&vstr, sz);
135 : 54 : mp_uint_t more_bytes = sz;
136 : 54 : mp_uint_t last_buf_offset = 0;
137 [ + + ]: 104 : while (more_bytes > 0) {
138 : 68 : char *p = vstr_add_len(&vstr, more_bytes);
139 : 68 : int error;
140 : 68 : mp_uint_t out_sz = mp_stream_read_exactly(args[0], p, more_bytes, &error);
141 [ + + ]: 68 : if (error != 0) {
142 : 16 : vstr_cut_tail_bytes(&vstr, more_bytes);
143 [ + + ]: 16 : if (mp_is_nonblocking_error(error)) {
144 : : // With non-blocking streams, we read as much as we can.
145 : : // If we read nothing, return None, just like read().
146 : : // Otherwise, return data read so far.
147 : : // TODO what if we have read only half a non-ASCII char?
148 [ + - ]: 2 : if (vstr.len == 0) {
149 : 2 : vstr_clear(&vstr);
150 : 2 : return mp_const_none;
151 : : }
152 : 2 : break;
153 : : }
154 : 14 : mp_raise_OSError(error);
155 : : }
156 : :
157 [ + + ]: 52 : if (out_sz < more_bytes) {
158 : : // Finish reading.
159 : : // TODO what if we have read only half a non-ASCII char?
160 : 4 : vstr_cut_tail_bytes(&vstr, more_bytes - out_sz);
161 [ + + ]: 4 : if (out_sz == 0) {
162 : : break;
163 : : }
164 : : }
165 : :
166 : : // count chars from bytes just read
167 : 50 : for (mp_uint_t off = last_buf_offset;;) {
168 : 120 : byte b = vstr.buf[off];
169 : 120 : int n;
170 [ + + ]: 120 : if (!UTF8_IS_NONASCII(b)) {
171 : : // 1-byte ASCII char
172 : : n = 1;
173 [ + + ]: 22 : } else if ((b & 0xe0) == 0xc0) {
174 : : // 2-byte char
175 : : n = 2;
176 [ + + ]: 8 : } else if ((b & 0xf0) == 0xe0) {
177 : : // 3-byte char
178 : : n = 3;
179 [ - + ]: 4 : } else if ((b & 0xf8) == 0xf0) {
180 : : // 4-byte char
181 : : n = 4;
182 : : } else {
183 : : // TODO
184 : 0 : n = 5;
185 : : }
186 [ + + ]: 120 : if (off + n <= vstr.len) {
187 : : // got a whole char in n bytes
188 : 110 : off += n;
189 : 110 : sz -= 1;
190 : 110 : last_buf_offset = off;
191 [ + + ]: 110 : if (off >= vstr.len) {
192 : 40 : more_bytes = sz;
193 : 40 : break;
194 : : }
195 : : } else {
196 : : // didn't get a whole char, so work out how many extra bytes are needed for
197 : : // this partial char, plus bytes for additional chars that we want
198 : 10 : more_bytes = (off + n - vstr.len) + (sz - 1);
199 : 10 : break;
200 : : }
201 : : }
202 : : }
203 : :
204 : 38 : return mp_obj_new_str_from_vstr(&vstr);
205 : : }
206 : : #endif
207 : :
208 : 138 : vstr_t vstr;
209 : 138 : vstr_init_len(&vstr, sz);
210 : 138 : int error;
211 : 138 : mp_uint_t out_sz = mp_stream_rw(args[0], vstr.buf, sz, &error, flags);
212 [ + + ]: 134 : if (error != 0) {
213 : 22 : vstr_clear(&vstr);
214 [ + + ]: 22 : if (mp_is_nonblocking_error(error)) {
215 : : // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read
216 : : // "If the object is in non-blocking mode and no bytes are available,
217 : : // None is returned."
218 : : // This is actually very weird, as naive truth check will treat
219 : : // this as EOF.
220 : : return mp_const_none;
221 : : }
222 : 8 : mp_raise_OSError(error);
223 : : } else {
224 : 112 : vstr.len = out_sz;
225 [ - + ]: 112 : if (stream_p->is_text) {
226 : 0 : return mp_obj_new_str_from_vstr(&vstr);
227 : : } else {
228 : 112 : return mp_obj_new_bytes_from_vstr(&vstr);
229 : : }
230 : : }
231 : : }
232 : :
233 : 462 : static mp_obj_t stream_read(size_t n_args, const mp_obj_t *args) {
234 : 462 : return stream_read_generic(n_args, args, MP_STREAM_RW_READ);
235 : : }
236 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj, 1, 2, stream_read);
237 : :
238 : 2 : static mp_obj_t stream_read1(size_t n_args, const mp_obj_t *args) {
239 : 2 : return stream_read_generic(n_args, args, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
240 : : }
241 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read1_obj, 1, 2, stream_read1);
242 : :
243 : 733032 : mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte flags) {
244 : 733032 : int error;
245 : 733032 : mp_uint_t out_sz = mp_stream_rw(self_in, (void *)buf, len, &error, flags);
246 [ + + ]: 733028 : if (error != 0) {
247 [ + + ]: 30 : if (mp_is_nonblocking_error(error)) {
248 : : // http://docs.python.org/3/library/io.html#io.RawIOBase.write
249 : : // "None is returned if the raw stream is set not to block and
250 : : // no single byte could be readily written to it."
251 : : return mp_const_none;
252 : : }
253 : 22 : mp_raise_OSError(error);
254 : : } else {
255 : 732998 : return MP_OBJ_NEW_SMALL_INT(out_sz);
256 : : }
257 : : }
258 : :
259 : : // This is used to adapt a stream object to an mp_print_t interface
260 : 730564 : void mp_stream_write_adaptor(void *self, const char *buf, size_t len) {
261 : 730564 : mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len, MP_STREAM_RW_WRITE);
262 : 730564 : }
263 : :
264 : 2466 : static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) {
265 : 2466 : mp_buffer_info_t bufinfo;
266 : 2466 : mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
267 : 2466 : size_t max_len = (size_t)-1;
268 : 2466 : size_t off = 0;
269 [ + + ]: 2466 : if (n_args == 3) {
270 : 8 : max_len = mp_obj_get_int_truncated(args[2]);
271 [ + + ]: 2458 : } else if (n_args == 4) {
272 : 12 : off = mp_obj_get_int_truncated(args[2]);
273 : 12 : max_len = mp_obj_get_int_truncated(args[3]);
274 [ + + ]: 12 : if (off > bufinfo.len) {
275 : 4 : off = bufinfo.len;
276 : : }
277 : : }
278 : 2466 : bufinfo.len -= off;
279 : 2466 : return mp_stream_write(args[0], (byte *)bufinfo.buf + off, MIN(bufinfo.len, max_len), MP_STREAM_RW_WRITE);
280 : : }
281 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj, 2, 4, stream_write_method);
282 : :
283 : 2 : static mp_obj_t stream_write1_method(mp_obj_t self_in, mp_obj_t arg) {
284 : 2 : mp_buffer_info_t bufinfo;
285 : 2 : mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
286 : 2 : return mp_stream_write(self_in, bufinfo.buf, bufinfo.len, MP_STREAM_RW_WRITE | MP_STREAM_RW_ONCE);
287 : : }
288 : : MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_write1_obj, stream_write1_method);
289 : :
290 : 16 : static mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) {
291 : 16 : mp_buffer_info_t bufinfo;
292 : 16 : mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
293 : :
294 : : // CPython extension: if 2nd arg is provided, that's max len to read,
295 : : // instead of full buffer. Similar to
296 : : // https://docs.python.org/3/library/socket.html#socket.socket.recv_into
297 : 16 : mp_uint_t len = bufinfo.len;
298 [ + + ]: 16 : if (n_args > 2) {
299 : 4 : len = mp_obj_get_int(args[2]);
300 [ + + ]: 4 : if (len > bufinfo.len) {
301 : 2 : len = bufinfo.len;
302 : : }
303 : : }
304 : :
305 : 16 : int error;
306 : 16 : mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error);
307 [ + + ]: 16 : if (error != 0) {
308 [ + + ]: 4 : if (mp_is_nonblocking_error(error)) {
309 : : return mp_const_none;
310 : : }
311 : 2 : mp_raise_OSError(error);
312 : : } else {
313 : 12 : return MP_OBJ_NEW_SMALL_INT(out_sz);
314 : : }
315 : : }
316 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj, 2, 3, stream_readinto);
317 : :
318 : 272 : static mp_obj_t stream_readall(mp_obj_t self_in) {
319 : 272 : const mp_stream_p_t *stream_p = mp_get_stream(self_in);
320 : :
321 : 272 : mp_uint_t total_size = 0;
322 : 272 : vstr_t vstr;
323 : 272 : vstr_init(&vstr, DEFAULT_BUFFER_SIZE);
324 : 272 : char *p = vstr.buf;
325 : 272 : mp_uint_t current_read = DEFAULT_BUFFER_SIZE;
326 : 374 : while (true) {
327 : 646 : int error;
328 : 646 : mp_uint_t out_sz = stream_p->read(self_in, p, current_read, &error);
329 [ + + ]: 636 : if (out_sz == MP_STREAM_ERROR) {
330 [ + + ]: 42 : if (mp_is_nonblocking_error(error)) {
331 : : // With non-blocking streams, we read as much as we can.
332 : : // If we read nothing, return None, just like read().
333 : : // Otherwise, return data read so far.
334 [ + - ]: 2 : if (total_size == 0) {
335 : 2 : return mp_const_none;
336 : : }
337 : 220 : break;
338 : : }
339 : 40 : mp_raise_OSError(error);
340 : : }
341 [ + + ]: 594 : if (out_sz == 0) {
342 : : break;
343 : : }
344 : 374 : total_size += out_sz;
345 [ + + ]: 374 : if (out_sz < current_read) {
346 : 196 : current_read -= out_sz;
347 : 196 : p += out_sz;
348 : : } else {
349 : 178 : p = vstr_extend(&vstr, DEFAULT_BUFFER_SIZE);
350 : 178 : current_read = DEFAULT_BUFFER_SIZE;
351 : : }
352 : : }
353 : :
354 : 220 : vstr.len = total_size;
355 [ + + ]: 220 : if (stream_p->is_text) {
356 : 88 : return mp_obj_new_str_from_vstr(&vstr);
357 : : } else {
358 : 132 : return mp_obj_new_bytes_from_vstr(&vstr);
359 : : }
360 : : }
361 : :
362 : : // Unbuffered, inefficient implementation of readline() for raw I/O files.
363 : 120 : static mp_obj_t stream_unbuffered_readline(size_t n_args, const mp_obj_t *args) {
364 [ + + ]: 120 : const mp_stream_p_t *stream_p = mp_get_stream(args[0]);
365 : :
366 : 120 : mp_int_t max_size = -1;
367 [ + + ]: 120 : if (n_args > 1) {
368 : 8 : max_size = MP_OBJ_SMALL_INT_VALUE(args[1]);
369 : : }
370 : :
371 : 8 : vstr_t vstr;
372 [ + - ]: 8 : if (max_size != -1) {
373 : 8 : vstr_init(&vstr, max_size);
374 : : } else {
375 : 112 : vstr_init(&vstr, 16);
376 : : }
377 : :
378 [ + + + + ]: 830 : while (max_size == -1 || max_size-- != 0) {
379 : 826 : char *p = vstr_add_len(&vstr, 1);
380 : 826 : int error;
381 : 826 : mp_uint_t out_sz = stream_p->read(args[0], p, 1, &error);
382 [ + + ]: 826 : if (out_sz == MP_STREAM_ERROR) {
383 [ + + ]: 6 : if (mp_is_nonblocking_error(error)) {
384 [ + + ]: 4 : if (vstr.len == 1) {
385 : : // We just incremented it, but otherwise we read nothing
386 : : // and immediately got EAGAIN. This case is not well
387 : : // specified in
388 : : // https://docs.python.org/3/library/io.html#io.IOBase.readline
389 : : // unlike similar case for read(). But we follow the latter's
390 : : // behavior - return None.
391 : 2 : vstr_clear(&vstr);
392 : 2 : return mp_const_none;
393 : : } else {
394 : 2 : goto done;
395 : : }
396 : : }
397 : 2 : mp_raise_OSError(error);
398 : : }
399 [ + + ]: 820 : if (out_sz == 0) {
400 : 20 : done:
401 : : // Back out previously added byte
402 : : // Consider, what's better - read a char and get OutOfMemory (so read
403 : : // char is lost), or allocate first as we do.
404 : 22 : vstr_cut_tail_bytes(&vstr, 1);
405 : 134 : break;
406 : : }
407 [ + + ]: 800 : if (*p == '\n') {
408 : : break;
409 : : }
410 : : }
411 : :
412 [ + + ]: 116 : if (stream_p->is_text) {
413 : 96 : return mp_obj_new_str_from_vstr(&vstr);
414 : : } else {
415 : 20 : return mp_obj_new_bytes_from_vstr(&vstr);
416 : : }
417 : : }
418 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj, 1, 2, stream_unbuffered_readline);
419 : :
420 : : // TODO take an optional extra argument (what does it do exactly?)
421 : 10 : static mp_obj_t stream_unbuffered_readlines(mp_obj_t self) {
422 : 10 : mp_obj_t lines = mp_obj_new_list(0, NULL);
423 : 30 : for (;;) {
424 : 40 : mp_obj_t line = stream_unbuffered_readline(1, &self);
425 [ + + ]: 40 : if (!mp_obj_is_true(line)) {
426 : : break;
427 : : }
428 : 30 : mp_obj_list_append(lines, line);
429 : : }
430 : 10 : return lines;
431 : : }
432 : : MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj, stream_unbuffered_readlines);
433 : :
434 : 48 : mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) {
435 : 48 : mp_obj_t l_in = stream_unbuffered_readline(1, &self);
436 [ + + ]: 48 : if (mp_obj_is_true(l_in)) {
437 : 38 : return l_in;
438 : : }
439 : : return MP_OBJ_STOP_ITERATION;
440 : : }
441 : :
442 : 8364 : mp_obj_t mp_stream_close(mp_obj_t stream) {
443 : 8364 : const mp_stream_p_t *stream_p = mp_get_stream(stream);
444 : 8364 : int error;
445 : 8364 : mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error);
446 [ + + ]: 8364 : if (res == MP_STREAM_ERROR) {
447 : 8 : mp_raise_OSError(error);
448 : : }
449 : 8356 : return mp_const_none;
450 : : }
451 : : MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_close_obj, mp_stream_close);
452 : :
453 : 342 : static mp_obj_t mp_stream___exit__(size_t n_args, const mp_obj_t *args) {
454 : 342 : (void)n_args;
455 : 342 : return mp_stream_close(args[0]);
456 : : }
457 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream___exit___obj, 4, 4, mp_stream___exit__);
458 : :
459 : 116 : static mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) {
460 : : // TODO: Could be uint64
461 : 116 : mp_off_t offset = mp_obj_get_int(args[1]);
462 : 116 : int whence = SEEK_SET;
463 [ + + ]: 116 : if (n_args == 3) {
464 : 70 : whence = mp_obj_get_int(args[2]);
465 : : }
466 : :
467 : : // In POSIX, it's error to seek before end of stream, we enforce it here.
468 [ + + ]: 116 : if (whence == SEEK_SET && offset < 0) {
469 : 4 : mp_raise_OSError(MP_EINVAL);
470 : : }
471 : :
472 : 112 : int error;
473 : 112 : mp_off_t res = mp_stream_seek(args[0], offset, whence, &error);
474 [ + + ]: 110 : if (res == (mp_off_t)-1) {
475 : 4 : mp_raise_OSError(error);
476 : : }
477 : :
478 : : // TODO: Could be uint64
479 : 106 : return mp_obj_new_int_from_uint(res);
480 : : }
481 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj, 2, 3, stream_seek);
482 : :
483 : 28 : static mp_obj_t stream_tell(mp_obj_t self) {
484 : 28 : mp_obj_t offset = MP_OBJ_NEW_SMALL_INT(0);
485 : 28 : mp_obj_t whence = MP_OBJ_NEW_SMALL_INT(SEEK_CUR);
486 : 28 : const mp_obj_t args[3] = {self, offset, whence};
487 : 28 : return stream_seek(3, args);
488 : : }
489 : : MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_tell_obj, stream_tell);
490 : :
491 : 24 : static mp_obj_t stream_flush(mp_obj_t self) {
492 : 24 : const mp_stream_p_t *stream_p = mp_get_stream(self);
493 : 24 : int error;
494 : 24 : mp_uint_t res = stream_p->ioctl(self, MP_STREAM_FLUSH, 0, &error);
495 [ + + ]: 24 : if (res == MP_STREAM_ERROR) {
496 : 6 : mp_raise_OSError(error);
497 : : }
498 : 18 : return mp_const_none;
499 : : }
500 : : MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_flush_obj, stream_flush);
501 : :
502 : 44 : static mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) {
503 : 44 : mp_buffer_info_t bufinfo;
504 : 44 : uintptr_t val = 0;
505 [ + + ]: 44 : if (n_args > 2) {
506 [ + + ]: 38 : if (mp_get_buffer(args[2], &bufinfo, MP_BUFFER_WRITE)) {
507 : 2 : val = (uintptr_t)bufinfo.buf;
508 : : } else {
509 : 36 : val = mp_obj_get_int_truncated(args[2]);
510 : : }
511 : : }
512 : :
513 : 44 : const mp_stream_p_t *stream_p = mp_get_stream(args[0]);
514 : 44 : int error;
515 : 44 : mp_uint_t res = stream_p->ioctl(args[0], mp_obj_get_int(args[1]), val, &error);
516 [ + + ]: 44 : if (res == MP_STREAM_ERROR) {
517 : 10 : mp_raise_OSError(error);
518 : : }
519 : :
520 : 34 : return mp_obj_new_int(res);
521 : : }
522 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj, 2, 3, stream_ioctl);
523 : :
524 : : #if MICROPY_STREAMS_POSIX_API
525 : : /*
526 : : * POSIX-like functions
527 : : *
528 : : * These functions have POSIX-compatible signature (except for "void *stream"
529 : : * first argument instead of "int fd"). They are useful to port existing
530 : : * POSIX-compatible software to work with MicroPython streams.
531 : : */
532 : :
533 : : #include <errno.h>
534 : :
535 : 34 : ssize_t mp_stream_posix_write(void *stream, const void *buf, size_t len) {
536 : 34 : mp_obj_base_t *o = stream;
537 : 34 : const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol);
538 : 34 : mp_uint_t out_sz = stream_p->write(MP_OBJ_FROM_PTR(stream), buf, len, &errno);
539 [ + - ]: 34 : if (out_sz == MP_STREAM_ERROR) {
540 : : return -1;
541 : : } else {
542 : 34 : return out_sz;
543 : : }
544 : : }
545 : :
546 : 12 : ssize_t mp_stream_posix_read(void *stream, void *buf, size_t len) {
547 : 12 : mp_obj_base_t *o = stream;
548 : 12 : const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol);
549 : 12 : mp_uint_t out_sz = stream_p->read(MP_OBJ_FROM_PTR(stream), buf, len, &errno);
550 [ + + ]: 12 : if (out_sz == MP_STREAM_ERROR) {
551 : : return -1;
552 : : } else {
553 : 10 : return out_sz;
554 : : }
555 : : }
556 : :
557 : 44 : off_t mp_stream_posix_lseek(void *stream, off_t offset, int whence) {
558 : 44 : mp_off_t res = mp_stream_seek(MP_OBJ_FROM_PTR(stream), offset, whence, &errno);
559 : 44 : if (res == (mp_off_t)-1) {
560 : 44 : return -1;
561 : : }
562 : : return res;
563 : : }
564 : :
565 : 6 : int mp_stream_posix_fsync(void *stream) {
566 : 6 : mp_obj_base_t *o = stream;
567 : 6 : const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol);
568 : 6 : mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_FLUSH, 0, &errno);
569 [ + - ]: 6 : if (res == MP_STREAM_ERROR) {
570 : : return -1;
571 : : }
572 : 6 : return res;
573 : : }
574 : :
575 : : #endif
|