diff --git a/fs/object/object.go b/fs/object/object.go index 40aee8dec..9b4e25afe 100644 --- a/fs/object/object.go +++ b/fs/object/object.go @@ -279,19 +279,26 @@ func (o *MemoryObject) SetModTime(ctx context.Context, modTime time.Time) error // Open opens the file for read. Call Close() on the returned io.ReadCloser func (o *MemoryObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) { - content := o.content + var offset, limit int64 = 0, -1 for _, option := range options { switch x := option.(type) { case *fs.RangeOption: - content = o.content[x.Start:x.End] + offset, limit = x.Decode(o.Size()) case *fs.SeekOption: - content = o.content[x.Offset:] + offset = x.Offset default: if option.Mandatory() { fs.Logf(o, "Unsupported mandatory option: %v", option) } } } + content := o.content + offset = max(offset, 0) + if limit < 0 { + content = content[offset:] + } else { + content = content[offset:min(offset+limit, int64(len(content)))] + } return io.NopCloser(bytes.NewBuffer(content)), nil } diff --git a/fs/object/object_test.go b/fs/object/object_test.go index 2a4236272..be90c771e 100644 --- a/fs/object/object_test.go +++ b/fs/object/object_test.go @@ -110,6 +110,7 @@ func TestMemoryObject(t *testing.T) { assert.Equal(t, newNow, o.ModTime(context.Background())) checkOpen := func(rc io.ReadCloser, expected string) { + t.Helper() actual, err := io.ReadAll(rc) assert.NoError(t, err) err = rc.Close() @@ -118,6 +119,7 @@ func TestMemoryObject(t *testing.T) { } checkContent := func(o fs.Object, expected string) { + t.Helper() rc, err := o.Open(context.Background()) assert.NoError(t, err) checkOpen(rc, expected) @@ -127,12 +129,28 @@ func TestMemoryObject(t *testing.T) { rc, err := o.Open(context.Background(), &fs.RangeOption{Start: 1, End: 3}) assert.NoError(t, err) - checkOpen(rc, "ot") + checkOpen(rc, "ota") + + rc, err = o.Open(context.Background(), &fs.RangeOption{Start: 1, End: -1}) + assert.NoError(t, err) + checkOpen(rc, "otato") + + rc, err = o.Open(context.Background(), &fs.RangeOption{Start: 1, End: 4096}) + assert.NoError(t, err) + checkOpen(rc, "otato") + + rc, err = o.Open(context.Background(), &fs.RangeOption{Start: -1, End: 4}) + assert.NoError(t, err) + checkOpen(rc, "tato") rc, err = o.Open(context.Background(), &fs.SeekOption{Offset: 3}) assert.NoError(t, err) checkOpen(rc, "ato") + rc, err = o.Open(context.Background(), &fs.SeekOption{Offset: -100}) + assert.NoError(t, err) + checkOpen(rc, "potato") + // check it fits within the buffer newNow = now.Add(2 * time.Minute) newContent := bytes.NewBufferString("Rutabaga")