summaryrefslogtreecommitdiffstats
path: root/Modules/_io/bytesio.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_io/bytesio.c')
-rw-r--r--Modules/_io/bytesio.c657
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