mirror of
https://github.com/golang/go
synced 2024-11-24 23:27:57 -07:00
bytes.Buffer: restore panic on out-of-memory
Make the panic detectable, and use that in ioutil.ReadFile to give an error if the file is too big. R=golang-dev, minux.ma, bradfitz CC=golang-dev https://golang.org/cl/5563045
This commit is contained in:
parent
4d3b9d9757
commit
b0d2713b77
@ -33,7 +33,7 @@ const (
|
|||||||
opRead // Any other read operation.
|
opRead // Any other read operation.
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrTooLarge is returned if there is too much data to fit in a buffer.
|
// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer.
|
||||||
var ErrTooLarge = errors.New("bytes.Buffer: too large")
|
var ErrTooLarge = errors.New("bytes.Buffer: too large")
|
||||||
|
|
||||||
// Bytes returns a slice of the contents of the unread portion of the buffer;
|
// Bytes returns a slice of the contents of the unread portion of the buffer;
|
||||||
@ -73,8 +73,7 @@ func (b *Buffer) Reset() { b.Truncate(0) }
|
|||||||
|
|
||||||
// grow grows the buffer to guarantee space for n more bytes.
|
// grow grows the buffer to guarantee space for n more bytes.
|
||||||
// It returns the index where bytes should be written.
|
// It returns the index where bytes should be written.
|
||||||
// If the buffer can't grow, it returns -1, which will
|
// If the buffer can't grow it will panic with ErrTooLarge.
|
||||||
// become ErrTooLarge in the caller.
|
|
||||||
func (b *Buffer) grow(n int) int {
|
func (b *Buffer) grow(n int) int {
|
||||||
m := b.Len()
|
m := b.Len()
|
||||||
// If buffer is empty, reset to recover space.
|
// If buffer is empty, reset to recover space.
|
||||||
@ -88,9 +87,6 @@ func (b *Buffer) grow(n int) int {
|
|||||||
} else {
|
} else {
|
||||||
// not enough space anywhere
|
// not enough space anywhere
|
||||||
buf = makeSlice(2*cap(b.buf) + n)
|
buf = makeSlice(2*cap(b.buf) + n)
|
||||||
if buf == nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
copy(buf, b.buf[b.off:])
|
copy(buf, b.buf[b.off:])
|
||||||
}
|
}
|
||||||
b.buf = buf
|
b.buf = buf
|
||||||
@ -102,6 +98,8 @@ func (b *Buffer) grow(n int) int {
|
|||||||
|
|
||||||
// Write appends the contents of p to the buffer. The return
|
// Write appends the contents of p to the buffer. The return
|
||||||
// value n is the length of p; err is always nil.
|
// value n is the length of p; err is always nil.
|
||||||
|
// If the buffer becomes too large, Write will panic with
|
||||||
|
// ErrTooLarge.
|
||||||
func (b *Buffer) Write(p []byte) (n int, err error) {
|
func (b *Buffer) Write(p []byte) (n int, err error) {
|
||||||
b.lastRead = opInvalid
|
b.lastRead = opInvalid
|
||||||
m := b.grow(len(p))
|
m := b.grow(len(p))
|
||||||
@ -146,9 +144,6 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
// not enough space using beginning of buffer;
|
// not enough space using beginning of buffer;
|
||||||
// double buffer capacity
|
// double buffer capacity
|
||||||
newBuf = makeSlice(2*cap(b.buf) + MinRead)
|
newBuf = makeSlice(2*cap(b.buf) + MinRead)
|
||||||
if newBuf == nil {
|
|
||||||
return n, ErrTooLarge
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
copy(newBuf, b.buf[b.off:])
|
copy(newBuf, b.buf[b.off:])
|
||||||
b.buf = newBuf[:len(b.buf)-b.off]
|
b.buf = newBuf[:len(b.buf)-b.off]
|
||||||
@ -167,14 +162,14 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
return n, nil // err is EOF, so return nil explicitly
|
return n, nil // err is EOF, so return nil explicitly
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeSlice allocates a slice of size n, returning nil if the slice cannot be allocated.
|
// makeSlice allocates a slice of size n. If the allocation fails, it panics
|
||||||
|
// with ErrTooLarge.
|
||||||
func makeSlice(n int) []byte {
|
func makeSlice(n int) []byte {
|
||||||
if n < 0 {
|
// If the make fails, give a known error.
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Catch out of memory panics.
|
|
||||||
defer func() {
|
defer func() {
|
||||||
recover()
|
if recover() != nil {
|
||||||
|
panic(ErrTooLarge)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
return make([]byte, n)
|
return make([]byte, n)
|
||||||
}
|
}
|
||||||
|
@ -392,13 +392,18 @@ func TestHuge(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// We expect a panic.
|
||||||
|
defer func() {
|
||||||
|
if err, ok := recover().(error); ok && err == ErrTooLarge {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
t.Error(`expected "too large" error; got`, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
b := new(Buffer)
|
b := new(Buffer)
|
||||||
big := make([]byte, 500e6)
|
big := make([]byte, 500e6)
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
if _, err := b.Write(big); err != nil {
|
b.Write(big)
|
||||||
// Got error as expected. Stop
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t.Error("error expected")
|
t.Error("panic expected")
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,22 @@ import (
|
|||||||
|
|
||||||
// readAll reads from r until an error or EOF and returns the data it read
|
// readAll reads from r until an error or EOF and returns the data it read
|
||||||
// from the internal buffer allocated with a specified capacity.
|
// from the internal buffer allocated with a specified capacity.
|
||||||
func readAll(r io.Reader, capacity int64) ([]byte, error) {
|
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, capacity))
|
buf := bytes.NewBuffer(make([]byte, 0, capacity))
|
||||||
_, err := buf.ReadFrom(r)
|
// If the buffer overflows, we will get bytes.ErrTooLarge.
|
||||||
|
// Return that as an error. Any other panic remains.
|
||||||
|
defer func() {
|
||||||
|
e := recover()
|
||||||
|
if e == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
|
||||||
|
err = panicErr
|
||||||
|
} else {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
_, err = buf.ReadFrom(r)
|
||||||
return buf.Bytes(), err
|
return buf.Bytes(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user