From: Brad Fitzpatrick Date: Sun, 11 Aug 2013 02:22:19 +0000 (-0700) Subject: bufio: drop buffer recycling, add Reader.Reset and Writer.Reset X-Git-Tag: go1.2rc2~668 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ede9aa9e028e9bded416309981d8944d88366ffe;p=gostls13.git 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 993c22d61c..a548fd3f71 100644 --- a/src/pkg/bufio/bufio.go +++ b/src/pkg/bufio/bufio.go @@ -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) diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index 68a138e5c1..41bd3d4563 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -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