build(deps): bump express
[poolifier.git] / src / circular-array.ts
1 // Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
2
3 export const DEFAULT_CIRCULAR_ARRAY_SIZE = 1024
4
5 /**
6 * Array with a maximum length and shifting items when full.
7 *
8 * @typeParam T - Type of items.
9 * @internal
10 */
11 export class CircularArray<T> extends Array<T> {
12 public size: number
13
14 constructor (size: number = DEFAULT_CIRCULAR_ARRAY_SIZE, ...items: T[]) {
15 super()
16 this.checkSize(size)
17 this.size = size
18 if (arguments.length > 1) {
19 this.push(...items)
20 }
21 }
22
23 /** @inheritDoc */
24 public push (...items: T[]): number {
25 const length = super.push(...items)
26 if (length > this.size) {
27 super.splice(0, length - this.size)
28 }
29 return this.length
30 }
31
32 /** @inheritDoc */
33 public unshift (...items: T[]): number {
34 const length = super.unshift(...items)
35 if (length > this.size) {
36 super.splice(this.size, items.length)
37 }
38 return this.length
39 }
40
41 /** @inheritDoc */
42 public concat (...items: Array<T | ConcatArray<T>>): CircularArray<T> {
43 const concatenatedCircularArray = super.concat(
44 items as T[]
45 ) as CircularArray<T>
46 concatenatedCircularArray.size = this.size
47 if (concatenatedCircularArray.length > concatenatedCircularArray.size) {
48 concatenatedCircularArray.splice(
49 0,
50 concatenatedCircularArray.length - concatenatedCircularArray.size
51 )
52 }
53 return concatenatedCircularArray
54 }
55
56 /** @inheritDoc */
57 public splice (
58 start: number,
59 deleteCount?: number,
60 ...items: T[]
61 ): CircularArray<T> {
62 let itemsRemoved: T[] = []
63 if (arguments.length >= 3 && deleteCount != null) {
64 itemsRemoved = super.splice(start, deleteCount, ...items)
65 if (this.length > this.size) {
66 const itemsOverflowing = super.splice(0, this.length - this.size)
67 itemsRemoved = new CircularArray<T>(
68 itemsRemoved.length + itemsOverflowing.length,
69 ...itemsRemoved,
70 ...itemsOverflowing
71 )
72 }
73 } else if (arguments.length === 2) {
74 itemsRemoved = super.splice(start, deleteCount)
75 } else {
76 itemsRemoved = super.splice(start)
77 }
78 return itemsRemoved as CircularArray<T>
79 }
80
81 public resize (size: number): void {
82 this.checkSize(size)
83 if (size === 0) {
84 this.length = 0
85 } else if (size < this.size) {
86 for (let i = size; i < this.size; i++) {
87 super.pop()
88 }
89 }
90 this.size = size
91 }
92
93 public empty (): boolean {
94 return this.length === 0
95 }
96
97 public full (): boolean {
98 return this.length === this.size
99 }
100
101 private checkSize (size: number): void {
102 if (!Number.isSafeInteger(size)) {
103 throw new TypeError(
104 `Invalid circular array size: ${size} is not a safe integer`
105 )
106 }
107 if (size < 0) {
108 throw new RangeError(`Invalid circular array size: ${size} < 0`)
109 }
110 }
111 }