-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRingBuffer.ts
229 lines (199 loc) · 4.77 KB
/
RingBuffer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
export class RingBuffer<T> {
/**
* Create a new ring buffer from an array.
* @param data Array to import.
* @param size Optional size. Otherwise same size as the input data array.
*/
public static fromArray<T>(data: T[], size = 0): RingBuffer<T> {
const ringBuffer = new RingBuffer<T>(size)
ringBuffer.fromArray(data, size === 0)
return ringBuffer
}
protected buffer: T[] = []
protected size: number
protected pos = 0
/**
* Creates a new ring buffer.
* @param size Maximum size of the circular buffer.
*/
constructor(size: number) {
if (size < 0) {
throw new RangeError('Invalid size.')
}
this.size = size
}
/**
* Returns the maximum size.
*/
public getSize(): number {
return this.size
}
/**
* Returns the next internal index position.
*/
public getPos(): number {
return this.pos
}
/**
* Returns the current buffer size.
* The underlying array length.
*/
public getBufferLength(): number {
return this.buffer.length
}
/**
* Pushes new items to this buffer. (like Array#push)
* @param items Items to push to this buffer.
*/
public add(...items: T[]): void {
items.forEach((item) => {
this.buffer[this.pos] = item
this.pos = (this.pos + 1) % this.size
})
}
/**
* Returns the item on specific index.
* @param index Index of item.
*/
public get(index: number): T | undefined {
if (index < 0) {
index += this.buffer.length
}
if (index < 0 || index > this.buffer.length) {
return undefined
}
if (this.buffer.length < this.size) {
return this.buffer[index]
}
return this.buffer[(this.pos + index) % this.size]
}
/**
* Returns the first item.
* Same as #get(0).
*/
public getFirst(): T | undefined {
return this.get(0)
}
/**
* Returns the last item.
* Same as #get(-1) or #get(length - 1).
*/
public getLast(): T | undefined {
return this.get(-1)
}
/**
* Return the first `n` items as an array.
* @param n Number of items to return.
*/
public getFirstN(n: number): T[] {
if (n === 0) {
return []
}
if (n < 0) {
return this.getLastN(-n)
}
return this.toArray().slice(0, n)
}
/**
* Return the last `n` items as an array.
* @param n Number of items to return.
*/
public getLastN(n: number): T[] {
if (n === 0) {
return []
}
if (n < 0) {
return this.getFirstN(-n)
}
return this.toArray().slice(-n)
}
/**
* Removes one or more items form the buffer.
* @param index Start index of the item to remove.
* @param count The count of items to remove. (default: 1)
*/
public remove(index: number, count = 1): T[] {
if (index < 0) {
index += this.buffer.length
}
if (index < 0 || index > this.buffer.length) {
return []
}
const arr = this.toArray()
const removedItems = arr.splice(index, count)
this.fromArray(arr)
return removedItems
}
/**
* Removes the first item. Like #remove(0).
*/
public removeFirst(): T {
return this.remove(0)[0]
}
/**
* Removes the last item. Like #remove(-1).
*/
public removeLast(): T {
return this.remove(-1)[0]
}
/**
* Converts the ring buffer to a JavaScript standard array.
*/
public toArray(): T[] {
return this.buffer.slice(this.pos).concat(this.buffer.slice(0, this.pos))
}
/**
* Imports an array to this buffer. (overwrites)
* @param data JavaScript standard array.
* @param resize If true, sets the maximum size to the input data array length.
*/
public fromArray(data: T[], resize = false): void {
if (!Array.isArray(data)) {
throw new TypeError('Input value is not an array.')
}
if (resize) {
this.resize(data.length)
}
if (this.size === 0) {
return
}
this.buffer = data.slice(-this.size)
this.pos = this.buffer.length % this.size
}
/**
* Clears the the buffer. Removes all items.
*/
public clear(): void {
this.buffer = []
this.pos = 0
}
/**
* Resizes the circular buffer.
* @param newSize The new maximum size.
*/
public resize(newSize: number): void {
if (newSize < 0) {
throw new RangeError('The size does not allow negative values.')
}
if (newSize === 0) {
this.clear()
} else if (newSize !== this.size) {
const currentBuffer = this.toArray()
this.fromArray(currentBuffer.slice(-newSize))
this.pos = this.buffer.length % newSize
}
this.size = newSize
}
/**
* Returns true if the maximum size is reached.
*/
public isFull(): boolean {
return this.buffer.length === this.size
}
/**
* Returns true if the buffer is empty.
*/
public isEmpty(): boolean {
return this.buffer.length === 0
}
}