mirror of
https://github.com/rclone/rclone.git
synced 2026-01-08 19:43:58 +00:00
operations: add ReadAt method to ReOpen
This commit is contained in:
@@ -20,6 +20,7 @@ type AccountFn func(n int) error
|
||||
type ReOpen struct {
|
||||
ctx context.Context
|
||||
mu sync.Mutex // mutex to protect the below
|
||||
readAtMu sync.Mutex // mutex to serialize the ReadAt calls
|
||||
src fs.Object // object to open
|
||||
baseOptions []fs.OpenOption // options to pass to initial open and where offset == 0
|
||||
options []fs.OpenOption // option to pass on subsequent opens where offset != 0
|
||||
@@ -232,6 +233,35 @@ func (h *ReOpen) Read(p []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadAt reads len(p) bytes at absolute offset off without changing
|
||||
// the read position.
|
||||
//
|
||||
// Note: operations are serialized; it won't behave like a truly
|
||||
// concurrent ReaderAt.
|
||||
func (h *ReOpen) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
h.readAtMu.Lock()
|
||||
defer h.readAtMu.Unlock()
|
||||
|
||||
// Save current position
|
||||
cur, err := h.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Seek to requested offset
|
||||
if _, err = h.Seek(off, io.SeekStart); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Restore position on exit
|
||||
defer func() {
|
||||
if _, seekErr := h.Seek(cur, io.SeekStart); seekErr != nil && err == nil {
|
||||
err = seekErr
|
||||
}
|
||||
}()
|
||||
|
||||
// Fill p fully unless EOF
|
||||
return h.Read(p)
|
||||
}
|
||||
|
||||
// Seek sets the offset for the next Read or Write to offset, interpreted
|
||||
// according to whence: SeekStart means relative to the start of the file,
|
||||
// SeekCurrent means relative to the current offset, and SeekEnd means relative
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
// check interfaces
|
||||
var (
|
||||
_ io.ReadSeekCloser = (*ReOpen)(nil)
|
||||
_ io.ReaderAt = (*ReOpen)(nil)
|
||||
_ pool.DelayAccountinger = (*ReOpen)(nil)
|
||||
)
|
||||
|
||||
@@ -137,6 +138,16 @@ func TestReOpen(t *testing.T) {
|
||||
return rc, src, err
|
||||
}
|
||||
|
||||
// Reset the start after a seek, taking into account the offset
|
||||
setWantStart := func(src *reOpenTestObject, x int64) {
|
||||
src.wantStart = x
|
||||
if rangeOption != nil {
|
||||
src.wantStart += rangeOption.Start
|
||||
} else if seekOption != nil {
|
||||
src.wantStart += seekOption.Offset
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("Basics", func(t *testing.T) {
|
||||
// open
|
||||
h, _, err := testReOpen(nil, 10)
|
||||
@@ -215,6 +226,37 @@ func TestReOpen(t *testing.T) {
|
||||
assert.Equal(t, errFileClosed, h.Close())
|
||||
})
|
||||
|
||||
t.Run("ReadAt", func(t *testing.T) {
|
||||
// open
|
||||
h, src, err := testReOpen([]int64{2, 1, 3}, 10)
|
||||
assert.NoError(t, err)
|
||||
|
||||
buf := make([]byte, 5)
|
||||
|
||||
// Read at 0
|
||||
n, err := h.ReadAt(buf, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 5, n)
|
||||
assert.Equal(t, expectedRead[:n], buf[:n])
|
||||
|
||||
// Read at 1
|
||||
setWantStart(src, 1)
|
||||
n, err = h.ReadAt(buf[:3], 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 3, n)
|
||||
assert.Equal(t, expectedRead[1:n+1], buf[:n])
|
||||
|
||||
// check position unchanged
|
||||
pos, err := h.Seek(0, io.SeekCurrent)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(0), pos)
|
||||
|
||||
// check close
|
||||
assert.NoError(t, h.Close())
|
||||
_, err = h.Seek(0, io.SeekCurrent)
|
||||
assert.Equal(t, errFileClosed, err)
|
||||
})
|
||||
|
||||
t.Run("Seek", func(t *testing.T) {
|
||||
// open
|
||||
h, src, err := testReOpen([]int64{2, 1, 3}, 10)
|
||||
@@ -267,18 +309,8 @@ func TestReOpen(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, int(pos))
|
||||
|
||||
// Reset the start after a seek, taking into account the offset
|
||||
setWantStart := func(x int64) {
|
||||
src.wantStart = x
|
||||
if rangeOption != nil {
|
||||
src.wantStart += rangeOption.Start
|
||||
} else if seekOption != nil {
|
||||
src.wantStart += seekOption.Offset
|
||||
}
|
||||
}
|
||||
|
||||
// check read
|
||||
setWantStart(2)
|
||||
setWantStart(src, 2)
|
||||
n, err = h.Read(dst)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 5, n)
|
||||
@@ -305,7 +337,7 @@ func TestReOpen(t *testing.T) {
|
||||
|
||||
// check read
|
||||
dst = make([]byte, 3)
|
||||
setWantStart(int64(len(expectedRead) - 3))
|
||||
setWantStart(src, int64(len(expectedRead)-3))
|
||||
n, err = h.Read(dst)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, n)
|
||||
|
||||
Reference in New Issue
Block a user