build(deps-dev): apply updates
[poolifier.git] / src / priority-queue.ts
CommitLineData
95d1a734
JB
1// Copyright Jerome Benoit. 2024. All Rights Reserved.
2
9df282a0
JB
3import { FixedPriorityQueue } from './fixed-priority-queue.js'
4
5/**
6 * Default bucket size.
7 */
8export const defaultBucketSize = 2048
9
bcfb06ce 10/**
95d1a734 11 * Priority queue node.
95d1a734 12 * @typeParam T - Type of priority queue node data.
bcfb06ce
JB
13 * @internal
14 */
9df282a0
JB
15export interface PriorityQueueNode<T> extends FixedPriorityQueue<T> {
16 next?: FixedPriorityQueue<T>
bcfb06ce
JB
17}
18
19/**
95d1a734 20 * Priority queue.
bcfb06ce
JB
21 * @typeParam T - Type of priority queue data.
22 * @internal
23 */
24export class PriorityQueue<T> {
9df282a0
JB
25 private head!: PriorityQueueNode<T>
26 private tail!: PriorityQueueNode<T>
2107bc57 27 private readonly bucketSize: number
13ed544a 28 /** The priority queue maximum size. */
bcfb06ce
JB
29 public maxSize!: number
30
bd8b441c 31 /**
95d1a734 32 * Constructs a priority queue.
9df282a0 33 * @param bucketSize - Prioritized bucket size. @defaultValue defaultBucketSize
fcfc3353 34 * @param enablePriority - Whether to enable priority. @defaultValue false
9df282a0 35 * @returns PriorityQueue.
bd8b441c 36 */
fcfc3353
JB
37 public constructor (
38 bucketSize: number = defaultBucketSize,
39 enablePriority = false
40 ) {
e75373e6 41 if (!Number.isSafeInteger(bucketSize)) {
579aa5bd 42 throw new TypeError(
6e5d7052 43 `Invalid bucket size: '${bucketSize.toString()}' is not an integer`
579aa5bd 44 )
e75373e6 45 }
579aa5bd 46 if (bucketSize < 0) {
6e5d7052 47 throw new RangeError(`Invalid bucket size: ${bucketSize.toString()} < 0`)
e75373e6 48 }
2107bc57 49 this.bucketSize = bucketSize
fcfc3353
JB
50 this.head = this.tail = new FixedPriorityQueue(
51 this.bucketSize,
52 enablePriority
53 )
54 this.maxSize = 0
bcfb06ce
JB
55 }
56
e75373e6 57 /**
13ed544a 58 * The priority queue size.
e75373e6 59 */
9df282a0
JB
60 public get size (): number {
61 let node: PriorityQueueNode<T> | undefined = this.tail
62 let size = 0
63 while (node != null) {
64 size += node.size
65 node = node.next
66 }
67 return size
68 }
69
fcfc3353
JB
70 public get enablePriority (): boolean {
71 return this.head.enablePriority
72 }
73
74 public set enablePriority (enablePriority: boolean) {
75 if (this.head.enablePriority === enablePriority) {
76 return
77 }
78 let node: PriorityQueueNode<T> | undefined = this.tail
79 while (node != null) {
80 node.enablePriority = enablePriority
81 node = node.next
82 }
83 }
84
e75373e6
JB
85 /**
86 * The number of filled prioritized buckets.
87 */
88 public get buckets (): number {
89 return Math.trunc(this.size / this.bucketSize)
90 }
91
bcfb06ce
JB
92 /**
93 * Enqueue data into the priority queue.
bcfb06ce
JB
94 * @param data - Data to enqueue.
95 * @param priority - Priority of the data. Lower values have higher priority.
96 * @returns The new size of the priority queue.
97 */
98 public enqueue (data: T, priority?: number): number {
9df282a0 99 if (this.head.full()) {
fcfc3353
JB
100 this.head = this.head.next = new FixedPriorityQueue(
101 this.bucketSize,
102 this.enablePriority
103 )
bcfb06ce 104 }
9df282a0
JB
105 this.head.enqueue(data, priority)
106 const size = this.size
107 if (size > this.maxSize) {
108 this.maxSize = size
bcfb06ce 109 }
9df282a0 110 return size
bcfb06ce
JB
111 }
112
113 /**
114 * Dequeue data from the priority queue.
9df282a0 115 * @param bucket - The prioritized bucket to dequeue from.
bcfb06ce
JB
116 * @returns The dequeued data or `undefined` if the priority queue is empty.
117 */
9df282a0
JB
118 public dequeue (bucket?: number): T | undefined {
119 let tail: PriorityQueueNode<T> | undefined = this.tail
1d98bad2
JB
120 let tailChanged = false
121 if (bucket != null && bucket > 0) {
122 let currentBucket = 1
9df282a0 123 while (tail != null) {
9df282a0
JB
124 if (currentBucket === bucket) {
125 break
95d1a734 126 }
1d98bad2 127 ++currentBucket
9df282a0 128 tail = tail.next
1d98bad2 129 tailChanged = true
95d1a734
JB
130 }
131 }
9df282a0
JB
132 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
133 const data = tail!.dequeue()
134 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
135 if (tail!.empty() && tail!.next != null) {
1d98bad2
JB
136 if (!tailChanged) {
137 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
138 this.tail = tail!.next
9df282a0
JB
139 } else {
140 let node: PriorityQueueNode<T> | undefined = this.tail
141 while (node != null) {
142 if (node.next === tail) {
143 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
144 node.next = tail!.next
145 break
146 }
147 node = node.next
148 }
149 }
150 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
151 delete tail!.next
152 }
153 return data
bcfb06ce
JB
154 }
155
156 /**
157 * Clears the priority queue.
158 */
159 public clear (): void {
fcfc3353
JB
160 this.head = this.tail = new FixedPriorityQueue(
161 this.bucketSize,
162 this.enablePriority
163 )
bcfb06ce
JB
164 this.maxSize = 0
165 }
166
167 /**
95d1a734 168 * Returns an iterator for the priority queue.
2be9b405 169 * @returns An iterator for the priority queue.
95d1a734
JB
170 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
171 */
9df282a0 172 public [Symbol.iterator] (): Iterator<T> {
1d98bad2 173 let index = 0
9df282a0 174 let node = this.tail
95d1a734
JB
175 return {
176 next: () => {
1d98bad2
JB
177 const value = node.get(index) as T
178 if (value == null) {
95d1a734
JB
179 return {
180 value: undefined,
3a502712 181 done: true,
95d1a734
JB
182 }
183 }
1d98bad2
JB
184 ++index
185 if (index === node.capacity && node.next != null) {
9df282a0 186 node = node.next
1d98bad2 187 index = 0
9df282a0 188 }
95d1a734
JB
189 return {
190 value,
3a502712 191 done: false,
95d1a734 192 }
3a502712 193 },
95d1a734
JB
194 }
195 }
bcfb06ce 196}