Commit b1c4a8ef authored by Rémy Oudompheng's avatar Rémy Oudompheng

bytes: avoid duplicate malloc/copy in Buffer.ReadString

Twice faster and twice less garbage.

R=golang-dev, dave, daniel.morsing, bradfitz
CC=golang-dev
https://golang.org/cl/6849128
parent bcea0dd1
...@@ -360,16 +360,24 @@ func (b *Buffer) UnreadByte() error { ...@@ -360,16 +360,24 @@ func (b *Buffer) UnreadByte() error {
// ReadBytes returns err != nil if and only if the returned data does not end in // ReadBytes returns err != nil if and only if the returned data does not end in
// delim. // delim.
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
slice, err := b.readSlice(delim)
// return a copy of slice. The buffer's backing array may
// be overwritten by later calls.
line = append(line, slice...)
return
}
// readSlice is like readBytes but returns a reference to internal buffer data.
func (b *Buffer) readSlice(delim byte) (line []byte, err error) {
i := IndexByte(b.buf[b.off:], delim) i := IndexByte(b.buf[b.off:], delim)
size := i + 1 end := b.off + i + 1
if i < 0 { if i < 0 {
size = len(b.buf) - b.off end = len(b.buf)
err = io.EOF err = io.EOF
} }
line = make([]byte, size) line = b.buf[b.off:end]
copy(line, b.buf[b.off:]) b.off = end
b.off += size return line, err
return
} }
// ReadString reads until the first occurrence of delim in the input, // ReadString reads until the first occurrence of delim in the input,
...@@ -379,8 +387,8 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { ...@@ -379,8 +387,8 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
// ReadString returns err != nil if and only if the returned data does not end // ReadString returns err != nil if and only if the returned data does not end
// in delim. // in delim.
func (b *Buffer) ReadString(delim byte) (line string, err error) { func (b *Buffer) ReadString(delim byte) (line string, err error) {
bytes, err := b.ReadBytes(delim) slice, err := b.readSlice(delim)
return string(bytes), err return string(slice), err
} }
// NewBuffer creates and initializes a new Buffer using buf as its initial // NewBuffer creates and initializes a new Buffer using buf as its initial
......
...@@ -375,6 +375,41 @@ func TestReadBytes(t *testing.T) { ...@@ -375,6 +375,41 @@ func TestReadBytes(t *testing.T) {
} }
} }
func TestReadString(t *testing.T) {
for _, test := range readBytesTests {
buf := NewBufferString(test.buffer)
var err error
for _, expected := range test.expected {
var s string
s, err = buf.ReadString(test.delim)
if s != expected {
t.Errorf("expected %q, got %q", expected, s)
}
if err != nil {
break
}
}
if err != test.err {
t.Errorf("expected error %v, got %v", test.err, err)
}
}
}
func BenchmarkReadString(b *testing.B) {
const n = 32 << 10
data := make([]byte, n)
data[n-1] = 'x'
b.SetBytes(int64(n))
for i := 0; i < b.N; i++ {
buf := NewBuffer(data)
_, err := buf.ReadString('x')
if err != nil {
b.Fatal(err)
}
}
}
func TestGrow(t *testing.T) { func TestGrow(t *testing.T) {
x := []byte{'x'} x := []byte{'x'}
y := []byte{'y'} y := []byte{'y'}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment