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  }