Skip to content

Commit cb115e2

Browse files
committed
Do not use arrays and closures for peers
1 parent d961010 commit cb115e2

1 file changed

Lines changed: 65 additions & 95 deletions

File tree

lib/fast-tracker.ts

Lines changed: 65 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -74,54 +74,35 @@ export class FastTracker implements Tracker {
7474
debug("disconnect peer:", Buffer.from(peerId).toString("hex"));
7575
}
7676

77-
for (const swarmContext of (peer as InternalPeerContext).swarms!) {
78-
const swarm = swarmContext.swarm;
79-
swarm.removePeer(peer);
80-
if (swarm.peers.size === 0) {
77+
for (const swarm of this._swarms.values()) {
78+
if (!swarm.removePeer(peer)) {
79+
continue;
80+
} else if (swarm.peers.size === 0) {
8181
if (debugEnabled) {
8282
debug("disconnect peer: swarm removed (empty)", Buffer.from(swarm.infoHash).toString("hex"));
8383
}
8484
this._swarms.delete(swarm.infoHash);
85-
} else if (swarmContext.completed) {
86-
swarm.completedCount--;
8785
}
8886
}
8987

9088
peer.id = undefined;
91-
(peer as InternalPeerContext).swarms = undefined;
9289
}
9390

94-
private processAnnounce(json: any, peer: InternalPeerContext, completed: boolean = false) {
91+
private processAnnounce(json: any, peer: PeerContext, completed: boolean = false) {
9592
const infoHash = json.info_hash;
9693
const peerId = json.peer_id;
9794

98-
let swarmContext: SwarmContext | undefined;
99-
10095
if (peer.id === undefined) {
10196
if (typeof peerId !== "string") {
10297
throw new TrackerError("announce: peer_id field is missing or wrong");
10398
}
10499

105100
peer.id = peerId;
106-
peer.swarms = [];
107-
} else {
108-
if (peer.id !== peerId) {
109-
throw new TrackerError("announce: different peer_id on the same connection");
110-
}
111-
swarmContext = peer.swarms!.find(s => s.swarm.infoHash === infoHash);
101+
} else if (peer.id !== peerId) {
102+
throw new TrackerError("announce: different peer_id on the same connection");
112103
}
113104

114-
if (swarmContext === undefined) {
115-
swarmContext = this.addPeerToSwarm(peer, infoHash);
116-
}
117-
118-
const swarm = swarmContext.swarm;
119-
const swarmPeers = swarm.peersOrdered;
120-
121-
if (!swarmContext.completed && (completed || json.left === 0)) {
122-
swarmContext.completed = true;
123-
swarm.completedCount++;
124-
}
105+
const swarm = this.processPeerInSwarm(peer, infoHash, completed || json.left === 0);
125106

126107
peer.sendMessage({
127108
action: "announce",
@@ -131,10 +112,10 @@ export class FastTracker implements Tracker {
131112
incomplete: swarm.peers.size - swarm.completedCount,
132113
}, peer);
133114

134-
this.sendOffersToPeers(json, swarmPeers, peer, infoHash);
115+
this.sendOffersToPeers(json, swarm.peersOrdered, peer, infoHash);
135116
}
136117

137-
private addPeerToSwarm(peer: InternalPeerContext, infoHash: any) {
118+
private processPeerInSwarm(peer: PeerContext, infoHash: any, completed: boolean) {
138119
let swarm = this._swarms.get(infoHash);
139120

140121
if (swarm === undefined) {
@@ -145,28 +126,31 @@ export class FastTracker implements Tracker {
145126
if (debugEnabled) {
146127
debug("announce: swarm created:", Buffer.from(infoHash).toString("hex"));
147128
}
129+
148130
swarm = new Swarm(infoHash);
149131
this._swarms.set(infoHash, swarm);
150132
}
151133

152-
if (debugEnabled) {
153-
debug("announce: peer", Buffer.from(peer.id!).toString("hex"), "added to swarm", Buffer.from(infoHash).toString("hex"));
134+
const peerAlreadyInSwarm = swarm.peers.get(peer.id!);
135+
if (peerAlreadyInSwarm === peer) {
136+
if (completed) {
137+
swarm.setCompleted(peer);
138+
}
139+
return swarm;
140+
} else if (peerAlreadyInSwarm !== undefined) {
141+
swarm.removePeer(peerAlreadyInSwarm);
154142
}
155143

156-
const previousPeer = swarm.peers.get(peer.id!);
157-
if (previousPeer !== undefined) {
158-
removePeerFromSwarm(previousPeer, infoHash);
144+
if (debugEnabled) {
145+
debug("announce: peer", Buffer.from(peer.id!).toString("hex"), "added to swarm", Buffer.from(infoHash).toString("hex"));
159146
}
160147

161-
swarm.addPeer(peer);
162-
const swarmContext = { completed: false, swarm: swarm };
163-
peer.swarms!.push(swarmContext);
164-
165-
return swarmContext;
148+
swarm.addPeer(peer, completed);
149+
return swarm;
166150
}
167151

168152
// tslint:disable-next-line:cognitive-complexity
169-
private sendOffersToPeers(json: any, peers: ReadonlyArray<InternalPeerContext>, peer: InternalPeerContext, infoHash: string) {
153+
private sendOffersToPeers(json: any, peers: ReadonlyArray<PeerContext>, peer: PeerContext, infoHash: string) {
170154
if (peers.length <= 1) {
171155
return;
172156
}
@@ -217,22 +201,16 @@ export class FastTracker implements Tracker {
217201
debug("announce: sent offers", countOffersToSend < 0 ? 0 : countOffersToSend);
218202
}
219203

220-
private processAnswer(json: any, peer: InternalPeerContext) {
204+
private processAnswer(json: any, peer: PeerContext) {
221205
const infoHash: string = json.info_hash;
222-
const peerSwarms = peer.swarms;
223-
224-
if (peerSwarms === undefined) {
225-
throw new TrackerError("answer: peer is not in the swarm");
226-
}
227-
228-
const swarmContext = peerSwarms.find(s => s.swarm.infoHash === infoHash);
206+
const swarm = this._swarms.get(infoHash);
229207

230-
if (swarmContext === undefined) {
231-
throw new TrackerError("answer: peer is not in the swarm");
208+
if (swarm === undefined) {
209+
throw new TrackerError("answer: no such swarm");
232210
}
233211

234212
const toPeerId = json.to_peer_id;
235-
const toPeer = swarmContext.swarm.peers.get(toPeerId);
213+
const toPeer = swarm.peers.get(toPeerId);
236214
if (toPeer === undefined) {
237215
throw new TrackerError("answer: to_peer_id is not in the swarm");
238216
}
@@ -245,7 +223,7 @@ export class FastTracker implements Tracker {
245223
}
246224
}
247225

248-
private processStop(json: any, peer: InternalPeerContext) {
226+
private processStop(json: any, peer: PeerContext) {
249227
const peerId: string | undefined = json.peer_id;
250228
if (peer.id !== peerId) {
251229
throw new TrackerError("stop event: different peer_id on the same connection");
@@ -257,7 +235,7 @@ export class FastTracker implements Tracker {
257235

258236
const infoHash: string = json.info_hash;
259237

260-
const swarm = removePeerFromSwarm(peer, infoHash);
238+
const swarm = this._swarms.get(infoHash);
261239

262240
if (swarm === undefined) {
263241
debug("stop event: peer not in the swarm");
@@ -268,6 +246,7 @@ export class FastTracker implements Tracker {
268246
debug("stop event: peer", Buffer.from(peerId).toString("hex"), "remove from swarm", Buffer.from(infoHash).toString("hex"));
269247
}
270248

249+
swarm.removePeer(peer);
271250
if (swarm.peers.size === 0) {
272251
if (debugEnabled) {
273252
debug("stop event: swarm removed (empty)", Buffer.from(infoHash).toString("hex"));
@@ -329,46 +308,62 @@ export class FastTracker implements Tracker {
329308
}
330309
}
331310

332-
interface SwarmContext {
333-
completed: boolean;
334-
swarm: Swarm;
335-
}
336-
337-
interface InternalPeerContext extends PeerContext {
338-
swarms?: SwarmContext[];
339-
}
340-
341311
// tslint:disable-next-line:max-classes-per-file
342312
class Swarm {
343313
public completedCount = 0;
344314

345-
private _peers = new Map<string, InternalPeerContext>();
346-
private _peersOrdered: InternalPeerContext[] = [];
315+
private _peers = new Map<string, PeerContext>();
316+
private _peersOrdered: PeerContext[] = [];
317+
private isPeerCompleted: boolean[] = [];
347318

348319
constructor(readonly infoHash: string) {}
349320

350-
public addPeer(peer: InternalPeerContext) {
321+
public addPeer(peer: PeerContext, completed: boolean) {
351322
const peerId = peer.id!;
352323
this._peersOrdered.push(peer);
324+
this.isPeerCompleted.push(completed);
353325
this._peers.set(peerId, peer);
326+
if (completed) {
327+
this.completedCount++;
328+
}
354329
}
355330

356-
public removePeer(peer: InternalPeerContext) {
357-
this._peers.delete(peer.id!);
331+
public removePeer(peer: PeerContext) {
332+
if (!this._peers.delete(peer.id!)) {
333+
return false;
334+
}
358335

359-
// Delete peerId from array without calling splice
360336
const index = this._peersOrdered.indexOf(peer);
337+
338+
if (this.isPeerCompleted[index]) {
339+
this.completedCount--;
340+
}
341+
342+
// Delete peerId from arrays without calling splice
361343
const last = this._peersOrdered.pop()!;
344+
const lastIsCompleted = this.isPeerCompleted.pop()!;
362345
if (index < this._peersOrdered.length) {
363346
this._peersOrdered[index] = last;
347+
this.isPeerCompleted[index] = lastIsCompleted;
348+
}
349+
350+
return true;
351+
}
352+
353+
public setCompleted(peer: PeerContext) {
354+
const index = this._peersOrdered.indexOf(peer);
355+
356+
if (!this.isPeerCompleted[index]) {
357+
this.completedCount++;
358+
this.isPeerCompleted[index] = true;
364359
}
365360
}
366361

367-
public get peers(): ReadonlyMap<string, InternalPeerContext> {
362+
public get peers(): ReadonlyMap<string, PeerContext> {
368363
return this._peers;
369364
}
370365

371-
public get peersOrdered(): ReadonlyArray<InternalPeerContext> {
366+
public get peersOrdered(): ReadonlyArray<PeerContext> {
372367
return this._peersOrdered;
373368
}
374369
}
@@ -396,28 +391,3 @@ function sendOffer(offerItem: {offer?: { sdp?: string }, offer_id?: string } | n
396391
},
397392
}, toPeer);
398393
}
399-
400-
function removePeerFromSwarm(peer: InternalPeerContext, infoHash: string): Swarm | undefined {
401-
const peerSwarms = peer.swarms!;
402-
const swarmIndex = peerSwarms.findIndex(s => s.swarm.infoHash === infoHash);
403-
404-
if (swarmIndex === -1) {
405-
return undefined;
406-
}
407-
408-
const swarmContext = peerSwarms[swarmIndex];
409-
const swarm = swarmContext.swarm;
410-
411-
swarm.removePeer(peer);
412-
if (swarmContext.completed) {
413-
swarm.completedCount--;
414-
}
415-
416-
// Delete swarm from array without calling splice
417-
const last = peerSwarms.pop()!;
418-
if (swarmIndex < peerSwarms.length) {
419-
peerSwarms[swarmIndex] = last;
420-
}
421-
422-
return swarm;
423-
}

0 commit comments

Comments
 (0)