diff --git a/src/bytes/buffer.go b/src/bytes/buffer.go index 14c5bc38d6..087cc0e427 100644 --- a/src/bytes/buffer.go +++ b/src/bytes/buffer.go @@ -12,13 +12,15 @@ import ( "unicode/utf8" ) +// smallBufferSize is an initial allocation minimal capacity. +const smallBufferSize = 64 + // A Buffer is a variable-sized buffer of bytes with Read and Write methods. // The zero value for Buffer is an empty buffer ready to use. type Buffer struct { - buf []byte // contents are the bytes buf[off : len(buf)] - off int // read at &buf[off], write at &buf[len(buf)] - bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation. - lastRead readOp // last read operation, so that Unread* can work correctly. + buf []byte // contents are the bytes buf[off : len(buf)] + off int // read at &buf[off], write at &buf[len(buf)] + lastRead readOp // last read operation, so that Unread* can work correctly. // FIXME: it would be advisable to align Buffer to cachelines to avoid false // sharing. @@ -125,9 +127,8 @@ func (b *Buffer) grow(n int) int { 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] + if b.buf == nil && n <= smallBufferSize { + b.buf = make([]byte, n, smallBufferSize) return 0 } c := cap(b.buf) diff --git a/test/fixedbugs/issue7921.go b/test/fixedbugs/issue7921.go new file mode 100644 index 0000000000..d32221a209 --- /dev/null +++ b/test/fixedbugs/issue7921.go @@ -0,0 +1,39 @@ +// errorcheck -0 -m + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package foo + +import "bytes" + +// In order to get desired results, we need a combination of +// both escape analysis and inlining. + +func bufferNotEscape() string { + // b itself does not escape, only its buf field will be + // copied during String() call, but object "handle" itself + // can be stack-allocated. + var b bytes.Buffer + b.WriteString("123") // ERROR "b does not escape" + b.Write([]byte{'4'}) // ERROR "b does not escape" "\[\]byte literal does not escape" + return b.String() // ERROR "b does not escape" "inlining call" "string\(bytes\.b\.buf\[bytes.b.off:\]\) escapes to heap" +} + +func bufferNoEscape2(xs []string) int { // ERROR "xs does not escape" + b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "inlining call" "make\(\[\]byte, 0, 64\) does not escape" "&bytes.Buffer literal does not escape" + for _, x := range xs { + b.WriteString(x) + } + return b.Len() // ERROR "inlining call" +} + +func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape" + b := bytes.NewBuffer(make([]byte, 0, 64)) // ERROR "inlining call" "make\(\[\]byte, 0, 64\) does not escape" "&bytes.Buffer literal does not escape" + for _, x := range xs { + b.WriteString(x) + b.WriteByte(',') + } + return b.String() // ERROR "inlining call" "string\(bytes.b.buf\[bytes\.b\.off:\]\) escapes to heap" +}