diff options
Diffstat (limited to 'Modules/_io')
-rw-r--r-- | Modules/_io/_iomodule.c | 28 | ||||
-rw-r--r-- | Modules/_io/bufferedio.c | 66 | ||||
-rw-r--r-- | Modules/_io/bytesio.c | 202 | ||||
-rw-r--r-- | Modules/_io/fileio.c | 46 | ||||
-rw-r--r-- | Modules/_io/textio.c | 40 |
5 files changed, 268 insertions, 114 deletions
diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 7c4f9cb651..6f7af41382 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -237,8 +237,8 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) PyObject *raw, *modeobj = NULL, *buffer, *wrapper, *result = NULL; + _Py_IDENTIFIER(_blksize); _Py_IDENTIFIER(isatty); - _Py_IDENTIFIER(fileno); _Py_IDENTIFIER(mode); _Py_IDENTIFIER(close); @@ -380,24 +380,14 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds) line_buffering = 0; if (buffering < 0) { - buffering = DEFAULT_BUFFER_SIZE; -#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - { - struct stat st; - long fileno; - PyObject *res = _PyObject_CallMethodId(raw, &PyId_fileno, NULL); - if (res == NULL) - goto error; - - fileno = PyLong_AsLong(res); - Py_DECREF(res); - if (fileno == -1 && PyErr_Occurred()) - goto error; - - if (fstat(fileno, &st) >= 0 && st.st_blksize > 1) - buffering = st.st_blksize; - } -#endif + PyObject *blksize_obj; + blksize_obj = _PyObject_GetAttrId(raw, &PyId__blksize); + if (blksize_obj == NULL) + goto error; + buffering = PyLong_AsLong(blksize_obj); + Py_DECREF(blksize_obj); + if (buffering == -1 && PyErr_Occurred()) + goto error; } if (buffering < 0) { PyErr_SetString(PyExc_ValueError, diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 4c0262e25a..2feda5a350 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -24,6 +24,7 @@ _Py_IDENTIFIER(read); _Py_IDENTIFIER(read1); _Py_IDENTIFIER(readable); _Py_IDENTIFIER(readinto); +_Py_IDENTIFIER(readinto1); _Py_IDENTIFIER(writable); _Py_IDENTIFIER(write); @@ -47,17 +48,21 @@ PyDoc_STRVAR(bufferediobase_doc, ); static PyObject * -bufferediobase_readinto(PyObject *self, PyObject *args) +_bufferediobase_readinto_generic(PyObject *self, PyObject *args, char readinto1) { Py_buffer buf; Py_ssize_t len; PyObject *data; - if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) { + if (!PyArg_ParseTuple(args, + readinto1 ? "w*:readinto1" : "w*:readinto", + &buf)) { return NULL; } - data = _PyObject_CallMethodId(self, &PyId_read, "n", buf.len); + data = _PyObject_CallMethodId(self, + readinto1 ? &PyId_read1 : &PyId_read, + "n", buf.len); if (data == NULL) goto error; @@ -89,6 +94,18 @@ bufferediobase_readinto(PyObject *self, PyObject *args) } static PyObject * +bufferediobase_readinto(PyObject *self, PyObject *args) +{ + return _bufferediobase_readinto_generic(self, args, 0); +} + +static PyObject * +bufferediobase_readinto1(PyObject *self, PyObject *args) +{ + return _bufferediobase_readinto_generic(self, args, 1); +} + +static PyObject * bufferediobase_unsupported(const char *message) { _PyIO_State *state = IO_STATE(); @@ -167,6 +184,7 @@ static PyMethodDef bufferediobase_methods[] = { {"read", bufferediobase_read, METH_VARARGS, bufferediobase_read_doc}, {"read1", bufferediobase_read1, METH_VARARGS, bufferediobase_read1_doc}, {"readinto", bufferediobase_readinto, METH_VARARGS, NULL}, + {"readinto1", bufferediobase_readinto1, METH_VARARGS, NULL}, {"write", bufferediobase_write, METH_VARARGS, bufferediobase_write_doc}, {NULL, NULL} }; @@ -989,7 +1007,7 @@ buffered_read1(buffered *self, PyObject *args) } static PyObject * -buffered_readinto(buffered *self, PyObject *args) +_buffered_readinto_generic(buffered *self, PyObject *args, char readinto1) { Py_buffer buf; Py_ssize_t n, written = 0, remaining; @@ -997,7 +1015,9 @@ buffered_readinto(buffered *self, PyObject *args) CHECK_INITIALIZED(self) - if (!PyArg_ParseTuple(args, "w*:readinto", &buf)) + if (!PyArg_ParseTuple(args, + readinto1 ? "w*:readinto1" : "w*:readinto", + &buf)) return NULL; n = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); @@ -1035,7 +1055,10 @@ buffered_readinto(buffered *self, PyObject *args) n = _bufferedreader_raw_read(self, (char *) buf.buf + written, remaining); } - else { + + /* In readinto1 mode, we do not want to fill the internal + buffer if we already have some data to return */ + else if (!(readinto1 && written)) { n = _bufferedreader_fill_buffer(self); if (n > 0) { if (n > remaining) @@ -1046,6 +1069,9 @@ buffered_readinto(buffered *self, PyObject *args) continue; /* short circuit */ } } + else + n = 0; + if (n == 0 || (n == -2 && written > 0)) break; if (n < 0) { @@ -1055,6 +1081,12 @@ buffered_readinto(buffered *self, PyObject *args) } goto end; } + + /* At most one read in readinto1 mode */ + if (readinto1) { + written += n; + break; + } } res = PyLong_FromSsize_t(written); @@ -1066,6 +1098,19 @@ end_unlocked: } static PyObject * +buffered_readinto(buffered *self, PyObject *args) +{ + return _buffered_readinto_generic(self, args, 0); +} + +static PyObject * +buffered_readinto1(buffered *self, PyObject *args) +{ + return _buffered_readinto_generic(self, args, 1); +} + + +static PyObject * _buffered_readline(buffered *self, Py_ssize_t limit) { PyObject *res = NULL; @@ -1750,6 +1795,7 @@ static PyMethodDef bufferedreader_methods[] = { {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"seek", (PyCFunction)buffered_seek, METH_VARARGS}, {"tell", (PyCFunction)buffered_tell, METH_NOARGS}, @@ -2349,6 +2395,12 @@ bufferedrwpair_readinto(rwpair *self, PyObject *args) } static PyObject * +bufferedrwpair_readinto1(rwpair *self, PyObject *args) +{ + return _forward_call(self->reader, &PyId_readinto1, args); +} + +static PyObject * bufferedrwpair_write(rwpair *self, PyObject *args) { return _forward_call(self->writer, &PyId_write, args); @@ -2413,6 +2465,7 @@ static PyMethodDef bufferedrwpair_methods[] = { {"peek", (PyCFunction)bufferedrwpair_peek, METH_VARARGS}, {"read1", (PyCFunction)bufferedrwpair_read1, METH_VARARGS}, {"readinto", (PyCFunction)bufferedrwpair_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)bufferedrwpair_readinto1, METH_VARARGS}, {"write", (PyCFunction)bufferedrwpair_write, METH_VARARGS}, {"flush", (PyCFunction)bufferedrwpair_flush, METH_NOARGS}, @@ -2561,6 +2614,7 @@ static PyMethodDef bufferedrandom_methods[] = { {"read", (PyCFunction)buffered_read, METH_VARARGS}, {"read1", (PyCFunction)buffered_read1, METH_VARARGS}, {"readinto", (PyCFunction)buffered_readinto, METH_VARARGS}, + {"readinto1", (PyCFunction)buffered_readinto1, METH_VARARGS}, {"readline", (PyCFunction)buffered_readline, METH_VARARGS}, {"peek", (PyCFunction)buffered_peek, METH_VARARGS}, {"write", (PyCFunction)bufferedwriter_write, METH_VARARGS}, diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 54840bb88a..d8de916185 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -11,6 +11,10 @@ typedef struct { PyObject *dict; PyObject *weakreflist; Py_ssize_t exports; + /** If `initvalue' != NULL, `buf' is a read-only pointer into the PyBytes + * referenced by `initvalue'. It must be copied prior to mutation, and + * released during finalization */ + PyObject *initvalue; } bytesio; typedef struct { @@ -19,11 +23,11 @@ typedef struct { } bytesiobuf; -#define CHECK_CLOSED(self) \ +#define CHECK_CLOSED(self, ret) \ if ((self)->buf == NULL) { \ PyErr_SetString(PyExc_ValueError, \ "I/O operation on closed file."); \ - return NULL; \ + return ret; \ } #define CHECK_EXPORTS(self) \ @@ -33,6 +37,45 @@ typedef struct { return NULL; \ } +/* Ensure we have a buffer suitable for writing, in the case that an initvalue + * object was provided, and we're currently borrowing its buffer. `size' + * indicates the new buffer size allocated as part of unsharing, to avoid a + * redundant reallocation caused by any subsequent mutation. `truncate' + * indicates whether truncation should occur if `size` < self->string_size. + * + * Do nothing if the buffer wasn't shared. Returns 0 on success, or sets an + * exception and returns -1 on failure. Existing state is preserved on failure. + */ +static int +unshare(bytesio *self, size_t preferred_size, int truncate) +{ + if (self->initvalue) { + Py_ssize_t copy_size; + char *new_buf; + + if((! truncate) && preferred_size < self->string_size) { + preferred_size = self->string_size; + } + + new_buf = (char *)PyMem_Malloc(preferred_size); + if (new_buf == NULL) { + PyErr_NoMemory(); + return -1; + } + + copy_size = self->string_size; + if (copy_size > preferred_size) { + copy_size = preferred_size; + } + + memcpy(new_buf, self->buf, copy_size); + Py_CLEAR(self->initvalue); + self->buf = new_buf; + self->buf_size = preferred_size; + self->string_size = (Py_ssize_t) copy_size; + } + return 0; +} /* Internal routine to get a line from the buffer of a BytesIO object. Returns the length between the current position to the @@ -125,11 +168,18 @@ resize_buffer(bytesio *self, size_t size) static Py_ssize_t write_bytes(bytesio *self, const char *bytes, Py_ssize_t len) { + size_t desired; + assert(self->buf != NULL); assert(self->pos >= 0); assert(len >= 0); - if ((size_t)self->pos + len > self->buf_size) { + desired = (size_t)self->pos + len; + if (unshare(self, desired, 0) < 0) { + return -1; + } + + if (desired > self->buf_size) { if (resize_buffer(self, (size_t)self->pos + len) < 0) return -1; } @@ -160,6 +210,74 @@ write_bytes(bytesio *self, const char *bytes, Py_ssize_t len) return len; } +/* Release or free any existing buffer, and place the BytesIO in the closed + * state. */ +static void +reset(bytesio *self) +{ + if (self->initvalue) { + Py_CLEAR(self->initvalue); + } else if (self->buf) { + PyMem_Free(self->buf); + } + self->buf = NULL; + self->string_size = 0; + self->pos = 0; +} + +/* Reinitialize with a new heap-allocated buffer of size `size`. Returns 0 on + * success, or sets an exception and returns -1 on failure. Existing state is + * preserved on failure. */ +static int +reinit_private(bytesio *self, Py_ssize_t size) +{ + char *tmp = (char *)PyMem_Malloc(size); + if (tmp == NULL) { + PyErr_NoMemory(); + return -1; + } + reset(self); + self->buf = tmp; + self->buf_size = size; + return 0; +} + +/* Internal version of BytesIO.__init__; resets the object to its initial + * (closed) state before repopulating it, optionally by sharing a PyBytes + * buffer provided by `initvalue'. Returns 0 on success, or sets an exception + * and returns -1 on failure. */ +static int +reinit(bytesio *self, PyObject *initvalue) +{ + CHECK_CLOSED(self, -1); + + if (initvalue == NULL || initvalue == Py_None) { + if (reinit_private(self, 0) < 0) { + return -1; + } + } else if (PyBytes_CheckExact(initvalue)) { + reset(self); + Py_INCREF(initvalue); + self->initvalue = initvalue; + self->buf = PyBytes_AS_STRING(initvalue); + self->buf_size = PyBytes_GET_SIZE(initvalue); + self->string_size = PyBytes_GET_SIZE(initvalue); + } else { + Py_buffer buf; + if (PyObject_GetBuffer(initvalue, &buf, PyBUF_CONTIG_RO) < 0) { + return -1; + } + if (reinit_private(self, buf.len) < 0) { + PyBuffer_Release(&buf); + return -1; + } + memcpy(self->buf, buf.buf, buf.len); + self->string_size = buf.len; + PyBuffer_Release(&buf); + } + return 0; +} + static PyObject * bytesio_get_closed(bytesio *self) { @@ -184,7 +302,7 @@ PyDoc_STRVAR(seekable_doc, static PyObject * return_not_closed(bytesio *self) { - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); Py_RETURN_TRUE; } @@ -194,7 +312,7 @@ PyDoc_STRVAR(flush_doc, static PyObject * bytesio_flush(bytesio *self) { - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); Py_RETURN_NONE; } @@ -210,7 +328,7 @@ bytesio_getbuffer(bytesio *self) bytesiobuf *buf; PyObject *view; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); buf = (bytesiobuf *) type->tp_alloc(type, 0); if (buf == NULL) @@ -230,7 +348,7 @@ PyDoc_STRVAR(getval_doc, static PyObject * bytesio_getvalue(bytesio *self) { - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); return PyBytes_FromStringAndSize(self->buf, self->string_size); } @@ -243,7 +361,7 @@ PyDoc_STRVAR(isatty_doc, static PyObject * bytesio_isatty(bytesio *self) { - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); Py_RETURN_FALSE; } @@ -253,7 +371,7 @@ PyDoc_STRVAR(tell_doc, static PyObject * bytesio_tell(bytesio *self) { - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); return PyLong_FromSsize_t(self->pos); } @@ -270,7 +388,7 @@ bytesio_read(bytesio *self, PyObject *args) char *output; PyObject *arg = Py_None; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); if (!PyArg_ParseTuple(args, "|O:read", &arg)) return NULL; @@ -339,7 +457,7 @@ bytesio_readline(bytesio *self, PyObject *args) char *output; PyObject *arg = Py_None; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); if (!PyArg_ParseTuple(args, "|O:readline", &arg)) return NULL; @@ -385,7 +503,7 @@ bytesio_readlines(bytesio *self, PyObject *args) char *output; PyObject *arg = Py_None; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); if (!PyArg_ParseTuple(args, "|O:readlines", &arg)) return NULL; @@ -442,7 +560,7 @@ bytesio_readinto(bytesio *self, PyObject *buffer) void *raw_buffer; Py_ssize_t len, n; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); if (PyObject_AsWriteBuffer(buffer, &raw_buffer, &len) == -1) return NULL; @@ -475,7 +593,7 @@ bytesio_truncate(bytesio *self, PyObject *args) Py_ssize_t size; PyObject *arg = Py_None; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); CHECK_EXPORTS(self); if (!PyArg_ParseTuple(args, "|O:truncate", &arg)) @@ -502,6 +620,10 @@ bytesio_truncate(bytesio *self, PyObject *args) return NULL; } + if (unshare(self, size, 1) < 0) { + return NULL; + } + if (size < self->string_size) { self->string_size = size; if (resize_buffer(self, size) < 0) @@ -517,7 +639,7 @@ bytesio_iternext(bytesio *self) char *next; Py_ssize_t n; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); n = get_line(self, &next); @@ -542,7 +664,7 @@ bytesio_seek(bytesio *self, PyObject *args) Py_ssize_t pos; int mode = 0; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &mode)) return NULL; @@ -597,7 +719,7 @@ bytesio_write(bytesio *self, PyObject *obj) Py_buffer buf; PyObject *result = NULL; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); CHECK_EXPORTS(self); if (PyObject_GetBuffer(obj, &buf, PyBUF_CONTIG_RO) < 0) @@ -625,7 +747,7 @@ bytesio_writelines(bytesio *self, PyObject *v) PyObject *it, *item; PyObject *ret; - CHECK_CLOSED(self); + CHECK_CLOSED(self, NULL); it = PyObject_GetIter(v); if (it == NULL) @@ -655,10 +777,7 @@ PyDoc_STRVAR(close_doc, static PyObject * bytesio_close(bytesio *self) { - if (self->buf != NULL) { - PyMem_Free(self->buf); - self->buf = NULL; - } + reset(self); Py_RETURN_NONE; } @@ -706,11 +825,11 @@ bytesio_getstate(bytesio *self) static PyObject * bytesio_setstate(bytesio *self, PyObject *state) { - PyObject *result; PyObject *position_obj; PyObject *dict; Py_ssize_t pos; + CHECK_EXPORTS(self); assert(state != NULL); /* We allow the state tuple to be longer than 3, because we may need @@ -722,18 +841,13 @@ bytesio_setstate(bytesio *self, PyObject *state) Py_TYPE(self)->tp_name, Py_TYPE(state)->tp_name); return NULL; } - CHECK_EXPORTS(self); - /* Reset the object to its default state. This is only needed to handle - the case of repeated calls to __setstate__. */ - self->string_size = 0; - self->pos = 0; - /* Set the value of the internal buffer. If state[0] does not support the - buffer protocol, bytesio_write will raise the appropriate TypeError. */ - result = bytesio_write(self, PyTuple_GET_ITEM(state, 0)); - if (result == NULL) + /* Reset the object to its default state and set the value of the internal + * buffer. If state[0] does not support the buffer protocol, reinit() will + * raise the appropriate TypeError. */ + if (reinit(self, PyTuple_GET_ITEM(state, 0)) < 0) { return NULL; - Py_DECREF(result); + } /* Set carefully the position value. Alternatively, we could use the seek method instead of modifying self->pos directly to better protect the @@ -788,10 +902,9 @@ bytesio_dealloc(bytesio *self) "deallocated BytesIO object has exported buffers"); PyErr_Print(); } - if (self->buf != NULL) { - PyMem_Free(self->buf); - self->buf = NULL; - } + + reset(self); + Py_CLEAR(self->dict); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); @@ -830,20 +943,7 @@ bytesio_init(bytesio *self, PyObject *args, PyObject *kwds) &initvalue)) return -1; - /* In case, __init__ is called multiple times. */ - self->string_size = 0; - self->pos = 0; - - if (initvalue && initvalue != Py_None) { - PyObject *res; - res = bytesio_write(self, initvalue); - if (res == NULL) - return -1; - Py_DECREF(res); - self->pos = 0; - } - - return 0; + return reinit(self, initvalue); } static PyObject * diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 81e2906a26..280523bc2b 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -53,6 +53,7 @@ typedef struct { signed int seekable : 2; /* -1 means unknown */ unsigned int closefd : 1; char finalizing; + unsigned int blksize; PyObject *weakreflist; PyObject *dict; } fileio; @@ -161,6 +162,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->writable = 0; self->appending = 0; self->seekable = -1; + self->blksize = 0; self->closefd = 1; self->weakreflist = NULL; } @@ -168,26 +170,6 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *) self; } -/* On Unix, open will succeed for directories. - In Python, there should be no file objects referring to - directories, so we need a check. */ - -static int -dircheck(fileio* self, PyObject *nameobj) -{ -#if defined(HAVE_FSTAT) && defined(S_ISDIR) && defined(EISDIR) - struct stat buf; - if (self->fd < 0) - return 0; - if (fstat(self->fd, &buf) == 0 && S_ISDIR(buf.st_mode)) { - errno = EISDIR; - PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj); - return -1; - } -#endif - return 0; -} - static int check_fd(int fd) { @@ -233,6 +215,9 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) #elif !defined(MS_WINDOWS) int *atomic_flag_works = NULL; #endif +#ifdef HAVE_FSTAT + struct stat fdfstat; +#endif assert(PyFileIO_Check(oself)); if (self->fd >= 0) { @@ -421,8 +406,26 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds) goto error; #endif } - if (dircheck(self, nameobj) < 0) + + self->blksize = DEFAULT_BUFFER_SIZE; +#ifdef HAVE_FSTAT + if (fstat(self->fd, &fdfstat) < 0) goto error; +#if defined(S_ISDIR) && defined(EISDIR) + /* On Unix, open will succeed for directories. + In Python, there should be no file objects referring to + directories, so we need a check. */ + if (S_ISDIR(fdfstat.st_mode)) { + errno = EISDIR; + PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj); + goto error; + } +#endif /* defined(S_ISDIR) */ +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + if (fdfstat.st_blksize > 1) + self->blksize = fdfstat.st_blksize; +#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ +#endif /* HAVE_FSTAT */ #if defined(MS_WINDOWS) || defined(__CYGWIN__) /* don't translate newlines (\r\n <=> \n) */ @@ -1216,6 +1219,7 @@ static PyGetSetDef fileio_getsetlist[] = { }; static PyMemberDef fileio_members[] = { + {"_blksize", T_UINT, offsetof(fileio, blksize), 0}, {"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0}, {NULL} }; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 635093efff..aed5b2d606 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -224,8 +224,8 @@ typedef struct { PyObject_HEAD PyObject *decoder; PyObject *errors; - signed int pendingcr: 1; - signed int translate: 1; + unsigned int pendingcr: 1; + unsigned int translate: 1; unsigned int seennl: 3; } nldecoder_object; @@ -546,7 +546,7 @@ incrementalnewlinedecoder_setstate(nldecoder_object *self, PyObject *state) if (!PyArg_Parse(state, "(OK)", &buffer, &flag)) return NULL; - self->pendingcr = (int) flag & 1; + self->pendingcr = (int) (flag & 1); flag >>= 1; if (self->decoder != Py_None) @@ -1440,6 +1440,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) PyObject *dec_buffer = NULL; PyObject *dec_flags = NULL; PyObject *input_chunk = NULL; + Py_buffer input_chunk_buf; PyObject *decoded_chars, *chunk_size; Py_ssize_t nbytes, nchars; int eof; @@ -1471,6 +1472,15 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) Py_DECREF(state); return -1; } + + if (!PyBytes_Check(dec_buffer)) { + PyErr_Format(PyExc_TypeError, + "decoder getstate() should have returned a bytes " + "object, not '%.200s'", + Py_TYPE(dec_buffer)->tp_name); + Py_DECREF(state); + return -1; + } Py_INCREF(dec_buffer); Py_INCREF(dec_flags); Py_DECREF(state); @@ -1483,23 +1493,24 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) chunk_size = PyLong_FromSsize_t(Py_MAX(self->chunk_size, size_hint)); if (chunk_size == NULL) goto fail; + input_chunk = PyObject_CallMethodObjArgs(self->buffer, (self->has_read1 ? _PyIO_str_read1: _PyIO_str_read), chunk_size, NULL); Py_DECREF(chunk_size); if (input_chunk == NULL) goto fail; - if (!PyBytes_Check(input_chunk)) { + + if (PyObject_GetBuffer(input_chunk, &input_chunk_buf, 0) != 0) { PyErr_Format(PyExc_TypeError, - "underlying %s() should have returned a bytes object, " + "underlying %s() should have returned a bytes-like object, " "not '%.200s'", (self->has_read1 ? "read1": "read"), Py_TYPE(input_chunk)->tp_name); goto fail; } - nbytes = PyBytes_Size(input_chunk); + nbytes = input_chunk_buf.len; eof = (nbytes == 0); - if (Py_TYPE(self->decoder) == &PyIncrementalNewlineDecoder_Type) { decoded_chars = _PyIncrementalNewlineDecoder_decode( self->decoder, input_chunk, eof); @@ -1508,6 +1519,7 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) decoded_chars = PyObject_CallMethodObjArgs(self->decoder, _PyIO_str_decode, input_chunk, eof ? Py_True : Py_False, NULL); } + PyBuffer_Release(&input_chunk_buf); if (check_decoded(decoded_chars) < 0) goto fail; @@ -1524,18 +1536,12 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) /* At the snapshot point, len(dec_buffer) bytes before the read, the * next input to be decoded is dec_buffer + input_chunk. */ - PyObject *next_input = PyNumber_Add(dec_buffer, input_chunk); - if (next_input == NULL) - goto fail; - if (!PyBytes_Check(next_input)) { - PyErr_Format(PyExc_TypeError, - "decoder getstate() should have returned a bytes " - "object, not '%.200s'", - Py_TYPE(next_input)->tp_name); - Py_DECREF(next_input); + PyObject *next_input = dec_buffer; + PyBytes_Concat(&next_input, input_chunk); + if (next_input == NULL) { + dec_buffer = NULL; /* Reference lost to PyBytes_Concat */ goto fail; } - Py_DECREF(dec_buffer); Py_CLEAR(self->snapshot); self->snapshot = Py_BuildValue("NN", dec_flags, next_input); } |