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