]> Sergey Matveev's repositories - btrtrc.git/blob - misc.go
Set priorities ahead of the current read position
[btrtrc.git] / misc.go
1 package torrent
2
3 import (
4         "crypto"
5         "errors"
6         "fmt"
7         "math/rand"
8         "os"
9         "path/filepath"
10         "sync"
11         "time"
12
13         "bitbucket.org/anacrolix/go.torrent/mmap_span"
14         "bitbucket.org/anacrolix/go.torrent/peer_protocol"
15         "github.com/anacrolix/libtorgo/metainfo"
16         "launchpad.net/gommap"
17 )
18
19 const (
20         pieceHash          = crypto.SHA1
21         maxRequests        = 250        // Maximum pending requests we allow peers to send us.
22         chunkSize          = 0x4000     // 16KiB
23         BEP20              = "-GT0000-" // Peer ID client identifier prefix
24         nominalDialTimeout = time.Second * 30
25         minDialTimeout     = 5 * time.Second
26 )
27
28 type (
29         InfoHash [20]byte
30         pieceSum [20]byte
31 )
32
33 func (ih *InfoHash) AsString() string {
34         return string(ih[:])
35 }
36
37 func (ih *InfoHash) HexString() string {
38         return fmt.Sprintf("%x", ih[:])
39 }
40
41 type piecePriority byte
42
43 const (
44         piecePriorityNone piecePriority = iota
45         piecePriorityNormal
46         piecePriorityReadahead
47         piecePriorityNext
48         piecePriorityNow
49 )
50
51 type piece struct {
52         Hash              pieceSum
53         PendingChunkSpecs map[chunkSpec]struct{}
54         Hashing           bool
55         QueuedForHash     bool
56         EverHashed        bool
57         Event             sync.Cond
58         Priority          piecePriority
59 }
60
61 func (p *piece) shuffledPendingChunkSpecs() (css []chunkSpec) {
62         if len(p.PendingChunkSpecs) == 0 {
63                 return
64         }
65         css = make([]chunkSpec, 0, len(p.PendingChunkSpecs))
66         for cs := range p.PendingChunkSpecs {
67                 css = append(css, cs)
68         }
69         if len(css) <= 1 {
70                 return
71         }
72         for i := range css {
73                 j := rand.Intn(i + 1)
74                 css[i], css[j] = css[j], css[i]
75         }
76         return
77 }
78
79 func (p *piece) Complete() bool {
80         return len(p.PendingChunkSpecs) == 0 && p.EverHashed
81 }
82
83 func lastChunkSpec(pieceLength peer_protocol.Integer) (cs chunkSpec) {
84         cs.Begin = (pieceLength - 1) / chunkSize * chunkSize
85         cs.Length = pieceLength - cs.Begin
86         return
87 }
88
89 type chunkSpec struct {
90         Begin, Length peer_protocol.Integer
91 }
92
93 type request struct {
94         Index peer_protocol.Integer
95         chunkSpec
96 }
97
98 func newRequest(index, begin, length peer_protocol.Integer) request {
99         return request{index, chunkSpec{begin, length}}
100 }
101
102 type pieceByBytesPendingSlice struct {
103         Pending, Indices []peer_protocol.Integer
104 }
105
106 func (pcs pieceByBytesPendingSlice) Len() int {
107         return len(pcs.Indices)
108 }
109
110 func (me pieceByBytesPendingSlice) Less(i, j int) bool {
111         return me.Pending[me.Indices[i]] < me.Pending[me.Indices[j]]
112 }
113
114 func (me pieceByBytesPendingSlice) Swap(i, j int) {
115         me.Indices[i], me.Indices[j] = me.Indices[j], me.Indices[i]
116 }
117
118 var (
119         // Requested data not yet available.
120         ErrDataNotReady = errors.New("data not ready")
121 )
122
123 func upvertedSingleFileInfoFiles(info *metainfo.Info) []metainfo.FileInfo {
124         if len(info.Files) != 0 {
125                 return info.Files
126         }
127         return []metainfo.FileInfo{{Length: info.Length, Path: nil}}
128 }
129
130 func mmapTorrentData(md *metainfo.Info, location string) (mms *mmap_span.MMapSpan, err error) {
131         mms = &mmap_span.MMapSpan{}
132         defer func() {
133                 if err != nil {
134                         mms.Close()
135                         mms = nil
136                 }
137         }()
138         for _, miFile := range upvertedSingleFileInfoFiles(md) {
139                 fileName := filepath.Join(append([]string{location, md.Name}, miFile.Path...)...)
140                 err = os.MkdirAll(filepath.Dir(fileName), 0777)
141                 if err != nil {
142                         err = fmt.Errorf("error creating data directory %q: %s", filepath.Dir(fileName), err)
143                         return
144                 }
145                 var file *os.File
146                 file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
147                 if err != nil {
148                         return
149                 }
150                 func() {
151                         defer file.Close()
152                         var fi os.FileInfo
153                         fi, err = file.Stat()
154                         if err != nil {
155                                 return
156                         }
157                         if fi.Size() < miFile.Length {
158                                 err = file.Truncate(miFile.Length)
159                                 if err != nil {
160                                         return
161                                 }
162                         }
163                         if miFile.Length == 0 {
164                                 // Can't mmap() regions with length 0.
165                                 return
166                         }
167                         var mMap gommap.MMap
168                         mMap, err = gommap.MapRegion(file.Fd(), 0, miFile.Length, gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
169                         if err != nil {
170                                 err = fmt.Errorf("error mapping file %q, length %d: %s", file.Name(), miFile.Length, err)
171                                 return
172                         }
173                         if int64(len(mMap)) != miFile.Length {
174                                 panic("mmap has wrong length")
175                         }
176                         mms.Append(mMap)
177                 }()
178                 if err != nil {
179                         return
180                 }
181         }
182         return
183 }
184
185 // The size in bytes of a metadata extension piece.
186 func metadataPieceSize(totalSize int, piece int) int {
187         ret := totalSize - piece*(1<<14)
188         if ret > 1<<14 {
189                 ret = 1 << 14
190         }
191         return ret
192 }