github.com/anacrolix/torrent@v1.61.0/internal/request-strategy/order.go (about) 1 package requestStrategy 2 3 import ( 4 "bytes" 5 6 "github.com/anacrolix/multiless" 7 8 "github.com/anacrolix/torrent/metainfo" 9 "github.com/anacrolix/torrent/types" 10 ) 11 12 type ( 13 RequestIndex uint32 14 ChunkIndex = RequestIndex 15 Request = types.Request 16 piecePriority = types.PiecePriority 17 // This can be made into a type-param later, will be great for testing. 18 ChunkSpec = types.ChunkSpec 19 ) 20 21 // Piece request ordering factoring in storage limits, user-assigned priority, network availability. 22 // Is it missing the random piece affinity assigned per torrent? Can we do that deterministically 23 // per-client? 24 func pieceOrderLess(i, j *PieceRequestOrderItem) multiless.Computation { 25 return multiless.New().Int( 26 int(j.State.Priority), int(i.State.Priority), 27 // TODO: Should we match on complete here to prevent churn when availability changes? (Answer: Yes). 28 ).Bool( 29 j.State.Partial, i.State.Partial, 30 ).Int( 31 // If this is done with relative availability, do we lose some determinism? If completeness 32 // is used, would that push this far enough down? What happens if we have a piece in the 33 // order, but it has availability 0? 34 i.State.Availability, j.State.Availability, 35 ).Int( 36 i.Key.Index, j.Key.Index, 37 ).Lazy(func() multiless.Computation { 38 a := i.Key.InfoHash.Value() 39 b := j.Key.InfoHash.Value() 40 return multiless.New().Cmp(bytes.Compare(a[:], b[:])) 41 }) 42 } 43 44 // This did return true if the piece should be considered against the unverified bytes limit. But 45 // that would cause thrashing on completion: The order should be stable. This is now a 3-tuple 46 // iterator. 47 type RequestPieceFunc func(ih metainfo.Hash, pieceIndex int, orderState PieceRequestOrderState) bool 48 49 // Calls f with requestable pieces in order. Returns false if iteration should stop. 50 func GetRequestablePieces( 51 input Input, pro *PieceRequestOrder, 52 // Pieces submitted to this callback passed Piece.Request and so are ready for immediate 53 // download. 54 requestPiece RequestPieceFunc, 55 ) bool { 56 // Storage capacity left for this run, keyed by the storage capacity pointer on the storage 57 // TorrentImpl. A nil value means no capacity limit. 58 var storageLeft *int64 59 if cap, ok := input.Capacity(); ok { 60 storageLeft = &cap 61 } 62 var ( 63 allTorrentsUnverifiedBytes int64 64 maxUnverifiedBytes = input.MaxUnverifiedBytes() 65 ) 66 for item := range pro.tree.Scan { 67 ih := item.Key.InfoHash.Value() 68 t := input.Torrent(ih) 69 piece := t.Piece(item.Key.Index) 70 pieceLength := t.PieceLength() 71 // Storage limits will always apply against requestable pieces, since we need to keep the 72 // highest priority pieces, even if they're complete or in an undesirable state. 73 if storageLeft != nil { 74 if *storageLeft < pieceLength { 75 break 76 } 77 *storageLeft -= pieceLength 78 } 79 if piece.Request() { 80 if !requestPiece(ih, item.Key.Index, item.State) { 81 // No blocks are being considered from this piece, so it won't result in unverified 82 // bytes. 83 return false 84 } 85 } else if !piece.CountUnverified() { 86 // The piece is pristine, and we're not considering it for requests. 87 continue 88 } 89 allTorrentsUnverifiedBytes += pieceLength 90 if maxUnverifiedBytes != 0 && allTorrentsUnverifiedBytes >= maxUnverifiedBytes { 91 break 92 } 93 } 94 return true 95 } 96 97 type Input interface { 98 Torrent(metainfo.Hash) Torrent 99 // Storage capacity, shared among all Torrents with the same storage.TorrentCapacity pointer in 100 // their storage.Torrent references. 101 Capacity() (cap int64, capped bool) 102 // Across all the Torrents. This might be partitioned by storage capacity key now. 103 MaxUnverifiedBytes() int64 104 }