mirror of
https://github.com/golang/go
synced 2024-11-12 04:40:22 -07:00
io: make chained multiReader Read more efficient
before this change, when io.MultiReader was called many times but contain few underlying readers, calls to Read were unnecessarily expensive. Fixes #13558 Change-Id: I3ec4e88c7b50c075b148331fb1b7348a5840adbe Reviewed-on: https://go-review.googlesource.com/17873 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
28c201860e
commit
ccdca832c5
@ -10,6 +10,13 @@ type multiReader struct {
|
||||
|
||||
func (mr *multiReader) Read(p []byte) (n int, err error) {
|
||||
for len(mr.readers) > 0 {
|
||||
// Optimization to flatten nested multiReaders (Issue 13558)
|
||||
if len(mr.readers) == 1 {
|
||||
if r, ok := mr.readers[0].(*multiReader); ok {
|
||||
mr.readers = r.readers
|
||||
continue
|
||||
}
|
||||
}
|
||||
n, err = mr.readers[0].Read(p)
|
||||
if n > 0 || err != EOF {
|
||||
if err == EOF {
|
||||
|
@ -7,9 +7,11 @@ package io_test
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"fmt"
|
||||
. "io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -164,3 +166,33 @@ func TestMultiWriterCopy(t *testing.T) {
|
||||
t.Errorf("buf.String() = %q, want %q", buf.String(), "hello world")
|
||||
}
|
||||
}
|
||||
|
||||
// readerFunc is an io.Reader implemented by the underlying func.
|
||||
type readerFunc func(p []byte) (int, error)
|
||||
|
||||
func (f readerFunc) Read(p []byte) (int, error) {
|
||||
return f(p)
|
||||
}
|
||||
|
||||
// Test that MultiReader properly flattens chained multiReaders when Read is called
|
||||
func TestMultiReaderFlatten(t *testing.T) {
|
||||
pc := make([]uintptr, 1000) // 1000 should fit the full stack
|
||||
var myDepth = runtime.Callers(0, pc)
|
||||
var readDepth int // will contain the depth from which fakeReader.Read was called
|
||||
var r Reader = MultiReader(readerFunc(func(p []byte) (int, error) {
|
||||
readDepth = runtime.Callers(1, pc)
|
||||
return 0, errors.New("irrelevant")
|
||||
}))
|
||||
|
||||
// chain a bunch of multiReaders
|
||||
for i := 0; i < 100; i++ {
|
||||
r = MultiReader(r)
|
||||
}
|
||||
|
||||
r.Read(nil) // don't care about errors, just want to check the call-depth for Read
|
||||
|
||||
if readDepth != myDepth+2 { // 2 should be multiReader.Read and fakeReader.Read
|
||||
t.Errorf("multiReader did not flatten chained multiReaders: expected readDepth %d, got %d",
|
||||
myDepth+2, readDepth)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user