Commit 99df54f1 authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Brad Fitzpatrick

bytes: encode size of rune read by ReadRune into lastRead to speed up UnreadRune

In ReadRune store the size of the rune that was read into lastRead
to avoid the need to call DecodeRuneLast in UnreadRune.

fmt:
name        old time/op  new time/op  delta
ScanInts-4   481µs ± 4%   458µs ± 3%  -4.64%  (p=0.000 n=20+20)

Change-Id: I500848e663a975f426402a4b3d27a541e5cac06c
Reviewed-on: https://go-review.googlesource.com/28817Reviewed-by: 's avatarRuss Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Run-TryBot: Martin Möhrmann <martisch@uos.de>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 1e775fe4
...@@ -22,14 +22,18 @@ type Buffer struct { ...@@ -22,14 +22,18 @@ type Buffer struct {
} }
// The readOp constants describe the last action performed on // The readOp constants describe the last action performed on
// the buffer, so that UnreadRune and UnreadByte can // the buffer, so that UnreadRune and UnreadByte can check for
// check for invalid usage. // invalid usage. opReadRuneX constants are choosen such that
// converted to int they correspond to the rune size that was read.
type readOp int type readOp int
const ( const (
opInvalid readOp = iota // Non-read operation. opRead readOp = -1 // Any other read operation.
opReadRune // Read rune. opInvalid = 0 // Non-read operation.
opRead // Any other read operation. opReadRune1 = 1 // Read rune of size 1.
opReadRune2 = 2 // Read rune of size 2.
opReadRune3 = 3 // Read rune of size 3.
opReadRune4 = 4 // Read rune of size 4.
) )
// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer. // ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer.
...@@ -319,14 +323,15 @@ func (b *Buffer) ReadRune() (r rune, size int, err error) { ...@@ -319,14 +323,15 @@ func (b *Buffer) ReadRune() (r rune, size int, err error) {
b.Truncate(0) b.Truncate(0)
return 0, 0, io.EOF return 0, 0, io.EOF
} }
b.lastRead = opReadRune
c := b.buf[b.off] c := b.buf[b.off]
if c < utf8.RuneSelf { if c < utf8.RuneSelf {
b.off++ b.off++
b.lastRead = opReadRune1
return rune(c), 1, nil return rune(c), 1, nil
} }
r, n := utf8.DecodeRune(b.buf[b.off:]) r, n := utf8.DecodeRune(b.buf[b.off:])
b.off += n b.off += n
b.lastRead = readOp(n)
return r, n, nil return r, n, nil
} }
...@@ -336,14 +341,13 @@ func (b *Buffer) ReadRune() (r rune, size int, err error) { ...@@ -336,14 +341,13 @@ func (b *Buffer) ReadRune() (r rune, size int, err error) {
// it is stricter than UnreadByte, which will unread the last byte // it is stricter than UnreadByte, which will unread the last byte
// from any read operation.) // from any read operation.)
func (b *Buffer) UnreadRune() error { func (b *Buffer) UnreadRune() error {
if b.lastRead != opReadRune { if b.lastRead <= opInvalid {
return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune") return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune")
} }
b.lastRead = opInvalid if b.off >= int(b.lastRead) {
if b.off > 0 { b.off -= int(b.lastRead)
_, n := utf8.DecodeLastRune(b.buf[0:b.off])
b.off -= n
} }
b.lastRead = opInvalid
return nil return nil
} }
...@@ -351,7 +355,7 @@ func (b *Buffer) UnreadRune() error { ...@@ -351,7 +355,7 @@ func (b *Buffer) UnreadRune() error {
// read operation. If write has happened since the last read, UnreadByte // read operation. If write has happened since the last read, UnreadByte
// returns an error. // returns an error.
func (b *Buffer) UnreadByte() error { func (b *Buffer) UnreadByte() error {
if b.lastRead != opReadRune && b.lastRead != opRead { if b.lastRead == opInvalid {
return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read") return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read")
} }
b.lastRead = opInvalid b.lastRead = opInvalid
......
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