aboutsummaryrefslogtreecommitdiff
path: root/src/bytes/buffer.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/bytes/buffer.go')
-rw-r--r--src/bytes/buffer.go73
1 files changed, 50 insertions, 23 deletions
diff --git a/src/bytes/buffer.go b/src/bytes/buffer.go
index 9b6369de08..b241170e5d 100644
--- a/src/bytes/buffer.go
+++ b/src/bytes/buffer.go
@@ -93,6 +93,17 @@ func (b *Buffer) Reset() {
b.lastRead = opInvalid
}
+// tryGrowByReslice is a inlineable version of grow for the fast-case where the
+// internal buffer only needs to be resliced.
+// It returns the index where bytes should be written and whether it succeeded.
+func (b *Buffer) tryGrowByReslice(n int) (int, bool) {
+ if l := len(b.buf); l+n <= cap(b.buf) {
+ b.buf = b.buf[:l+n]
+ return l, true
+ }
+ return 0, false
+}
+
// grow grows the buffer to guarantee space for n more bytes.
// It returns the index where bytes should be written.
// If the buffer can't grow it will panic with ErrTooLarge.
@@ -102,27 +113,31 @@ func (b *Buffer) grow(n int) int {
if m == 0 && b.off != 0 {
b.Reset()
}
- if len(b.buf)+n > cap(b.buf) {
- var buf []byte
- if b.buf == nil && n <= len(b.bootstrap) {
- buf = b.bootstrap[0:]
- } else if m+n <= cap(b.buf)/2 {
- // We can slide things down instead of allocating a new
- // slice. We only need m+n <= cap(b.buf) to slide, but
- // we instead let capacity get twice as large so we
- // don't spend all our time copying.
- copy(b.buf[:], b.buf[b.off:])
- buf = b.buf[:m]
- } else {
- // not enough space anywhere
- buf = makeSlice(2*cap(b.buf) + n)
- copy(buf, b.buf[b.off:])
- }
+ // Try to grow by means of a reslice.
+ if i, ok := b.tryGrowByReslice(n); ok {
+ return i
+ }
+ // Check if we can make use of bootstrap array.
+ if b.buf == nil && n <= len(b.bootstrap) {
+ b.buf = b.bootstrap[:n]
+ return 0
+ }
+ if m+n <= cap(b.buf)/2 {
+ // We can slide things down instead of allocating a new
+ // slice. We only need m+n <= cap(b.buf) to slide, but
+ // we instead let capacity get twice as large so we
+ // don't spend all our time copying.
+ copy(b.buf[:], b.buf[b.off:])
+ } else {
+ // Not enough space anywhere, we need to allocate.
+ buf := makeSlice(2*cap(b.buf) + n)
+ copy(buf, b.buf[b.off:])
b.buf = buf
- b.off = 0
}
- b.buf = b.buf[0 : b.off+m+n]
- return b.off + m
+ // Restore b.off and len(b.buf).
+ b.off = 0
+ b.buf = b.buf[:m+n]
+ return m
}
// Grow grows the buffer's capacity, if necessary, to guarantee space for
@@ -143,7 +158,10 @@ func (b *Buffer) Grow(n int) {
// buffer becomes too large, Write will panic with ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
b.lastRead = opInvalid
- m := b.grow(len(p))
+ m, ok := b.tryGrowByReslice(len(p))
+ if !ok {
+ m = b.grow(len(p))
+ }
return copy(b.buf[m:], p), nil
}
@@ -152,7 +170,10 @@ func (b *Buffer) Write(p []byte) (n int, err error) {
// buffer becomes too large, WriteString will panic with ErrTooLarge.
func (b *Buffer) WriteString(s string) (n int, err error) {
b.lastRead = opInvalid
- m := b.grow(len(s))
+ m, ok := b.tryGrowByReslice(len(s))
+ if !ok {
+ m = b.grow(len(s))
+ }
return copy(b.buf[m:], s), nil
}
@@ -244,7 +265,10 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
// ErrTooLarge.
func (b *Buffer) WriteByte(c byte) error {
b.lastRead = opInvalid
- m := b.grow(1)
+ m, ok := b.tryGrowByReslice(1)
+ if !ok {
+ m = b.grow(1)
+ }
b.buf[m] = c
return nil
}
@@ -259,7 +283,10 @@ func (b *Buffer) WriteRune(r rune) (n int, err error) {
return 1, nil
}
b.lastRead = opInvalid
- m := b.grow(utf8.UTFMax)
+ m, ok := b.tryGrowByReslice(utf8.UTFMax)
+ if !ok {
+ m = b.grow(utf8.UTFMax)
+ }
n = utf8.EncodeRune(b.buf[m:m+utf8.UTFMax], r)
b.buf = b.buf[:m+n]
return n, nil