Skip to main content

Modules/_io/bufferedio.c

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

Modules/_io/bufferedio.c implements the buffered I/O layer of CPython's _io module. It provides BufferedReader, BufferedWriter, BufferedRandom, and BufferedRWPair, each wrapping a raw FileIO-like object. BufferedReader maintains a read-ahead buffer; BufferedWriter coalesces small writes into a larger block before calling raw.write(); BufferedRandom combines both for seekable files.

Map​

LinesSymbolRole
1-200buffered structBuffer, pos, raw pointer, lock, and readahead fields
201-600BufferedReader init and _bufferedreader_read_genericRead path with buffer refill
601-900_bufferedreader_readintoReadinto bypassing buffer when request exceeds buffer size
901-1200BufferedWriter init and _bufferedwriter_flush_unlockedWrite path and flush
1201-1500buffered_seek, buffered_tellSeek adjusting logical position and discarding stale buffer
1501-1800BufferedRandomCombines reader and writer, switching modes on seek
1801-2200BufferedRWPairWrapper pairing two separate reader and writer objects
2201-2800type objects and PyInitPyBufferedReader_Type, PyBufferedWriter_Type

Reading​

Read-ahead buffer and buffer refill​

BufferedReader maintains a contiguous buffer of buffer_size bytes. read(n) first copies from the buffer, then calls raw.read() to refill when needed. The logical read position is raw_pos - (raw_buf_end - pos), tracking how far ahead the raw layer has read.

// CPython: Modules/_io/bufferedio.c:300 _bufferedreader_fill_buffer
static Py_ssize_t
_bufferedreader_fill_buffer(buffered *self)
{
Py_ssize_t start, len, n;
if (VALID_READ_BUFFER(self))
start = self->read_end;
else
start = 0;
len = self->buffer_size - start;
n = self->raw->ob_type->tp_as_buffer... /* raw.readinto(view) */
...
self->read_end = start + n;
return n;
}

_bufferedwriter_flush_unlocked: write coalescing​

BufferedWriter accumulates writes in self->buffer. On flush (or when the buffer is full), it calls raw.write() in a loop until all buffered data is consumed. The loop handles short writes by advancing the buffer position.

// CPython: Modules/_io/bufferedio.c:990 _bufferedwriter_flush_unlocked
static PyObject *
_bufferedwriter_flush_unlocked(buffered *self)
{
Py_ssize_t written = 0;
Py_buffer data;
...
while (self->write_pos < self->write_end) {
n = self->raw_write(self->raw, ...); /* raw.write() */
...
self->write_pos += n;
written += n;
}
return PyLong_FromSsize_t(written);
}

buffered_seek: discarding stale read-ahead​

When seek() moves the file position, any buffered read-ahead data is invalid. The implementation resets read_pos and read_end to zero and calls raw.seek() to set the underlying file descriptor position. For BufferedRandom, a flush of the write buffer precedes the seek.

// CPython: Modules/_io/bufferedio.c:1520 buffered_seek_impl
static PyObject *
buffered_seek_impl(buffered *self, PyObject *targetobj, int whence)
{
...
if (self->readable)
_bufferedreader_reset_buf(self);
n = PyObject_CallMethodObjArgs(self->raw, _PyIO_str_seek, ...);
...
self->pos = n;
return PyLong_FromSsize_t(n);
}

gopy notes​

Not yet ported. The Go equivalent is bufio.Reader and bufio.Writer. A module/io/ port would wrap these and expose BufferedReader and BufferedWriter as gopy types. The locking model (self->lock in CPython) maps to sync.Mutex protecting the buffer fields.

CPython 3.14 changes​

3.14 improved BufferedWriter.write() to avoid an extra copy when the incoming buffer is larger than the internal buffer size. BufferedReader gained read1() performance improvements for the common single-chunk case.