]> Sergey Matveev's repositories - btrtrc.git/blob - misc.go
Big visibility/doc clean-up, and extract mmap_span package
[btrtrc.git] / misc.go
1 package torrent
2
3 import (
4         "bitbucket.org/anacrolix/go.torrent/mmap_span"
5         "crypto"
6         "errors"
7         "os"
8         "path/filepath"
9         "time"
10
11         "bitbucket.org/anacrolix/go.torrent/peer_protocol"
12         metainfo "github.com/nsf/libtorgo/torrent"
13         "launchpad.net/gommap"
14 )
15
16 const (
17         pieceHash   = crypto.SHA1
18         maxRequests = 250
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) 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 type pieceByBytesPendingSlice struct {
69         Pending, Indices []peer_protocol.Integer
70 }
71
72 func (pcs pieceByBytesPendingSlice) Len() int {
73         return len(pcs.Indices)
74 }
75
76 func (me pieceByBytesPendingSlice) Less(i, j int) bool {
77         return me.Pending[me.Indices[i]] < me.Pending[me.Indices[j]]
78 }
79
80 func (me pieceByBytesPendingSlice) Swap(i, j int) {
81         me.Indices[i], me.Indices[j] = me.Indices[j], me.Indices[i]
82 }
83
84 var (
85         // Requested data not yet available.
86         ErrDataNotReady = errors.New("data not ready")
87 )
88
89 func mmapTorrentData(metaInfo *metainfo.MetaInfo, location string) (mms mmap_span.MMapSpan, err error) {
90         defer func() {
91                 if err != nil {
92                         mms.Close()
93                         mms = nil
94                 }
95         }()
96         for _, miFile := range metaInfo.Files {
97                 fileName := filepath.Join(append([]string{location, metaInfo.Name}, miFile.Path...)...)
98                 err = os.MkdirAll(filepath.Dir(fileName), 0777)
99                 if err != nil {
100                         return
101                 }
102                 var file *os.File
103                 file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
104                 if err != nil {
105                         return
106                 }
107                 func() {
108                         defer file.Close()
109                         var fi os.FileInfo
110                         fi, err = file.Stat()
111                         if err != nil {
112                                 return
113                         }
114                         if fi.Size() < miFile.Length {
115                                 err = file.Truncate(miFile.Length)
116                                 if err != nil {
117                                         return
118                                 }
119                         }
120                         var mMap gommap.MMap
121                         mMap, err = gommap.MapRegion(file.Fd(), 0, miFile.Length, gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
122                         if err != nil {
123                                 return
124                         }
125                         if int64(len(mMap)) != miFile.Length {
126                                 panic("mmap has wrong length")
127                         }
128                         mms = append(mms, mMap)
129                 }()
130                 if err != nil {
131                         return
132                 }
133         }
134         return
135 }