84 lines
1.6 KiB
Go
84 lines
1.6 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
type rangeOpener func(offset int64) (io.ReadCloser, context.CancelFunc, error)
|
|
|
|
type s3Reader struct {
|
|
size int64
|
|
position int64
|
|
bodyOffset int64
|
|
body io.ReadCloser
|
|
cancel context.CancelFunc
|
|
reopen rangeOpener
|
|
}
|
|
|
|
func newS3Reader(size int64, body io.ReadCloser, cancel context.CancelFunc, reopen rangeOpener) *s3Reader {
|
|
return &s3Reader{size: size, body: body, cancel: cancel, reopen: reopen}
|
|
}
|
|
|
|
func (r *s3Reader) Read(p []byte) (int, error) {
|
|
if r.position >= r.size {
|
|
return 0, io.EOF
|
|
}
|
|
if r.body == nil || r.bodyOffset != r.position {
|
|
if err := r.openAtPosition(); err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
n, err := r.body.Read(p)
|
|
r.position += int64(n)
|
|
r.bodyOffset = r.position
|
|
return n, err
|
|
}
|
|
|
|
func (r *s3Reader) openAtPosition() error {
|
|
r.release()
|
|
body, cancel, err := r.reopen(r.position)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.body, r.cancel, r.bodyOffset = body, cancel, r.position
|
|
return nil
|
|
}
|
|
|
|
func (r *s3Reader) Seek(offset int64, whence int) (int64, error) {
|
|
var target int64
|
|
switch whence {
|
|
case io.SeekStart:
|
|
target = offset
|
|
case io.SeekCurrent:
|
|
target = r.position + offset
|
|
case io.SeekEnd:
|
|
target = r.size + offset
|
|
default:
|
|
return 0, errors.New("invalid seek whence")
|
|
}
|
|
if target < 0 {
|
|
return 0, fmt.Errorf("negative seek position %d", target)
|
|
}
|
|
r.position = target
|
|
return target, nil
|
|
}
|
|
|
|
func (r *s3Reader) Close() error {
|
|
r.release()
|
|
return nil
|
|
}
|
|
|
|
func (r *s3Reader) release() {
|
|
if r.body != nil {
|
|
_ = r.body.Close()
|
|
r.body = nil
|
|
}
|
|
if r.cancel != nil {
|
|
r.cancel()
|
|
r.cancel = nil
|
|
}
|
|
}
|