perf: optimize task(s) stealing
[poolifier.git] / src / priority-queue.ts
CommitLineData
95d1a734
JB
1// Copyright Jerome Benoit. 2024. All Rights Reserved.
2
bcfb06ce 3/**
95d1a734
JB
4 * Priority queue node.
5 *
6 * @typeParam T - Type of priority queue node data.
bcfb06ce
JB
7 * @internal
8 */
95d1a734 9export interface PriorityQueueNode<T> {
bcfb06ce
JB
10 data: T
11 priority: number
12}
13
14/**
95d1a734 15 * Priority queue.
bcfb06ce
JB
16 *
17 * @typeParam T - Type of priority queue data.
18 * @internal
19 */
20export class PriorityQueue<T> {
21 private nodeArray!: Array<PriorityQueueNode<T>>
bd8b441c
JB
22 /** Prioritized bucket size. */
23 private readonly k: number
bcfb06ce
JB
24 /** The size of the priority queue. */
25 public size!: number
26 /** The maximum size of the priority queue. */
27 public maxSize!: number
28
0d4e88b3
JB
29 /**
30 * The number of filled prioritized buckets.
31 */
32 public get buckets (): number {
33 return this.k === Infinity ? 1 : Math.trunc(this.nodeArray.length / this.k)
34 }
35
bd8b441c 36 /**
95d1a734 37 * Constructs a priority queue.
bd8b441c 38 *
2be9b405 39 * @param k - Prioritized bucket size. @defaultValue Infinity
bd8b441c
JB
40 */
41 public constructor (k = Infinity) {
e41b0271
JB
42 if (k !== Infinity && !Number.isSafeInteger(k)) {
43 throw new TypeError('k must be an integer')
44 }
45 if (k < 1) {
46 throw new RangeError('k must be greater than or equal to 1')
47 }
bd8b441c 48 this.k = k
e41b0271 49 this.clear()
bcfb06ce
JB
50 }
51
52 /**
53 * Enqueue data into the priority queue.
54 *
55 * @param data - Data to enqueue.
56 * @param priority - Priority of the data. Lower values have higher priority.
57 * @returns The new size of the priority queue.
58 */
59 public enqueue (data: T, priority?: number): number {
60 priority = priority ?? 0
0d4e88b3 61 const startIndex = this.k === Infinity ? 0 : this.buckets * this.k
bcfb06ce 62 let inserted = false
e41b0271
JB
63 for (let index = startIndex; index < this.nodeArray.length; index++) {
64 if (this.nodeArray[index].priority > priority) {
bcfb06ce
JB
65 this.nodeArray.splice(index, 0, { data, priority })
66 inserted = true
67 break
68 }
69 }
70 if (!inserted) {
71 this.nodeArray.push({ data, priority })
72 }
73 return this.incrementSize()
74 }
75
76 /**
77 * Dequeue data from the priority queue.
78 *
95d1a734 79 * @param bucket - The prioritized bucket to dequeue from. @defaultValue 0
bcfb06ce
JB
80 * @returns The dequeued data or `undefined` if the priority queue is empty.
81 */
95d1a734
JB
82 public dequeue (bucket = 0): T | undefined {
83 if (this.k !== Infinity && bucket > 0) {
84 while (bucket > 0) {
85 const index = bucket * this.k
86 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
87 if (this.nodeArray[index] != null) {
88 --this.size
89 return this.nodeArray.splice(index, 1)[0].data
90 }
91 --bucket
92 }
93 }
bcfb06ce
JB
94 if (this.size > 0) {
95 --this.size
96 }
97 return this.nodeArray.shift()?.data
98 }
99
100 /**
101 * Peeks at the first data.
102 * @returns The first data or `undefined` if the priority queue is empty.
103 */
104 public peekFirst (): T | undefined {
105 return this.nodeArray[0]?.data
106 }
107
108 /**
109 * Peeks at the last data.
110 * @returns The last data or `undefined` if the priority queue is empty.
111 */
112 public peekLast (): T | undefined {
113 return this.nodeArray[this.nodeArray.length - 1]?.data
114 }
115
116 /**
117 * Clears the priority queue.
118 */
119 public clear (): void {
120 this.nodeArray = []
121 this.size = 0
122 this.maxSize = 0
123 }
124
125 /**
95d1a734
JB
126 * Returns an iterator for the priority queue.
127 *
2be9b405 128 * @returns An iterator for the priority queue.
95d1a734
JB
129 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
130 */
131 [Symbol.iterator] (): Iterator<T> {
132 let i = 0
133 return {
134 next: () => {
135 if (i >= this.nodeArray.length) {
136 return {
137 value: undefined,
138 done: true
139 }
140 }
141 const value = this.nodeArray[i].data
142 i++
143 return {
144 value,
145 done: false
146 }
147 }
148 }
149 }
150
151 /**
152 * Increments the size of the priority queue.
bcfb06ce 153 *
95d1a734 154 * @returns The new size of the priority queue.
bcfb06ce
JB
155 */
156 private incrementSize (): number {
157 ++this.size
158 if (this.size > this.maxSize) {
159 this.maxSize = this.size
160 }
161 return this.size
162 }
163}