]> Sergey Matveev's repositories - btrtrc.git/blob - piece.go
Allow subscribing to torrent piece state changes
[btrtrc.git] / piece.go
1 package torrent
2
3 import (
4         "math/rand"
5         "sync"
6
7         pp "github.com/anacrolix/torrent/peer_protocol"
8 )
9
10 // Piece priority describes the importance of obtaining a particular piece.
11
12 type piecePriority byte
13
14 const (
15         PiecePriorityNone      piecePriority = iota // Not wanted.
16         PiecePriorityNormal                         // Wanted.
17         PiecePriorityReadahead                      // May be required soon.
18         PiecePriorityNext                           // Succeeds a piece where a read occurred.
19         PiecePriorityNow                            // A read occurred in this piece.
20 )
21
22 type piece struct {
23         // The completed piece SHA1 hash, from the metainfo "pieces" field.
24         Hash pieceSum
25         // Chunks we don't have. The offset and length can be determined by the
26         // request chunkSize in use.
27         PendingChunkSpecs []bool
28         Hashing           bool
29         QueuedForHash     bool
30         EverHashed        bool
31         Event             sync.Cond
32         Priority          piecePriority
33         PublicPieceState  PieceState
34
35         pendingWritesMutex sync.Mutex
36         pendingWrites      int
37         noPendingWrites    sync.Cond
38 }
39
40 func (p *piece) pendingChunk(cs chunkSpec, chunkSize pp.Integer) bool {
41         if p.PendingChunkSpecs == nil {
42                 return false
43         }
44         return p.PendingChunkSpecs[chunkIndex(cs, chunkSize)]
45 }
46
47 func (p *piece) numPendingChunks() (ret int) {
48         for _, pending := range p.PendingChunkSpecs {
49                 if pending {
50                         ret++
51                 }
52         }
53         return
54 }
55
56 func (p *piece) unpendChunkIndex(i int) {
57         if p.PendingChunkSpecs == nil {
58                 return
59         }
60         p.PendingChunkSpecs[i] = false
61 }
62
63 func chunkIndexSpec(index int, pieceLength, chunkSize pp.Integer) chunkSpec {
64         ret := chunkSpec{pp.Integer(index) * chunkSize, chunkSize}
65         if ret.Begin+ret.Length > pieceLength {
66                 ret.Length = pieceLength - ret.Begin
67         }
68         return ret
69 }
70
71 func (p *piece) shuffledPendingChunkSpecs(pieceLength, chunkSize pp.Integer) (css []chunkSpec) {
72         if p.numPendingChunks() == 0 {
73                 return
74         }
75         css = make([]chunkSpec, 0, p.numPendingChunks())
76         for i, pending := range p.PendingChunkSpecs {
77                 if pending {
78                         css = append(css, chunkIndexSpec(i, pieceLength, chunkSize))
79                 }
80         }
81         if len(css) <= 1 {
82                 return
83         }
84         for i := range css {
85                 j := rand.Intn(i + 1)
86                 css[i], css[j] = css[j], css[i]
87         }
88         return
89 }