Skip to main content

Modules/_io/bufferedio.c (part 6)

Source:

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

This annotation covers the buffered writer internals. See module_io5_detail for BufferedReader.read, peek, readline, and the BufferedIOBase ABC.

Map

LinesSymbolRole
1-80BufferedWriter.__init__Set up write buffer
81-180BufferedWriter.writeBuffer data, flush when buffer fills
181-280BufferedWriter.flushWrite buffered data to raw stream
281-360_adjust_positionHandle seek on a buffered writer
361-500BufferedRandomBidirectional buffered I/O

Reading

BufferedWriter.write

// CPython: Modules/_io/bufferedio.c:1680 bufferedwriter_write
static PyObject *
bufferedwriter_write(buffered *self, PyObject *args)
{
Py_buffer data;
PyArg_ParseTuple(args, "y*", &data);

/* If data fits in remaining buffer space, copy it in */
if (data.len <= self->buffer_size - self->write_pos) {
memcpy(self->buffer + self->write_pos, data.buf, data.len);
self->write_pos += data.len;
} else {
/* Flush existing buffer first, then handle large writes */
bufferedwriter_flush_locked(self);
if (data.len >= self->buffer_size) {
/* Large write: bypass buffer entirely */
return rawio_write(self, data.buf, data.len);
}
memcpy(self->buffer, data.buf, data.len);
self->write_pos = data.len;
}
PyBuffer_Release(&data);
return PyLong_FromSsize_t(data.len);
}

The write buffer accumulates small writes. When the buffer fills, it is flushed to the raw stream. Very large writes bypass the buffer entirely, going directly to the raw I/O object. This avoids double-copying large data.

BufferedWriter.flush

// CPython: Modules/_io/bufferedio.c:1760 bufferedwriter_flush_locked
static int
bufferedwriter_flush_locked(buffered *self)
{
Py_ssize_t written = 0;
while (written < self->write_pos) {
Py_ssize_t n = rawio_write_into(self,
self->buffer + written,
self->write_pos - written);
if (n < 0) return -1;
written += n;
}
self->write_pos = 0;
return 0;
}

flush loops until all buffered bytes are written. The raw write may do a short write, so the loop is necessary. After flush, write_pos is reset to 0.

BufferedRandom

// CPython: Modules/_io/bufferedio.c:2080 bufferedrandom_seek
static PyObject *
bufferedrandom_seek(buffered *self, PyObject *args)
{
/* On seek: flush write buffer, discard read buffer, then seek raw */
if (self->write_pos > 0) {
bufferedwriter_flush_locked(self);
}
self->read_pos = self->read_end = 0;
return rawio_seek(self, cookie, whence);
}

BufferedRandom combines read and write buffering. On seek, both buffers are invalidated to maintain consistency. The underlying raw stream's position is the authoritative source of truth after a seek.

gopy notes

BufferedWriter.write is module/io.BufferedWriterWrite in module/io/module.go. The write buffer is a []byte slice. The flush loop calls rawWriter.Write in a loop until all bytes are consumed. BufferedRandom embeds both bufio.Reader and bufio.Writer and resets both on Seek.