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