bufio: drop buffer recycling, add Reader.Reset and Writer.Reset

Fixes #6086

R=golang-dev, pieter, r, rsc
CC=golang-dev
https://golang.org/cl/12603049
diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go
index 993c22d..a548fd3 100644
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -29,8 +29,7 @@
 
 // Reader implements buffering for an io.Reader object.
 type Reader struct {
-	buf          []byte // either nil or []byte of length bufSize
-	bufSize      int
+	buf          []byte
 	rd           io.Reader
 	r, w         int
 	err          error
@@ -46,22 +45,14 @@
 func NewReaderSize(rd io.Reader, size int) *Reader {
 	// Is it already a Reader?
 	b, ok := rd.(*Reader)
-	if ok && b.bufSize >= size {
+	if ok && len(b.buf) >= size {
 		return b
 	}
 	if size < minReadBufferSize {
 		size = minReadBufferSize
 	}
-	r := &Reader{
-		bufSize:      size,
-		rd:           rd,
-		lastByte:     -1,
-		lastRuneSize: -1,
-	}
-	if size > defaultBufSize {
-		// TODO(bradfitz): make all buffer sizes recycle
-		r.buf = make([]byte, r.bufSize)
-	}
+	r := new(Reader)
+	r.reset(make([]byte, size), rd)
 	return r
 }
 
@@ -70,44 +61,25 @@
 	return NewReaderSize(rd, defaultBufSize)
 }
 
+// Reset discards any buffered data, resets all state, and switches
+// the buffered reader to read from r.
+func (b *Reader) Reset(r io.Reader) {
+	b.reset(b.buf, r)
+}
+
+func (b *Reader) reset(buf []byte, r io.Reader) {
+	*b = Reader{
+		buf:          buf,
+		rd:           r,
+		lastByte:     -1,
+		lastRuneSize: -1,
+	}
+}
+
 var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
 
-// TODO: use a sync.Cache instead of this:
-const arbitrarySize = 8
-
-// bufCache holds only byte slices with capacity defaultBufSize.
-var bufCache = make(chan []byte, arbitrarySize)
-
-// allocBuf makes b.buf non-nil.
-func (b *Reader) allocBuf() {
-	if b.buf != nil {
-		return
-	}
-	select {
-	case b.buf = <-bufCache:
-		b.buf = b.buf[:b.bufSize]
-	default:
-		b.buf = make([]byte, b.bufSize, defaultBufSize)
-	}
-}
-
-// putBuf returns b.buf if it's unused.
-func (b *Reader) putBuf() {
-	if b.r == b.w && b.err == io.EOF && cap(b.buf) == defaultBufSize {
-		select {
-		case bufCache <- b.buf:
-			b.buf = nil
-			b.r = 0
-			b.w = 0
-		default:
-		}
-	}
-}
-
 // fill reads a new chunk into the buffer.
 func (b *Reader) fill() {
-	b.allocBuf()
-
 	// Slide existing data to beginning.
 	if b.r > 0 {
 		copy(b.buf, b.buf[b.r:b.w])
@@ -140,7 +112,7 @@
 	if n < 0 {
 		return nil, ErrNegativeCount
 	}
-	if n > b.bufSize {
+	if n > len(b.buf) {
 		return nil, ErrBufferFull
 	}
 	for b.w-b.r < n && b.err == nil {
@@ -174,7 +146,7 @@
 		if b.err != nil {
 			return 0, b.readErr()
 		}
-		if len(p) >= b.bufSize {
+		if len(p) >= len(b.buf) {
 			// Large read, empty buffer.
 			// Read directly into p to avoid copy.
 			n, b.err = b.rd.Read(p)
@@ -197,7 +169,6 @@
 	b.r += n
 	b.lastByte = int(b.buf[b.r-1])
 	b.lastRuneSize = -1
-	b.putBuf()
 	return n, nil
 }
 
@@ -214,9 +185,6 @@
 	c = b.buf[b.r]
 	b.r++
 	b.lastByte = int(c)
-	if b.err != nil { // avoid putBuf call in the common case
-		b.putBuf()
-	}
 	return c, nil
 }
 
@@ -224,7 +192,6 @@
 func (b *Reader) UnreadByte() error {
 	b.lastRuneSize = -1
 	if b.r == b.w && b.lastByte >= 0 {
-		b.allocBuf()
 		b.w = 1
 		b.r = 0
 		b.buf[0] = byte(b.lastByte)
@@ -279,7 +246,7 @@
 
 // ReadSlice reads until the first occurrence of delim in the input,
 // returning a slice pointing at the bytes in the buffer.
-// The bytes stop being valid at the next read call.
+// The bytes stop being valid at the next read.
 // If ReadSlice encounters an error before finding a delimiter,
 // it returns all the data in the buffer and the error itself (often io.EOF).
 // ReadSlice fails with error ErrBufferFull if the buffer fills without a delim.
@@ -314,7 +281,7 @@
 		}
 
 		// Buffer is full?
-		if b.Buffered() >= b.bufSize {
+		if b.Buffered() >= len(b.buf) {
 			b.r = b.w
 			return b.buf, ErrBufferFull
 		}
@@ -427,7 +394,6 @@
 func (b *Reader) ReadString(delim byte) (line string, err error) {
 	bytes, err := b.ReadBytes(delim)
 	line = string(bytes)
-	b.putBuf()
 	return line, err
 }
 
@@ -463,7 +429,6 @@
 func (b *Reader) writeBuf(w io.Writer) (int64, error) {
 	n, err := w.Write(b.buf[b.r:b.w])
 	b.r += n
-	b.putBuf()
 	return int64(n), err
 }
 
@@ -473,69 +438,46 @@
 // If an error occurs writing to a Writer, no more data will be
 // accepted and all subsequent writes will return the error.
 type Writer struct {
-	err     error
-	buf     []byte // either nil or []byte of length bufSize
-	bufSize int
-	n       int
-	wr      io.Writer
+	err error
+	buf []byte
+	n   int
+	wr  io.Writer
 }
 
 // NewWriterSize returns a new Writer whose buffer has at least the specified
 // size. If the argument io.Writer is already a Writer with large enough
 // size, it returns the underlying Writer.
-func NewWriterSize(wr io.Writer, size int) *Writer {
+func NewWriterSize(w io.Writer, size int) *Writer {
 	// Is it already a Writer?
-	b, ok := wr.(*Writer)
-	if ok && b.bufSize >= size {
+	b, ok := w.(*Writer)
+	if ok && len(b.buf) >= size {
 		return b
 	}
 	if size <= 0 {
 		size = defaultBufSize
 	}
-	b = &Writer{
-		wr:      wr,
-		bufSize: size,
+	return &Writer{
+		buf: make([]byte, size),
+		wr:  w,
 	}
-	if size > defaultBufSize {
-		// TODO(bradfitz): make all buffer sizes recycle
-		b.buf = make([]byte, b.bufSize)
-	}
-	return b
 }
 
 // NewWriter returns a new Writer whose buffer has the default size.
-func NewWriter(wr io.Writer) *Writer {
-	return NewWriterSize(wr, defaultBufSize)
+func NewWriter(w io.Writer) *Writer {
+	return NewWriterSize(w, defaultBufSize)
 }
 
-// allocBuf makes b.buf non-nil.
-func (b *Writer) allocBuf() {
-	if b.buf != nil {
-		return
-	}
-	select {
-	case b.buf = <-bufCache:
-		b.buf = b.buf[:b.bufSize]
-	default:
-		b.buf = make([]byte, b.bufSize, defaultBufSize)
-	}
-}
-
-// putBuf returns b.buf if it's unused.
-func (b *Writer) putBuf() {
-	if b.n == 0 && cap(b.buf) == defaultBufSize {
-		select {
-		case bufCache <- b.buf:
-			b.buf = nil
-		default:
-		}
-	}
+// Reset discards any unflushed buffered data, clears any error, and
+// resets b to write its output to w.
+func (b *Writer) Reset(w io.Writer) {
+	b.err = nil
+	b.n = 0
+	b.wr = w
 }
 
 // Flush writes any buffered data to the underlying io.Writer.
 func (b *Writer) Flush() error {
 	err := b.flush()
-	b.putBuf()
 	return err
 }
 
@@ -563,7 +505,7 @@
 }
 
 // Available returns how many bytes are unused in the buffer.
-func (b *Writer) Available() int { return b.bufSize - b.n }
+func (b *Writer) Available() int { return len(b.buf) - b.n }
 
 // Buffered returns the number of bytes that have been written into the current buffer.
 func (b *Writer) Buffered() int { return b.n }
@@ -573,7 +515,6 @@
 // If nn < len(p), it also returns an error explaining
 // why the write is short.
 func (b *Writer) Write(p []byte) (nn int, err error) {
-	b.allocBuf()
 	for len(p) > b.Available() && b.err == nil {
 		var n int
 		if b.Buffered() == 0 {
@@ -605,9 +546,6 @@
 	if b.Available() <= 0 && b.flush() != nil {
 		return b.err
 	}
-	if b.buf == nil {
-		b.allocBuf()
-	}
 	b.buf[b.n] = c
 	b.n++
 	return nil
@@ -616,9 +554,6 @@
 // WriteRune writes a single Unicode code point, returning
 // the number of bytes written and any error.
 func (b *Writer) WriteRune(r rune) (size int, err error) {
-	if b.buf == nil {
-		b.allocBuf()
-	}
 	if r < utf8.RuneSelf {
 		err = b.WriteByte(byte(r))
 		if err != nil {
@@ -650,7 +585,6 @@
 // If the count is less than len(s), it also returns an error explaining
 // why the write is short.
 func (b *Writer) WriteString(s string) (int, error) {
-	b.allocBuf()
 	nn := 0
 	for len(s) > b.Available() && b.err == nil {
 		n := copy(b.buf[b.n:], s)
@@ -670,7 +604,6 @@
 
 // ReadFrom implements io.ReaderFrom.
 func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
-	b.allocBuf()
 	if b.Buffered() == 0 {
 		if w, ok := b.wr.(io.ReaderFrom); ok {
 			return w.ReadFrom(r)
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go
index 68a138e..41bd3d4 100644
--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -1025,6 +1025,38 @@
 	}
 }
 
+func TestReaderReset(t *testing.T) {
+	r := NewReader(strings.NewReader("foo foo"))
+	buf := make([]byte, 3)
+	r.Read(buf)
+	if string(buf) != "foo" {
+		t.Errorf("buf = %q; want foo", buf)
+	}
+	r.Reset(strings.NewReader("bar bar"))
+	all, err := ioutil.ReadAll(r)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if string(all) != "bar bar" {
+		t.Errorf("ReadAll = %q; want bar bar", all)
+	}
+}
+
+func TestWriterReset(t *testing.T) {
+	var buf1, buf2 bytes.Buffer
+	w := NewWriter(&buf1)
+	w.WriteString("foo")
+	w.Reset(&buf2) // and not flushed
+	w.WriteString("bar")
+	w.Flush()
+	if buf1.String() != "" {
+		t.Errorf("buf1 = %q; want empty", buf1.String())
+	}
+	if buf2.String() != "bar" {
+		t.Errorf("buf2 = %q; want bar", buf2.String())
+	}
+}
+
 // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
 type onlyReader struct {
 	r io.Reader