]> Sergey Matveev's repositories - btrtrc.git/blobdiff - storage/wrappers.go
Drop support for go 1.20
[btrtrc.git] / storage / wrappers.go
index 8e90f0f0890598aa4e3b66a32bca67a10864e24b..a3907e1d2f4ed6a193c1a6cdedd85b9dbabf6e58 100644 (file)
@@ -1,17 +1,16 @@
 package storage
 
 import (
-       "errors"
        "io"
        "os"
 
-       "github.com/anacrolix/missinggo"
+       "github.com/anacrolix/missinggo/v2"
 
        "github.com/anacrolix/torrent/metainfo"
 )
 
 type Client struct {
-       ClientImpl
+       ci ClientImpl
 }
 
 func NewClient(cl ClientImpl) *Client {
@@ -19,8 +18,11 @@ func NewClient(cl ClientImpl) *Client {
 }
 
 func (cl Client) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (*Torrent, error) {
-       t, err := cl.ClientImpl.OpenTorrent(info, infoHash)
-       return &Torrent{t}, err
+       t, err := cl.ci.OpenTorrent(info, infoHash)
+       if err != nil {
+               return nil, err
+       }
+       return &Torrent{t}, nil
 }
 
 type Torrent struct {
@@ -36,15 +38,33 @@ type Piece struct {
        mip metainfo.Piece
 }
 
-func (p Piece) WriteAt(b []byte, off int64) (n int, err error) {
-       if p.GetIsComplete() {
-               err = errors.New("piece completed")
-               return
+var _ io.WriterTo = Piece{}
+
+// Why do we have this wrapper? Well PieceImpl doesn't implement io.Reader, so we can't let io.Copy
+// and friends check for io.WriterTo and fallback for us since they expect an io.Reader.
+func (p Piece) WriteTo(w io.Writer) (int64, error) {
+       if i, ok := p.PieceImpl.(io.WriterTo); ok {
+               return i.WriteTo(w)
        }
+       n := p.mip.Length()
+       r := io.NewSectionReader(p, 0, n)
+       return io.CopyN(w, r, n)
+}
+
+func (p Piece) WriteAt(b []byte, off int64) (n int, err error) {
+       // Callers should not be writing to completed pieces, but it's too
+       // expensive to be checking this on every single write using uncached
+       // completions.
+
+       // c := p.Completion()
+       // if c.Ok && c.Complete {
+       //      err = errors.New("piece already completed")
+       //      return
+       // }
        if off+int64(len(b)) > p.mip.Length() {
                panic("write overflows piece")
        }
-       missinggo.LimitLen(&b, p.mip.Length()-off)
+       b = missinggo.LimitLen(b, p.mip.Length()-off)
        return p.PieceImpl.WriteAt(b, off)
 }
 
@@ -57,7 +77,7 @@ func (p Piece) ReadAt(b []byte, off int64) (n int, err error) {
                err = io.EOF
                return
        }
-       missinggo.LimitLen(&b, p.mip.Length()-off)
+       b = missinggo.LimitLen(b, p.mip.Length()-off)
        if len(b) == 0 {
                return
        }
@@ -65,18 +85,19 @@ func (p Piece) ReadAt(b []byte, off int64) (n int, err error) {
        if n > len(b) {
                panic(n)
        }
-       off += int64(n)
-       if err == io.EOF && off < p.mip.Length() {
-               err = io.ErrUnexpectedEOF
-       }
-       if err == nil && off >= p.mip.Length() {
-               err = io.EOF
-       }
        if n == 0 && err == nil {
-               err = io.ErrUnexpectedEOF
+               panic("io.Copy will get stuck")
        }
-       if off < p.mip.Length() && err != nil {
-               p.MarkNotComplete()
+       off += int64(n)
+
+       // Doing this here may be inaccurate. There's legitimate reasons we may fail to read while the
+       // data is still there, such as too many open files. There should probably be a specific error
+       // to return if the data has been lost.
+       if off < p.mip.Length() {
+               if err == io.EOF {
+                       p.MarkNotComplete()
+               }
        }
+
        return
 }