Source File
io.go
Belonging Package
internal/saferio
// Copyright 2022 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 saferio provides I/O functions that avoid allocating large
// amounts of memory unnecessarily. This is intended for packages that
// read data from an [io.Reader] where the size is part of the input
// data but the input may be corrupt, or may be provided by an
// untrustworthy attacker.
package saferio
import (
)
// chunk is an arbitrary limit on how much memory we are willing
// to allocate without concern.
const chunk = 10 << 20 // 10M
// ReadData reads n bytes from the input stream, but avoids allocating
// all n bytes if n is large. This avoids crashing the program by
// allocating all n bytes in cases where n is incorrect.
//
// The error is io.EOF only if no bytes were read.
// If an io.EOF happens after reading some but not all the bytes,
// ReadData returns io.ErrUnexpectedEOF.
func ( io.Reader, uint64) ([]byte, error) {
if int64() < 0 || != uint64(int()) {
// n is too large to fit in int, so we can't allocate
// a buffer large enough. Treat this as a read failure.
return nil, io.ErrUnexpectedEOF
}
if < chunk {
:= make([]byte, )
, := io.ReadFull(, )
if != nil {
return nil,
}
return , nil
}
var []byte
:= make([]byte, chunk)
for > 0 {
:=
if > chunk {
= chunk
}
, := io.ReadFull(, [:])
if != nil {
if len() > 0 && == io.EOF {
= io.ErrUnexpectedEOF
}
return nil,
}
= append(, [:]...)
-=
}
return , nil
}
// ReadDataAt reads n bytes from the input stream at off, but avoids
// allocating all n bytes if n is large. This avoids crashing the program
// by allocating all n bytes in cases where n is incorrect.
func ( io.ReaderAt, uint64, int64) ([]byte, error) {
if int64() < 0 || != uint64(int()) {
// n is too large to fit in int, so we can't allocate
// a buffer large enough. Treat this as a read failure.
return nil, io.ErrUnexpectedEOF
}
if < chunk {
:= make([]byte, )
, := .ReadAt(, )
if != nil {
// io.SectionReader can return EOF for n == 0,
// but for our purposes that is a success.
if != io.EOF || > 0 {
return nil,
}
}
return , nil
}
var []byte
:= make([]byte, chunk)
for > 0 {
:=
if > chunk {
= chunk
}
, := .ReadAt([:], )
if != nil {
return nil,
}
= append(, [:]...)
-=
+= int64()
}
return , nil
}
// SliceCap returns the capacity to use when allocating a slice.
// After the slice is allocated with the capacity, it should be
// built using append. This will avoid allocating too much memory
// if the capacity is large and incorrect.
//
// A negative result means that the value is always too big.
//
// The element type is described by passing a pointer to a value of that type.
// This would ideally use generics, but this code is built with
// the bootstrap compiler which need not support generics.
// We use a pointer so that we can handle slices of interface type.
func ( any, uint64) int {
if int64() < 0 || != uint64(int()) {
return -1
}
:= reflect.TypeOf()
if .Kind() != reflect.Ptr {
panic("SliceCap called with non-pointer type")
}
:= uint64(.Elem().Size())
if > 0 && > (1<<64-1)/ {
return -1
}
if * > chunk {
= uint64(chunk / )
if == 0 {
= 1
}
}
return int()
}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |