mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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:
parent
bcea0dd1d0
commit
b1c4a8efa9
2 changed files with 51 additions and 8 deletions
|
|
@ -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) {
|
||||||
// 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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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'}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue