]> Sergey Matveev's repositories - btrtrc.git/blob - piece.go
Add File.Cancel
[btrtrc.git] / piece.go
1 package torrent
2
3 import (
4         "math/rand"
5         "sync"
6
7         "github.com/bradfitz/iter"
8
9         pp "github.com/anacrolix/torrent/peer_protocol"
10 )
11
12 // Piece priority describes the importance of obtaining a particular piece.
13
14 type piecePriority byte
15
16 func (me *piecePriority) Raise(maybe piecePriority) {
17         if maybe > *me {
18                 *me = maybe
19         }
20 }
21
22 const (
23         PiecePriorityNone      piecePriority = iota // Not wanted.
24         PiecePriorityNormal                         // Wanted.
25         PiecePriorityReadahead                      // May be required soon.
26         PiecePriorityNext                           // Succeeds a piece where a read occurred.
27         PiecePriorityNow                            // A read occurred in this piece.
28 )
29
30 type piece struct {
31         // The completed piece SHA1 hash, from the metainfo "pieces" field.
32         Hash pieceSum
33         // Chunks we've written to since the last check. The chunk offset and
34         // length can be determined by the request chunkSize in use.
35         DirtyChunks      []bool
36         Hashing          bool
37         QueuedForHash    bool
38         EverHashed       bool
39         PublicPieceState PieceState
40         priority         piecePriority
41
42         pendingWritesMutex sync.Mutex
43         pendingWrites      int
44         noPendingWrites    sync.Cond
45 }
46
47 func (p *piece) pendingChunk(cs chunkSpec, chunkSize pp.Integer) bool {
48         ci := chunkIndex(cs, chunkSize)
49         if ci >= len(p.DirtyChunks) {
50                 return true
51         }
52         return !p.DirtyChunks[ci]
53 }
54
55 func (p *piece) hasDirtyChunks() bool {
56         for _, dirty := range p.DirtyChunks {
57                 if dirty {
58                         return true
59                 }
60         }
61         return false
62 }
63
64 func (p *piece) numDirtyChunks() (ret int) {
65         for _, dirty := range p.DirtyChunks {
66                 if dirty {
67                         ret++
68                 }
69         }
70         return
71 }
72
73 func (p *piece) unpendChunkIndex(i int) {
74         for i >= len(p.DirtyChunks) {
75                 p.DirtyChunks = append(p.DirtyChunks, false)
76         }
77         p.DirtyChunks[i] = true
78 }
79
80 func (p *piece) pendChunkIndex(i int) {
81         if i >= len(p.DirtyChunks) {
82                 return
83         }
84         p.DirtyChunks[i] = false
85 }
86
87 func chunkIndexSpec(index int, pieceLength, chunkSize pp.Integer) chunkSpec {
88         ret := chunkSpec{pp.Integer(index) * chunkSize, chunkSize}
89         if ret.Begin+ret.Length > pieceLength {
90                 ret.Length = pieceLength - ret.Begin
91         }
92         return ret
93 }
94
95 func (p *piece) shuffledPendingChunkSpecs(t *torrent, piece int) (css []chunkSpec) {
96         // defer func() {
97         //      log.Println(piece, css)
98         // }()
99         numPending := t.pieceNumPendingChunks(piece)
100         if numPending == 0 {
101                 return
102         }
103         css = make([]chunkSpec, 0, numPending)
104         for ci := range iter.N(t.pieceNumChunks(piece)) {
105                 if ci >= len(p.DirtyChunks) || !p.DirtyChunks[ci] {
106                         css = append(css, t.chunkIndexSpec(ci, piece))
107                 }
108         }
109         if len(css) <= 1 {
110                 return
111         }
112         for i := range css {
113                 j := rand.Intn(i + 1)
114                 css[i], css[j] = css[j], css[i]
115         }
116         return
117 }
118
119 func (p *piece) incrementPendingWrites() {
120         p.pendingWritesMutex.Lock()
121         p.pendingWrites++
122         p.pendingWritesMutex.Unlock()
123 }
124
125 func (p *piece) decrementPendingWrites() {
126         p.pendingWritesMutex.Lock()
127         if p.pendingWrites == 0 {
128                 panic("assertion")
129         }
130         p.pendingWrites--
131         if p.pendingWrites == 0 {
132                 p.noPendingWrites.Broadcast()
133         }
134         p.pendingWritesMutex.Unlock()
135 }
136
137 func (p *piece) waitNoPendingWrites() {
138         p.pendingWritesMutex.Lock()
139         for p.pendingWrites != 0 {
140                 p.noPendingWrites.Wait()
141         }
142         p.pendingWritesMutex.Unlock()
143 }