]> Sergey Matveev's repositories - btrtrc.git/blob - storage/wrappers.go
cmd/btrtrc client
[btrtrc.git] / storage / wrappers.go
1 package storage
2
3 import (
4         "io"
5         "os"
6
7         g "github.com/anacrolix/generics"
8         "github.com/anacrolix/missinggo/v2"
9
10         "github.com/anacrolix/torrent/metainfo"
11 )
12
13 type Client struct {
14         ci ClientImpl
15 }
16
17 func NewClient(cl ClientImpl) *Client {
18         return &Client{cl}
19 }
20
21 func (cl Client) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (*Torrent, error) {
22         t, err := cl.ci.OpenTorrent(info, infoHash)
23         if err != nil {
24                 return nil, err
25         }
26         return &Torrent{t}, nil
27 }
28
29 type Torrent struct {
30         TorrentImpl
31 }
32
33 // Deprecated. Use PieceWithHash, as this doesn't work with pure v2 torrents.
34 func (t Torrent) Piece(p metainfo.Piece) Piece {
35         return t.PieceWithHash(p, g.Some(p.V1Hash().Unwrap().Bytes()))
36 }
37
38 func (t Torrent) PieceWithHash(p metainfo.Piece, pieceHash g.Option[[]byte]) Piece {
39         var pieceImpl PieceImpl
40         if t.TorrentImpl.PieceWithHash != nil {
41                 pieceImpl = t.TorrentImpl.PieceWithHash(p, pieceHash)
42         } else {
43                 pieceImpl = t.TorrentImpl.Piece(p)
44         }
45         return Piece{pieceImpl, p}
46 }
47
48 type Piece struct {
49         PieceImpl
50         mip metainfo.Piece
51 }
52
53 var _ io.WriterTo = Piece{}
54
55 // Why do we have this wrapper? Well PieceImpl doesn't implement io.Reader, so we can't let io.Copy
56 // and friends check for io.WriterTo and fallback for us since they expect an io.Reader.
57 func (p Piece) WriteTo(w io.Writer) (int64, error) {
58         if i, ok := p.PieceImpl.(io.WriterTo); ok {
59                 return i.WriteTo(w)
60         }
61         n := p.mip.Length()
62         r := io.NewSectionReader(p, 0, n)
63         return io.CopyN(w, r, n)
64 }
65
66 func (p Piece) WriteAt(b []byte, off int64) (n int, err error) {
67         // Callers should not be writing to completed pieces, but it's too
68         // expensive to be checking this on every single write using uncached
69         // completions.
70
71         // c := p.Completion()
72         // if c.Ok && c.Complete {
73         //      err = errors.New("piece already completed")
74         //      return
75         // }
76         if off+int64(len(b)) > p.mip.Length() {
77                 panic("write overflows piece")
78         }
79         b = missinggo.LimitLen(b, p.mip.Length()-off)
80         return p.PieceImpl.WriteAt(b, off)
81 }
82
83 func (p Piece) ReadAt(b []byte, off int64) (n int, err error) {
84         if off < 0 {
85                 err = os.ErrInvalid
86                 return
87         }
88         if off >= p.mip.Length() {
89                 err = io.EOF
90                 return
91         }
92         b = missinggo.LimitLen(b, p.mip.Length()-off)
93         if len(b) == 0 {
94                 return
95         }
96         n, err = p.PieceImpl.ReadAt(b, off)
97         if n > len(b) {
98                 panic(n)
99         }
100         if n == 0 && err == nil {
101                 panic("io.Copy will get stuck")
102         }
103         off += int64(n)
104
105         // Doing this here may be inaccurate. There's legitimate reasons we may fail to read while the
106         // data is still there, such as too many open files. There should probably be a specific error
107         // to return if the data has been lost.
108         if off < p.mip.Length() {
109                 if err == io.EOF {
110                         p.MarkNotComplete()
111                 }
112         }
113
114         return
115 }