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