diff --git a/backend/cache/object.go b/backend/cache/object.go index 87395db8a..c17b458fb 100644 --- a/backend/cache/object.go +++ b/backend/cache/object.go @@ -220,11 +220,11 @@ func (o *Object) Open(options ...fs.OpenOption) (io.ReadCloser, error) { var err error cacheReader := NewObjectHandle(o) - var offset, limit int64 + var offset, limit int64 = 0, -1 for _, option := range options { switch x := option.(type) { case *fs.SeekOption: - offset, limit = x.Offset, 0 + offset = x.Offset case *fs.RangeOption: offset, limit = x.Decode(o.Size()) } diff --git a/backend/ftp/ftp.go b/backend/ftp/ftp.go index 7f333550d..683a2963a 100644 --- a/backend/ftp/ftp.go +++ b/backend/ftp/ftp.go @@ -634,11 +634,11 @@ func (f *ftpReadCloser) Close() error { func (o *Object) Open(options ...fs.OpenOption) (rc io.ReadCloser, err error) { // defer fs.Trace(o, "")("rc=%v, err=%v", &rc, &err) path := path.Join(o.fs.root, o.remote) - var offset, limit int64 + var offset, limit int64 = 0, -1 for _, option := range options { switch x := option.(type) { case *fs.SeekOption: - offset, limit = x.Offset, 0 + offset = x.Offset case *fs.RangeOption: offset, limit = x.Decode(o.Size()) default: diff --git a/backend/local/local.go b/backend/local/local.go index 55362a5c2..573ffa0ab 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -676,13 +676,12 @@ func (file *localOpenFile) Close() (err error) { // Open an object for read func (o *Object) Open(options ...fs.OpenOption) (in io.ReadCloser, err error) { - var offset int64 - var limit int64 + var offset, limit int64 = 0, -1 hashes := hash.Supported for _, option := range options { switch x := option.(type) { case *fs.SeekOption: - offset, limit = x.Offset, 0 + offset = x.Offset case *fs.RangeOption: offset, limit = x.Decode(o.size) case *fs.HashesOption: diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go index a885c1267..4b3c9dec5 100644 --- a/backend/sftp/sftp.go +++ b/backend/sftp/sftp.go @@ -855,11 +855,11 @@ func (file *ObjectReader) Close() (err error) { // Open a remote sftp file object for reading. Seek is supported func (o *Object) Open(options ...fs.OpenOption) (in io.ReadCloser, err error) { - var offset, limit int64 + var offset, limit int64 = 0, -1 for _, option := range options { switch x := option.(type) { case *fs.SeekOption: - offset, limit = x.Offset, 0 + offset = x.Offset case *fs.RangeOption: offset, limit = x.Decode(o.Size()) default: diff --git a/fs/options.go b/fs/options.go index 66345466d..9eae1d24d 100644 --- a/fs/options.go +++ b/fs/options.go @@ -97,17 +97,25 @@ func (o *RangeOption) Mandatory() bool { } // Decode interprets the RangeOption into an offset and a limit +// +// The offset is the start of the stream and the limit is how many +// bytes should be read from it. If the limit is -1 then the stream +// should be read to the end. func (o *RangeOption) Decode(size int64) (offset, limit int64) { if o.Start >= 0 { offset = o.Start if o.End >= 0 { limit = o.End - o.Start + 1 } else { - limit = 0 + limit = -1 } } else { - offset = size - o.End - limit = 0 + if o.End >= 0 { + offset = size - o.End + } else { + offset = 0 + } + limit = -1 } return offset, limit } diff --git a/fs/options_test.go b/fs/options_test.go index 70ad2883a..bad992390 100644 --- a/fs/options_test.go +++ b/fs/options_test.go @@ -37,3 +37,24 @@ func TestParseRangeOption(t *testing.T) { } } } + +func TestRangeOptionDecode(t *testing.T) { + for _, test := range []struct { + in RangeOption + size int64 + wantOffset int64 + wantLimit int64 + }{ + {in: RangeOption{Start: 1, End: 10}, size: 100, wantOffset: 1, wantLimit: 10}, + {in: RangeOption{Start: 10, End: 10}, size: 100, wantOffset: 10, wantLimit: 1}, + {in: RangeOption{Start: 10, End: 9}, size: 100, wantOffset: 10, wantLimit: 0}, + {in: RangeOption{Start: 1, End: -1}, size: 100, wantOffset: 1, wantLimit: -1}, + {in: RangeOption{Start: -1, End: 90}, size: 100, wantOffset: 10, wantLimit: -1}, + {in: RangeOption{Start: -1, End: -1}, size: 100, wantOffset: 0, wantLimit: -1}, + } { + gotOffset, gotLimit := test.in.Decode(test.size) + what := fmt.Sprintf("%+v size=%d", test.in, test.size) + assert.Equal(t, test.wantOffset, gotOffset, "offset "+what) + assert.Equal(t, test.wantLimit, gotLimit, "limit "+what) + } +} diff --git a/lib/readers/limited.go b/lib/readers/limited.go index ad28763ed..218dd661a 100644 --- a/lib/readers/limited.go +++ b/lib/readers/limited.go @@ -9,10 +9,10 @@ type LimitedReadCloser struct { } // NewLimitedReadCloser returns a LimitedReadCloser wrapping rc to -// limit it to reading limit bytes. If limit == 0 then it does not +// limit it to reading limit bytes. If limit < 0 then it does not // wrap rc, it just returns it. func NewLimitedReadCloser(rc io.ReadCloser, limit int64) (lrc io.ReadCloser) { - if limit == 0 { + if limit < 0 { return rc } return &LimitedReadCloser{