Spaces:
Running
Running
/** | |
* Mapping of torrent pieces to their respective availability in the torrent swarm. Used | |
* by the torrent manager for implementing the rarest piece first selection strategy. | |
*/ | |
export default class RarityMap { | |
constructor (torrent) { | |
this._torrent = torrent | |
this._numPieces = torrent.pieces.length | |
this._pieces = new Array(this._numPieces) | |
this._onWire = wire => { | |
this.recalculate() | |
this._initWire(wire) | |
} | |
this._onWireHave = index => { | |
this._pieces[index] += 1 | |
} | |
this._onWireBitfield = () => { | |
this.recalculate() | |
} | |
this._torrent.wires.forEach(wire => { | |
this._initWire(wire) | |
}) | |
this._torrent.on('wire', this._onWire) | |
this.recalculate() | |
} | |
/** | |
* Get the index of the rarest piece. Optionally, pass a filter function to exclude | |
* certain pieces (for instance, those that we already have). | |
* | |
* @param {function} pieceFilterFunc | |
* @return {number} index of rarest piece, or -1 | |
*/ | |
getRarestPiece (pieceFilterFunc) { | |
let candidates = [] | |
let min = Infinity | |
for (let i = 0; i < this._numPieces; ++i) { | |
if (pieceFilterFunc && !pieceFilterFunc(i)) continue | |
const availability = this._pieces[i] | |
if (availability === min) { | |
candidates.push(i) | |
} else if (availability < min) { | |
candidates = [i] | |
min = availability | |
} | |
} | |
if (candidates.length) { | |
// if there are multiple pieces with the same availability, choose one randomly | |
return candidates[Math.random() * candidates.length | 0] | |
} else { | |
return -1 | |
} | |
} | |
destroy () { | |
this._torrent.removeListener('wire', this._onWire) | |
this._torrent.wires.forEach(wire => { | |
this._cleanupWireEvents(wire) | |
}) | |
this._torrent = null | |
this._pieces = null | |
this._onWire = null | |
this._onWireHave = null | |
this._onWireBitfield = null | |
} | |
_initWire (wire) { | |
wire._onClose = () => { | |
this._cleanupWireEvents(wire) | |
for (let i = 0; i < this._numPieces; ++i) { | |
this._pieces[i] -= wire.peerPieces.get(i) | |
} | |
} | |
wire.on('have', this._onWireHave) | |
wire.on('bitfield', this._onWireBitfield) | |
wire.once('close', wire._onClose) | |
} | |
/** | |
* Recalculates piece availability across all peers in the torrent. | |
*/ | |
recalculate () { | |
this._pieces.fill(0) | |
for (const wire of this._torrent.wires) { | |
for (let i = 0; i < this._numPieces; ++i) { | |
this._pieces[i] += wire.peerPieces.get(i) | |
} | |
} | |
} | |
_cleanupWireEvents (wire) { | |
wire.removeListener('have', this._onWireHave) | |
wire.removeListener('bitfield', this._onWireBitfield) | |
if (wire._onClose) wire.removeListener('close', wire._onClose) | |
wire._onClose = null | |
} | |
} | |