
After some analysis, it was found that Content.Reader was generally redudant to an io.ReaderAt. This change removes `Content.Reader` in favor of a `Content.ReaderAt`. In general, `ReaderAt` can perform better over interfaces with indeterminant latency because it avoids remote state for reads. Where a reader is required, a helper is provided to convert it into an `io.SectionReader`. Signed-off-by: Stephen J Day <stephen.day@docker.com>
50 lines
906 B
Go
50 lines
906 B
Go
package content
|
|
|
|
import (
|
|
"context"
|
|
|
|
contentapi "github.com/containerd/containerd/api/services/content/v1"
|
|
digest "github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
type remoteReaderAt struct {
|
|
ctx context.Context
|
|
digest digest.Digest
|
|
size int64
|
|
client contentapi.ContentClient
|
|
}
|
|
|
|
func (ra *remoteReaderAt) Size() int64 {
|
|
return ra.size
|
|
}
|
|
|
|
func (ra *remoteReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
|
|
rr := &contentapi.ReadContentRequest{
|
|
Digest: ra.digest,
|
|
Offset: off,
|
|
Size_: int64(len(p)),
|
|
}
|
|
rc, err := ra.client.Read(ra.ctx, rr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
for len(p) > 0 {
|
|
var resp *contentapi.ReadContentResponse
|
|
// fill our buffer up until we can fill p.
|
|
resp, err = rc.Recv()
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
copied := copy(p, resp.Data)
|
|
n += copied
|
|
p = p[copied:]
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func (rr *remoteReaderAt) Close() error {
|
|
return nil
|
|
}
|