Skip to main content

Modules/_io/ (part 8)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/_io/bufferedio.c

This annotation covers buffered I/O read/write paths. See modules_io7_detail for BufferedReader.__init__, BufferedWriter.__init__, and the RawIOBase interface.

Map

LinesSymbolRole
1-100BufferedReader.readFill buffer, return requested bytes
101-200BufferedReader.peekReturn buffered data without consuming
201-300BufferedWriter.writeAppend to buffer, flush when full
301-400BufferedWriter.flushWrite buffer to raw stream
401-600BufferedRWPairCombine separate reader and writer raw streams

Reading

BufferedReader.read

// CPython: Modules/_io/bufferedio.c:1080 buffered_read
static PyObject *
buffered_read(buffered *self, PyObject *args)
{
Py_ssize_t n = -1;
PyObject *res;

if (n == 0) return PyBytes_FromStringAndSize(NULL, 0);
if (n < 0) return _bufferedreader_read_all(self);

/* How much is already buffered? */
Py_ssize_t have = READAHEAD(self);
if (n <= have) {
/* Fast path: entirely in buffer */
res = PyBytes_FromStringAndSize(self->buffer + self->pos, n);
self->pos += n;
return res;
}
/* Slow path: need to refill */
return _bufferedreader_read_generic(self, n);
}

READAHEAD(self) is self->readable.buffer_size - self->pos. The fast path avoids a syscall entirely. _bufferedreader_read_generic may call the raw stream's readinto one or more times to accumulate enough data.

BufferedWriter.write

// CPython: Modules/_io/bufferedio.c:1580 buffered_write
static PyObject *
buffered_write(buffered *self, PyObject *args)
{
Py_ssize_t written = 0;
Py_buffer data;
PyArg_ParseTuple(args, "y*:write", &data);

/* If data fits in remaining buffer space, just copy */
Py_ssize_t avail = self->buffer_size - self->writable.write_end;
if (data.len <= avail) {
memcpy(self->buffer + self->writable.write_end, data.buf, data.len);
self->writable.write_end += data.len;
written = data.len;
} else {
/* Flush buffer, then write */
if (_bufferedwriter_flush_unlocked(self) < 0) goto end;
if (data.len > self->buffer_size) {
/* Bypass buffer for large writes */
written = _bufferedwriter_raw_write(self, data.buf, data.len);
} else {
memcpy(self->buffer, data.buf, data.len);
self->writable.write_end = data.len;
written = data.len;
}
}
...
}

Small writes are copied into the buffer. Large writes bypass the buffer and go directly to the raw stream. This avoids double-copying large data through the buffer.

BufferedReader.peek

// CPython: Modules/_io/bufferedio.c:1160 bufferedreader_peek
static PyObject *
bufferedreader_peek(buffered *self, PyObject *args)
{
Py_ssize_t n = 0;
/* Return whatever is in the buffer without consuming it.
May return less or more than n bytes. */
if (READAHEAD(self) > 0) {
return PyBytes_FromStringAndSize(
self->buffer + self->pos,
self->readable.buffer_size - self->pos);
}
/* Buffer empty: fill it */
if (_bufferedreader_fill_buffer(self) <= 0) return PyBytes_FromStringAndSize(NULL, 0);
return PyBytes_FromStringAndSize(self->buffer, self->readable.buffer_size - self->pos);
}

peek() returns buffered data without advancing pos. Used by TextIOWrapper to look ahead for multi-byte sequences and line endings without consuming bytes that belong to the next read.

gopy notes

BufferedReader is module/io.BufferedReader in module/io/bufferedio.go. read uses a Go byte slice buffer; the fast path is buf[pos:pos+n]. BufferedWriter.write appends to a bytes.Buffer and calls flush when full. peek returns buf[pos:] without advancing.