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