PiecePicker

class PiecePicker(numPieces: Int, blocksPerPiece: Int)

Rarest-first piece selection: a pure-Kotlin port of the core of libtorrent's piece_picker (src/piece_picker.cpp, include/libtorrent/piece_picker.hpp).

The picker decides which block to request from which peer. It tracks, per piece:

  1. availability: how many connected peers have the piece (incRefcount/ decRefcount driven by HAVE / BITFIELD / disconnect events), plus a seed counter so seeds don't each bump every piece individually;

  2. priority: a manually-set 0..7 level (setPiecePriority); 0 = filtered (never downloaded);

  3. block state: for partially downloaded pieces, the BlockState.NONE/BlockState.REQUESTED/BlockState.WRITING/BlockState.FINISHED state of every block, and which peer each was requested from;

  4. have: whether the piece has passed its hash check.

pickPieces returns blocks in rarest-first order: the controlling key is piece_pos::priority() (ported verbatim in effectivePriority), where a lower value is picked first. Availability dominates that value, so a piece only one peer has outranks a piece every peer has; the manual priority and a partial-vs-open adjustment break ties exactly as upstream does.

Peers are kept fully opaque: callers identify a peer by any token (peerId: Any?), mirroring libtorrent's torrent_peer* without dragging in a peer_connection.

Scope (faithful core)

Ported: availability + seed accounting, the exact priority formula and its rarest-first / sequential / random ordering, the download-queue bucketing (open / downloading / full / finished / zero-prio) via updatePieceState, the full block state machine (markAsDownloading/markAsWriting/markAsFinished/ markAsCanceled/abortDownload/writeFailed/restorePiece), cursors, and the want/have/num_have counters.

Deliberately omitted (and noted in the module summary): the reverse, on_parole, piece_extent_affinity, prefer_contiguous_blocks/expand_piece whole-piece heuristics, suggested-pieces, end-game busy-block stealing, pad files, hashing-job details, and the mutable m_pieces/m_priority_boundaries swap-list with random in-bucket shuffling. Instead of that swap-list, pickPieces computes the pick order by stably sorting the pickable pieces on the same priority() key the swap-list encodes. The ordering semantics are the same, with far less machinery. Equal-priority ties are broken by piece index (the swap list randomizes them); pass a Random to pickPieces to restore randomized tie-breaking.

Constructors

Link copied to clipboard
constructor(numPieces: Int, blocksPerPiece: Int)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

Number of pieces tracked.

Functions

Link copied to clipboard
fun abortDownload(block: PieceBlock, peerId: Any? = null)

Cancel a requested block for peerId. Mirrors abort_download. Decrements the block's peer count; if other peers still have it requested the block stays requested. When it drops to zero the block returns to free, and if the whole piece becomes empty it is removed from the download list.

Link copied to clipboard

Availability of every piece, into a fresh IntArray. get_availability(vector&).

fun availability(piece: Int): Int

Availability of piece: connected peers (incl. seeds) that have it. get_availability.

Link copied to clipboard

Blocks in the final piece. Mirrors piece_picker::blocks_in_last_piece().

Link copied to clipboard
fun blocksInPiece(index: Int): Int

Blocks in piece index: blocksInLastPiece for the last piece, else blocksPerPiece.

Link copied to clipboard

Blocks per (non-last) piece.

Link copied to clipboard
fun clearPeer(peerId: Any?)

Clear peerId from every block that referenced it (a peer disconnected). clear_peer.

Link copied to clipboard
fun cursor(): Int

Lowest piece index we don't have. cursor.

Link copied to clipboard
fun decRefcount(bitmask: Bitfield, peerId: Any? = null)

Decrement availability for every piece in bitmask (a peer with this bitfield disconnected). Mirrors the bitfield dec_refcount, with the same break-one-seed fix-up per piece.

fun decRefcount(index: Int, peerId: Any? = null)

Decrement the availability of index (a peer that had it disconnected, or sent dont-have). Mirrors piece_picker::dec_refcount(piece_index_t, ...), including the "break one seed" fix-up: if the per-piece count is already 0 but seeds exist, one seed is converted into explicit per-piece counts first so the counter never goes negative.

