mirror of
https://github.com/golang/go
synced 2024-11-17 16:34:43 -07:00
io: NopCloser forward WriterTo implementations if the reader supports it
The io_test.go don't bother to test actually reading or WritingTo of the NopCloser because the logic is simple. It didn't even had direct tests before. Fixes #51566
This commit is contained in:
parent
7a22c8a07f
commit
a6b9af4e94
15
src/io/io.go
15
src/io/io.go
@ -621,7 +621,12 @@ func (discard) ReadFrom(r Reader) (n int64, err error) {
|
|||||||
|
|
||||||
// NopCloser returns a ReadCloser with a no-op Close method wrapping
|
// NopCloser returns a ReadCloser with a no-op Close method wrapping
|
||||||
// the provided Reader r.
|
// the provided Reader r.
|
||||||
|
// If r implements WriterTo, the returned ReadCloser will implement WriterTo
|
||||||
|
// by forwarding calls to r.
|
||||||
func NopCloser(r Reader) ReadCloser {
|
func NopCloser(r Reader) ReadCloser {
|
||||||
|
if _, ok := r.(WriterTo); ok {
|
||||||
|
return nopCloserWriterTo{r}
|
||||||
|
}
|
||||||
return nopCloser{r}
|
return nopCloser{r}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,6 +636,16 @@ type nopCloser struct {
|
|||||||
|
|
||||||
func (nopCloser) Close() error { return nil }
|
func (nopCloser) Close() error { return nil }
|
||||||
|
|
||||||
|
type nopCloserWriterTo struct {
|
||||||
|
Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nopCloserWriterTo) Close() error { return nil }
|
||||||
|
|
||||||
|
func (c nopCloserWriterTo) WriteTo(w Writer) (n int64, err error) {
|
||||||
|
return c.Reader.(WriterTo).WriteTo(w)
|
||||||
|
}
|
||||||
|
|
||||||
// ReadAll reads from r until an error or EOF and returns the data it read.
|
// ReadAll reads from r until an error or EOF and returns the data it read.
|
||||||
// A successful call returns err == nil, not err == EOF. Because ReadAll is
|
// A successful call returns err == nil, not err == EOF. Because ReadAll is
|
||||||
// defined to read from src until EOF, it does not treat an EOF from Read
|
// defined to read from src until EOF, it does not treat an EOF from Read
|
||||||
|
@ -471,3 +471,24 @@ func TestCopyLargeWriter(t *testing.T) {
|
|||||||
t.Errorf("Copy error: got %v, want %v", err, want)
|
t.Errorf("Copy error: got %v, want %v", err, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNopCloserWriterToForwarding(t *testing.T) {
|
||||||
|
for _, tc := range [...]struct {
|
||||||
|
Name string
|
||||||
|
r Reader
|
||||||
|
}{
|
||||||
|
{"not a WriterTo", Reader(nil)},
|
||||||
|
{"a WriterTo", struct {
|
||||||
|
Reader
|
||||||
|
WriterTo
|
||||||
|
}{}},
|
||||||
|
} {
|
||||||
|
nc := NopCloser(tc.r)
|
||||||
|
|
||||||
|
_, expected := tc.r.(WriterTo)
|
||||||
|
_, got := nc.(WriterTo)
|
||||||
|
if expected != got {
|
||||||
|
t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -422,8 +422,8 @@ func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err
|
|||||||
//
|
//
|
||||||
// This function is only intended for use in writeBody.
|
// This function is only intended for use in writeBody.
|
||||||
func (t *transferWriter) unwrapBody() io.Reader {
|
func (t *transferWriter) unwrapBody() io.Reader {
|
||||||
if reflect.TypeOf(t.Body) == nopCloserType {
|
if r, ok := unwrapNopCloser(t.Body); ok {
|
||||||
return reflect.ValueOf(t.Body).Field(0).Interface().(io.Reader)
|
return r
|
||||||
}
|
}
|
||||||
if r, ok := t.Body.(*readTrackingBody); ok {
|
if r, ok := t.Body.(*readTrackingBody); ok {
|
||||||
r.didRead = true
|
r.didRead = true
|
||||||
@ -1081,6 +1081,21 @@ func (fr finishAsyncByteRead) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var nopCloserType = reflect.TypeOf(io.NopCloser(nil))
|
var nopCloserType = reflect.TypeOf(io.NopCloser(nil))
|
||||||
|
var nopCloserWriterToType = reflect.TypeOf(io.NopCloser(struct {
|
||||||
|
io.Reader
|
||||||
|
io.WriterTo
|
||||||
|
}{}))
|
||||||
|
|
||||||
|
// unwrapNopCloser return the underlying reader and true if r is a NopCloser
|
||||||
|
// else it return false
|
||||||
|
func unwrapNopCloser(r io.Reader) (underlyingReader io.Reader, isNopCloser bool) {
|
||||||
|
switch reflect.TypeOf(r) {
|
||||||
|
case nopCloserType, nopCloserWriterToType:
|
||||||
|
return reflect.ValueOf(r).Field(0).Interface().(io.Reader), true
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// isKnownInMemoryReader reports whether r is a type known to not
|
// isKnownInMemoryReader reports whether r is a type known to not
|
||||||
// block on Read. Its caller uses this as an optional optimization to
|
// block on Read. Its caller uses this as an optional optimization to
|
||||||
@ -1090,8 +1105,8 @@ func isKnownInMemoryReader(r io.Reader) bool {
|
|||||||
case *bytes.Reader, *bytes.Buffer, *strings.Reader:
|
case *bytes.Reader, *bytes.Buffer, *strings.Reader:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if reflect.TypeOf(r) == nopCloserType {
|
if r, ok := unwrapNopCloser(r); ok {
|
||||||
return isKnownInMemoryReader(reflect.ValueOf(r).Field(0).Interface().(io.Reader))
|
return isKnownInMemoryReader(r)
|
||||||
}
|
}
|
||||||
if r, ok := r.(*readTrackingBody); ok {
|
if r, ok := r.(*readTrackingBody); ok {
|
||||||
return isKnownInMemoryReader(r.ReadCloser)
|
return isKnownInMemoryReader(r.ReadCloser)
|
||||||
|
Loading…
Reference in New Issue
Block a user