github.com/sashka/siva@v1.6.0/reader.go (about)

     1  package siva
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  )
     7  
     8  var (
     9  	ErrPendingContent   = errors.New("entry wasn't fully read")
    10  	ErrInvalidCheckshum = errors.New("invalid checksum")
    11  	ErrInvalidReaderAt  = errors.New("reader provided dosen't implement ReaderAt interface")
    12  )
    13  
    14  // A Reader provides random access to the contents of a siva archive.
    15  type Reader interface {
    16  	io.Reader
    17  	Seek(e *IndexEntry) (int64, error)
    18  	Index() (Index, error)
    19  	Get(e *IndexEntry) (*io.SectionReader, error)
    20  }
    21  
    22  type reader struct {
    23  	r io.ReadSeeker
    24  
    25  	getIndexFunc func() (Index, error)
    26  	index        Index
    27  	current      *IndexEntry
    28  	pending      uint64
    29  	offset       uint64
    30  }
    31  
    32  // NewReader creates a new Reader reading from r, reader requires be seekable
    33  // and optionally should implement io.ReaderAt to make usage of the Get method
    34  func NewReader(r io.ReadSeeker) Reader {
    35  	return &reader{r: r}
    36  }
    37  
    38  // NewReaderWithOffset creates a new Reader giving the position of the index.
    39  // This is useful to open siva files that are being written or reading an
    40  // old index.
    41  func NewReaderWithOffset(r io.ReadSeeker, o uint64) Reader {
    42  	return &reader{
    43  		r:      r,
    44  		offset: o,
    45  	}
    46  }
    47  
    48  func newReaderWithIndex(r io.ReadSeeker, getIndexFunc func() (Index, error)) *reader {
    49  	return &reader{
    50  		r:            r,
    51  		getIndexFunc: getIndexFunc,
    52  	}
    53  }
    54  
    55  // Index reads the index of the siva file from the provided reader
    56  func (r *reader) Index() (Index, error) {
    57  	if r.getIndexFunc != nil {
    58  		return r.getIndexFunc()
    59  	}
    60  
    61  	if r.index == nil {
    62  		i, err := readIndex(r.r, r.offset)
    63  		if err != nil && err != ErrEmptyIndex {
    64  			return nil, err
    65  		}
    66  
    67  		index := OrderedIndex(i.filter())
    68  		index.Sort()
    69  		r.index = Index(index)
    70  	}
    71  
    72  	return r.index, nil
    73  }
    74  
    75  // Get returns a new io.SectionReader allowing concurrent read access to the
    76  // content of the read
    77  func (r *reader) Get(e *IndexEntry) (*io.SectionReader, error) {
    78  	ra, ok := r.r.(io.ReaderAt)
    79  	if !ok {
    80  		return nil, ErrInvalidReaderAt
    81  	}
    82  
    83  	return io.NewSectionReader(ra, int64(e.absStart), int64(e.Size)), nil
    84  }
    85  
    86  // Seek seek the internal reader to the starting position of the content for the
    87  // given IndexEntry
    88  func (r *reader) Seek(e *IndexEntry) (int64, error) {
    89  	r.current = e
    90  	r.pending = e.Size
    91  
    92  	return r.r.Seek(int64(e.absStart), io.SeekStart)
    93  }
    94  
    95  // Read reads up to len(p) bytes, starting at the current position set by Seek
    96  // and ending in the end of the content, retuning a io.EOF when its reached
    97  func (r *reader) Read(p []byte) (n int, err error) {
    98  	if r.pending == 0 {
    99  		return 0, io.EOF
   100  	}
   101  
   102  	if uint64(len(p)) > r.pending {
   103  		p = p[0:r.pending]
   104  	}
   105  
   106  	n, err = r.r.Read(p)
   107  	r.pending -= uint64(n)
   108  
   109  	if err == io.EOF && r.pending > 0 {
   110  		err = io.ErrUnexpectedEOF
   111  	}
   112  
   113  	return
   114  }