]> Cypherpunks repositories - gostls13.git/commitdiff
bufio: drop buffer recycling, add Reader.Reset and Writer.Reset
authorBrad Fitzpatrick <bradfitz@golang.org>
Sun, 11 Aug 2013 02:22:19 +0000 (19:22 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Sun, 11 Aug 2013 02:22:19 +0000 (19:22 -0700)
Fixes #6086

R=golang-dev, pieter, r, rsc
CC=golang-dev
https://golang.org/cl/12603049

src/pkg/bufio/bufio.go
src/pkg/bufio/bufio_test.go

index 993c22d61cd827e909617858661b9232e69e1cfc..a548fd3f719213039d06722f9ef734ac76464156 100644 (file)
@@ -29,8 +29,7 @@ var (
 
 // 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 @@ const minReadBufferSize = 16
 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 @@ func NewReader(rd io.Reader) *Reader {
        return NewReaderSize(rd, defaultBufSize)
 }
 
-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)
-       }
+// 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)
 }
 
-// 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:
-               }
+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")
+
 // 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 @@ func (b *Reader) Peek(n int) ([]byte, error) {
        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 @@ func (b *Reader) Read(p []byte) (n int, err error) {
                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 @@ func (b *Reader) Read(p []byte) (n int, err error) {
        b.r += n
        b.lastByte = int(b.buf[b.r-1])
        b.lastRuneSize = -1
-       b.putBuf()
        return n, nil
 }
 
@@ -214,9 +185,6 @@ func (b *Reader) ReadByte() (c byte, err error) {
        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) ReadByte() (c byte, err error) {
 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 @@ func (b *Reader) Buffered() int { return b.w - b.r }
 
 // 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 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
                }
 
                // 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) ReadBytes(delim byte) (line []byte, err error) {
 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) WriteTo(w io.Writer) (n int64, err error) {
 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 @@ func (b *Reader) writeBuf(w io.Writer) (int64, error) {
 // 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,
-       }
-       if size > defaultBufSize {
-               // TODO(bradfitz): make all buffer sizes recycle
-               b.buf = make([]byte, b.bufSize)
+       return &Writer{
+               buf: make([]byte, size),
+               wr:  w,
        }
-       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 @@ func (b *Writer) flush() error {
 }
 
 // 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 @@ func (b *Writer) Buffered() int { return b.n }
 // 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 @@ func (b *Writer) WriteByte(c byte) error {
        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 @@ func (b *Writer) WriteByte(c byte) error {
 // 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 @@ func (b *Writer) WriteRune(r rune) (size int, err error) {
 // 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 @@ func (b *Writer) WriteString(s string) (int, error) {
 
 // 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)
index 68a138e5c1e296ecff67d2c0cc5aed93e8b101b9..41bd3d45633854c02d7041c0cec83b9a65f70cc4 100644 (file)
@@ -1025,6 +1025,38 @@ func TestWriterReadFromWhileFull(t *testing.T) {
        }
 }
 
+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