Link copied to clipboard
fun decRefcountAll(peerId: Any? = null)

Unregister a seed. If seeds is positive it is simply decremented; if it is already 0 (because a former seed was split into per-piece counts) every piece's peerCount is decremented instead. Mirrors dec_refcount_all.

Link copied to clipboard

Pieces in any download bucket (downloading/full/finished/zero-prio): get_download_queue_size. The strict end-game gate compares this to numWantLeft: only when every wanted piece is already in the download queue may a busy block be double-requested.

Link copied to clipboard

The effective priority / sort value of piece: a verbatim port of piece_pos::priority(piece_picker const*).

Link copied to clipboard

The peer token block was last requested/downloaded from, or null. get_downloader.

Link copied to clipboard
fun getDownloaders(piece: Int): Array<Any?>

The peer token for every block of piece (null where unknown). Length is blocksInPiece. Mirrors get_downloaders.

Link copied to clipboard
fun havePiece(piece: Int): Boolean

True if we have piece (it passed the hash check). Like have_piece, this is also true for a still-downloading piece whose hash already verified.

Link copied to clipboard
fun incRefcount(bitmask: Bitfield, peerId: Any? = null)

Increment availability for every piece in bitmask for peerId (a BITFIELD message). If the bitmask covers every piece it is treated as a seed and routed through incRefcountAll. Mirrors the bitfield inc_refcount.

fun incRefcount(index: Int, peerId: Any? = null)

Increment the availability of index for peerId (a HAVE message). Mirrors piece_picker::inc_refcount(piece_index_t, torrent_peer const*). The peer token is unused here (libtorrent only uses it for debug refcount sets) but kept in the signature for symmetry with the bitfield overload.

Link copied to clipboard
fun incRefcountAll(peerId: Any? = null)

Register a connected seed: bumps the seeds counter for every piece at once. Mirrors inc_refcount_all. Used when a seed joins the swarm.

Link copied to clipboard

