bufio: permit r.Reset(r) without infinite recursion

This can happen in reasonable code because NewReader(r) can return r,
if r is already a Reader.

Similarly for Writer.

Fixes #58423

Change-Id: Iff9d9265410bee68fbaeb7175369847bd737eb2c
Reviewed-on: https://go-review.googlesource.com/c/go/+/466815
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Ian Lance Taylor 2023-02-08 19:50:06 -08:00 committed by Gopher Robot
parent a7fe9ada10
commit 851f6fd614
2 changed files with 52 additions and 15 deletions

View file

@ -70,7 +70,14 @@ func (b *Reader) Size() int { return len(b.buf) }
// the buffered reader to read from r.
// Calling Reset on the zero value of Reader initializes the internal buffer
// to the default size.
// Calling b.Reset(b) (that is, resetting a Reader to itself) does nothing.
func (b *Reader) Reset(r io.Reader) {
// If a Reader r is passed to NewReader, NewReader will return r.
// Different layers of code may do that, and then later pass r
// to Reset. Avoid infinite recursion in that case.
if b == r {
return
}
if b.buf == nil {
b.buf = make([]byte, defaultBufSize)
}
@ -608,7 +615,14 @@ func (b *Writer) Size() int { return len(b.buf) }
// resets b to write its output to w.
// Calling Reset on the zero value of Writer initializes the internal buffer
// to the default size.
// Calling w.Reset(w) (that is, resetting a Writer to itself) does nothing.
func (b *Writer) Reset(w io.Writer) {
// If a Writer w is passed to NewWriter, NewWriter will return w.
// Different layers of code may do that, and then later pass w
// to Reset. Avoid infinite recursion in that case.
if b == w {
return
}
if b.buf == nil {
b.buf = make([]byte, defaultBufSize)
}

View file

@ -1482,6 +1482,17 @@ func TestReadZero(t *testing.T) {
}
func TestReaderReset(t *testing.T) {
checkAll := func(r *Reader, want string) {
t.Helper()
all, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if string(all) != want {
t.Errorf("ReadAll returned %q, want %q", all, want)
}
}
r := NewReader(strings.NewReader("foo foo"))
buf := make([]byte, 3)
r.Read(buf)
@ -1490,27 +1501,23 @@ func TestReaderReset(t *testing.T) {
}
r.Reset(strings.NewReader("bar bar"))
all, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if string(all) != "bar bar" {
t.Errorf("ReadAll = %q; want bar bar", all)
}
checkAll(r, "bar bar")
*r = Reader{} // zero out the Reader
r.Reset(strings.NewReader("bar bar"))
all, err = io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if string(all) != "bar bar" {
t.Errorf("ReadAll = %q; want bar bar", all)
}
checkAll(r, "bar bar")
// Wrap a reader and then Reset to that reader.
r.Reset(strings.NewReader("recur"))
r2 := NewReader(r)
checkAll(r2, "recur")
r.Reset(strings.NewReader("recur2"))
r2.Reset(r)
checkAll(r2, "recur2")
}
func TestWriterReset(t *testing.T) {
var buf1, buf2, buf3 strings.Builder
var buf1, buf2, buf3, buf4, buf5 strings.Builder
w := NewWriter(&buf1)
w.WriteString("foo")
@ -1534,6 +1541,22 @@ func TestWriterReset(t *testing.T) {
if buf3.String() != "bar" {
t.Errorf("buf3 = %q; want bar", buf3.String())
}
// Wrap a writer and then Reset to that writer.
w.Reset(&buf4)
w2 := NewWriter(w)
w2.WriteString("recur")
w2.Flush()
if buf4.String() != "recur" {
t.Errorf("buf4 = %q, want %q", buf4.String(), "recur")
}
w.Reset(&buf5)
w2.Reset(w)
w2.WriteString("recur2")
w2.Flush()
if buf5.String() != "recur2" {
t.Errorf("buf5 = %q, want %q", buf5.String(), "recur2")
}
}
func TestReaderDiscard(t *testing.T) {