io.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package internal
  2. import (
  3. "bufio"
  4. "bytes"
  5. "compress/gzip"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "os"
  10. "path/filepath"
  11. "sync"
  12. )
  13. // NewBufferedSectionReader wraps an io.ReaderAt in an appropriately-sized
  14. // buffered reader. It is a convenience function for reading subsections of
  15. // ELF sections while minimizing the amount of read() syscalls made.
  16. //
  17. // Syscall overhead is non-negligible in continuous integration context
  18. // where ELFs might be accessed over virtual filesystems with poor random
  19. // access performance. Buffering reads makes sense because (sub)sections
  20. // end up being read completely anyway.
  21. //
  22. // Use instead of the r.Seek() + io.LimitReader() pattern.
  23. func NewBufferedSectionReader(ra io.ReaderAt, off, n int64) *bufio.Reader {
  24. // Clamp the size of the buffer to one page to avoid slurping large parts
  25. // of a file into memory. bufio.NewReader uses a hardcoded default buffer
  26. // of 4096. Allow arches with larger pages to allocate more, but don't
  27. // allocate a fixed 4k buffer if we only need to read a small segment.
  28. buf := n
  29. if ps := int64(os.Getpagesize()); n > ps {
  30. buf = ps
  31. }
  32. return bufio.NewReaderSize(io.NewSectionReader(ra, off, n), int(buf))
  33. }
  34. // DiscardZeroes makes sure that all written bytes are zero
  35. // before discarding them.
  36. type DiscardZeroes struct{}
  37. func (DiscardZeroes) Write(p []byte) (int, error) {
  38. for _, b := range p {
  39. if b != 0 {
  40. return 0, errors.New("encountered non-zero byte")
  41. }
  42. }
  43. return len(p), nil
  44. }
  45. // ReadAllCompressed decompresses a gzipped file into memory.
  46. func ReadAllCompressed(file string) ([]byte, error) {
  47. fh, err := os.Open(file)
  48. if err != nil {
  49. return nil, err
  50. }
  51. defer fh.Close()
  52. gz, err := gzip.NewReader(fh)
  53. if err != nil {
  54. return nil, err
  55. }
  56. defer gz.Close()
  57. return io.ReadAll(gz)
  58. }
  59. // ReadUint64FromFile reads a uint64 from a file.
  60. //
  61. // format specifies the contents of the file in fmt.Scanf syntax.
  62. func ReadUint64FromFile(format string, path ...string) (uint64, error) {
  63. filename := filepath.Join(path...)
  64. data, err := os.ReadFile(filename)
  65. if err != nil {
  66. return 0, fmt.Errorf("reading file %q: %w", filename, err)
  67. }
  68. var value uint64
  69. n, err := fmt.Fscanf(bytes.NewReader(data), format, &value)
  70. if err != nil {
  71. return 0, fmt.Errorf("parsing file %q: %w", filename, err)
  72. }
  73. if n != 1 {
  74. return 0, fmt.Errorf("parsing file %q: expected 1 item, got %d", filename, n)
  75. }
  76. return value, nil
  77. }
  78. type uint64FromFileKey struct {
  79. format, path string
  80. }
  81. var uint64FromFileCache = struct {
  82. sync.RWMutex
  83. values map[uint64FromFileKey]uint64
  84. }{
  85. values: map[uint64FromFileKey]uint64{},
  86. }
  87. // ReadUint64FromFileOnce is like readUint64FromFile but memoizes the result.
  88. func ReadUint64FromFileOnce(format string, path ...string) (uint64, error) {
  89. filename := filepath.Join(path...)
  90. key := uint64FromFileKey{format, filename}
  91. uint64FromFileCache.RLock()
  92. if value, ok := uint64FromFileCache.values[key]; ok {
  93. uint64FromFileCache.RUnlock()
  94. return value, nil
  95. }
  96. uint64FromFileCache.RUnlock()
  97. value, err := ReadUint64FromFile(format, filename)
  98. if err != nil {
  99. return 0, err
  100. }
  101. uint64FromFileCache.Lock()
  102. defer uint64FromFileCache.Unlock()
  103. if value, ok := uint64FromFileCache.values[key]; ok {
  104. // Someone else got here before us, use what is cached.
  105. return value, nil
  106. }
  107. uint64FromFileCache.values[key] = value
  108. return value, nil
  109. }