True if block is finished or writing (i.e. we've received it). is_downloaded.

Link copied to clipboard

True if piece is partially or fully in a download bucket. is_downloading.

Link copied to clipboard

True once every piece we want has passed its hash check. Mirrors is_finished(): numPieces - numFiltered <= numHave.

True if block has been written to disk. is_finished.

Link copied to clipboard

True if every block of piece is finished or writing (or we already have it). Mirrors is_piece_finished. Note this is about blocks received, not the hash check. A piece can be "finished" before it has "passed".

Link copied to clipboard

True if piece is fully written to disk (is_piece_flushed). In this core a piece is "flushed" exactly when havePiece flips it on via markAsFinished of the last block or an explicit pieceFlushed.

Link copied to clipboard
fun isPieceFree(piece: Int, bitmask: Bitfield): Boolean

True if piece is a candidate to pick from bitmask: the peer has it, we don't, and it isn't filtered. Mirrors is_piece_free.

Link copied to clipboard

True if block is currently in the BlockState.REQUESTED state. is_requested.

Link copied to clipboard

True once we have every piece. is_seeding.

Link copied to clipboard
fun lockPiece(piece: Int)

Lock piece so no blocks are picked from it until restorePiece. lock_piece.

Link copied to clipboard
fun markAsCanceled(block: PieceBlock, peerId: Any? = null)

Undo a writing block back to free (a write was cancelled). Mirrors mark_as_canceled. Only acts on a writing block; a no-op otherwise. If the piece becomes empty it is removed from the download list.

Link copied to clipboard
fun markAsDownloading(block: PieceBlock, peerId: Any? = null): Boolean

Mark block as requested from peerId. Mirrors mark_as_downloading. Returns false (without changing state) if the block is already writing or finished. If the block was already requested from another peer, this just bumps its peer count (end-game style). Lazily creates the piece's DownloadingPiece the first time a block of an open piece is touched.

Link copied to clipboard
fun markAsFinished(block: PieceBlock, peerId: Any? = null)

Mark block as finished (on disk), downloaded from peerId. Mirrors mark_as_finished. Idempotent on an already-finished block. When this completes the piece and its hash already passed, the piece is flushed.

Link copied to clipboard
fun markAsWriting(block: PieceBlock, peerId: Any? = null): Boolean

Mark block as writing (received, flushing to disk), downloaded from peerId. Mirrors mark_as_writing. Returns false if the block was already writing or finished. Clears the block's peer count (all competing requests are considered cancelled now).

Link copied to clipboard

Number of pieces in the downloading bucket only (partially requested, free blocks remain): m_downloads[piece_downloading].size(), the count behind the partial-sprawl cap.

Link copied to clipboard
fun numFreeBlocksIn(piece: Int): Int

Blocks of piece still free to request (not requested/writing/finished), derived from the piece_info counters. snub_peer uses this to decide whether a stalled request actually blocks the piece's completion.

Link copied to clipboard
fun numHave(): Int

Number of pieces whose hash has passed. num_have.

Link copied to clipboard
fun numPeers(block: PieceBlock): Int

How many peers currently have block in a request queue. num_peers.

Link copied to clipboard
fun numSeeds(): Int

The current seed count.

Link copied to clipboard
fun numWant(): Int

Number of pieces we want (not filtered). The num_pieces of want().

Link copied to clipboard

Pieces we want and don't yet have. num_want_left.

Link copied to clipboard
fun pickPieces(peerBitfield: Bitfield, numBlocksWanted: Int, options: Int = OPTION_RAREST_FIRST, random: Random? = null, numPeers: Int = 0, requesterPeer: Any? = null): List<PieceBlock>

Pick up to numBlocksWanted interesting blocks for a peer whose available pieces are described by peerBitfield. This is a port of pick_pieces, including its option modes and the end-game busy-block tail.

Link copied to clipboard
fun pieceFlushed(piece: Int)

Mark piece as completely downloaded and flushed to disk. The picker now counts it as had and stops tracking its blocks. Mirrors piece_flushed. Safe to call straight from open (e.g. loading resume data): the have count is accounted as if it had been downloaded and hash-checked first.

Link copied to clipboard
fun piecePassed(piece: Int)

Mark that the hash of piece passed. The piece may still be flushing to disk. If all blocks are already finished, this also flushes it (we "have" it). Mirrors piece_passed.

Link copied to clipboard

The manual priority of every piece, into a fresh IntArray. piece_priorities.

Link copied to clipboard
fun piecePriority(piece: Int): Int

The manual priority (0..7) currently set on piece. piece_priority.

Link copied to clipboard

A snapshot of piece's state. piece_stats.

Link copied to clipboard
fun restorePiece(piece: Int, blocks: IntArray = IntArray(0))

Restore piece after a hash/write failure so it can be re-downloaded. Mirrors restore_piece. With no blocks given the whole piece is reset (its download entry is dropped). With specific blocks, only those writing/finished blocks are freed; the piece is dropped only once it is empty.

Link copied to clipboard

One past the last piece we don't have. reverse_cursor.

Link copied to clipboard

Override the block count of the final piece (for a short last piece). Must be called before any blocks are picked. This core does not derive it from a total byte size; the higher-level torrent layer supplies it.

Link copied to clipboard
fun setPiecePriority(piece: Int, newPriority: Int): Boolean

Set the manual priority of piece to newPriority (0..7). Returns true iff this flipped the piece between filtered (0) and not-filtered, updating the filtered/have-filtered counters and cursors accordingly. Mirrors set_piece_priority.

Link copied to clipboard
fun weDontHave(piece: Int)

Mark that we no longer have piece (e.g. it failed a later check and must be redownloaded). Mirrors we_dont_have. Restores cursors and the have count, and clears any partial download state.

Link copied to clipboard
fun weHaveAll()

Mark every piece as had. This sets up a seed's picker. Mirrors we_have_all.

Link copied to clipboard

Mark a writing block as failed-to-write: it returns to free and the piece is locked (no more picks) until restorePiece is called. If the piece had already passed its hash check, that is undone and the have count restored. Mirrors write_failed.