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