order.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package requestStrategy
  2. import (
  3. "bytes"
  4. "github.com/anacrolix/multiless"
  5. "github.com/anacrolix/torrent/metainfo"
  6. "github.com/anacrolix/torrent/types"
  7. )
  8. type (
  9. RequestIndex uint32
  10. ChunkIndex = RequestIndex
  11. Request = types.Request
  12. pieceIndex = types.PieceIndex
  13. piecePriority = types.PiecePriority
  14. // This can be made into a type-param later, will be great for testing.
  15. ChunkSpec = types.ChunkSpec
  16. )
  17. func pieceOrderLess(i, j *pieceRequestOrderItem) multiless.Computation {
  18. return multiless.New().Int(
  19. int(j.state.Priority), int(i.state.Priority),
  20. // TODO: Should we match on complete here to prevent churn when availability changes?
  21. ).Bool(
  22. j.state.Partial, i.state.Partial,
  23. ).Int(
  24. // If this is done with relative availability, do we lose some determinism? If completeness
  25. // is used, would that push this far enough down?
  26. i.state.Availability, j.state.Availability,
  27. ).Int(
  28. i.key.Index, j.key.Index,
  29. ).Lazy(func() multiless.Computation {
  30. return multiless.New().Cmp(bytes.Compare(
  31. i.key.InfoHash[:],
  32. j.key.InfoHash[:],
  33. ))
  34. })
  35. }
  36. // Calls f with requestable pieces in order.
  37. func GetRequestablePieces(
  38. input Input, pro *PieceRequestOrder,
  39. // Returns true if the piece should be considered against the unverified bytes limit.
  40. requestPiece func(ih metainfo.Hash, pieceIndex int, orderState PieceRequestOrderState) bool,
  41. ) {
  42. // Storage capacity left for this run, keyed by the storage capacity pointer on the storage
  43. // TorrentImpl. A nil value means no capacity limit.
  44. var storageLeft *int64
  45. if cap, ok := input.Capacity(); ok {
  46. storageLeft = &cap
  47. }
  48. var (
  49. allTorrentsUnverifiedBytes int64
  50. maxUnverifiedBytes = input.MaxUnverifiedBytes()
  51. )
  52. pro.tree.Scan(func(item pieceRequestOrderItem) bool {
  53. ih := item.key.InfoHash
  54. var t = input.Torrent(ih)
  55. var piece = t.Piece(item.key.Index)
  56. pieceLength := t.PieceLength()
  57. // Storage limits will always apply against requestable pieces, since we need to keep the
  58. // highest priority pieces, even if they're complete or in an undesirable state.
  59. if storageLeft != nil {
  60. if *storageLeft < pieceLength {
  61. return false
  62. }
  63. *storageLeft -= pieceLength
  64. }
  65. if piece.Request() {
  66. if !requestPiece(ih, item.key.Index, item.state) {
  67. // No blocks are being considered from this piece, so it won't result in unverified
  68. // bytes.
  69. return true
  70. }
  71. } else if !piece.CountUnverified() {
  72. // The piece is pristine, and we're not considering it for requests.
  73. return true
  74. }
  75. allTorrentsUnverifiedBytes += pieceLength
  76. return maxUnverifiedBytes == 0 || allTorrentsUnverifiedBytes < maxUnverifiedBytes
  77. })
  78. return
  79. }
  80. type Input interface {
  81. Torrent(metainfo.Hash) Torrent
  82. // Storage capacity, shared among all Torrents with the same storage.TorrentCapacity pointer in
  83. // their storage.Torrent references.
  84. Capacity() (cap int64, capped bool)
  85. // Across all the Torrents. This might be partitioned by storage capacity key now.
  86. MaxUnverifiedBytes() int64
  87. }