// 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 saferioimport ()// 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) {ifint64() < 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.returnnil, io.ErrUnexpectedEOF }if < chunk { := make([]byte, ) , := io.ReadFull(, )if != nil {returnnil, }return , nil }var []byte := make([]byte, chunk)for > 0 { := if > chunk { = chunk } , := io.ReadFull(, [:])if != nil {iflen() > 0 && == io.EOF { = io.ErrUnexpectedEOF }returnnil, } = 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) {ifint64() < 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.returnnil, 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 {returnnil, } }return , nil }var []byte := make([]byte, chunk)for > 0 { := if > chunk { = chunk } , := .ReadAt([:], )if != nil {returnnil, } = append(, [:]...) -= += int64() }return , nil}// SliceCapWithSize 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.func (, uint64) int {ifint64() < 0 || != uint64(int()) {return -1 }if > 0 && > (1<<64-1)/ {return -1 }if * > chunk { = chunk / if == 0 { = 1 } }returnint()}// SliceCap is like SliceCapWithSize but using generics.func [ any]( uint64) int {var := uint64(unsafe.Sizeof())returnSliceCapWithSize(, )}
The pages are generated with Goldsv0.6.9-preview. (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.