diff options
Diffstat (limited to 'Modules/_io/bytesio.c')
-rw-r--r-- | Modules/_io/bytesio.c | 657 |
1 files changed, 396 insertions, 261 deletions
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 4652356..d46430d 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -2,12 +2,17 @@ #include "structmember.h" /* for offsetof() */ #include "_iomodule.h" +/*[clinic input] +module _io +class _io.BytesIO "bytesio *" "&PyBytesIO_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7f50ec034f5c0b26]*/ + typedef struct { PyObject_HEAD - char *buf; + PyObject *buf; Py_ssize_t pos; Py_ssize_t string_size; - size_t buf_size; PyObject *dict; PyObject *weakreflist; Py_ssize_t exports; @@ -18,6 +23,12 @@ typedef struct { bytesio *source; } bytesiobuf; +/* The bytesio object can be in three states: + * Py_REFCNT(buf) == 1, exports == 0. + * Py_REFCNT(buf) > 1. exports == 0, + first modification or export causes the internal buffer copying. + * exports > 0. Py_REFCNT(buf) == 1, any modifications are forbidden. +*/ #define CHECK_CLOSED(self) \ if ((self)->buf == NULL) { \ @@ -33,40 +44,60 @@ typedef struct { return NULL; \ } +#define SHARED_BUF(self) (Py_REFCNT((self)->buf) > 1) + /* Internal routine to get a line from the buffer of a BytesIO object. Returns the length between the current position to the next newline character. */ static Py_ssize_t -get_line(bytesio *self, char **output) +scan_eol(bytesio *self, Py_ssize_t len) { - char *n; - const char *str_end; - Py_ssize_t len; + const char *start, *n; + Py_ssize_t maxlen; assert(self->buf != NULL); /* Move to the end of the line, up to the end of the string, s. */ - str_end = self->buf + self->string_size; - for (n = self->buf + self->pos; - n < str_end && *n != '\n'; - n++); - - /* Skip the newline character */ - if (n < str_end) - n++; - - /* Get the length from the current position to the end of the line. */ - len = n - (self->buf + self->pos); - *output = self->buf + self->pos; - + start = PyBytes_AS_STRING(self->buf) + self->pos; + maxlen = self->string_size - self->pos; + if (len < 0 || len > maxlen) + len = maxlen; + + if (len) { + n = memchr(start, '\n', len); + if (n) + /* Get the length from the current position to the end of + the line. */ + len = n - start + 1; + } assert(len >= 0); assert(self->pos < PY_SSIZE_T_MAX - len); - self->pos += len; return len; } +/* Internal routine for detaching the shared buffer of BytesIO objects. + The caller should ensure that the 'size' argument is non-negative and + not lesser than self->string_size. Returns 0 on success, -1 otherwise. */ +static int +unshare_buffer(bytesio *self, size_t size) +{ + PyObject *new_buf, *old_buf; + assert(SHARED_BUF(self)); + assert(self->exports == 0); + assert(size >= (size_t)self->string_size); + new_buf = PyBytes_FromStringAndSize(NULL, size); + if (new_buf == NULL) + return -1; + memcpy(PyBytes_AS_STRING(new_buf), PyBytes_AS_STRING(self->buf), + self->string_size); + old_buf = self->buf; + self->buf = new_buf; + Py_DECREF(old_buf); + return 0; +} + /* Internal routine for changing the size of the buffer of BytesIO objects. The caller should ensure that the 'size' argument is non-negative. Returns 0 on success, -1 otherwise. */ @@ -75,8 +106,7 @@ resize_buffer(bytesio *self, size_t size) { /* Here, unsigned types are used to avoid dealing with signed integer overflow, which is undefined in C. */ - size_t alloc = self->buf_size; - char *new_buf = NULL; + size_t alloc = PyBytes_GET_SIZE(self->buf); assert(self->buf != NULL); @@ -104,13 +134,15 @@ resize_buffer(bytesio *self, size_t size) if (alloc > ((size_t)-1) / sizeof(char)) goto overflow; - new_buf = (char *)PyMem_Realloc(self->buf, alloc * sizeof(char)); - if (new_buf == NULL) { - PyErr_NoMemory(); - return -1; + + if (SHARED_BUF(self)) { + if (unshare_buffer(self, alloc) < 0) + return -1; + } + else { + if (_PyBytes_Resize(&self->buf, alloc) < 0) + return -1; } - self->buf_size = alloc; - self->buf = new_buf; return 0; @@ -125,12 +157,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 endpos; assert(self->buf != NULL); assert(self->pos >= 0); assert(len >= 0); - if ((size_t)self->pos + len > self->buf_size) { - if (resize_buffer(self, (size_t)self->pos + len) < 0) + endpos = (size_t)self->pos + len; + if (endpos > (size_t)PyBytes_GET_SIZE(self->buf)) { + if (resize_buffer(self, endpos) < 0) + return -1; + } + else if (SHARED_BUF(self)) { + if (unshare_buffer(self, Py_MAX(endpos, (size_t)self->string_size)) < 0) return -1; } @@ -143,18 +181,18 @@ write_bytes(bytesio *self, const char *bytes, Py_ssize_t len) | | <--to pad-->|<---to write---> | 0 buf position */ - memset(self->buf + self->string_size, '\0', + memset(PyBytes_AS_STRING(self->buf) + self->string_size, '\0', (self->pos - self->string_size) * sizeof(char)); } /* Copy the data to the internal buffer, overwriting some of the existing data if self->pos < self->string_size. */ - memcpy(self->buf + self->pos, bytes, len); - self->pos += len; + memcpy(PyBytes_AS_STRING(self->buf) + self->pos, bytes, len); + self->pos = endpos; /* Set the new length of the internal string if it has changed. */ - if (self->string_size < self->pos) { - self->string_size = self->pos; + if ((size_t)self->string_size < endpos) { + self->string_size = endpos; } return len; @@ -171,40 +209,71 @@ bytesio_get_closed(bytesio *self) } } -PyDoc_STRVAR(readable_doc, -"readable() -> bool. Returns True if the IO object can be read."); +/*[clinic input] +_io.BytesIO.readable + +Returns True if the IO object can be read. +[clinic start generated code]*/ + +static PyObject * +_io_BytesIO_readable_impl(bytesio *self) +/*[clinic end generated code: output=4e93822ad5b62263 input=96c5d0cccfb29f5c]*/ +{ + CHECK_CLOSED(self); + Py_RETURN_TRUE; +} + +/*[clinic input] +_io.BytesIO.writable + +Returns True if the IO object can be written. +[clinic start generated code]*/ -PyDoc_STRVAR(writable_doc, -"writable() -> bool. Returns True if the IO object can be written."); +static PyObject * +_io_BytesIO_writable_impl(bytesio *self) +/*[clinic end generated code: output=64ff6a254b1150b8 input=700eed808277560a]*/ +{ + CHECK_CLOSED(self); + Py_RETURN_TRUE; +} -PyDoc_STRVAR(seekable_doc, -"seekable() -> bool. Returns True if the IO object can be seeked."); +/*[clinic input] +_io.BytesIO.seekable + +Returns True if the IO object can be seeked. +[clinic start generated code]*/ -/* Generic getter for the writable, readable and seekable properties */ static PyObject * -return_not_closed(bytesio *self) +_io_BytesIO_seekable_impl(bytesio *self) +/*[clinic end generated code: output=6b417f46dcc09b56 input=9421f65627a344dd]*/ { CHECK_CLOSED(self); Py_RETURN_TRUE; } -PyDoc_STRVAR(flush_doc, -"flush() -> None. Does nothing."); +/*[clinic input] +_io.BytesIO.flush + +Does nothing. +[clinic start generated code]*/ static PyObject * -bytesio_flush(bytesio *self) +_io_BytesIO_flush_impl(bytesio *self) +/*[clinic end generated code: output=187e3d781ca134a0 input=561ea490be4581a7]*/ { CHECK_CLOSED(self); Py_RETURN_NONE; } -PyDoc_STRVAR(getbuffer_doc, -"getbuffer() -> bytes.\n" -"\n" -"Get a read-write view over the contents of the BytesIO object."); +/*[clinic input] +_io.BytesIO.getbuffer + +Get a read-write view over the contents of the BytesIO object. +[clinic start generated code]*/ static PyObject * -bytesio_getbuffer(bytesio *self) +_io_BytesIO_getbuffer_impl(bytesio *self) +/*[clinic end generated code: output=72cd7c6e13aa09ed input=8f738ef615865176]*/ { PyTypeObject *type = &_PyBytesIOBuffer_Type; bytesiobuf *buf; @@ -222,59 +291,104 @@ bytesio_getbuffer(bytesio *self) return view; } -PyDoc_STRVAR(getval_doc, -"getvalue() -> bytes.\n" -"\n" -"Retrieve the entire contents of the BytesIO object."); +/*[clinic input] +_io.BytesIO.getvalue + +Retrieve the entire contents of the BytesIO object. +[clinic start generated code]*/ static PyObject * -bytesio_getvalue(bytesio *self) +_io_BytesIO_getvalue_impl(bytesio *self) +/*[clinic end generated code: output=b3f6a3233c8fd628 input=4b403ac0af3973ed]*/ { CHECK_CLOSED(self); - return PyBytes_FromStringAndSize(self->buf, self->string_size); + if (self->string_size <= 1 || self->exports > 0) + return PyBytes_FromStringAndSize(PyBytes_AS_STRING(self->buf), + self->string_size); + + if (self->string_size != PyBytes_GET_SIZE(self->buf)) { + if (SHARED_BUF(self)) { + if (unshare_buffer(self, self->string_size) < 0) + return NULL; + } + else { + if (_PyBytes_Resize(&self->buf, self->string_size) < 0) + return NULL; + } + } + Py_INCREF(self->buf); + return self->buf; } -PyDoc_STRVAR(isatty_doc, -"isatty() -> False.\n" -"\n" -"Always returns False since BytesIO objects are not connected\n" -"to a tty-like device."); +/*[clinic input] +_io.BytesIO.isatty + +Always returns False. + +BytesIO objects are not connected to a TTY-like device. +[clinic start generated code]*/ static PyObject * -bytesio_isatty(bytesio *self) +_io_BytesIO_isatty_impl(bytesio *self) +/*[clinic end generated code: output=df67712e669f6c8f input=6f97f0985d13f827]*/ { CHECK_CLOSED(self); Py_RETURN_FALSE; } -PyDoc_STRVAR(tell_doc, -"tell() -> current file position, an integer\n"); +/*[clinic input] +_io.BytesIO.tell + +Current file position, an integer. +[clinic start generated code]*/ static PyObject * -bytesio_tell(bytesio *self) +_io_BytesIO_tell_impl(bytesio *self) +/*[clinic end generated code: output=b54b0f93cd0e5e1d input=b106adf099cb3657]*/ { CHECK_CLOSED(self); return PyLong_FromSsize_t(self->pos); } -PyDoc_STRVAR(read_doc, -"read([size]) -> read at most size bytes, returned as a bytes object.\n" -"\n" -"If the size argument is negative, read until EOF is reached.\n" -"Return an empty bytes object at EOF."); +static PyObject * +read_bytes(bytesio *self, Py_ssize_t size) +{ + char *output; + + assert(self->buf != NULL); + assert(size <= self->string_size); + if (size > 1 && + self->pos == 0 && size == PyBytes_GET_SIZE(self->buf) && + self->exports == 0) { + self->pos += size; + Py_INCREF(self->buf); + return self->buf; + } + + output = PyBytes_AS_STRING(self->buf) + self->pos; + self->pos += size; + return PyBytes_FromStringAndSize(output, size); +} + +/*[clinic input] +_io.BytesIO.read + size as arg: object = None + / + +Read at most size bytes, returned as a bytes object. + +If the size argument is negative, read until EOF is reached. +Return an empty bytes object at EOF. +[clinic start generated code]*/ static PyObject * -bytesio_read(bytesio *self, PyObject *args) +_io_BytesIO_read_impl(bytesio *self, PyObject *arg) +/*[clinic end generated code: output=85dacb535c1e1781 input=cc7ba4a797bb1555]*/ { Py_ssize_t size, n; - char *output; - PyObject *arg = Py_None; CHECK_CLOSED(self); - if (!PyArg_ParseTuple(args, "|O:read", &arg)) - return NULL; - if (PyLong_Check(arg)) { size = PyLong_AsSsize_t(arg); if (size == -1 && PyErr_Occurred()) @@ -298,52 +412,48 @@ bytesio_read(bytesio *self, PyObject *args) size = 0; } - assert(self->buf != NULL); - output = self->buf + self->pos; - self->pos += size; - - return PyBytes_FromStringAndSize(output, size); + return read_bytes(self, size); } -PyDoc_STRVAR(read1_doc, -"read1(size) -> read at most size bytes, returned as a bytes object.\n" -"\n" -"If the size argument is negative or omitted, read until EOF is reached.\n" -"Return an empty bytes object at EOF."); +/*[clinic input] +_io.BytesIO.read1 + size: object + / + +Read at most size bytes, returned as a bytes object. + +If the size argument is negative or omitted, read until EOF is reached. +Return an empty bytes object at EOF. +[clinic start generated code]*/ static PyObject * -bytesio_read1(bytesio *self, PyObject *n) +_io_BytesIO_read1(bytesio *self, PyObject *size) +/*[clinic end generated code: output=16021f5d0ac3d4e2 input=d4f40bb8f2f99418]*/ { - PyObject *arg, *res; - - arg = PyTuple_Pack(1, n); - if (arg == NULL) - return NULL; - res = bytesio_read(self, arg); - Py_DECREF(arg); - return res; + return _io_BytesIO_read_impl(self, size); } -PyDoc_STRVAR(readline_doc, -"readline([size]) -> next line from the file, as a bytes object.\n" -"\n" -"Retain newline. A non-negative size argument limits the maximum\n" -"number of bytes to return (an incomplete line may be returned then).\n" -"Return an empty bytes object at EOF.\n"); +/*[clinic input] +_io.BytesIO.readline + size as arg: object = None + / + +Next line from the file, as a bytes object. + +Retain newline. A non-negative size argument limits the maximum +number of bytes to return (an incomplete line may be returned then). +Return an empty bytes object at EOF. +[clinic start generated code]*/ static PyObject * -bytesio_readline(bytesio *self, PyObject *args) +_io_BytesIO_readline_impl(bytesio *self, PyObject *arg) +/*[clinic end generated code: output=1c2115534a4f9276 input=ca31f06de6eab257]*/ { Py_ssize_t size, n; - char *output; - PyObject *arg = Py_None; CHECK_CLOSED(self); - if (!PyArg_ParseTuple(args, "|O:readline", &arg)) - return NULL; - if (PyLong_Check(arg)) { size = PyLong_AsSsize_t(arg); if (size == -1 && PyErr_Occurred()) @@ -359,37 +469,33 @@ bytesio_readline(bytesio *self, PyObject *args) return NULL; } - n = get_line(self, &output); - - if (size >= 0 && size < n) { - size = n - size; - n -= size; - self->pos -= size; - } + n = scan_eol(self, size); - return PyBytes_FromStringAndSize(output, n); + return read_bytes(self, n); } -PyDoc_STRVAR(readlines_doc, -"readlines([size]) -> list of strings, each a line from the file.\n" -"\n" -"Call readline() repeatedly and return a list of the lines so read.\n" -"The optional size argument, if given, is an approximate bound on the\n" -"total number of bytes in the lines returned.\n"); +/*[clinic input] +_io.BytesIO.readlines + size as arg: object = None + / + +List of bytes objects, each a line from the file. + +Call readline() repeatedly and return a list of the lines so read. +The optional size argument, if given, is an approximate bound on the +total number of bytes in the lines returned. +[clinic start generated code]*/ static PyObject * -bytesio_readlines(bytesio *self, PyObject *args) +_io_BytesIO_readlines_impl(bytesio *self, PyObject *arg) +/*[clinic end generated code: output=09b8e34c880808ff input=691aa1314f2c2a87]*/ { Py_ssize_t maxsize, size, n; PyObject *result, *line; char *output; - PyObject *arg = Py_None; CHECK_CLOSED(self); - if (!PyArg_ParseTuple(args, "|O:readlines", &arg)) - return NULL; - if (PyLong_Check(arg)) { maxsize = PyLong_AsSsize_t(arg); if (maxsize == -1 && PyErr_Occurred()) @@ -410,7 +516,9 @@ bytesio_readlines(bytesio *self, PyObject *args) if (!result) return NULL; - while ((n = get_line(self, &output)) != 0) { + output = PyBytes_AS_STRING(self->buf) + self->pos; + while ((n = scan_eol(self, -1)) != 0) { + self->pos += n; line = PyBytes_FromStringAndSize(output, n); if (!line) goto on_error; @@ -422,6 +530,7 @@ bytesio_readlines(bytesio *self, PyObject *args) size += n; if (maxsize > 0 && size >= maxsize) break; + output += n; } return result; @@ -430,25 +539,27 @@ bytesio_readlines(bytesio *self, PyObject *args) return NULL; } -PyDoc_STRVAR(readinto_doc, -"readinto(bytearray) -> int. Read up to len(b) bytes into b.\n" -"\n" -"Returns number of bytes read (0 for EOF), or None if the object\n" -"is set not to block as has no data to read."); +/*[clinic input] +_io.BytesIO.readinto + buffer: Py_buffer(accept={rwbuffer}) + / + +Read up to len(buffer) bytes into buffer. + +Returns number of bytes read (0 for EOF), or None if the object +is set not to block as has no data to read. +[clinic start generated code]*/ static PyObject * -bytesio_readinto(bytesio *self, PyObject *arg) +_io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer) +/*[clinic end generated code: output=a5d407217dcf0639 input=71581f32635c3a31]*/ { - Py_buffer buffer; Py_ssize_t len, n; CHECK_CLOSED(self); - if (!PyArg_Parse(arg, "w*", &buffer)) - return NULL; - /* adjust invalid sizes */ - len = buffer.len; + len = buffer->len; n = self->string_size - self->pos; if (len > n) { len = n; @@ -456,33 +567,34 @@ bytesio_readinto(bytesio *self, PyObject *arg) len = 0; } - memcpy(buffer.buf, self->buf + self->pos, len); + memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len); assert(self->pos + len < PY_SSIZE_T_MAX); assert(len >= 0); self->pos += len; - PyBuffer_Release(&buffer); return PyLong_FromSsize_t(len); } -PyDoc_STRVAR(truncate_doc, -"truncate([size]) -> int. Truncate the file to at most size bytes.\n" -"\n" -"Size defaults to the current file position, as returned by tell().\n" -"The current file position is unchanged. Returns the new size.\n"); +/*[clinic input] +_io.BytesIO.truncate + size as arg: object = None + / + +Truncate the file to at most size bytes. + +Size defaults to the current file position, as returned by tell(). +The current file position is unchanged. Returns the new size. +[clinic start generated code]*/ static PyObject * -bytesio_truncate(bytesio *self, PyObject *args) +_io_BytesIO_truncate_impl(bytesio *self, PyObject *arg) +/*[clinic end generated code: output=81e6be60e67ddd66 input=11ed1966835462ba]*/ { Py_ssize_t size; - PyObject *arg = Py_None; CHECK_CLOSED(self); CHECK_EXPORTS(self); - if (!PyArg_ParseTuple(args, "|O:truncate", &arg)) - return NULL; - if (PyLong_Check(arg)) { size = PyLong_AsSsize_t(arg); if (size == -1 && PyErr_Occurred()) @@ -516,49 +628,49 @@ bytesio_truncate(bytesio *self, PyObject *args) static PyObject * bytesio_iternext(bytesio *self) { - char *next; Py_ssize_t n; CHECK_CLOSED(self); - n = get_line(self, &next); + n = scan_eol(self, -1); - if (!next || n == 0) + if (n == 0) return NULL; - return PyBytes_FromStringAndSize(next, n); + return read_bytes(self, n); } -PyDoc_STRVAR(seek_doc, -"seek(pos, whence=0) -> int. Change stream position.\n" -"\n" -"Seek to byte offset pos relative to position indicated by whence:\n" -" 0 Start of stream (the default). pos should be >= 0;\n" -" 1 Current position - pos may be negative;\n" -" 2 End of stream - pos usually negative.\n" -"Returns the new absolute position."); +/*[clinic input] +_io.BytesIO.seek + pos: Py_ssize_t + whence: int = 0 + / + +Change stream position. + +Seek to byte offset pos relative to position indicated by whence: + 0 Start of stream (the default). pos should be >= 0; + 1 Current position - pos may be negative; + 2 End of stream - pos usually negative. +Returns the new absolute position. +[clinic start generated code]*/ static PyObject * -bytesio_seek(bytesio *self, PyObject *args) +_io_BytesIO_seek_impl(bytesio *self, Py_ssize_t pos, int whence) +/*[clinic end generated code: output=c26204a68e9190e4 input=1e875e6ebc652948]*/ { - Py_ssize_t pos; - int mode = 0; - CHECK_CLOSED(self); - if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &mode)) - return NULL; - - if (pos < 0 && mode == 0) { + if (pos < 0 && whence == 0) { PyErr_Format(PyExc_ValueError, "negative seek value %zd", pos); return NULL; } - /* mode 0: offset relative to beginning of the string. - mode 1: offset relative to current position. - mode 2: offset relative the end of the string. */ - if (mode == 1) { + /* whence = 0: offset relative to beginning of the string. + whence = 1: offset relative to current position. + whence = 2: offset relative the end of the string. */ + if (whence == 1) { if (pos > PY_SSIZE_T_MAX - self->pos) { PyErr_SetString(PyExc_OverflowError, "new position too large"); @@ -566,7 +678,7 @@ bytesio_seek(bytesio *self, PyObject *args) } pos += self->pos; } - else if (mode == 2) { + else if (whence == 2) { if (pos > PY_SSIZE_T_MAX - self->string_size) { PyErr_SetString(PyExc_OverflowError, "new position too large"); @@ -574,9 +686,9 @@ bytesio_seek(bytesio *self, PyObject *args) } pos += self->string_size; } - else if (mode != 0) { + else if (whence != 0) { PyErr_Format(PyExc_ValueError, - "invalid whence (%i, should be 0, 1 or 2)", mode); + "invalid whence (%i, should be 0, 1 or 2)", whence); return NULL; } @@ -587,54 +699,63 @@ bytesio_seek(bytesio *self, PyObject *args) return PyLong_FromSsize_t(self->pos); } -PyDoc_STRVAR(write_doc, -"write(bytes) -> int. Write bytes to file.\n" -"\n" -"Return the number of bytes written."); +/*[clinic input] +_io.BytesIO.write + b: object + / + +Write bytes to file. + +Return the number of bytes written. +[clinic start generated code]*/ static PyObject * -bytesio_write(bytesio *self, PyObject *obj) +_io_BytesIO_write(bytesio *self, PyObject *b) +/*[clinic end generated code: output=53316d99800a0b95 input=f5ec7c8c64ed720a]*/ { Py_ssize_t n = 0; Py_buffer buf; - PyObject *result = NULL; CHECK_CLOSED(self); CHECK_EXPORTS(self); - if (PyObject_GetBuffer(obj, &buf, PyBUF_CONTIG_RO) < 0) + if (PyObject_GetBuffer(b, &buf, PyBUF_CONTIG_RO) < 0) return NULL; if (buf.len != 0) n = write_bytes(self, buf.buf, buf.len); - if (n >= 0) - result = PyLong_FromSsize_t(n); PyBuffer_Release(&buf); - return result; + return n >= 0 ? PyLong_FromSsize_t(n) : NULL; } -PyDoc_STRVAR(writelines_doc, -"writelines(lines) -> None. Write bytes objects to the file.\n" -"\n" -"Note that newlines are not added. The argument can be any iterable\n" -"object producing bytes objects. This is equivalent to calling write() for\n" -"each bytes object."); +/*[clinic input] +_io.BytesIO.writelines + lines: object + / + +Write lines to the file. + +Note that newlines are not added. lines can be any iterable object +producing bytes-like objects. This is equivalent to calling write() for +each element. +[clinic start generated code]*/ static PyObject * -bytesio_writelines(bytesio *self, PyObject *v) +_io_BytesIO_writelines(bytesio *self, PyObject *lines) +/*[clinic end generated code: output=7f33aa3271c91752 input=e972539176fc8fc1]*/ { PyObject *it, *item; PyObject *ret; CHECK_CLOSED(self); - it = PyObject_GetIter(v); + it = PyObject_GetIter(lines); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { - ret = bytesio_write(self, item); + ret = _io_BytesIO_write(self, item); Py_DECREF(item); if (ret == NULL) { Py_DECREF(it); @@ -651,17 +772,18 @@ bytesio_writelines(bytesio *self, PyObject *v) Py_RETURN_NONE; } -PyDoc_STRVAR(close_doc, -"close() -> None. Disable all I/O operations."); +/*[clinic input] +_io.BytesIO.close + +Disable all I/O operations. +[clinic start generated code]*/ static PyObject * -bytesio_close(bytesio *self) +_io_BytesIO_close_impl(bytesio *self) +/*[clinic end generated code: output=1471bb9411af84a0 input=37e1f55556e61f60]*/ { CHECK_EXPORTS(self); - if (self->buf != NULL) { - PyMem_Free(self->buf); - self->buf = NULL; - } + Py_CLEAR(self->buf); Py_RETURN_NONE; } @@ -683,7 +805,7 @@ bytesio_close(bytesio *self) static PyObject * bytesio_getstate(bytesio *self) { - PyObject *initvalue = bytesio_getvalue(self); + PyObject *initvalue = _io_BytesIO_getvalue_impl(self); PyObject *dict; PyObject *state; @@ -733,7 +855,7 @@ bytesio_setstate(bytesio *self, PyObject *state) /* 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)); + result = _io_BytesIO_write(self, PyTuple_GET_ITEM(state, 0)); if (result == NULL) return NULL; Py_DECREF(result); @@ -791,10 +913,7 @@ bytesio_dealloc(bytesio *self) "deallocated BytesIO object has exported buffers"); PyErr_Print(); } - if (self->buf != NULL) { - PyMem_Free(self->buf); - self->buf = NULL; - } + Py_CLEAR(self->buf); Py_CLEAR(self->dict); if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *) self); @@ -814,7 +933,7 @@ bytesio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* tp_alloc initializes all the fields to zero. So we don't have to initialize them here. */ - self->buf = (char *)PyMem_Malloc(0); + self->buf = PyBytes_FromStringAndSize(NULL, 0); if (self->buf == NULL) { Py_DECREF(self); return PyErr_NoMemory(); @@ -823,27 +942,41 @@ bytesio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)self; } -static int -bytesio_init(bytesio *self, PyObject *args, PyObject *kwds) -{ - char *kwlist[] = {"initial_bytes", NULL}; - PyObject *initvalue = NULL; +/*[clinic input] +_io.BytesIO.__init__ + initial_bytes as initvalue: object(c_default="NULL") = b'' - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:BytesIO", kwlist, - &initvalue)) - return -1; +Buffered I/O implementation using an in-memory bytes buffer. +[clinic start generated code]*/ +static int +_io_BytesIO___init___impl(bytesio *self, PyObject *initvalue) +/*[clinic end generated code: output=65c0c51e24c5b621 input=aac7f31b67bf0fb6]*/ +{ /* In case, __init__ is called multiple times. */ self->string_size = 0; self->pos = 0; + if (self->exports > 0) { + PyErr_SetString(PyExc_BufferError, + "Existing exports of data: object cannot be re-sized"); + return -1; + } if (initvalue && initvalue != Py_None) { - PyObject *res; - res = bytesio_write(self, initvalue); - if (res == NULL) - return -1; - Py_DECREF(res); - self->pos = 0; + if (PyBytes_CheckExact(initvalue)) { + Py_INCREF(initvalue); + Py_XDECREF(self->buf); + self->buf = initvalue; + self->string_size = PyBytes_GET_SIZE(initvalue); + } + else { + PyObject *res; + res = _io_BytesIO_write(self, initvalue); + if (res == NULL) + return -1; + Py_DECREF(res); + self->pos = 0; + } } return 0; @@ -855,8 +988,8 @@ bytesio_sizeof(bytesio *self, void *unused) Py_ssize_t res; res = sizeof(bytesio); - if (self->buf) - res += self->buf_size; + if (self->buf && !SHARED_BUF(self)) + res += _PySys_GetSizeOf(self->buf); return PyLong_FromSsize_t(res); } @@ -875,6 +1008,8 @@ bytesio_clear(bytesio *self) } +#include "clinic/bytesio.c.h" + static PyGetSetDef bytesio_getsetlist[] = { {"closed", (getter)bytesio_get_closed, NULL, "True if the file is closed."}, @@ -882,36 +1017,30 @@ static PyGetSetDef bytesio_getsetlist[] = { }; static struct PyMethodDef bytesio_methods[] = { - {"readable", (PyCFunction)return_not_closed, METH_NOARGS, readable_doc}, - {"seekable", (PyCFunction)return_not_closed, METH_NOARGS, seekable_doc}, - {"writable", (PyCFunction)return_not_closed, METH_NOARGS, writable_doc}, - {"close", (PyCFunction)bytesio_close, METH_NOARGS, close_doc}, - {"flush", (PyCFunction)bytesio_flush, METH_NOARGS, flush_doc}, - {"isatty", (PyCFunction)bytesio_isatty, METH_NOARGS, isatty_doc}, - {"tell", (PyCFunction)bytesio_tell, METH_NOARGS, tell_doc}, - {"write", (PyCFunction)bytesio_write, METH_O, write_doc}, - {"writelines", (PyCFunction)bytesio_writelines, METH_O, writelines_doc}, - {"read1", (PyCFunction)bytesio_read1, METH_O, read1_doc}, - {"readinto", (PyCFunction)bytesio_readinto, METH_O, readinto_doc}, - {"readline", (PyCFunction)bytesio_readline, METH_VARARGS, readline_doc}, - {"readlines", (PyCFunction)bytesio_readlines, METH_VARARGS, readlines_doc}, - {"read", (PyCFunction)bytesio_read, METH_VARARGS, read_doc}, - {"getbuffer", (PyCFunction)bytesio_getbuffer, METH_NOARGS, getbuffer_doc}, - {"getvalue", (PyCFunction)bytesio_getvalue, METH_NOARGS, getval_doc}, - {"seek", (PyCFunction)bytesio_seek, METH_VARARGS, seek_doc}, - {"truncate", (PyCFunction)bytesio_truncate, METH_VARARGS, truncate_doc}, + _IO_BYTESIO_READABLE_METHODDEF + _IO_BYTESIO_SEEKABLE_METHODDEF + _IO_BYTESIO_WRITABLE_METHODDEF + _IO_BYTESIO_CLOSE_METHODDEF + _IO_BYTESIO_FLUSH_METHODDEF + _IO_BYTESIO_ISATTY_METHODDEF + _IO_BYTESIO_TELL_METHODDEF + _IO_BYTESIO_WRITE_METHODDEF + _IO_BYTESIO_WRITELINES_METHODDEF + _IO_BYTESIO_READ1_METHODDEF + _IO_BYTESIO_READINTO_METHODDEF + _IO_BYTESIO_READLINE_METHODDEF + _IO_BYTESIO_READLINES_METHODDEF + _IO_BYTESIO_READ_METHODDEF + _IO_BYTESIO_GETBUFFER_METHODDEF + _IO_BYTESIO_GETVALUE_METHODDEF + _IO_BYTESIO_SEEK_METHODDEF + _IO_BYTESIO_TRUNCATE_METHODDEF {"__getstate__", (PyCFunction)bytesio_getstate, METH_NOARGS, NULL}, {"__setstate__", (PyCFunction)bytesio_setstate, METH_O, NULL}, {"__sizeof__", (PyCFunction)bytesio_sizeof, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; -PyDoc_STRVAR(bytesio_doc, -"BytesIO([buffer]) -> object\n" -"\n" -"Create a buffered I/O implementation using an in-memory bytes\n" -"buffer, ready for reading and writing."); - PyTypeObject PyBytesIO_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_io.BytesIO", /*tp_name*/ @@ -934,7 +1063,7 @@ PyTypeObject PyBytesIO_Type = { 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ - bytesio_doc, /*tp_doc*/ + _io_BytesIO___init____doc__, /*tp_doc*/ (traverseproc)bytesio_traverse, /*tp_traverse*/ (inquiry)bytesio_clear, /*tp_clear*/ 0, /*tp_richcompare*/ @@ -949,7 +1078,7 @@ PyTypeObject PyBytesIO_Type = { 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ offsetof(bytesio, dict), /*tp_dictoffset*/ - (initproc)bytesio_init, /*tp_init*/ + _io_BytesIO___init__, /*tp_init*/ 0, /*tp_alloc*/ bytesio_new, /*tp_new*/ }; @@ -964,18 +1093,24 @@ PyTypeObject PyBytesIO_Type = { static int bytesiobuf_getbuffer(bytesiobuf *obj, Py_buffer *view, int flags) { - int ret; bytesio *b = (bytesio *) obj->source; + if (view == NULL) { - b->exports++; - return 0; + PyErr_SetString(PyExc_BufferError, + "bytesiobuf_getbuffer: view==NULL argument is obsolete"); + return -1; } - ret = PyBuffer_FillInfo(view, (PyObject*)obj, b->buf, b->string_size, - 0, flags); - if (ret >= 0) { - b->exports++; + if (SHARED_BUF(b)) { + if (unshare_buffer(b, b->string_size) < 0) + return -1; } - return ret; + + /* cannot fail if view != NULL and readonly == 0 */ + (void)PyBuffer_FillInfo(view, (PyObject*)obj, + PyBytes_AS_STRING(b->buf), b->string_size, + 0, flags); + b->exports++; + return 0; } static void |