13 "bitbucket.org/anacrolix/go.torrent/mmap_span"
14 "bitbucket.org/anacrolix/go.torrent/peer_protocol"
15 "github.com/anacrolix/libtorgo/metainfo"
16 "launchpad.net/gommap"
20 pieceHash = crypto.SHA1
21 maxRequests = 250 // Maximum pending requests we allow peers to send us.
22 chunkSize = 0x4000 // 16KiB
23 BEP20 = "-GT0000-" // Peer ID client identifier prefix
24 nominalDialTimeout = time.Second * 30
25 minDialTimeout = 5 * time.Second
33 func (ih *InfoHash) AsString() string {
37 func (ih *InfoHash) HexString() string {
38 return fmt.Sprintf("%x", ih[:])
41 type piecePriority byte
44 piecePriorityNone piecePriority = iota
46 piecePriorityReadahead
53 PendingChunkSpecs map[chunkSpec]struct{}
58 Priority piecePriority
61 func (p *piece) shuffledPendingChunkSpecs() (css []chunkSpec) {
62 if len(p.PendingChunkSpecs) == 0 {
65 css = make([]chunkSpec, 0, len(p.PendingChunkSpecs))
66 for cs := range p.PendingChunkSpecs {
74 css[i], css[j] = css[j], css[i]
79 func (p *piece) Complete() bool {
80 return len(p.PendingChunkSpecs) == 0 && p.EverHashed
83 func lastChunkSpec(pieceLength peer_protocol.Integer) (cs chunkSpec) {
84 cs.Begin = (pieceLength - 1) / chunkSize * chunkSize
85 cs.Length = pieceLength - cs.Begin
89 type chunkSpec struct {
90 Begin, Length peer_protocol.Integer
94 Index peer_protocol.Integer
98 func newRequest(index, begin, length peer_protocol.Integer) request {
99 return request{index, chunkSpec{begin, length}}
102 type pieceByBytesPendingSlice struct {
103 Pending, Indices []peer_protocol.Integer
106 func (pcs pieceByBytesPendingSlice) Len() int {
107 return len(pcs.Indices)
110 func (me pieceByBytesPendingSlice) Less(i, j int) bool {
111 return me.Pending[me.Indices[i]] < me.Pending[me.Indices[j]]
114 func (me pieceByBytesPendingSlice) Swap(i, j int) {
115 me.Indices[i], me.Indices[j] = me.Indices[j], me.Indices[i]
119 // Requested data not yet available.
120 ErrDataNotReady = errors.New("data not ready")
123 func upvertedSingleFileInfoFiles(info *metainfo.Info) []metainfo.FileInfo {
124 if len(info.Files) != 0 {
127 return []metainfo.FileInfo{{Length: info.Length, Path: nil}}
130 func mmapTorrentData(md *metainfo.Info, location string) (mms *mmap_span.MMapSpan, err error) {
131 mms = &mmap_span.MMapSpan{}
138 for _, miFile := range upvertedSingleFileInfoFiles(md) {
139 fileName := filepath.Join(append([]string{location, md.Name}, miFile.Path...)...)
140 err = os.MkdirAll(filepath.Dir(fileName), 0777)
142 err = fmt.Errorf("error creating data directory %q: %s", filepath.Dir(fileName), err)
146 file, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0666)
153 fi, err = file.Stat()
157 if fi.Size() < miFile.Length {
158 err = file.Truncate(miFile.Length)
163 if miFile.Length == 0 {
164 // Can't mmap() regions with length 0.
168 mMap, err = gommap.MapRegion(file.Fd(), 0, miFile.Length, gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
170 err = fmt.Errorf("error mapping file %q, length %d: %s", file.Name(), miFile.Length, err)
173 if int64(len(mMap)) != miFile.Length {
174 panic("mmap has wrong length")
185 // The size in bytes of a metadata extension piece.
186 func metadataPieceSize(totalSize int, piece int) int {
187 ret := totalSize - piece*(1<<14)