perf: optimize tasks queuing implementation
[poolifier.git] / src / fixed-queue.ts
1 import { defaultQueueSize, type FixedQueueNode, type IFixedQueue } from './utility-types.js'
2
3 /**
4 * Fixed queue.
5 * @typeParam T - Type of fixed queue data.
6 * @internal
7 */
8 export class FixedQueue<T> implements IFixedQueue<T> {
9 private start!: number
10 /** @inheritdoc */
11 public readonly capacity: number
12 /** @inheritdoc */
13 public size!: number
14 /** @inheritdoc */
15 public nodeArray: FixedQueueNode<T>[]
16
17 /**
18 * Constructs a fixed queue.
19 * @param size - Fixed queue size. @defaultValue defaultQueueSize
20 * @returns FixedQueue.
21 */
22 constructor (size: number = defaultQueueSize) {
23 this.checkSize(size)
24 this.capacity = size
25 this.nodeArray = new Array<FixedQueueNode<T>>(this.capacity)
26 this.clear()
27 }
28
29 /** @inheritdoc */
30 public empty (): boolean {
31 return this.size === 0
32 }
33
34 /** @inheritdoc */
35 public full (): boolean {
36 return this.size === this.capacity
37 }
38
39 /** @inheritdoc */
40 public enqueue (data: T, priority?: number): number {
41 if (this.full()) {
42 throw new Error('Priority queue is full')
43 }
44 let index = this.start + this.size
45 if (index >= this.capacity) {
46 index -= this.capacity
47 }
48 this.nodeArray[index] = { data, priority: priority ?? 0 }
49 return ++this.size
50 }
51
52 /** @inheritdoc */
53 public get (index: number): T | undefined {
54 if (this.empty() || index >= this.size) {
55 return undefined
56 }
57 index += this.start
58 if (index >= this.capacity) {
59 index -= this.capacity
60 }
61 return this.nodeArray[index].data
62 }
63
64 /** @inheritdoc */
65 public dequeue (): T | undefined {
66 if (this.empty()) {
67 return undefined
68 }
69 const index = this.start
70 --this.size
71 ++this.start
72 if (this.start === this.capacity) {
73 this.start = 0
74 }
75 return this.nodeArray[index].data
76 }
77
78 /** @inheritdoc */
79 public clear (): void {
80 this.start = 0
81 this.size = 0
82 }
83
84 /** @inheritdoc */
85 public [Symbol.iterator] (): Iterator<T> {
86 let index = this.start
87 let i = 0
88 return {
89 next: () => {
90 if (i >= this.size) {
91 return {
92 value: undefined,
93 done: true,
94 }
95 }
96 const value = this.nodeArray[index].data
97 ++index
98 ++i
99 if (index === this.capacity) {
100 index = 0
101 }
102 return {
103 value,
104 done: false,
105 }
106 },
107 }
108 }
109
110 /**
111 * Checks the fixed queue size.
112 * @param size - Queue size.
113 */
114 private checkSize (size: number): void {
115 if (!Number.isSafeInteger(size)) {
116 throw new TypeError(
117 `Invalid fixed queue size: '${size.toString()}' is not an integer`
118 )
119 }
120 if (size < 0) {
121 throw new RangeError(
122 `Invalid fixed queue size: ${size.toString()} < 0`
123 )
124 }
125 }
126 }