]> Sergey Matveev's repositories - btrtrc.git/blob - misc.go
Begin adding magnet and ut_metadata support
[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() []string {
113         return nil
114 }
115 func (me metaInfoMetaData) PieceLength() int64 { return me.mi.PieceLength }
116 func (me metaInfoMetaData) PieceCount() int {
117         return len(me.mi.Pieces) / pieceHash.Size()
118 }
119
120 func NewMetaDataFromMetaInfo(mi *metainfo.MetaInfo) MetaData {
121         return metaInfoMetaData{mi}
122 }
123
124 type MetaData interface {
125         PieceHashes() []string
126         Files() []metainfo.FileInfo
127         Name() string
128         PieceLength() int64
129         PieceCount() int
130 }
131
132 func mmapTorrentData(md MetaData, location string) (mms mmap_span.MMapSpan, err error) {
133         defer func() {
134                 if err != nil {
135                         mms.Close()
136                         mms = nil
137                 }
138         }()
139         for _, miFile := range md.Files() {
140                 fileName := filepath.Join(append([]string{location, md.Name()}, miFile.Path...)...)
141                 err = os.MkdirAll(filepath.Dir(fileName), 0777)
142                 if err != nil {
143                         return
144                 }
145                 var file *os.File
146                 file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
147                 if err != nil {
148                         return
149                 }
150                 func() {
151                         defer file.Close()
152                         var fi os.FileInfo
153                         fi, err = file.Stat()
154                         if err != nil {
155                                 return
156                         }
157                         if fi.Size() < miFile.Length {
158                                 err = file.Truncate(miFile.Length)
159                                 if err != nil {
160                                         return
161                                 }
162                         }
163                         var mMap gommap.MMap
164                         mMap, err = gommap.MapRegion(file.Fd(), 0, miFile.Length, gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
165                         if err != nil {
166                                 return
167                         }
168                         if int64(len(mMap)) != miFile.Length {
169                                 panic("mmap has wrong length")
170                         }
171                         mms = append(mms, mMap)
172                 }()
173                 if err != nil {
174                         return
175                 }
176         }
177         return
178 }
179
180 func metadataPieceSize(totalSize int, piece int) int {
181         ret := totalSize - piece*(1<<14)
182         if ret > 1<<14 {
183                 ret = 1 << 14
184         }
185         return ret
186 }