mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
strings: fix two Builder bugs allowing mutation of strings, remove ReadFrom
The Builder's ReadFrom method allows the underlying unsafe slice to escape, and for callers to subsequently modify memory that had been unsafely converted into an immutable string. In the original proposal for Builder (#18990), I'd noted there should be no Read methods: > There would be no Reset or Bytes or Truncate or Read methods. > Nothing that could mutate the []byte once it was unsafely converted > to a string. And in my prototype (https://golang.org/cl/37767), I handled ReadFrom properly, but when https://golang.org/cl/74931 arrived, I missed that it had a ReadFrom method and approved it. Because we're so close to the Go 1.10 release, just remove the ReadFrom method rather than think about possible fixes. It has marginal utility in a Builder anyway. Also, fix a separate bug that also allowed mutation of a slice's backing array after it had been converted into a slice by disallowing copies of the Builder by value. Updates #18990 Fixes #23083 Fixes #23084 Change-Id: Id1f860f8a4f5f88b32213cf85108ebc609acb95f Reviewed-on: https://go-review.googlesource.com/83255 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
29be20a111
commit
3058d38632
3 changed files with 120 additions and 127 deletions
|
|
@ -5,16 +5,24 @@
|
|||
package strings
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// A Builder is used to efficiently build a string using Write methods.
|
||||
// It minimizes memory copying. The zero value is ready to use.
|
||||
// Do not copy a non-zero Builder.
|
||||
type Builder struct {
|
||||
buf []byte
|
||||
addr *Builder // of receiver, to detect copies by value
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (b *Builder) copyCheck() {
|
||||
if b.addr == nil {
|
||||
b.addr = b
|
||||
} else if b.addr != b {
|
||||
panic("strings: illegal use of non-zero Builder copied by value")
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the accumulated string.
|
||||
|
|
@ -26,7 +34,10 @@ func (b *Builder) String() string {
|
|||
func (b *Builder) Len() int { return len(b.buf) }
|
||||
|
||||
// Reset resets the Builder to be empty.
|
||||
func (b *Builder) Reset() { b.buf = nil }
|
||||
func (b *Builder) Reset() {
|
||||
b.addr = nil
|
||||
b.buf = nil
|
||||
}
|
||||
|
||||
// grow copies the buffer to a new, larger buffer so that there are at least n
|
||||
// bytes of capacity beyond len(b.buf).
|
||||
|
|
@ -40,6 +51,7 @@ func (b *Builder) grow(n int) {
|
|||
// another n bytes. After Grow(n), at least n bytes can be written to b
|
||||
// without another allocation. If n is negative, Grow panics.
|
||||
func (b *Builder) Grow(n int) {
|
||||
b.copyCheck()
|
||||
if n < 0 {
|
||||
panic("strings.Builder.Grow: negative count")
|
||||
}
|
||||
|
|
@ -51,6 +63,7 @@ func (b *Builder) Grow(n int) {
|
|||
// Write appends the contents of p to b's buffer.
|
||||
// Write always returns len(p), nil.
|
||||
func (b *Builder) Write(p []byte) (int, error) {
|
||||
b.copyCheck()
|
||||
b.buf = append(b.buf, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
|
@ -58,6 +71,7 @@ func (b *Builder) Write(p []byte) (int, error) {
|
|||
// WriteByte appends the byte c to b's buffer.
|
||||
// The returned error is always nil.
|
||||
func (b *Builder) WriteByte(c byte) error {
|
||||
b.copyCheck()
|
||||
b.buf = append(b.buf, c)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -65,6 +79,7 @@ func (b *Builder) WriteByte(c byte) error {
|
|||
// WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer.
|
||||
// It returns the length of r and a nil error.
|
||||
func (b *Builder) WriteRune(r rune) (int, error) {
|
||||
b.copyCheck()
|
||||
if r < utf8.RuneSelf {
|
||||
b.buf = append(b.buf, byte(r))
|
||||
return 1, nil
|
||||
|
|
@ -81,38 +96,7 @@ func (b *Builder) WriteRune(r rune) (int, error) {
|
|||
// WriteString appends the contents of s to b's buffer.
|
||||
// It returns the length of s and a nil error.
|
||||
func (b *Builder) WriteString(s string) (int, error) {
|
||||
b.copyCheck()
|
||||
b.buf = append(b.buf, s...)
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
// minRead is the minimum slice passed to a Read call by Builder.ReadFrom.
|
||||
// It is the same as bytes.MinRead.
|
||||
const minRead = 512
|
||||
|
||||
// errNegativeRead is the panic value if the reader passed to Builder.ReadFrom
|
||||
// returns a negative count.
|
||||
var errNegativeRead = errors.New("strings.Builder: reader returned negative count from Read")
|
||||
|
||||
// ReadFrom reads data from r until EOF and appends it to b's buffer.
|
||||
// The return value n is the number of bytes read.
|
||||
// Any error except io.EOF encountered during the read is also returned.
|
||||
func (b *Builder) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
for {
|
||||
l := len(b.buf)
|
||||
if cap(b.buf)-l < minRead {
|
||||
b.grow(minRead)
|
||||
}
|
||||
m, e := r.Read(b.buf[l:cap(b.buf)])
|
||||
if m < 0 {
|
||||
panic(errNegativeRead)
|
||||
}
|
||||
b.buf = b.buf[:l+m]
|
||||
n += int64(m)
|
||||
if e == io.EOF {
|
||||
return n, nil
|
||||
}
|
||||
if e != nil {
|
||||
return n, e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue