From f4afaec9b5d83ad9ae9e2595f04abb4b95276adc Mon Sep 17 00:00:00 2001 From: Jarek Kowalski Date: Sun, 24 Nov 2019 18:48:18 -0800 Subject: [PATCH] object: fixed Seek() behavior for seeking to end of object --- repo/object/object_manager_test.go | 52 ++++++++++++++++++++++++++++++ repo/object/object_reader.go | 20 ++++++------ 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/repo/object/object_manager_test.go b/repo/object/object_manager_test.go index c2fd91ad0..994b64a8a 100644 --- a/repo/object/object_manager_test.go +++ b/repo/object/object_manager_test.go @@ -8,6 +8,7 @@ "encoding/hex" "encoding/json" "fmt" + "io" "io/ioutil" "math/rand" "runtime/debug" @@ -349,3 +350,54 @@ func verify(ctx context.Context, t *testing.T, om *Manager, objectID ID, expecte } } } + +// nolint:gocyclo +func TestSeek(t *testing.T) { + ctx := context.Background() + _, om := setupTest(t) + + for _, size := range []int{0, 1, 500000, 15000000} { + randomData := make([]byte, size) + cryptorand.Read(randomData) //nolint:errcheck + + writer := om.NewWriter(ctx, WriterOptions{}) + if _, err := writer.Write(randomData); err != nil { + t.Errorf("write error: %v", err) + } + + objectID, err := writer.Result() + if err != nil { + t.Fatalf("unable to write: %v", err) + } + + r, err := om.Open(ctx, objectID) + if err != nil { + t.Fatalf("open error: %v", err) + } + + if pos, err := r.Seek(0, io.SeekStart); err != nil || pos != 0 { + t.Errorf("invalid seek-start result %v %v", pos, err) + } + + if pos, err := r.Seek(0, io.SeekCurrent); err != nil || pos != 0 { + t.Errorf("invalid seek-current at start result %v %v", pos, err) + } + + if pos, err := r.Seek(0, io.SeekEnd); err != nil || pos != int64(size) { + t.Errorf("invalid seek-end result %v %v", pos, err) + } + + if pos, err := r.Seek(0, io.SeekCurrent); err != nil || pos != int64(size) { + t.Errorf("invalid seek-current at end result %v %v, wanted %v", pos, err, size) + } + + if pos, err := r.Seek(1, io.SeekCurrent); err != nil || pos != int64(size)+1 { + t.Errorf("unexpected result when seeking past end of file: %v, %v, wanted %v", pos, err, size+1) + } + + buf := make([]byte, 5) + if n, err := r.Read(buf); n != 0 || err != io.EOF { + t.Errorf("unexpected read result %v %v", n, err) + } + } +} diff --git a/repo/object/object_reader.go b/repo/object/object_reader.go index 4ac8fac95..99d1eda57 100644 --- a/repo/object/object_reader.go +++ b/repo/object/object_reader.go @@ -29,6 +29,10 @@ func (r *objectReader) Read(buffer []byte) (int, error) { readBytes := 0 remaining := len(buffer) + if r.currentPosition >= r.totalLength { + return 0, io.EOF + } + for remaining > 0 { if r.currentChunkData != nil { toCopy := len(r.currentChunkData) - r.currentChunkPosition @@ -114,20 +118,19 @@ func (r *objectReader) findChunkIndexForOffset(offset int64) (int, error) { } func (r *objectReader) Seek(offset int64, whence int) (int64, error) { - if whence == 1 { + if whence == io.SeekCurrent { return r.Seek(r.currentPosition+offset, 0) } - if whence == 2 { + if whence == io.SeekEnd { return r.Seek(r.totalLength+offset, 0) } - if offset < 0 { - return -1, errors.Errorf("invalid seek %v %v", offset, whence) - } - - if offset > r.totalLength { - offset = r.totalLength + if offset >= r.totalLength { + r.currentChunkIndex = len(r.seekTable) + r.currentChunkData = nil + r.currentPosition = offset + return offset, nil } index, err := r.findChunkIndexForOffset(offset) @@ -136,7 +139,6 @@ func (r *objectReader) Seek(offset int64, whence int) (int64, error) { } chunkStartOffset := r.seekTable[index].Start - if index != r.currentChunkIndex { r.closeCurrentChunk() r.currentChunkIndex = index