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
This commit is contained in:
Rémy Oudompheng 2012-12-03 14:04:18 +01:00
parent bcea0dd1d0
commit b1c4a8efa9
2 changed files with 51 additions and 8 deletions

View File

@ -360,16 +360,24 @@ func (b *Buffer) UnreadByte() error {
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
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)
size := i + 1
end := b.off + i + 1
if i < 0 {
size = len(b.buf) - b.off
end = len(b.buf)
err = io.EOF
}
line = make([]byte, size)
copy(line, b.buf[b.off:])
b.off += size
return
line = b.buf[b.off:end]
b.off = end
return line, err
}
// 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) {
// ReadString returns err != nil if and only if the returned data does not end
// in delim.
func (b *Buffer) ReadString(delim byte) (line string, err error) {
bytes, err := b.ReadBytes(delim)
return string(bytes), err
slice, err := b.readSlice(delim)
return string(slice), err
}
// NewBuffer creates and initializes a new Buffer using buf as its initial

View File

@ -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) {
x := []byte{'x'}
y := []byte{'y'}