mirror of
https://github.com/rclone/rclone.git
synced 2024-11-23 08:35:07 +08:00
accounting: support WriterTo for less memory copying
This should reduce memory copying when the async buffer is in use and improve speeds.
This commit is contained in:
parent
7f15cc9556
commit
fdada79ebf
|
@ -249,6 +249,44 @@ func (acc *Account) Read(p []byte) (n int, err error) {
|
||||||
return acc.read(acc.in, p)
|
return acc.read(acc.in, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thin wrapper for w
|
||||||
|
type accountWriteTo struct {
|
||||||
|
w io.Writer
|
||||||
|
acc *Account
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes len(p) bytes from p to the underlying data stream. It
|
||||||
|
// returns the number of bytes written from p (0 <= n <= len(p)) and
|
||||||
|
// any error encountered that caused the write to stop early. Write
|
||||||
|
// must return a non-nil error if it returns n < len(p). Write must
|
||||||
|
// not modify the slice data, even temporarily.
|
||||||
|
//
|
||||||
|
// Implementations must not retain p.
|
||||||
|
func (awt *accountWriteTo) Write(p []byte) (n int, err error) {
|
||||||
|
_, err = awt.acc.checkRead()
|
||||||
|
if err == nil {
|
||||||
|
n, err = awt.w.Write(p)
|
||||||
|
awt.acc.accountRead(n)
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo writes data to w until there's no more data to write or
|
||||||
|
// when an error occurs. The return value n is the number of bytes
|
||||||
|
// written. Any error encountered during the write is also returned.
|
||||||
|
func (acc *Account) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
acc.mu.Lock()
|
||||||
|
in := acc.in
|
||||||
|
acc.mu.Unlock()
|
||||||
|
wrappedWriter := accountWriteTo{w: w, acc: acc}
|
||||||
|
if do, ok := in.(io.WriterTo); ok {
|
||||||
|
n, err = do.WriteTo(&wrappedWriter)
|
||||||
|
} else {
|
||||||
|
n, err = io.Copy(&wrappedWriter, in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// AccountRead account having read n bytes
|
// AccountRead account having read n bytes
|
||||||
func (acc *Account) AccountRead(n int) (err error) {
|
func (acc *Account) AccountRead(n int) (err error) {
|
||||||
acc.mu.Lock()
|
acc.mu.Lock()
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
// Check it satisfies the interfaces
|
// Check it satisfies the interfaces
|
||||||
var (
|
var (
|
||||||
_ io.ReadCloser = &Account{}
|
_ io.ReadCloser = &Account{}
|
||||||
|
_ io.WriterTo = &Account{}
|
||||||
_ io.Reader = &accountStream{}
|
_ io.Reader = &accountStream{}
|
||||||
_ Accounter = &Account{}
|
_ Accounter = &Account{}
|
||||||
_ Accounter = &accountStream{}
|
_ Accounter = &accountStream{}
|
||||||
|
@ -117,6 +118,46 @@ func TestAccountRead(t *testing.T) {
|
||||||
assert.NoError(t, acc.Close())
|
assert.NoError(t, acc.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccountWriteTo(t *testing.T, withBuffer bool) {
|
||||||
|
buf := make([]byte, 2*asyncreader.BufferSize+1)
|
||||||
|
for i := range buf {
|
||||||
|
buf[i] = byte(i % 251)
|
||||||
|
}
|
||||||
|
in := ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||||
|
stats := NewStats()
|
||||||
|
acc := newAccountSizeName(stats, in, int64(len(buf)), "test")
|
||||||
|
if withBuffer {
|
||||||
|
acc = acc.WithBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, acc.start.IsZero())
|
||||||
|
assert.Equal(t, 0, acc.lpBytes)
|
||||||
|
assert.Equal(t, int64(0), acc.bytes)
|
||||||
|
assert.Equal(t, int64(0), stats.bytes)
|
||||||
|
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
n, err := acc.WriteTo(&out)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(len(buf)), n)
|
||||||
|
assert.Equal(t, buf, out.Bytes())
|
||||||
|
|
||||||
|
assert.False(t, acc.start.IsZero())
|
||||||
|
assert.Equal(t, len(buf), acc.lpBytes)
|
||||||
|
assert.Equal(t, int64(len(buf)), acc.bytes)
|
||||||
|
assert.Equal(t, int64(len(buf)), stats.bytes)
|
||||||
|
|
||||||
|
assert.NoError(t, acc.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountWriteTo(t *testing.T) {
|
||||||
|
testAccountWriteTo(t, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountWriteToWithBuffer(t *testing.T) {
|
||||||
|
testAccountWriteTo(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccountString(t *testing.T) {
|
func TestAccountString(t *testing.T) {
|
||||||
in := ioutil.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
|
in := ioutil.NopCloser(bytes.NewBuffer([]byte{1, 2, 3}))
|
||||||
stats := NewStats()
|
stats := NewStats()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user