]> Sergey Matveev's repositories - btrtrc.git/blob - misc.go
Shuffle chunk spec request order for readahead pieces
[btrtrc.git] / misc.go
1 package torrent
2
3 import (
4         "bitbucket.org/anacrolix/go.torrent/mmap_span"
5         "crypto"
6         "errors"
7         "math/rand"
8         "os"
9         "path/filepath"
10         "time"
11
12         "bitbucket.org/anacrolix/go.torrent/peer_protocol"
13         metainfo "github.com/nsf/libtorgo/torrent"
14         "launchpad.net/gommap"
15 )
16
17 const (
18         pieceHash   = crypto.SHA1
19         maxRequests = 250        // Maximum pending requests we allow peers to send us.
20         chunkSize   = 0x4000     // 16KiB
21         BEP20       = "-GT0000-" // Peer ID client identifier prefix
22         dialTimeout = time.Second * 15
23 )
24
25 type InfoHash [20]byte
26
27 type pieceSum [20]byte
28
29 func copyHashSum(dst, src []byte) {
30         if len(dst) != len(src) || copy(dst, src) != len(dst) {
31                 panic("hash sum sizes differ")
32         }
33 }
34
35 func BytesInfoHash(b []byte) (ih InfoHash) {
36         if len(b) != len(ih) || copy(ih[:], b) != len(ih) {
37                 panic("bad infohash bytes")
38         }
39         return
40 }
41
42 type piece struct {
43         Hash              pieceSum
44         PendingChunkSpecs map[chunkSpec]struct{}
45         Hashing           bool
46         QueuedForHash     bool
47         EverHashed        bool
48 }
49
50 func (p *piece) shuffledPendingChunkSpecs() (css []chunkSpec) {
51         css = make([]chunkSpec, 0, len(p.PendingChunkSpecs))
52         for cs := range p.PendingChunkSpecs {
53                 css = append(css, cs)
54         }
55         for i := range css {
56                 j := rand.Intn(i + 1)
57                 css[i], css[j] = css[j], css[i]
58         }
59         return
60 }
61
62 func (p *piece) Complete() bool {
63         return len(p.PendingChunkSpecs) == 0 && p.EverHashed
64 }
65
66 func lastChunkSpec(pieceLength peer_protocol.Integer) (cs chunkSpec) {
67         cs.Begin = (pieceLength - 1) / chunkSize * chunkSize
68         cs.Length = pieceLength - cs.Begin
69         return
70 }
71
72 type chunkSpec struct {
73         Begin, Length peer_protocol.Integer
74 }
75
76 type request struct {
77         Index peer_protocol.Integer
78         chunkSpec
79 }
80
81 func newRequest(index, begin, length peer_protocol.Integer) request {
82         return request{index, chunkSpec{begin, length}}
83 }
84
85 type pieceByBytesPendingSlice struct {
86         Pending, Indices []peer_protocol.Integer
87 }
88
89 func (pcs pieceByBytesPendingSlice) Len() int {
90         return len(pcs.Indices)
91 }
92
93 func (me pieceByBytesPendingSlice) Less(i, j int) bool {
94         return me.Pending[me.Indices[i]] < me.Pending[me.Indices[j]]
95 }
96
97 func (me pieceByBytesPendingSlice) Swap(i, j int) {
98         me.Indices[i], me.Indices[j] = me.Indices[j], me.Indices[i]
99 }
100
101 var (
102         // Requested data not yet available.
103         ErrDataNotReady = errors.New("data not ready")
104 )
105
106 func mmapTorrentData(metaInfo *metainfo.MetaInfo, location string) (mms mmap_span.MMapSpan, err error) {
107         defer func() {
108                 if err != nil {
109                         mms.Close()
110                         mms = nil
111                 }
112         }()
113         for _, miFile := range metaInfo.Files {
114                 fileName := filepath.Join(append([]string{location, metaInfo.Name}, miFile.Path...)...)
115                 err = os.MkdirAll(filepath.Dir(fileName), 0777)
116                 if err != nil {
117                         return
118                 }
119                 var file *os.File
120                 file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
121                 if err != nil {
122                         return
123                 }
124                 func() {
125                         defer file.Close()
126                         var fi os.FileInfo
127                         fi, err = file.Stat()
128                         if err != nil {
129                                 return
130                         }
131                         if fi.Size() < miFile.Length {
132                                 err = file.Truncate(miFile.Length)
133                                 if err != nil {
134                                         return
135                                 }
136                         }
137                         var mMap gommap.MMap
138                         mMap, err = gommap.MapRegion(file.Fd(), 0, miFile.Length, gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
139                         if err != nil {
140                                 return
141                         }
142                         if int64(len(mMap)) != miFile.Length {
143                                 panic("mmap has wrong length")
144                         }
145                         mms = append(mms, mMap)
146                 }()
147                 if err != nil {
148                         return
149                 }
150         }
151         return
152 }