fix: fix priority queue iterator
[poolifier.git] / src / fixed-priority-queue.ts
CommitLineData
f8d5d8fd
JB
1/**
2 * Default buffer size.
3 */
4export const defaultQueueSize = 2048
5
6/**
65c178f1 7 * Fixed priority queue node.
f8d5d8fd
JB
8 *
9 * @typeParam T - Type of priority queue node data.
10 * @internal
11 */
65c178f1 12export interface FixedPriorityQueueNode<T> {
f8d5d8fd
JB
13 data: T
14 priority: number
15}
16
eadb37e2
JB
17/**
18 * Fixed priority queue.
19 *
20 * @typeParam T - Type of fixed priority queue data.
21 * @internal
22 */
f8d5d8fd
JB
23export class FixedPriorityQueue<T> {
24 private start!: number
65c178f1 25 private readonly nodeArray: Array<FixedPriorityQueueNode<T>>
9df282a0 26 public readonly capacity: number
f8d5d8fd
JB
27 public size!: number
28 public maxSize!: number
29
9df282a0
JB
30 /**
31 * Constructs a fixed priority queue.
32 *
33 * @param size - Fixed priority queue size. @defaultValue defaultQueueSize
34 * @returns FixedPriorityQueue.
35 */
f8d5d8fd
JB
36 constructor (size: number = defaultQueueSize) {
37 this.checkSize(size)
9df282a0 38 this.capacity = size
65c178f1 39 this.nodeArray = new Array<FixedPriorityQueueNode<T>>(this.capacity)
f8d5d8fd
JB
40 this.clear()
41 }
42
e75373e6
JB
43 /**
44 * Checks if the fixed priority queue is empty.
45 *
46 * @returns `true` if the fixed priority queue is empty, `false` otherwise.
47 */
f8d5d8fd
JB
48 public empty (): boolean {
49 return this.size === 0
50 }
51
e75373e6
JB
52 /**
53 * Checks if the fixed priority queue is full.
54 *
55 * @returns `true` if the fixed priority queue is full, `false` otherwise.
56 */
f8d5d8fd 57 public full (): boolean {
9df282a0 58 return this.size === this.capacity
f8d5d8fd
JB
59 }
60
e75373e6
JB
61 /**
62 * Enqueue data into the fixed priority queue.
63 *
64 * @param data - Data to enqueue.
65 * @param priority - Priority of the data. Lower values have higher priority.
66 * @returns The new size of the priority queue.
67 */
f8d5d8fd
JB
68 public enqueue (data: T, priority?: number): number {
69 if (this.full()) {
70 throw new Error('Priority queue is full')
71 }
72 priority = priority ?? 0
3451940f 73 let index = this.start
f8d5d8fd 74 let inserted = false
3451940f 75 for (let i = 0; i < this.size; i++) {
f8d5d8fd
JB
76 if (this.nodeArray[index]?.priority > priority) {
77 this.nodeArray.splice(index, 0, { data, priority })
78 inserted = true
79 break
80 }
02c769d0 81 ++index
9df282a0 82 if (index === this.capacity) {
3451940f
JB
83 index = 0
84 }
f8d5d8fd 85 }
9df282a0
JB
86 this.nodeArray.length !== this.capacity &&
87 (this.nodeArray.length = this.capacity)
f8d5d8fd
JB
88 if (!inserted) {
89 let index = this.start + this.size
9df282a0
JB
90 if (index >= this.capacity) {
91 index -= this.capacity
f8d5d8fd
JB
92 }
93 this.nodeArray[index] = { data, priority }
94 }
95 return this.incrementSize()
96 }
97
1d98bad2
JB
98 /**
99 * Gets data from the fixed priority queue.
100 *
101 * @param index - The index of the data to get.
102 * @returns The data at the index or `undefined` if the fixed priority queue is empty or the index is out of bounds.
103 */
104 public get (index: number): T | undefined {
105 if (this.empty() || index >= this.size) {
106 return undefined
107 }
108 index += this.start
109 if (index >= this.capacity) {
110 index -= this.capacity
111 }
112 return this.nodeArray[index].data
113 }
114
e75373e6
JB
115 /**
116 * Dequeue data from the fixed priority queue.
117 *
118 * @returns The dequeued data or `undefined` if the priority queue is empty.
119 */
f8d5d8fd
JB
120 public dequeue (): T | undefined {
121 if (this.empty()) {
122 return undefined
123 }
124 const index = this.start
125 --this.size
126 ++this.start
9df282a0 127 if (this.start === this.capacity) {
f8d5d8fd
JB
128 this.start = 0
129 }
130 return this.nodeArray[index].data
131 }
132
133 /**
134 * Clears the fixed priority queue.
135 */
136 public clear (): void {
137 this.start = 0
138 this.size = 0
139 this.maxSize = 0
140 }
141
142 /**
143 * Returns an iterator for the fixed priority queue.
144 *
145 * @returns An iterator for the fixed priority queue.
146 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
147 */
9df282a0 148 public [Symbol.iterator] (): Iterator<T> {
3451940f
JB
149 let index = this.start
150 let i = 0
f8d5d8fd
JB
151 return {
152 next: () => {
153 if (i >= this.size) {
154 return {
155 value: undefined,
156 done: true
157 }
158 }
3451940f 159 const value = this.nodeArray[index].data
02c769d0
JB
160 ++index
161 ++i
9df282a0 162 if (index === this.capacity) {
3451940f
JB
163 index = 0
164 }
f8d5d8fd
JB
165 return {
166 value,
167 done: false
168 }
169 }
170 }
171 }
172
173 /**
174 * Increments the size of the fixed priority queue.
175 *
176 * @returns The new size of the fixed priority queue.
177 */
178 private incrementSize (): number {
179 ++this.size
180 if (this.size > this.maxSize) {
181 this.maxSize = this.size
182 }
183 return this.size
184 }
185
e75373e6
JB
186 /**
187 * Checks the size.
188 *
189 * @param size - The size to check.
190 */
f8d5d8fd
JB
191 private checkSize (size: number): void {
192 if (!Number.isSafeInteger(size)) {
193 throw new TypeError(
f45a8925 194 `Invalid fixed priority queue size: '${size}' is not an integer`
f8d5d8fd
JB
195 )
196 }
197 if (size < 0) {
198 throw new RangeError(`Invalid fixed priority queue size: ${size} < 0`)
199 }
200 }
201}