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