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