Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
py/stream.c@0:5868e8752d44, 2016-04-16 (annotated)
- Committer:
- pythontech
- Date:
- Sat Apr 16 17:11:56 2016 +0000
- Revision:
- 0:5868e8752d44
Split off library from repl
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| pythontech | 0:5868e8752d44 | 1 | /* |
| pythontech | 0:5868e8752d44 | 2 | * This file is part of the Micro Python project, http://micropython.org/ |
| pythontech | 0:5868e8752d44 | 3 | * |
| pythontech | 0:5868e8752d44 | 4 | * The MIT License (MIT) |
| pythontech | 0:5868e8752d44 | 5 | * |
| pythontech | 0:5868e8752d44 | 6 | * Copyright (c) 2013, 2014 Damien P. George |
| pythontech | 0:5868e8752d44 | 7 | * Copyright (c) 2014 Paul Sokolovsky |
| pythontech | 0:5868e8752d44 | 8 | * |
| pythontech | 0:5868e8752d44 | 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| pythontech | 0:5868e8752d44 | 10 | * of this software and associated documentation files (the "Software"), to deal |
| pythontech | 0:5868e8752d44 | 11 | * in the Software without restriction, including without limitation the rights |
| pythontech | 0:5868e8752d44 | 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| pythontech | 0:5868e8752d44 | 13 | * copies of the Software, and to permit persons to whom the Software is |
| pythontech | 0:5868e8752d44 | 14 | * furnished to do so, subject to the following conditions: |
| pythontech | 0:5868e8752d44 | 15 | * |
| pythontech | 0:5868e8752d44 | 16 | * The above copyright notice and this permission notice shall be included in |
| pythontech | 0:5868e8752d44 | 17 | * all copies or substantial portions of the Software. |
| pythontech | 0:5868e8752d44 | 18 | * |
| pythontech | 0:5868e8752d44 | 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| pythontech | 0:5868e8752d44 | 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| pythontech | 0:5868e8752d44 | 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| pythontech | 0:5868e8752d44 | 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| pythontech | 0:5868e8752d44 | 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| pythontech | 0:5868e8752d44 | 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| pythontech | 0:5868e8752d44 | 25 | * THE SOFTWARE. |
| pythontech | 0:5868e8752d44 | 26 | */ |
| pythontech | 0:5868e8752d44 | 27 | |
| pythontech | 0:5868e8752d44 | 28 | #include <string.h> |
| pythontech | 0:5868e8752d44 | 29 | #include <unistd.h> |
| pythontech | 0:5868e8752d44 | 30 | |
| pythontech | 0:5868e8752d44 | 31 | #include "py/nlr.h" |
| pythontech | 0:5868e8752d44 | 32 | #include "py/objstr.h" |
| pythontech | 0:5868e8752d44 | 33 | #include "py/stream.h" |
| pythontech | 0:5868e8752d44 | 34 | |
| pythontech | 0:5868e8752d44 | 35 | #if MICROPY_STREAMS_NON_BLOCK |
| pythontech | 0:5868e8752d44 | 36 | #include <errno.h> |
| pythontech | 0:5868e8752d44 | 37 | #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) |
| pythontech | 0:5868e8752d44 | 38 | #define EWOULDBLOCK 140 |
| pythontech | 0:5868e8752d44 | 39 | #endif |
| pythontech | 0:5868e8752d44 | 40 | #endif |
| pythontech | 0:5868e8752d44 | 41 | |
| pythontech | 0:5868e8752d44 | 42 | // This file defines generic Python stream read/write methods which |
| pythontech | 0:5868e8752d44 | 43 | // dispatch to the underlying stream interface of an object. |
| pythontech | 0:5868e8752d44 | 44 | |
| pythontech | 0:5868e8752d44 | 45 | // TODO: should be in mpconfig.h |
| pythontech | 0:5868e8752d44 | 46 | #define DEFAULT_BUFFER_SIZE 256 |
| pythontech | 0:5868e8752d44 | 47 | |
| pythontech | 0:5868e8752d44 | 48 | STATIC mp_obj_t stream_readall(mp_obj_t self_in); |
| pythontech | 0:5868e8752d44 | 49 | |
| pythontech | 0:5868e8752d44 | 50 | #define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) |
| pythontech | 0:5868e8752d44 | 51 | |
| pythontech | 0:5868e8752d44 | 52 | const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { |
| pythontech | 0:5868e8752d44 | 53 | mp_obj_base_t *o = (mp_obj_base_t*)MP_OBJ_TO_PTR(self_in); |
| pythontech | 0:5868e8752d44 | 54 | const mp_stream_p_t *stream_p = o->type->stream_p; |
| pythontech | 0:5868e8752d44 | 55 | if (stream_p == NULL |
| pythontech | 0:5868e8752d44 | 56 | || ((flags & MP_STREAM_OP_READ) && stream_p->read == NULL) |
| pythontech | 0:5868e8752d44 | 57 | || ((flags & MP_STREAM_OP_WRITE) && stream_p->write == NULL) |
| pythontech | 0:5868e8752d44 | 58 | || ((flags & MP_STREAM_OP_IOCTL) && stream_p->ioctl == NULL)) { |
| pythontech | 0:5868e8752d44 | 59 | // CPython: io.UnsupportedOperation, OSError subclass |
| pythontech | 0:5868e8752d44 | 60 | nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "stream operation not supported")); |
| pythontech | 0:5868e8752d44 | 61 | } |
| pythontech | 0:5868e8752d44 | 62 | return stream_p; |
| pythontech | 0:5868e8752d44 | 63 | } |
| pythontech | 0:5868e8752d44 | 64 | |
| pythontech | 0:5868e8752d44 | 65 | STATIC mp_obj_t stream_read(size_t n_args, const mp_obj_t *args) { |
| pythontech | 0:5868e8752d44 | 66 | const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_READ); |
| pythontech | 0:5868e8752d44 | 67 | |
| pythontech | 0:5868e8752d44 | 68 | // What to do if sz < -1? Python docs don't specify this case. |
| pythontech | 0:5868e8752d44 | 69 | // CPython does a readall, but here we silently let negatives through, |
| pythontech | 0:5868e8752d44 | 70 | // and they will cause a MemoryError. |
| pythontech | 0:5868e8752d44 | 71 | mp_int_t sz; |
| pythontech | 0:5868e8752d44 | 72 | if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) { |
| pythontech | 0:5868e8752d44 | 73 | return stream_readall(args[0]); |
| pythontech | 0:5868e8752d44 | 74 | } |
| pythontech | 0:5868e8752d44 | 75 | |
| pythontech | 0:5868e8752d44 | 76 | #if MICROPY_PY_BUILTINS_STR_UNICODE |
| pythontech | 0:5868e8752d44 | 77 | if (stream_p->is_text) { |
| pythontech | 0:5868e8752d44 | 78 | // We need to read sz number of unicode characters. Because we don't have any |
| pythontech | 0:5868e8752d44 | 79 | // buffering, and because the stream API can only read bytes, we must read here |
| pythontech | 0:5868e8752d44 | 80 | // in units of bytes and must never over read. If we want sz chars, then reading |
| pythontech | 0:5868e8752d44 | 81 | // sz bytes will never over-read, so we follow this approach, in a loop to keep |
| pythontech | 0:5868e8752d44 | 82 | // reading until we have exactly enough chars. This will be 1 read for text |
| pythontech | 0:5868e8752d44 | 83 | // with ASCII-only chars, and about 2 reads for text with a couple of non-ASCII |
| pythontech | 0:5868e8752d44 | 84 | // chars. For text with lots of non-ASCII chars, it'll be pretty inefficient |
| pythontech | 0:5868e8752d44 | 85 | // in time and memory. |
| pythontech | 0:5868e8752d44 | 86 | |
| pythontech | 0:5868e8752d44 | 87 | vstr_t vstr; |
| pythontech | 0:5868e8752d44 | 88 | vstr_init(&vstr, sz); |
| pythontech | 0:5868e8752d44 | 89 | mp_uint_t more_bytes = sz; |
| pythontech | 0:5868e8752d44 | 90 | mp_uint_t last_buf_offset = 0; |
| pythontech | 0:5868e8752d44 | 91 | while (more_bytes > 0) { |
| pythontech | 0:5868e8752d44 | 92 | char *p = vstr_add_len(&vstr, more_bytes); |
| pythontech | 0:5868e8752d44 | 93 | if (p == NULL) { |
| pythontech | 0:5868e8752d44 | 94 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_MemoryError, "out of memory")); |
| pythontech | 0:5868e8752d44 | 95 | } |
| pythontech | 0:5868e8752d44 | 96 | int error; |
| pythontech | 0:5868e8752d44 | 97 | mp_uint_t out_sz = stream_p->read(args[0], p, more_bytes, &error); |
| pythontech | 0:5868e8752d44 | 98 | if (out_sz == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 99 | vstr_cut_tail_bytes(&vstr, more_bytes); |
| pythontech | 0:5868e8752d44 | 100 | if (mp_is_nonblocking_error(error)) { |
| pythontech | 0:5868e8752d44 | 101 | // With non-blocking streams, we read as much as we can. |
| pythontech | 0:5868e8752d44 | 102 | // If we read nothing, return None, just like read(). |
| pythontech | 0:5868e8752d44 | 103 | // Otherwise, return data read so far. |
| pythontech | 0:5868e8752d44 | 104 | // TODO what if we have read only half a non-ASCII char? |
| pythontech | 0:5868e8752d44 | 105 | if (vstr.len == 0) { |
| pythontech | 0:5868e8752d44 | 106 | vstr_clear(&vstr); |
| pythontech | 0:5868e8752d44 | 107 | return mp_const_none; |
| pythontech | 0:5868e8752d44 | 108 | } |
| pythontech | 0:5868e8752d44 | 109 | break; |
| pythontech | 0:5868e8752d44 | 110 | } |
| pythontech | 0:5868e8752d44 | 111 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 112 | } |
| pythontech | 0:5868e8752d44 | 113 | |
| pythontech | 0:5868e8752d44 | 114 | if (out_sz < more_bytes) { |
| pythontech | 0:5868e8752d44 | 115 | // Finish reading. |
| pythontech | 0:5868e8752d44 | 116 | // TODO what if we have read only half a non-ASCII char? |
| pythontech | 0:5868e8752d44 | 117 | vstr_cut_tail_bytes(&vstr, more_bytes - out_sz); |
| pythontech | 0:5868e8752d44 | 118 | if (out_sz == 0) { |
| pythontech | 0:5868e8752d44 | 119 | break; |
| pythontech | 0:5868e8752d44 | 120 | } |
| pythontech | 0:5868e8752d44 | 121 | } |
| pythontech | 0:5868e8752d44 | 122 | |
| pythontech | 0:5868e8752d44 | 123 | // count chars from bytes just read |
| pythontech | 0:5868e8752d44 | 124 | for (mp_uint_t off = last_buf_offset;;) { |
| pythontech | 0:5868e8752d44 | 125 | byte b = vstr.buf[off]; |
| pythontech | 0:5868e8752d44 | 126 | int n; |
| pythontech | 0:5868e8752d44 | 127 | if (!UTF8_IS_NONASCII(b)) { |
| pythontech | 0:5868e8752d44 | 128 | // 1-byte ASCII char |
| pythontech | 0:5868e8752d44 | 129 | n = 1; |
| pythontech | 0:5868e8752d44 | 130 | } else if ((b & 0xe0) == 0xc0) { |
| pythontech | 0:5868e8752d44 | 131 | // 2-byte char |
| pythontech | 0:5868e8752d44 | 132 | n = 2; |
| pythontech | 0:5868e8752d44 | 133 | } else if ((b & 0xf0) == 0xe0) { |
| pythontech | 0:5868e8752d44 | 134 | // 3-byte char |
| pythontech | 0:5868e8752d44 | 135 | n = 3; |
| pythontech | 0:5868e8752d44 | 136 | } else if ((b & 0xf8) == 0xf0) { |
| pythontech | 0:5868e8752d44 | 137 | // 4-byte char |
| pythontech | 0:5868e8752d44 | 138 | n = 4; |
| pythontech | 0:5868e8752d44 | 139 | } else { |
| pythontech | 0:5868e8752d44 | 140 | // TODO |
| pythontech | 0:5868e8752d44 | 141 | n = 5; |
| pythontech | 0:5868e8752d44 | 142 | } |
| pythontech | 0:5868e8752d44 | 143 | if (off + n <= vstr.len) { |
| pythontech | 0:5868e8752d44 | 144 | // got a whole char in n bytes |
| pythontech | 0:5868e8752d44 | 145 | off += n; |
| pythontech | 0:5868e8752d44 | 146 | sz -= 1; |
| pythontech | 0:5868e8752d44 | 147 | last_buf_offset = off; |
| pythontech | 0:5868e8752d44 | 148 | if (off >= vstr.len) { |
| pythontech | 0:5868e8752d44 | 149 | more_bytes = sz; |
| pythontech | 0:5868e8752d44 | 150 | break; |
| pythontech | 0:5868e8752d44 | 151 | } |
| pythontech | 0:5868e8752d44 | 152 | } else { |
| pythontech | 0:5868e8752d44 | 153 | // didn't get a whole char, so work out how many extra bytes are needed for |
| pythontech | 0:5868e8752d44 | 154 | // this partial char, plus bytes for additional chars that we want |
| pythontech | 0:5868e8752d44 | 155 | more_bytes = (off + n - vstr.len) + (sz - 1); |
| pythontech | 0:5868e8752d44 | 156 | break; |
| pythontech | 0:5868e8752d44 | 157 | } |
| pythontech | 0:5868e8752d44 | 158 | } |
| pythontech | 0:5868e8752d44 | 159 | } |
| pythontech | 0:5868e8752d44 | 160 | |
| pythontech | 0:5868e8752d44 | 161 | return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); |
| pythontech | 0:5868e8752d44 | 162 | } |
| pythontech | 0:5868e8752d44 | 163 | #endif |
| pythontech | 0:5868e8752d44 | 164 | |
| pythontech | 0:5868e8752d44 | 165 | vstr_t vstr; |
| pythontech | 0:5868e8752d44 | 166 | vstr_init_len(&vstr, sz); |
| pythontech | 0:5868e8752d44 | 167 | int error; |
| pythontech | 0:5868e8752d44 | 168 | mp_uint_t out_sz = stream_p->read(args[0], vstr.buf, sz, &error); |
| pythontech | 0:5868e8752d44 | 169 | if (out_sz == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 170 | vstr_clear(&vstr); |
| pythontech | 0:5868e8752d44 | 171 | if (mp_is_nonblocking_error(error)) { |
| pythontech | 0:5868e8752d44 | 172 | // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read |
| pythontech | 0:5868e8752d44 | 173 | // "If the object is in non-blocking mode and no bytes are available, |
| pythontech | 0:5868e8752d44 | 174 | // None is returned." |
| pythontech | 0:5868e8752d44 | 175 | // This is actually very weird, as naive truth check will treat |
| pythontech | 0:5868e8752d44 | 176 | // this as EOF. |
| pythontech | 0:5868e8752d44 | 177 | return mp_const_none; |
| pythontech | 0:5868e8752d44 | 178 | } |
| pythontech | 0:5868e8752d44 | 179 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 180 | } else { |
| pythontech | 0:5868e8752d44 | 181 | vstr.len = out_sz; |
| pythontech | 0:5868e8752d44 | 182 | return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &vstr); |
| pythontech | 0:5868e8752d44 | 183 | } |
| pythontech | 0:5868e8752d44 | 184 | } |
| pythontech | 0:5868e8752d44 | 185 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj, 1, 2, stream_read); |
| pythontech | 0:5868e8752d44 | 186 | |
| pythontech | 0:5868e8752d44 | 187 | mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len) { |
| pythontech | 0:5868e8752d44 | 188 | const mp_stream_p_t *stream_p = mp_get_stream_raise(self_in, MP_STREAM_OP_WRITE); |
| pythontech | 0:5868e8752d44 | 189 | |
| pythontech | 0:5868e8752d44 | 190 | int error; |
| pythontech | 0:5868e8752d44 | 191 | mp_uint_t out_sz = stream_p->write(self_in, buf, len, &error); |
| pythontech | 0:5868e8752d44 | 192 | if (out_sz == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 193 | if (mp_is_nonblocking_error(error)) { |
| pythontech | 0:5868e8752d44 | 194 | // http://docs.python.org/3/library/io.html#io.RawIOBase.write |
| pythontech | 0:5868e8752d44 | 195 | // "None is returned if the raw stream is set not to block and |
| pythontech | 0:5868e8752d44 | 196 | // no single byte could be readily written to it." |
| pythontech | 0:5868e8752d44 | 197 | // This is for consistency with read() behavior, still weird, |
| pythontech | 0:5868e8752d44 | 198 | // see abobe. |
| pythontech | 0:5868e8752d44 | 199 | return mp_const_none; |
| pythontech | 0:5868e8752d44 | 200 | } |
| pythontech | 0:5868e8752d44 | 201 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 202 | } else { |
| pythontech | 0:5868e8752d44 | 203 | return MP_OBJ_NEW_SMALL_INT(out_sz); |
| pythontech | 0:5868e8752d44 | 204 | } |
| pythontech | 0:5868e8752d44 | 205 | } |
| pythontech | 0:5868e8752d44 | 206 | |
| pythontech | 0:5868e8752d44 | 207 | // XXX hack |
| pythontech | 0:5868e8752d44 | 208 | void mp_stream_write_adaptor(void *self, const char *buf, size_t len) { |
| pythontech | 0:5868e8752d44 | 209 | mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len); |
| pythontech | 0:5868e8752d44 | 210 | } |
| pythontech | 0:5868e8752d44 | 211 | |
| pythontech | 0:5868e8752d44 | 212 | // Works only with blocking streams |
| pythontech | 0:5868e8752d44 | 213 | mp_uint_t mp_stream_writeall(mp_obj_t stream, const byte *buf, mp_uint_t size, int *errcode) { |
| pythontech | 0:5868e8752d44 | 214 | mp_obj_base_t* s = (mp_obj_base_t*)MP_OBJ_TO_PTR(stream); |
| pythontech | 0:5868e8752d44 | 215 | mp_uint_t org_size = size; |
| pythontech | 0:5868e8752d44 | 216 | while (size > 0) { |
| pythontech | 0:5868e8752d44 | 217 | mp_uint_t out_sz = s->type->stream_p->write(stream, buf, size, errcode); |
| pythontech | 0:5868e8752d44 | 218 | if (out_sz == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 219 | return MP_STREAM_ERROR; |
| pythontech | 0:5868e8752d44 | 220 | } |
| pythontech | 0:5868e8752d44 | 221 | buf += out_sz; |
| pythontech | 0:5868e8752d44 | 222 | size -= out_sz; |
| pythontech | 0:5868e8752d44 | 223 | } |
| pythontech | 0:5868e8752d44 | 224 | return org_size; |
| pythontech | 0:5868e8752d44 | 225 | } |
| pythontech | 0:5868e8752d44 | 226 | |
| pythontech | 0:5868e8752d44 | 227 | STATIC mp_obj_t stream_write_method(mp_obj_t self_in, mp_obj_t arg) { |
| pythontech | 0:5868e8752d44 | 228 | mp_buffer_info_t bufinfo; |
| pythontech | 0:5868e8752d44 | 229 | mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); |
| pythontech | 0:5868e8752d44 | 230 | return mp_stream_write(self_in, bufinfo.buf, bufinfo.len); |
| pythontech | 0:5868e8752d44 | 231 | } |
| pythontech | 0:5868e8752d44 | 232 | MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_write_obj, stream_write_method); |
| pythontech | 0:5868e8752d44 | 233 | |
| pythontech | 0:5868e8752d44 | 234 | STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { |
| pythontech | 0:5868e8752d44 | 235 | const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_READ); |
| pythontech | 0:5868e8752d44 | 236 | mp_buffer_info_t bufinfo; |
| pythontech | 0:5868e8752d44 | 237 | mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); |
| pythontech | 0:5868e8752d44 | 238 | |
| pythontech | 0:5868e8752d44 | 239 | // CPython extension: if 2nd arg is provided, that's max len to read, |
| pythontech | 0:5868e8752d44 | 240 | // instead of full buffer. Similar to |
| pythontech | 0:5868e8752d44 | 241 | // https://docs.python.org/3/library/socket.html#socket.socket.recv_into |
| pythontech | 0:5868e8752d44 | 242 | mp_uint_t len = bufinfo.len; |
| pythontech | 0:5868e8752d44 | 243 | if (n_args > 2) { |
| pythontech | 0:5868e8752d44 | 244 | len = mp_obj_get_int(args[2]); |
| pythontech | 0:5868e8752d44 | 245 | if (len > bufinfo.len) { |
| pythontech | 0:5868e8752d44 | 246 | len = bufinfo.len; |
| pythontech | 0:5868e8752d44 | 247 | } |
| pythontech | 0:5868e8752d44 | 248 | } |
| pythontech | 0:5868e8752d44 | 249 | |
| pythontech | 0:5868e8752d44 | 250 | int error; |
| pythontech | 0:5868e8752d44 | 251 | mp_uint_t out_sz = stream_p->read(args[0], bufinfo.buf, len, &error); |
| pythontech | 0:5868e8752d44 | 252 | if (out_sz == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 253 | if (mp_is_nonblocking_error(error)) { |
| pythontech | 0:5868e8752d44 | 254 | return mp_const_none; |
| pythontech | 0:5868e8752d44 | 255 | } |
| pythontech | 0:5868e8752d44 | 256 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 257 | } else { |
| pythontech | 0:5868e8752d44 | 258 | return MP_OBJ_NEW_SMALL_INT(out_sz); |
| pythontech | 0:5868e8752d44 | 259 | } |
| pythontech | 0:5868e8752d44 | 260 | } |
| pythontech | 0:5868e8752d44 | 261 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj, 2, 3, stream_readinto); |
| pythontech | 0:5868e8752d44 | 262 | |
| pythontech | 0:5868e8752d44 | 263 | STATIC mp_obj_t stream_readall(mp_obj_t self_in) { |
| pythontech | 0:5868e8752d44 | 264 | const mp_stream_p_t *stream_p = mp_get_stream_raise(self_in, MP_STREAM_OP_READ); |
| pythontech | 0:5868e8752d44 | 265 | |
| pythontech | 0:5868e8752d44 | 266 | mp_uint_t total_size = 0; |
| pythontech | 0:5868e8752d44 | 267 | vstr_t vstr; |
| pythontech | 0:5868e8752d44 | 268 | vstr_init(&vstr, DEFAULT_BUFFER_SIZE); |
| pythontech | 0:5868e8752d44 | 269 | char *p = vstr.buf; |
| pythontech | 0:5868e8752d44 | 270 | mp_uint_t current_read = DEFAULT_BUFFER_SIZE; |
| pythontech | 0:5868e8752d44 | 271 | while (true) { |
| pythontech | 0:5868e8752d44 | 272 | int error; |
| pythontech | 0:5868e8752d44 | 273 | mp_uint_t out_sz = stream_p->read(self_in, p, current_read, &error); |
| pythontech | 0:5868e8752d44 | 274 | if (out_sz == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 275 | if (mp_is_nonblocking_error(error)) { |
| pythontech | 0:5868e8752d44 | 276 | // With non-blocking streams, we read as much as we can. |
| pythontech | 0:5868e8752d44 | 277 | // If we read nothing, return None, just like read(). |
| pythontech | 0:5868e8752d44 | 278 | // Otherwise, return data read so far. |
| pythontech | 0:5868e8752d44 | 279 | if (total_size == 0) { |
| pythontech | 0:5868e8752d44 | 280 | return mp_const_none; |
| pythontech | 0:5868e8752d44 | 281 | } |
| pythontech | 0:5868e8752d44 | 282 | break; |
| pythontech | 0:5868e8752d44 | 283 | } |
| pythontech | 0:5868e8752d44 | 284 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 285 | } |
| pythontech | 0:5868e8752d44 | 286 | if (out_sz == 0) { |
| pythontech | 0:5868e8752d44 | 287 | break; |
| pythontech | 0:5868e8752d44 | 288 | } |
| pythontech | 0:5868e8752d44 | 289 | total_size += out_sz; |
| pythontech | 0:5868e8752d44 | 290 | if (out_sz < current_read) { |
| pythontech | 0:5868e8752d44 | 291 | current_read -= out_sz; |
| pythontech | 0:5868e8752d44 | 292 | p += out_sz; |
| pythontech | 0:5868e8752d44 | 293 | } else { |
| pythontech | 0:5868e8752d44 | 294 | p = vstr_extend(&vstr, DEFAULT_BUFFER_SIZE); |
| pythontech | 0:5868e8752d44 | 295 | current_read = DEFAULT_BUFFER_SIZE; |
| pythontech | 0:5868e8752d44 | 296 | if (p == NULL) { |
| pythontech | 0:5868e8752d44 | 297 | // TODO |
| pythontech | 0:5868e8752d44 | 298 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError/*&mp_type_RuntimeError*/, "Out of memory")); |
| pythontech | 0:5868e8752d44 | 299 | } |
| pythontech | 0:5868e8752d44 | 300 | } |
| pythontech | 0:5868e8752d44 | 301 | } |
| pythontech | 0:5868e8752d44 | 302 | |
| pythontech | 0:5868e8752d44 | 303 | vstr.len = total_size; |
| pythontech | 0:5868e8752d44 | 304 | return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &vstr); |
| pythontech | 0:5868e8752d44 | 305 | } |
| pythontech | 0:5868e8752d44 | 306 | MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_readall_obj, stream_readall); |
| pythontech | 0:5868e8752d44 | 307 | |
| pythontech | 0:5868e8752d44 | 308 | // Unbuffered, inefficient implementation of readline() for raw I/O files. |
| pythontech | 0:5868e8752d44 | 309 | STATIC mp_obj_t stream_unbuffered_readline(size_t n_args, const mp_obj_t *args) { |
| pythontech | 0:5868e8752d44 | 310 | const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_READ); |
| pythontech | 0:5868e8752d44 | 311 | |
| pythontech | 0:5868e8752d44 | 312 | mp_int_t max_size = -1; |
| pythontech | 0:5868e8752d44 | 313 | if (n_args > 1) { |
| pythontech | 0:5868e8752d44 | 314 | max_size = MP_OBJ_SMALL_INT_VALUE(args[1]); |
| pythontech | 0:5868e8752d44 | 315 | } |
| pythontech | 0:5868e8752d44 | 316 | |
| pythontech | 0:5868e8752d44 | 317 | vstr_t vstr; |
| pythontech | 0:5868e8752d44 | 318 | if (max_size != -1) { |
| pythontech | 0:5868e8752d44 | 319 | vstr_init(&vstr, max_size); |
| pythontech | 0:5868e8752d44 | 320 | } else { |
| pythontech | 0:5868e8752d44 | 321 | vstr_init(&vstr, 16); |
| pythontech | 0:5868e8752d44 | 322 | } |
| pythontech | 0:5868e8752d44 | 323 | |
| pythontech | 0:5868e8752d44 | 324 | while (max_size == -1 || max_size-- != 0) { |
| pythontech | 0:5868e8752d44 | 325 | char *p = vstr_add_len(&vstr, 1); |
| pythontech | 0:5868e8752d44 | 326 | if (p == NULL) { |
| pythontech | 0:5868e8752d44 | 327 | nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_MemoryError, "out of memory")); |
| pythontech | 0:5868e8752d44 | 328 | } |
| pythontech | 0:5868e8752d44 | 329 | |
| pythontech | 0:5868e8752d44 | 330 | int error; |
| pythontech | 0:5868e8752d44 | 331 | mp_uint_t out_sz = stream_p->read(args[0], p, 1, &error); |
| pythontech | 0:5868e8752d44 | 332 | if (out_sz == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 333 | if (mp_is_nonblocking_error(error)) { |
| pythontech | 0:5868e8752d44 | 334 | if (vstr.len == 1) { |
| pythontech | 0:5868e8752d44 | 335 | // We just incremented it, but otherwise we read nothing |
| pythontech | 0:5868e8752d44 | 336 | // and immediately got EAGAIN. This is case is not well |
| pythontech | 0:5868e8752d44 | 337 | // specified in |
| pythontech | 0:5868e8752d44 | 338 | // https://docs.python.org/3/library/io.html#io.IOBase.readline |
| pythontech | 0:5868e8752d44 | 339 | // unlike similar case for read(). But we follow the latter's |
| pythontech | 0:5868e8752d44 | 340 | // behavior - return None. |
| pythontech | 0:5868e8752d44 | 341 | vstr_clear(&vstr); |
| pythontech | 0:5868e8752d44 | 342 | return mp_const_none; |
| pythontech | 0:5868e8752d44 | 343 | } else { |
| pythontech | 0:5868e8752d44 | 344 | goto done; |
| pythontech | 0:5868e8752d44 | 345 | } |
| pythontech | 0:5868e8752d44 | 346 | } |
| pythontech | 0:5868e8752d44 | 347 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 348 | } |
| pythontech | 0:5868e8752d44 | 349 | if (out_sz == 0) { |
| pythontech | 0:5868e8752d44 | 350 | done: |
| pythontech | 0:5868e8752d44 | 351 | // Back out previously added byte |
| pythontech | 0:5868e8752d44 | 352 | // Consider, what's better - read a char and get OutOfMemory (so read |
| pythontech | 0:5868e8752d44 | 353 | // char is lost), or allocate first as we do. |
| pythontech | 0:5868e8752d44 | 354 | vstr_cut_tail_bytes(&vstr, 1); |
| pythontech | 0:5868e8752d44 | 355 | break; |
| pythontech | 0:5868e8752d44 | 356 | } |
| pythontech | 0:5868e8752d44 | 357 | if (*p == '\n') { |
| pythontech | 0:5868e8752d44 | 358 | break; |
| pythontech | 0:5868e8752d44 | 359 | } |
| pythontech | 0:5868e8752d44 | 360 | } |
| pythontech | 0:5868e8752d44 | 361 | |
| pythontech | 0:5868e8752d44 | 362 | return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &vstr); |
| pythontech | 0:5868e8752d44 | 363 | } |
| pythontech | 0:5868e8752d44 | 364 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj, 1, 2, stream_unbuffered_readline); |
| pythontech | 0:5868e8752d44 | 365 | |
| pythontech | 0:5868e8752d44 | 366 | // TODO take an optional extra argument (what does it do exactly?) |
| pythontech | 0:5868e8752d44 | 367 | STATIC mp_obj_t stream_unbuffered_readlines(mp_obj_t self) { |
| pythontech | 0:5868e8752d44 | 368 | mp_obj_t lines = mp_obj_new_list(0, NULL); |
| pythontech | 0:5868e8752d44 | 369 | for (;;) { |
| pythontech | 0:5868e8752d44 | 370 | mp_obj_t line = stream_unbuffered_readline(1, &self); |
| pythontech | 0:5868e8752d44 | 371 | if (!mp_obj_is_true(line)) { |
| pythontech | 0:5868e8752d44 | 372 | break; |
| pythontech | 0:5868e8752d44 | 373 | } |
| pythontech | 0:5868e8752d44 | 374 | mp_obj_list_append(lines, line); |
| pythontech | 0:5868e8752d44 | 375 | } |
| pythontech | 0:5868e8752d44 | 376 | return lines; |
| pythontech | 0:5868e8752d44 | 377 | } |
| pythontech | 0:5868e8752d44 | 378 | MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj, stream_unbuffered_readlines); |
| pythontech | 0:5868e8752d44 | 379 | |
| pythontech | 0:5868e8752d44 | 380 | mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) { |
| pythontech | 0:5868e8752d44 | 381 | mp_obj_t l_in = stream_unbuffered_readline(1, &self); |
| pythontech | 0:5868e8752d44 | 382 | if (mp_obj_is_true(l_in)) { |
| pythontech | 0:5868e8752d44 | 383 | return l_in; |
| pythontech | 0:5868e8752d44 | 384 | } |
| pythontech | 0:5868e8752d44 | 385 | return MP_OBJ_STOP_ITERATION; |
| pythontech | 0:5868e8752d44 | 386 | } |
| pythontech | 0:5868e8752d44 | 387 | |
| pythontech | 0:5868e8752d44 | 388 | STATIC mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) { |
| pythontech | 0:5868e8752d44 | 389 | const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_IOCTL); |
| pythontech | 0:5868e8752d44 | 390 | |
| pythontech | 0:5868e8752d44 | 391 | struct mp_stream_seek_t seek_s; |
| pythontech | 0:5868e8752d44 | 392 | // TODO: Could be uint64 |
| pythontech | 0:5868e8752d44 | 393 | seek_s.offset = mp_obj_get_int(args[1]); |
| pythontech | 0:5868e8752d44 | 394 | seek_s.whence = 0; |
| pythontech | 0:5868e8752d44 | 395 | if (n_args == 3) { |
| pythontech | 0:5868e8752d44 | 396 | seek_s.whence = mp_obj_get_int(args[2]); |
| pythontech | 0:5868e8752d44 | 397 | } |
| pythontech | 0:5868e8752d44 | 398 | |
| pythontech | 0:5868e8752d44 | 399 | int error; |
| pythontech | 0:5868e8752d44 | 400 | mp_uint_t res = stream_p->ioctl(args[0], MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error); |
| pythontech | 0:5868e8752d44 | 401 | if (res == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 402 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 403 | } |
| pythontech | 0:5868e8752d44 | 404 | |
| pythontech | 0:5868e8752d44 | 405 | // TODO: Could be uint64 |
| pythontech | 0:5868e8752d44 | 406 | return mp_obj_new_int_from_uint(seek_s.offset); |
| pythontech | 0:5868e8752d44 | 407 | } |
| pythontech | 0:5868e8752d44 | 408 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj, 2, 3, stream_seek); |
| pythontech | 0:5868e8752d44 | 409 | |
| pythontech | 0:5868e8752d44 | 410 | STATIC mp_obj_t stream_tell(mp_obj_t self) { |
| pythontech | 0:5868e8752d44 | 411 | mp_obj_t offset = MP_OBJ_NEW_SMALL_INT(0); |
| pythontech | 0:5868e8752d44 | 412 | mp_obj_t whence = MP_OBJ_NEW_SMALL_INT(SEEK_CUR); |
| pythontech | 0:5868e8752d44 | 413 | const mp_obj_t args[3] = {self, offset, whence}; |
| pythontech | 0:5868e8752d44 | 414 | return stream_seek(3, args); |
| pythontech | 0:5868e8752d44 | 415 | } |
| pythontech | 0:5868e8752d44 | 416 | MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_tell_obj, stream_tell); |
| pythontech | 0:5868e8752d44 | 417 | |
| pythontech | 0:5868e8752d44 | 418 | STATIC mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) { |
| pythontech | 0:5868e8752d44 | 419 | const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_IOCTL); |
| pythontech | 0:5868e8752d44 | 420 | |
| pythontech | 0:5868e8752d44 | 421 | mp_buffer_info_t bufinfo; |
| pythontech | 0:5868e8752d44 | 422 | uintptr_t val = 0; |
| pythontech | 0:5868e8752d44 | 423 | if (n_args > 2) { |
| pythontech | 0:5868e8752d44 | 424 | if (mp_get_buffer(args[2], &bufinfo, MP_BUFFER_WRITE)) { |
| pythontech | 0:5868e8752d44 | 425 | val = (uintptr_t)bufinfo.buf; |
| pythontech | 0:5868e8752d44 | 426 | } else { |
| pythontech | 0:5868e8752d44 | 427 | val = mp_obj_get_int_truncated(args[2]); |
| pythontech | 0:5868e8752d44 | 428 | } |
| pythontech | 0:5868e8752d44 | 429 | } |
| pythontech | 0:5868e8752d44 | 430 | |
| pythontech | 0:5868e8752d44 | 431 | int error; |
| pythontech | 0:5868e8752d44 | 432 | mp_uint_t res = stream_p->ioctl(args[0], mp_obj_get_int(args[1]), val, &error); |
| pythontech | 0:5868e8752d44 | 433 | if (res == MP_STREAM_ERROR) { |
| pythontech | 0:5868e8752d44 | 434 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); |
| pythontech | 0:5868e8752d44 | 435 | } |
| pythontech | 0:5868e8752d44 | 436 | |
| pythontech | 0:5868e8752d44 | 437 | return mp_obj_new_int(res); |
| pythontech | 0:5868e8752d44 | 438 | } |
| pythontech | 0:5868e8752d44 | 439 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj, 2, 3, stream_ioctl); |