io.go (1735B)
1 package internal 2 3 import ( 4 "bufio" 5 "compress/gzip" 6 "errors" 7 "io" 8 "os" 9 ) 10 11 // NewBufferedSectionReader wraps an io.ReaderAt in an appropriately-sized 12 // buffered reader. It is a convenience function for reading subsections of 13 // ELF sections while minimizing the amount of read() syscalls made. 14 // 15 // Syscall overhead is non-negligible in continuous integration context 16 // where ELFs might be accessed over virtual filesystems with poor random 17 // access performance. Buffering reads makes sense because (sub)sections 18 // end up being read completely anyway. 19 // 20 // Use instead of the r.Seek() + io.LimitReader() pattern. 21 func NewBufferedSectionReader(ra io.ReaderAt, off, n int64) *bufio.Reader { 22 // Clamp the size of the buffer to one page to avoid slurping large parts 23 // of a file into memory. bufio.NewReader uses a hardcoded default buffer 24 // of 4096. Allow arches with larger pages to allocate more, but don't 25 // allocate a fixed 4k buffer if we only need to read a small segment. 26 buf := n 27 if ps := int64(os.Getpagesize()); n > ps { 28 buf = ps 29 } 30 31 return bufio.NewReaderSize(io.NewSectionReader(ra, off, n), int(buf)) 32 } 33 34 // DiscardZeroes makes sure that all written bytes are zero 35 // before discarding them. 36 type DiscardZeroes struct{} 37 38 func (DiscardZeroes) Write(p []byte) (int, error) { 39 for _, b := range p { 40 if b != 0 { 41 return 0, errors.New("encountered non-zero byte") 42 } 43 } 44 return len(p), nil 45 } 46 47 // ReadAllCompressed decompresses a gzipped file into memory. 48 func ReadAllCompressed(file string) ([]byte, error) { 49 fh, err := os.Open(file) 50 if err != nil { 51 return nil, err 52 } 53 defer fh.Close() 54 55 gz, err := gzip.NewReader(fh) 56 if err != nil { 57 return nil, err 58 } 59 defer gz.Close() 60 61 return io.ReadAll(gz) 62 }