1
0
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:
Rob Pike 2012-01-21 09:46:59 -08:00
parent 4d3b9d9757
commit b0d2713b77
3 changed files with 35 additions and 22 deletions

View File

@ -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)
} }

View File

@ -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")
} }

View File

@ -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
} }