FedP2P/request-strategy/order.go

118 lines
3.0 KiB
Go

package request_strategy
import (
"bytes"
"expvar"
"github.com/anacrolix/multiless"
"github.com/anacrolix/torrent/metainfo"
"github.com/google/btree"
"github.com/anacrolix/torrent/types"
)
type (
RequestIndex = uint32
ChunkIndex = uint32
Request = types.Request
pieceIndex = types.PieceIndex
piecePriority = types.PiecePriority
// This can be made into a type-param later, will be great for testing.
ChunkSpec = types.ChunkSpec
)
type pieceOrderInput struct {
PieceRequestOrderState
PieceRequestOrderKey
}
func pieceOrderLess(i, j pieceOrderInput) multiless.Computation {
return multiless.New().Int(
int(j.Priority), int(i.Priority),
).Bool(
j.Partial, i.Partial,
).Int64(
i.Availability, j.Availability,
).Int(
i.Index, j.Index,
).Lazy(func() multiless.Computation {
return multiless.New().Cmp(bytes.Compare(
i.InfoHash[:],
j.InfoHash[:],
))
})
}
type requestsPeer struct {
Peer
nextState PeerNextRequestState
requestablePiecesRemaining int
}
func (rp *requestsPeer) canFitRequest() bool {
return int(rp.nextState.Requests.GetCardinality()) < rp.MaxRequests
}
func (rp *requestsPeer) addNextRequest(r RequestIndex) {
if !rp.nextState.Requests.CheckedAdd(r) {
panic("should only add once")
}
}
type peersForPieceRequests struct {
requestsInPiece int
*requestsPeer
}
func (me *peersForPieceRequests) addNextRequest(r RequestIndex) {
me.requestsPeer.addNextRequest(r)
me.requestsInPiece++
}
var packageExpvarMap = expvar.NewMap("request-strategy")
// Calls f with requestable pieces in order.
func GetRequestablePieces(input Input, pro *PieceRequestOrder, f func(ih metainfo.Hash, pieceIndex int)) {
// Storage capacity left for this run, keyed by the storage capacity pointer on the storage
// TorrentImpl. A nil value means no capacity limit.
var storageLeft *int64
if cap, ok := input.Capacity(); ok {
storageLeft = &cap
}
var allTorrentsUnverifiedBytes int64
pro.tree.Ascend(func(i btree.Item) bool {
_i := i.(pieceRequestOrderItem)
ih := _i.key.InfoHash
var t Torrent = input.Torrent(ih)
var piece Piece = t.Piece(_i.key.Index)
pieceLength := t.PieceLength()
if storageLeft != nil {
if *storageLeft < pieceLength {
return true
}
*storageLeft -= pieceLength
}
if !piece.Request() || piece.NumPendingChunks() == 0 {
// TODO: Clarify exactly what is verified. Stuff that's being hashed should be
// considered unverified and hold up further requests.
return true
}
if input.MaxUnverifiedBytes() != 0 && allTorrentsUnverifiedBytes+pieceLength > input.MaxUnverifiedBytes() {
return true
}
allTorrentsUnverifiedBytes += pieceLength
f(ih, _i.key.Index)
return true
})
return
}
type Input interface {
Torrent(metainfo.Hash) Torrent
// Storage capacity, shared among all Torrents with the same storage.TorrentCapacity pointer in
// their storage.Torrent references.
Capacity() (cap int64, capped bool)
// Across all the Torrents. This might be partitioned by storage capacity key now.
MaxUnverifiedBytes() int64
}