---
[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 22 /** Prioritized bucket size. */
2107bc57 23 private readonly bucketSize: 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 {
2107bc57
JB
33 return this.bucketSize === Infinity
34 ? 1
35 : Math.trunc(this.nodeArray.length / this.bucketSize)
0d4e88b3
JB
36 }
37
bd8b441c 38 /**
95d1a734 39 * Constructs a priority queue.
bd8b441c 40 *
2107bc57 41 * @param bucketSize - Prioritized bucket size. @defaultValue Infinity
bd8b441c 42 */
2107bc57
JB
43 public constructor (bucketSize = Infinity) {
44 if (bucketSize !== Infinity && !Number.isSafeInteger(bucketSize)) {
45 throw new TypeError('bucketSize must be an integer')
e41b0271 46 }
2107bc57
JB
47 if (bucketSize < 1) {
48 throw new RangeError('bucketSize must be greater than or equal to 1')
e41b0271 49 }
2107bc57 50 this.bucketSize = bucketSize
e41b0271 51 this.clear()
bcfb06ce
JB
52 }
53
54 /**
55 * Enqueue data into the priority queue.
56 *
57 * @param data - Data to enqueue.
58 * @param priority - Priority of the data. Lower values have higher priority.
59 * @returns The new size of the priority queue.
60 */
61 public enqueue (data: T, priority?: number): number {
62 priority = priority ?? 0
2107bc57
JB
63 const startIndex =
64 this.bucketSize === Infinity ? 0 : this.buckets * this.bucketSize
bcfb06ce 65 let inserted = false
e41b0271
JB
66 for (let index = startIndex; index < this.nodeArray.length; index++) {
67 if (this.nodeArray[index].priority > priority) {
bcfb06ce
JB
68 this.nodeArray.splice(index, 0, { data, priority })
69 inserted = true
70 break
71 }
72 }
73 if (!inserted) {
74 this.nodeArray.push({ data, priority })
75 }
76 return this.incrementSize()
77 }
78
79 /**
80 * Dequeue data from the priority queue.
81 *
95d1a734 82 * @param bucket - The prioritized bucket to dequeue from. @defaultValue 0
bcfb06ce
JB
83 * @returns The dequeued data or `undefined` if the priority queue is empty.
84 */
95d1a734 85 public dequeue (bucket = 0): T | undefined {
2107bc57 86 if (this.bucketSize !== Infinity && bucket > 0) {
95d1a734 87 while (bucket > 0) {
2107bc57 88 const index = bucket * this.bucketSize
95d1a734
JB
89 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
90 if (this.nodeArray[index] != null) {
91 --this.size
92 return this.nodeArray.splice(index, 1)[0].data
93 }
94 --bucket
95 }
96 }
65729b19 97 this.decrementSize()
bcfb06ce
JB
98 return this.nodeArray.shift()?.data
99 }
100
101 /**
102 * Peeks at the first data.
103 * @returns The first data or `undefined` if the priority queue is empty.
104 */
105 public peekFirst (): T | undefined {
106 return this.nodeArray[0]?.data
107 }
108
109 /**
110 * Peeks at the last data.
111 * @returns The last data or `undefined` if the priority queue is empty.
112 */
113 public peekLast (): T | undefined {
114 return this.nodeArray[this.nodeArray.length - 1]?.data
115 }
116
117 /**
118 * Clears the priority queue.
119 */
120 public clear (): void {
121 this.nodeArray = []
122 this.size = 0
123 this.maxSize = 0
124 }
125
126 /**
95d1a734
JB
127 * Returns an iterator for the priority queue.
128 *
2be9b405 129 * @returns An iterator for the priority queue.
95d1a734
JB
130 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
131 */
132 [Symbol.iterator] (): Iterator<T> {
133 let i = 0
134 return {
135 next: () => {
136 if (i >= this.nodeArray.length) {
137 return {
138 value: undefined,
139 done: true
140 }
141 }
142 const value = this.nodeArray[i].data
143 i++
144 return {
145 value,
146 done: false
147 }
148 }
149 }
150 }
151
152 /**
153 * Increments the size of the priority queue.
bcfb06ce 154 *
95d1a734 155 * @returns The new size of the priority queue.
bcfb06ce
JB
156 */
157 private incrementSize (): number {
158 ++this.size
159 if (this.size > this.maxSize) {
160 this.maxSize = this.size
161 }
162 return this.size
163 }
65729b19
JB
164
165 /**
166 * Decrements the size of the priority queue.
167 *
168 * @returns The new size of the priority queue.
169 */
170 private decrementSize (): number {
171 if (this.size > 0) {
172 --this.size
173 }
174 return this.size
175 }
bcfb06ce 176}