Skip to content

io: clearly document when CopyBuffer does not use the buffer #32306

Closed
@bcmills

Description

@bcmills

io.CopyBuffer is difficult to use appropriately: it has a subtle interaction with ReadFrom and WriteTo methods (#16474, #32276) that can result in unintended use of small buffers and/or unintended allocation of large slices.

For the cases that avoid that subtle interaction, io.CopyBuffer mostly duplicates the functionality of bufio.Reader, particularly given the (*bufio.Reader).Reset method (which allows a single bufio.Reader to wrap multiple io.Reader instances).

I propose that we deprecate io.CopyBuffer, and encourage explicit use of bufio.Reader as a replacement.

In particular, note that if the caller intends to always use the buffer, call sites of the form:

var buf = make([]byte, bufLen)

[…]

n, err = io.CopyBuffer(dst, src, buf)

can be expressed more clearly (and more robustly) as

var srcBuf = bufio.NewReaderSize(nil, bufLen)

[…]

srcBuf.Reset(src)
n, err = io.Copy(dst, srcBuf)

The two cases I've found where the existing behavior of io.CopyBuffer is always beneficial are:

  • when the src argument is a *bytes.Reader (or similar io.WriterTo with an internal []byte containing its complete output), or
  • when the src argument is a *strings.Reader (or similar io.WriterTo with an internal string containing the complete output) and the dst argument implements io.StringWriter.

In those cases, io.CopyBuffer avoids an extra copy without inducing an extra allocation. However, as noted in #16474 (comment), detecting those cases requires much more detailed information than what io.CopyBuffer currently uses (and what ReaderFrom and WriterTo currently provide).

Moreover, if the caller knows that the WriterTo method, if any, is likely to be more efficient than buffering, it's easy enough to invoke explicitly, with explicit use of buffering on the fallback path:

var srcBuf = bufio.NewReaderSize(nil, bufLen)

wt, ok := src.(io.WriterTo)
if !ok {
	srcBuf.Reset(src)
	wt = srcBuf
}
n, err = wt.WriteTo(dst)

Metadata

Metadata

Assignees

No one assigned

    Labels

    DocumentationIssues describing a change to documentation.FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.help wanted

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions