forked from PearInc/PearPlayer.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rarity-map.js
executable file
·121 lines (103 loc) · 2.95 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
module.exports = RarityMap
/**
* 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.
*/
function RarityMap (torrent) {
var self = this
self._torrent = torrent
self._numPieces = torrent.pieces.length
self._pieces = []
self._onWire = function (wire) {
self.recalculate()
self._initWire(wire)
}
self._onWireHave = function (index) {
self._pieces[index] += 1
}
self._onWireBitfield = function () {
self.recalculate()
}
self._torrent.wires.forEach(function (wire) {
self._initWire(wire)
})
self._torrent.on('wire', self._onWire)
self.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
*/
RarityMap.prototype.getRarestPiece = function (pieceFilterFunc) {
if (!pieceFilterFunc) pieceFilterFunc = trueFn
var candidates = []
var min = Infinity
for (var i = 0; i < this._numPieces; ++i) {
if (!pieceFilterFunc(i)) continue
var availability = this._pieces[i]
if (availability === min) {
candidates.push(i)
} else if (availability < min) {
candidates = [ i ]
min = availability
}
}
if (candidates.length > 0) {
// if there are multiple pieces with the same availability, choose one randomly
return candidates[Math.random() * candidates.length | 0]
} else {
return -1
}
}
RarityMap.prototype.destroy = function () {
var self = this
self._torrent.removeListener('wire', self._onWire)
self._torrent.wires.forEach(function (wire) {
self._cleanupWireEvents(wire)
})
self._torrent = null
self._pieces = null
self._onWire = null
self._onWireHave = null
self._onWireBitfield = null
}
RarityMap.prototype._initWire = function (wire) {
var self = this
wire._onClose = function () {
self._cleanupWireEvents(wire)
for (var i = 0; i < this._numPieces; ++i) {
self._pieces[i] -= wire.peerPieces.get(i)
}
}
wire.on('have', self._onWireHave)
wire.on('bitfield', self._onWireBitfield)
wire.once('close', wire._onClose)
}
/**
* Recalculates piece availability across all peers in the torrent.
*/
RarityMap.prototype.recalculate = function () {
var i
for (i = 0; i < this._numPieces; ++i) {
this._pieces[i] = 0
}
var numWires = this._torrent.wires.length
for (i = 0; i < numWires; ++i) {
var wire = this._torrent.wires[i]
for (var j = 0; j < this._numPieces; ++j) {
this._pieces[j] += wire.peerPieces.get(j)
}
}
}
RarityMap.prototype._cleanupWireEvents = function (wire) {
wire.removeListener('have', this._onWireHave)
wire.removeListener('bitfield', this._onWireBitfield)
if (wire._onClose) wire.removeListener('close', wire._onClose)
wire._onClose = null
}
function trueFn () {
return true
}