Skip to content

Commit 5b9ef11

Browse files
apapirovskiBridgeAR
authored andcommitted
timers: fix priority queue removeAt
PR-URL: #24322 Fixes: #24320 Fixes: #24362 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Franziska Hinkelmann <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Weijia Wang <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
1 parent dc26247 commit 5b9ef11

File tree

2 files changed

+58
-21
lines changed

2 files changed

+58
-21
lines changed

lib/internal/priority_queue.js

+28-21
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,13 @@ module.exports = class PriorityQueue {
2828

2929
insert(value) {
3030
const heap = this[kHeap];
31-
let pos = ++this[kSize];
31+
const pos = ++this[kSize];
32+
heap[pos] = value;
3233

3334
if (heap.length === pos)
3435
heap.length *= 2;
3536

36-
const compare = this[kCompare];
37-
const setPosition = this[kSetPosition];
38-
while (pos > 1) {
39-
const parent = heap[pos / 2 | 0];
40-
if (compare(parent, value) <= 0)
41-
break;
42-
heap[pos] = parent;
43-
if (setPosition !== undefined)
44-
setPosition(parent, pos);
45-
pos = pos / 2 | 0;
46-
}
47-
heap[pos] = value;
48-
if (setPosition !== undefined)
49-
setPosition(value, pos);
37+
this.percolateUp(pos);
5038
}
5139

5240
peek() {
@@ -77,18 +65,37 @@ module.exports = class PriorityQueue {
7765
setPosition(item, pos);
7866
}
7967

68+
percolateUp(pos) {
69+
const heap = this[kHeap];
70+
const compare = this[kCompare];
71+
const setPosition = this[kSetPosition];
72+
const item = heap[pos];
73+
74+
while (pos > 1) {
75+
const parent = heap[pos / 2 | 0];
76+
if (compare(parent, item) <= 0)
77+
break;
78+
heap[pos] = parent;
79+
if (setPosition !== undefined)
80+
setPosition(parent, pos);
81+
pos = pos / 2 | 0;
82+
}
83+
heap[pos] = item;
84+
if (setPosition !== undefined)
85+
setPosition(item, pos);
86+
}
87+
8088
removeAt(pos) {
8189
const heap = this[kHeap];
8290
const size = --this[kSize];
8391
heap[pos] = heap[size + 1];
8492
heap[size + 1] = undefined;
8593

86-
if (size > 0) {
87-
// If not removing the last item, update the shifted item's position.
88-
if (pos <= size && this[kSetPosition] !== undefined)
89-
this[kSetPosition](heap[pos], pos);
90-
91-
this.percolateDown(1);
94+
if (size > 0 && pos <= size) {
95+
if (pos > 1 && this[kCompare](heap[pos / 2 | 0], heap[pos]) > 0)
96+
this.percolateUp(pos);
97+
else
98+
this.percolateDown(pos);
9299
}
93100
}
94101

test/parallel/test-priority-queue.js

+30
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,33 @@ const PriorityQueue = require('internal/priority_queue');
131131

132132
assert.strictEqual(queue.shift(), undefined);
133133
}
134+
135+
136+
{
137+
// Checks that removeAt respects binary heap properties
138+
const queue = new PriorityQueue((a, b) => {
139+
return a.value - b.value;
140+
}, (node, pos) => (node.position = pos));
141+
142+
const i3 = { value: 3, position: null };
143+
const i7 = { value: 7, position: null };
144+
const i8 = { value: 8, position: null };
145+
146+
queue.insert({ value: 1, position: null });
147+
queue.insert({ value: 6, position: null });
148+
queue.insert({ value: 2, position: null });
149+
queue.insert(i7);
150+
queue.insert(i8);
151+
queue.insert(i3);
152+
153+
assert.strictEqual(i7.position, 4);
154+
queue.removeAt(4);
155+
156+
// 3 should percolate up to swap with 6 (up)
157+
assert.strictEqual(i3.position, 2);
158+
159+
queue.removeAt(2);
160+
161+
// 8 should swap places with 6 (down)
162+
assert.strictEqual(i8.position, 4);
163+
}

0 commit comments

Comments
 (0)