github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/internal/buffered_seek_reader.go (about) 1 package internal 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 8 "github.com/anchore/syft/internal/log" 9 ) 10 11 var _ io.ReadSeekCloser = (*bufferedSeekReader)(nil) 12 13 // bufferedSeekReader wraps an io.ReadCloser to provide io.Seeker functionality. 14 // It only supports seeking from the start and cannot seek past what has already been read. 15 type bufferedSeekReader struct { 16 r io.ReadCloser 17 buf *bytes.Reader 18 data []byte 19 pos int64 20 closed bool 21 } 22 23 func NewBufferedSeeker(rc io.ReadCloser) io.ReadSeekCloser { 24 return &bufferedSeekReader{ 25 r: rc, 26 } 27 } 28 29 func (bs *bufferedSeekReader) Read(p []byte) (int, error) { 30 if bs.closed { 31 return 0, errors.New("cannot read from closed reader") 32 } 33 if bs.pos == int64(len(bs.data)) { 34 // if we're at the end of our buffer, read more data into it 35 tmp := make([]byte, len(p)) 36 37 n, err := bs.r.Read(tmp) 38 if err != nil && err != io.EOF { 39 return 0, err 40 } else if err == io.EOF { 41 bs.closed = true 42 } 43 bs.data = append(bs.data, tmp[:n]...) 44 bs.buf = bytes.NewReader(bs.data) 45 } 46 47 n, err := bs.buf.ReadAt(p, bs.pos) 48 if err != nil && err != io.EOF { 49 log.WithFields("error", err).Trace("buffered seek reader failed to read from underlying reader") 50 } 51 bs.pos += int64(n) 52 53 return n, nil 54 } 55 56 func (bs *bufferedSeekReader) Seek(offset int64, whence int) (int64, error) { 57 var abs int64 58 switch whence { 59 case io.SeekStart: 60 abs = offset 61 case io.SeekCurrent: 62 abs = bs.pos + offset 63 case io.SeekEnd: 64 return 0, errors.New("'SeekEnd' not supported") 65 default: 66 return 0, errors.New("invalid seek option") 67 } 68 69 if abs < 0 { 70 return 0, errors.New("unable to seek before start") 71 } 72 73 if abs > int64(len(bs.data)) { 74 return 0, errors.New("unable to seek past read data") 75 } 76 77 bs.pos = abs 78 return bs.pos, nil 79 } 80 81 func (bs *bufferedSeekReader) Close() error { 82 bs.closed = true 83 return bs.r.Close() 84 }