]> Sergey Matveev's repositories - btrtrc.git/blob - misc.go
Actually announce to DHT, don't just get peers
[btrtrc.git] / misc.go
1 package torrent
2
3 import (
4         "crypto"
5         "errors"
6         "math/rand"
7         "os"
8         "path/filepath"
9         "time"
10
11         "bitbucket.org/anacrolix/go.torrent/mmap_span"
12         "bitbucket.org/anacrolix/go.torrent/peer_protocol"
13         "github.com/anacrolix/libtorgo/metainfo"
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 * 30
23 )
24
25 type (
26         InfoHash [20]byte
27         pieceSum [20]byte
28 )
29
30 func (ih *InfoHash) AsString() string {
31         return string(ih[:])
32 }
33
34 type piece struct {
35         Hash              pieceSum
36         PendingChunkSpecs map[chunkSpec]struct{}
37         Hashing           bool
38         QueuedForHash     bool
39         EverHashed        bool
40 }
41
42 func (p *piece) shuffledPendingChunkSpecs() (css []chunkSpec) {
43         if len(p.PendingChunkSpecs) == 0 {
44                 return
45         }
46         css = make([]chunkSpec, 0, len(p.PendingChunkSpecs))
47         for cs := range p.PendingChunkSpecs {
48                 css = append(css, cs)
49         }
50         if len(css) <= 1 {
51                 return
52         }
53         for i := range css {
54                 j := rand.Intn(i + 1)
55                 css[i], css[j] = css[j], css[i]
56         }
57         return
58 }
59
60 func (p *piece) Complete() bool {
61         return len(p.PendingChunkSpecs) == 0 && p.EverHashed
62 }
63
64 func lastChunkSpec(pieceLength peer_protocol.Integer) (cs chunkSpec) {
65         cs.Begin = (pieceLength - 1) / chunkSize * chunkSize
66         cs.Length = pieceLength - cs.Begin
67         return
68 }
69
70 type chunkSpec struct {
71         Begin, Length peer_protocol.Integer
72 }
73
74 type request struct {
75         Index peer_protocol.Integer
76         chunkSpec
77 }
78
79 func newRequest(index, begin, length peer_protocol.Integer) request {
80         return request{index, chunkSpec{begin, length}}
81 }
82
83 type pieceByBytesPendingSlice struct {
84         Pending, Indices []peer_protocol.Integer
85 }
86
87 func (pcs pieceByBytesPendingSlice) Len() int {
88         return len(pcs.Indices)
89 }
90
91 func (me pieceByBytesPendingSlice) Less(i, j int) bool {
92         return me.Pending[me.Indices[i]] < me.Pending[me.Indices[j]]
93 }
94
95 func (me pieceByBytesPendingSlice) Swap(i, j int) {
96         me.Indices[i], me.Indices[j] = me.Indices[j], me.Indices[i]
97 }
98
99 var (
100         // Requested data not yet available.
101         ErrDataNotReady = errors.New("data not ready")
102 )
103
104 func upvertedSingleFileInfoFiles(info *metainfo.Info) []metainfo.FileInfo {
105         if len(info.Files) != 0 {
106                 return info.Files
107         }
108         return []metainfo.FileInfo{{Length: info.Length, Path: nil}}
109 }
110
111 func mmapTorrentData(md *metainfo.Info, location string) (mms mmap_span.MMapSpan, err error) {
112         defer func() {
113                 if err != nil {
114                         mms.Close()
115                         mms = nil
116                 }
117         }()
118         for _, miFile := range upvertedSingleFileInfoFiles(md) {
119                 fileName := filepath.Join(append([]string{location, md.Name}, miFile.Path...)...)
120                 err = os.MkdirAll(filepath.Dir(fileName), 0777)
121                 if err != nil {
122                         return
123                 }
124                 var file *os.File
125                 file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
126                 if err != nil {
127                         return
128                 }
129                 func() {
130                         defer file.Close()
131                         var fi os.FileInfo
132                         fi, err = file.Stat()
133                         if err != nil {
134                                 return
135                         }
136                         if fi.Size() < miFile.Length {
137                                 err = file.Truncate(miFile.Length)
138                                 if err != nil {
139                                         return
140                                 }
141                         }
142                         var mMap gommap.MMap
143                         mMap, err = gommap.MapRegion(file.Fd(), 0, miFile.Length, gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
144                         if err != nil {
145                                 return
146                         }
147                         if int64(len(mMap)) != miFile.Length {
148                                 panic("mmap has wrong length")
149                         }
150                         mms = append(mms, mMap)
151                 }()
152                 if err != nil {
153                         return
154                 }
155         }
156         return
157 }
158
159 func metadataPieceSize(totalSize int, piece int) int {
160         ret := totalSize - piece*(1<<14)
161         if ret > 1<<14 {
162                 ret = 1 << 14
163         }
164         return ret
165 }