-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
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
srcargument is a*bytes.Reader(or similario.WriterTowith an internal[]bytecontaining its complete output), or - when the
srcargument is a*strings.Reader(or similario.WriterTowith an internalstringcontaining the complete output) and thedstargument implementsio.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)