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