]> Sergey Matveev's repositories - btrtrc.git/blob - misc.go
Fixes for cmd/torrent with the new torrent.Info field
[btrtrc.git] / misc.go
1 package torrent
2
3 import (
4         "bitbucket.org/anacrolix/go.torrent/mmap_span"
5         "crypto"
6         "errors"
7         metainfo "github.com/nsf/libtorgo/torrent"
8         "math/rand"
9         "os"
10         "path/filepath"
11         "time"
12
13         "bitbucket.org/anacrolix/go.torrent/peer_protocol"
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 type metaInfoMetaData struct {
107         mi *metainfo.MetaInfo
108 }
109
110 func (me metaInfoMetaData) Files() []metainfo.FileInfo { return me.mi.Files }
111 func (me metaInfoMetaData) Name() string               { return me.mi.Name }
112 func (me metaInfoMetaData) PieceHashes() (ret []string) {
113         for i := 0; i < len(me.mi.Pieces); i += 20 {
114                 ret = append(ret, string(me.mi.Pieces[i:i+20]))
115         }
116         return
117 }
118 func (me metaInfoMetaData) PieceLength() int64 { return me.mi.PieceLength }
119 func (me metaInfoMetaData) PieceCount() int {
120         return len(me.mi.Pieces) / pieceHash.Size()
121 }
122
123 func NewMetaDataFromMetaInfo(mi *metainfo.MetaInfo) MetaData {
124         return metaInfoMetaData{mi}
125 }
126
127 type MetaData interface {
128         PieceHashes() []string
129         Files() []metainfo.FileInfo
130         Name() string
131         PieceLength() int64
132         PieceCount() int
133 }
134
135 func mmapTorrentData(md MetaData, location string) (mms mmap_span.MMapSpan, err error) {
136         defer func() {
137                 if err != nil {
138                         mms.Close()
139                         mms = nil
140                 }
141         }()
142         for _, miFile := range md.Files() {
143                 fileName := filepath.Join(append([]string{location, md.Name()}, miFile.Path...)...)
144                 err = os.MkdirAll(filepath.Dir(fileName), 0777)
145                 if err != nil {
146                         return
147                 }
148                 var file *os.File
149                 file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
150                 if err != nil {
151                         return
152                 }
153                 func() {
154                         defer file.Close()
155                         var fi os.FileInfo
156                         fi, err = file.Stat()
157                         if err != nil {
158                                 return
159                         }
160                         if fi.Size() < miFile.Length {
161                                 err = file.Truncate(miFile.Length)
162                                 if err != nil {
163                                         return
164                                 }
165                         }
166                         var mMap gommap.MMap
167                         mMap, err = gommap.MapRegion(file.Fd(), 0, miFile.Length, gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
168                         if err != nil {
169                                 return
170                         }
171                         if int64(len(mMap)) != miFile.Length {
172                                 panic("mmap has wrong length")
173                         }
174                         mms = append(mms, mMap)
175                 }()
176                 if err != nil {
177                         return
178                 }
179         }
180         return
181 }
182
183 func metadataPieceSize(totalSize int, piece int) int {
184         ret := totalSize - piece*(1<<14)
185         if ret > 1<<14 {
186                 ret = 1 << 14
187         }
188         return ret
189 }