internal/httprange/http_ranged_reader.go (41 lines of code) (raw):
package httprange
import (
"context"
"io"
)
// RangedReader for a resource.
// Implements the io.ReaderAt interface that can be used with Go's archive/zip package.
type RangedReader struct {
Resource *Resource
cachedReader *Reader
}
func (rr *RangedReader) cachedRead(buf []byte, off int64) (int, error) {
_, err := rr.cachedReader.Seek(off, io.SeekStart)
if err != nil {
return 0, err
}
return io.ReadFull(rr.cachedReader, buf)
}
func (rr *RangedReader) ephemeralRead(buf []byte, offset int64) (n int, err error) {
// we can use context.Background and rely on the Reader's httpClient timeout for ephemeral reads
reader := NewReader(context.Background(), rr.Resource, offset, int64(len(buf)))
defer reader.Close()
return io.ReadFull(reader, buf)
}
// SectionReader partitions a resource from `offset` with a specified `size`
func (rr *RangedReader) SectionReader(ctx context.Context, offset, size int64) *Reader {
return NewReader(ctx, rr.Resource, offset, size)
}
// ReadAt reads from cachedReader if exists, otherwise fetches a new Resource first.
// Opens a resource and reads len(buf) bytes from offset into buf.
func (rr *RangedReader) ReadAt(buf []byte, offset int64) (n int, err error) {
if rr.cachedReader != nil {
return rr.cachedRead(buf, offset)
}
return rr.ephemeralRead(buf, offset)
}
// WithCachedReader creates a Reader and saves it to the RangedReader instance.
// It takes a readFunc that will Seek the contents from Reader.
func (rr *RangedReader) WithCachedReader(ctx context.Context, readFunc func()) {
rr.cachedReader = NewReader(ctx, rr.Resource, 0, rr.Resource.Size)
defer func() {
rr.cachedReader.Close()
rr.cachedReader = nil
}()
readFunc()
}
// NewRangedReader creates a RangedReader object on a given resource
func NewRangedReader(resource *Resource) *RangedReader {
return &RangedReader{Resource: resource}
}