]> Cypherpunks repositories - gostls13.git/commitdiff
bytes: encode size of rune read by ReadRune into lastRead to speed up UnreadRune
authorMartin Möhrmann <martisch@uos.de>
Tue, 6 Sep 2016 11:42:49 +0000 (13:42 +0200)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 17 Oct 2016 06:00:35 +0000 (06:00 +0000)
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/28817
Reviewed-by: Russ 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>

src/bytes/buffer.go

index fa4a51c17fcff7b878fe4acebe46381314b7307f..2ee3d738ef996551a004837899b94ba0771b67e9 100644 (file)
@@ -22,14 +22,18 @@ type Buffer struct {
 }
 
 // The readOp constants describe the last action performed on
-// the buffer, so that UnreadRune and UnreadByte can
-// check for invalid usage.
+// the buffer, so that UnreadRune and UnreadByte can check for
+// invalid usage. opReadRuneX constants are choosen such that
+// converted to int they correspond to the rune size that was read.
 type readOp int
 
 const (
-       opInvalid  readOp = iota // Non-read operation.
-       opReadRune               // Read rune.
-       opRead                   // Any other read operation.
+       opRead      readOp = -1 // Any other read operation.
+       opInvalid          = 0  // Non-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.
@@ -319,14 +323,15 @@ func (b *Buffer) ReadRune() (r rune, size int, err error) {
                b.Truncate(0)
                return 0, 0, io.EOF
        }
-       b.lastRead = opReadRune
        c := b.buf[b.off]
        if c < utf8.RuneSelf {
                b.off++
+               b.lastRead = opReadRune1
                return rune(c), 1, nil
        }
        r, n := utf8.DecodeRune(b.buf[b.off:])
        b.off += n
+       b.lastRead = readOp(n)
        return r, n, nil
 }
 
@@ -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
 // from any read operation.)
 func (b *Buffer) UnreadRune() error {
-       if b.lastRead != opReadRune {
+       if b.lastRead <= opInvalid {
                return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune")
        }
-       b.lastRead = opInvalid
-       if b.off > 0 {
-               _, n := utf8.DecodeLastRune(b.buf[0:b.off])
-               b.off -= n
+       if b.off >= int(b.lastRead) {
+               b.off -= int(b.lastRead)
        }
+       b.lastRead = opInvalid
        return nil
 }
 
@@ -351,7 +355,7 @@ func (b *Buffer) UnreadRune() error {
 // read operation. If write has happened since the last read, UnreadByte
 // returns an 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")
        }
        b.lastRead = opInvalid