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  }