package io
+type eofReader struct{}
+
+func (eofReader) Read([]byte) (int, error) {
+ return 0, EOF
+}
+
type multiReader struct {
readers []Reader
}
func (mr *multiReader) Read(p []byte) (n int, err error) {
for len(mr.readers) > 0 {
- // Optimization to flatten nested multiReaders (Issue 13558)
+ // Optimization to flatten nested multiReaders (Issue 13558).
if len(mr.readers) == 1 {
if r, ok := mr.readers[0].(*multiReader); ok {
mr.readers = r.readers
}
n, err = mr.readers[0].Read(p)
if err == EOF {
- mr.readers[0] = nil // permit earlier GC
+ // Use eofReader instead of nil to avoid nil panic
+ // after performing flatten (Issue 18232).
+ mr.readers[0] = eofReader{} // permit earlier GC
mr.readers = mr.readers[1:]
}
if n > 0 || err != EOF {
t.Fatalf(`ReadFull = %d (%q), %v; want 2, "ar", nil`, n, buf[:n], err)
}
}
+
+func TestInterleavedMultiReader(t *testing.T) {
+ r1 := strings.NewReader("123")
+ r2 := strings.NewReader("45678")
+
+ mr1 := MultiReader(r1, r2)
+ mr2 := MultiReader(mr1)
+
+ buf := make([]byte, 4)
+
+ // Have mr2 use mr1's []Readers.
+ // Consume r1 (and clear it for GC to handle) and consume part of r2.
+ n, err := ReadFull(mr2, buf)
+ if got := string(buf[:n]); got != "1234" || err != nil {
+ t.Errorf(`ReadFull(mr2) = (%q, %v), want ("1234", nil)`, got, err)
+ }
+
+ // Consume the rest of r2 via mr1.
+ // This should not panic even though mr2 cleared r1.
+ n, err = ReadFull(mr1, buf)
+ if got := string(buf[:n]); got != "5678" || err != nil {
+ t.Errorf(`ReadFull(mr1) = (%q, %v), want ("5678", nil)`, got, err)
+ }
+}