forked from webtorrent/webtorrent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrarity-map.js
106 lines (92 loc) · 2.7 KB
/
rarity-map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* 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
}
}