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