Skip to content

Commit 425db64

Browse files
committed
bufio: use underlying ReadFrom even when data is buffered
When (*bufio.Writer).ReadFrom is called with a partially filled buffer, fill out and flush the buffer and then call the underlying writer's ReadFrom method if present. Fixes #44815. Change-Id: I15b3ef0746d0d60fd62041189a9b9df11254dd29 Reviewed-on: https://go-review.googlesource.com/c/go/+/340530 Trust: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 33b3260 commit 425db64

File tree

2 files changed

+58
-9
lines changed

2 files changed

+58
-9
lines changed

src/bufio/bufio.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -745,26 +745,27 @@ func (b *Writer) WriteString(s string) (int, error) {
745745
}
746746

747747
// ReadFrom implements io.ReaderFrom. If the underlying writer
748-
// supports the ReadFrom method, and b has no buffered data yet,
749-
// this calls the underlying ReadFrom without buffering.
748+
// supports the ReadFrom method, this calls the underlying ReadFrom.
749+
// If there is buffered data and an underlying ReadFrom, this fills
750+
// the buffer and writes it before calling ReadFrom.
750751
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
751752
if b.err != nil {
752753
return 0, b.err
753754
}
754-
if b.Buffered() == 0 {
755-
if w, ok := b.wr.(io.ReaderFrom); ok {
756-
n, err = w.ReadFrom(r)
757-
b.err = err
758-
return n, err
759-
}
760-
}
755+
readerFrom, readerFromOK := b.wr.(io.ReaderFrom)
761756
var m int
762757
for {
763758
if b.Available() == 0 {
764759
if err1 := b.Flush(); err1 != nil {
765760
return n, err1
766761
}
767762
}
763+
if readerFromOK && b.Buffered() == 0 {
764+
nn, err := readerFrom.ReadFrom(r)
765+
b.err = err
766+
n += nn
767+
return n, err
768+
}
768769
nr := 0
769770
for nr < maxConsecutiveEmptyReads {
770771
m, err = r.Read(b.buf[b.n:])

src/bufio/bufio_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,54 @@ func TestWriterReadFromErrNoProgress(t *testing.T) {
13511351
}
13521352
}
13531353

1354+
type readFromWriter struct {
1355+
buf []byte
1356+
writeBytes int
1357+
readFromBytes int
1358+
}
1359+
1360+
func (w *readFromWriter) Write(p []byte) (int, error) {
1361+
w.buf = append(w.buf, p...)
1362+
w.writeBytes += len(p)
1363+
return len(p), nil
1364+
}
1365+
1366+
func (w *readFromWriter) ReadFrom(r io.Reader) (int64, error) {
1367+
b, err := io.ReadAll(r)
1368+
w.buf = append(w.buf, b...)
1369+
w.readFromBytes += len(b)
1370+
return int64(len(b)), err
1371+
}
1372+
1373+
// Test that calling (*Writer).ReadFrom with a partially-filled buffer
1374+
// fills the buffer before switching over to ReadFrom.
1375+
func TestWriterReadFromWithBufferedData(t *testing.T) {
1376+
const bufsize = 16
1377+
1378+
input := createTestInput(64)
1379+
rfw := &readFromWriter{}
1380+
w := NewWriterSize(rfw, bufsize)
1381+
1382+
const writeSize = 8
1383+
if n, err := w.Write(input[:writeSize]); n != writeSize || err != nil {
1384+
t.Errorf("w.Write(%v bytes) = %v, %v; want %v, nil", writeSize, n, err, writeSize)
1385+
}
1386+
n, err := w.ReadFrom(bytes.NewReader(input[writeSize:]))
1387+
if wantn := len(input[writeSize:]); int(n) != wantn || err != nil {
1388+
t.Errorf("io.Copy(w, %v bytes) = %v, %v; want %v, nil", wantn, n, err, wantn)
1389+
}
1390+
if err := w.Flush(); err != nil {
1391+
t.Errorf("w.Flush() = %v, want nil", err)
1392+
}
1393+
1394+
if got, want := rfw.writeBytes, bufsize; got != want {
1395+
t.Errorf("wrote %v bytes with Write, want %v", got, want)
1396+
}
1397+
if got, want := rfw.readFromBytes, len(input)-bufsize; got != want {
1398+
t.Errorf("wrote %v bytes with ReadFrom, want %v", got, want)
1399+
}
1400+
}
1401+
13541402
func TestReadZero(t *testing.T) {
13551403
for _, size := range []int{100, 2} {
13561404
t.Run(fmt.Sprintf("bufsize=%d", size), func(t *testing.T) {

0 commit comments

Comments
 (0)