mirror of
https://github.com/golang/go
synced 2024-11-25 07:17:56 -07:00
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
This commit is contained in:
parent
7910cd68b5
commit
ede9aa9e02
@ -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)
|
||||
}
|
||||
|
||||
// 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 @@ 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,
|
||||
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 @@ 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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user