@synnaxlabs/x 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/.eslintrc.cjs +18 -0
  2. package/.turbo/turbo-build.log +16 -0
  3. package/.vscode/settings.json +3 -0
  4. package/LICENSE +4 -0
  5. package/README.md +44 -0
  6. package/dist/binary/encoder.d.ts +59 -0
  7. package/dist/binary/encoder.spec.d.ts +1 -0
  8. package/dist/binary/index.d.ts +1 -0
  9. package/dist/case.d.ts +5 -0
  10. package/dist/change/change.d.ts +32 -0
  11. package/dist/change/index.d.ts +1 -0
  12. package/dist/clamp.d.ts +1 -0
  13. package/dist/compare/compare.d.ts +39 -0
  14. package/dist/compare/index.d.ts +1 -0
  15. package/dist/debounce.d.ts +2 -0
  16. package/dist/deep/copy.d.ts +1 -0
  17. package/dist/deep/delete.d.ts +2 -0
  18. package/dist/deep/delete.spec.d.ts +1 -0
  19. package/dist/deep/equal.d.ts +8 -0
  20. package/dist/deep/equal.spec.d.ts +1 -0
  21. package/dist/deep/external.d.ts +7 -0
  22. package/dist/deep/index.d.ts +1 -0
  23. package/dist/deep/key.d.ts +30 -0
  24. package/dist/deep/memo.d.ts +1 -0
  25. package/dist/deep/merge.d.ts +2 -0
  26. package/dist/deep/merge.spec.d.ts +1 -0
  27. package/dist/deep/partial.d.ts +3 -0
  28. package/dist/destructor.d.ts +1 -0
  29. package/dist/identity.d.ts +1 -0
  30. package/dist/index.d.ts +25 -0
  31. package/dist/join.d.ts +1 -0
  32. package/dist/kv/index.d.ts +1 -0
  33. package/dist/kv/types.d.ts +32 -0
  34. package/dist/mock/MockGLBufferController.d.ts +20 -0
  35. package/dist/mock/index.d.ts +1 -0
  36. package/dist/observe/index.d.ts +1 -0
  37. package/dist/observe/observe.d.ts +11 -0
  38. package/dist/optional.d.ts +1 -0
  39. package/dist/primitive.d.ts +8 -0
  40. package/dist/record.d.ts +14 -0
  41. package/dist/renderable.d.ts +4 -0
  42. package/dist/runtime/detect.d.ts +9 -0
  43. package/dist/runtime/external.d.ts +2 -0
  44. package/dist/runtime/index.d.ts +1 -0
  45. package/dist/runtime/os.d.ts +9 -0
  46. package/dist/search.d.ts +15 -0
  47. package/dist/spatial/base.d.ts +102 -0
  48. package/dist/spatial/bounds.d.ts +31 -0
  49. package/dist/spatial/bounds.spec.d.ts +1 -0
  50. package/dist/spatial/box.d.ts +265 -0
  51. package/dist/spatial/box.spec.d.ts +1 -0
  52. package/dist/spatial/dimensions.d.ts +59 -0
  53. package/dist/spatial/dimensions.spec.d.ts +1 -0
  54. package/dist/spatial/direction.d.ts +10 -0
  55. package/dist/spatial/direction.spec.d.ts +1 -0
  56. package/dist/spatial/external.d.ts +8 -0
  57. package/dist/spatial/index.d.ts +1 -0
  58. package/dist/spatial/location.d.ts +52 -0
  59. package/dist/spatial/location.spec.d.ts +1 -0
  60. package/dist/spatial/position.d.ts +2 -0
  61. package/dist/spatial/scale.d.ts +241 -0
  62. package/dist/spatial/scale.spec.d.ts +1 -0
  63. package/dist/spatial/spatial.d.ts +1 -0
  64. package/dist/spatial/xy.d.ts +116 -0
  65. package/dist/spatial/xy.spec.d.ts +1 -0
  66. package/dist/telem/encode.d.ts +1 -0
  67. package/dist/telem/generate.d.ts +2 -0
  68. package/dist/telem/gl.d.ts +11 -0
  69. package/dist/telem/index.d.ts +3 -0
  70. package/dist/telem/series.d.ts +104 -0
  71. package/dist/telem/series.spec.d.ts +1 -0
  72. package/dist/telem/telem.d.ts +633 -0
  73. package/dist/telem/telem.spec.d.ts +1 -0
  74. package/dist/toArray.d.ts +1 -0
  75. package/dist/transform.d.ts +1 -0
  76. package/dist/unique.d.ts +1 -0
  77. package/dist/url/index.d.ts +1 -0
  78. package/dist/url/url.d.ts +46 -0
  79. package/dist/url/url.spec.d.ts +1 -0
  80. package/dist/worker/worker.d.ts +32 -0
  81. package/dist/worker/worker.spec.d.ts +1 -0
  82. package/dist/x.cjs.js +9046 -0
  83. package/dist/x.cjs.js.map +1 -0
  84. package/dist/x.es.js +9047 -0
  85. package/dist/x.es.js.map +1 -0
  86. package/package.json +42 -0
  87. package/src/binary/encoder.spec.ts +31 -0
  88. package/src/binary/encoder.ts +118 -0
  89. package/src/binary/index.ts +10 -0
  90. package/src/case.ts +31 -0
  91. package/src/change/change.ts +31 -0
  92. package/src/change/index.ts +10 -0
  93. package/src/clamp.ts +14 -0
  94. package/src/compare/compare.ts +116 -0
  95. package/src/compare/index.ts +10 -0
  96. package/src/debounce.ts +45 -0
  97. package/src/deep/copy.ts +13 -0
  98. package/src/deep/delete.spec.ts +36 -0
  99. package/src/deep/delete.ts +27 -0
  100. package/src/deep/equal.spec.ts +82 -0
  101. package/src/deep/equal.ts +65 -0
  102. package/src/deep/external.ts +15 -0
  103. package/src/deep/index.ts +10 -0
  104. package/src/deep/key.ts +46 -0
  105. package/src/deep/merge.spec.ts +63 -0
  106. package/src/deep/merge.ts +41 -0
  107. package/src/deep/partial.ts +14 -0
  108. package/src/destructor.ts +10 -0
  109. package/src/identity.ts +14 -0
  110. package/src/index.ts +34 -0
  111. package/src/join.ts +14 -0
  112. package/src/kv/index.ts +10 -0
  113. package/src/kv/types.ts +52 -0
  114. package/src/mock/MockGLBufferController.ts +70 -0
  115. package/src/mock/index.ts +10 -0
  116. package/src/observe/index.ts +10 -0
  117. package/src/observe/observe.ts +33 -0
  118. package/src/optional.ts +10 -0
  119. package/src/primitive.ts +46 -0
  120. package/src/record.ts +45 -0
  121. package/src/renderable.ts +20 -0
  122. package/src/runtime/detect.ts +34 -0
  123. package/src/runtime/external.ts +11 -0
  124. package/src/runtime/index.ts +10 -0
  125. package/src/runtime/os.ts +38 -0
  126. package/src/search.ts +40 -0
  127. package/src/spatial/base.ts +80 -0
  128. package/src/spatial/bounds.spec.ts +99 -0
  129. package/src/spatial/bounds.ts +80 -0
  130. package/src/spatial/box.spec.ts +137 -0
  131. package/src/spatial/box.ts +326 -0
  132. package/src/spatial/dimensions.spec.ts +47 -0
  133. package/src/spatial/dimensions.ts +64 -0
  134. package/src/spatial/direction.spec.ts +25 -0
  135. package/src/spatial/direction.ts +47 -0
  136. package/src/spatial/external.ts +17 -0
  137. package/src/spatial/index.ts +10 -0
  138. package/src/spatial/location.spec.ts +24 -0
  139. package/src/spatial/location.ts +124 -0
  140. package/src/spatial/position.ts +26 -0
  141. package/src/spatial/scale.spec.ts +74 -0
  142. package/src/spatial/scale.ts +351 -0
  143. package/src/spatial/spatial.ts +17 -0
  144. package/src/spatial/xy.spec.ts +68 -0
  145. package/src/spatial/xy.ts +164 -0
  146. package/src/telem/encode.ts +22 -0
  147. package/src/telem/generate.ts +19 -0
  148. package/src/telem/gl.ts +22 -0
  149. package/src/telem/index.ts +12 -0
  150. package/src/telem/series.spec.ts +289 -0
  151. package/src/telem/series.ts +449 -0
  152. package/src/telem/telem.spec.ts +302 -0
  153. package/src/telem/telem.ts +1237 -0
  154. package/src/toArray.ts +11 -0
  155. package/src/transform.ts +10 -0
  156. package/src/unique.ts +10 -0
  157. package/src/url/index.ts +10 -0
  158. package/src/url/url.spec.ts +47 -0
  159. package/src/url/url.ts +113 -0
  160. package/src/worker/worker.spec.ts +41 -0
  161. package/src/worker/worker.ts +86 -0
  162. package/tsconfig.json +7 -0
  163. package/tsconfig.vite.json +4 -0
  164. package/vite.config.ts +23 -0
@@ -0,0 +1,449 @@
1
+ // Copyright 2023 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { type z } from "zod";
11
+
12
+ import { bounds } from "@/spatial";
13
+ import { type GLBufferController, type GLBufferUsage } from "@/telem/gl";
14
+ import {
15
+ convertDataType,
16
+ DataType,
17
+ type NativeTypedArray,
18
+ type Rate,
19
+ Size,
20
+ TimeRange,
21
+ type TimeStamp,
22
+ type CrudeDataType,
23
+ } from "@/telem/telem";
24
+ import { compare } from "@/compare";
25
+
26
+ export type SampleValue = number | bigint;
27
+
28
+ const validateFieldNotNull = (name: string, field: unknown): void => {
29
+ if (field == null) {
30
+ throw new Error(`field ${name} is null`);
31
+ }
32
+ };
33
+
34
+ interface GL {
35
+ control: GLBufferController | null;
36
+ buffer: WebGLBuffer | null;
37
+ prevBuffer: number;
38
+ bufferUsage: GLBufferUsage;
39
+ }
40
+
41
+ export interface SeriesDigest {
42
+ dataType: string;
43
+ sampleOffset: SampleValue;
44
+ alignment: number;
45
+ timeRange?: string;
46
+ length: number;
47
+ }
48
+
49
+ const FULL_BUFFER = -1;
50
+
51
+ /**
52
+ * Series is a strongly typed array of telemetry samples backed by an underlying binary
53
+ * buffer.
54
+ */
55
+ export class Series {
56
+ /** The data type of the array */
57
+ readonly dataType: DataType;
58
+ /**
59
+ * A sample offset that can be used to shift the values of all samples upwards or
60
+ * downwards. Typically used to convert arrays to lower precision while preserving
61
+ * the relative range of actual values.
62
+ */
63
+ sampleOffset: SampleValue;
64
+ /**
65
+ * Stores information about the buffer state of this array into a WebGL buffer.
66
+ */
67
+ private readonly gl: GL;
68
+ /** The underlying data. */
69
+ private readonly _data: ArrayBufferLike;
70
+ readonly _timeRange?: TimeRange;
71
+ readonly alignment: number = 0;
72
+ /** A cached minimum value. */
73
+ private _min?: SampleValue;
74
+ /** A cached maximum value. */
75
+ private _max?: SampleValue;
76
+ /** The write position of the buffer. */
77
+ private writePos: number = FULL_BUFFER;
78
+ /** Tracks the number of entities currently using this array. */
79
+ private _refCount: number = 0;
80
+
81
+ static alloc(
82
+ length: number,
83
+ dataType: CrudeDataType,
84
+ timeRange?: TimeRange,
85
+ sampleOffset?: SampleValue,
86
+ glBufferUsage: GLBufferUsage = "static",
87
+ alignment: number = 0,
88
+ ): Series {
89
+ if (length === 0)
90
+ throw new Error("[Series] - cannot allocate an array of length 0");
91
+ const data = new new DataType(dataType).Array(length);
92
+ const arr = new Series(
93
+ data.buffer,
94
+ dataType,
95
+ timeRange,
96
+ sampleOffset,
97
+ glBufferUsage,
98
+ alignment,
99
+ );
100
+ arr.writePos = 0;
101
+ return arr;
102
+ }
103
+
104
+ static generateTimestamps(length: number, rate: Rate, start: TimeStamp): Series {
105
+ const tr = start.spanRange(rate.span(length));
106
+ const data = new BigInt64Array(length);
107
+ for (let i = 0; i < length; i++) {
108
+ data[i] = BigInt(start.add(rate.span(i)).valueOf());
109
+ }
110
+ return new Series(data, DataType.TIMESTAMP, tr);
111
+ }
112
+
113
+ get refCount(): number {
114
+ return this._refCount;
115
+ }
116
+
117
+ static fromStrings(data: string[], timeRange?: TimeRange): Series {
118
+ const buffer = new TextEncoder().encode(data.join("\n") + "\n");
119
+ return new Series(buffer, DataType.STRING, timeRange);
120
+ }
121
+
122
+ static fromJSON<T>(data: T[], timeRange?: TimeRange): Series {
123
+ const buffer = new TextEncoder().encode(
124
+ data.map((d) => JSON.stringify(d)).join("\n") + "\n",
125
+ );
126
+ return new Series(buffer, DataType.JSON, timeRange);
127
+ }
128
+
129
+ constructor(
130
+ data: ArrayBuffer | NativeTypedArray,
131
+ dataType?: CrudeDataType,
132
+ timeRange?: TimeRange,
133
+ sampleOffset?: SampleValue,
134
+ glBufferUsage: GLBufferUsage = "static",
135
+ alignment: number = 0,
136
+ ) {
137
+ if (dataType == null && !(data instanceof ArrayBuffer)) {
138
+ this.dataType = new DataType(data);
139
+ } else if (dataType != null) {
140
+ this.dataType = new DataType(dataType);
141
+ } else {
142
+ throw new Error(
143
+ "must provide a data type when constructing a Series from a buffer",
144
+ );
145
+ }
146
+ this.alignment = alignment;
147
+ this.sampleOffset = sampleOffset ?? 0;
148
+ this._data = data;
149
+ this._timeRange = timeRange;
150
+ this.gl = {
151
+ control: null,
152
+ buffer: null,
153
+ prevBuffer: 0,
154
+ bufferUsage: glBufferUsage,
155
+ };
156
+ }
157
+
158
+ acquire(gl?: GLBufferController): void {
159
+ this._refCount++;
160
+ if (gl != null) this.updateGLBuffer(gl);
161
+ }
162
+
163
+ release(): void {
164
+ this._refCount--;
165
+ if (this._refCount === 0 && this.gl.control != null)
166
+ this.maybeGarbageCollectGLBuffer(this.gl.control);
167
+ else if (this._refCount < 0)
168
+ throw new Error("cannot release an array with a negative reference count");
169
+ }
170
+
171
+ write(other: Series): number {
172
+ if (!other.dataType.equals(this.dataType))
173
+ throw new Error("buffer must be of the same type as this array");
174
+
175
+ // We've filled the entire underlying buffer
176
+ if (this.writePos === FULL_BUFFER) return 0;
177
+ const available = this.cap - this.writePos;
178
+
179
+ const toWrite = available < other.length ? other.slice(0, available) : other;
180
+ this.underlyingData.set(toWrite.data as any, this.writePos);
181
+ this.maybeRecomputeMinMax(toWrite);
182
+ this.writePos += toWrite.length;
183
+ return toWrite.length;
184
+ }
185
+
186
+ /** @returns the underlying buffer backing this array. */
187
+ get buffer(): ArrayBufferLike {
188
+ return this._data;
189
+ }
190
+
191
+ private get underlyingData(): NativeTypedArray {
192
+ return new this.dataType.Array(this._data);
193
+ }
194
+
195
+ /** @returns a native typed array with the proper data type. */
196
+ get data(): NativeTypedArray {
197
+ if (this.writePos === FULL_BUFFER) return this.underlyingData;
198
+ return new this.dataType.Array(this._data, 0, this.writePos);
199
+ }
200
+
201
+ toStrings(): string[] {
202
+ if (!this.dataType.equals(DataType.STRING))
203
+ throw new Error("cannot convert non-string series to strings");
204
+ return new TextDecoder().decode(this.buffer).split("\n").slice(0, -1);
205
+ }
206
+
207
+ parseJSON<Z extends z.ZodTypeAny>(schema: Z): Array<z.output<Z>> {
208
+ if (!this.dataType.equals(DataType.JSON))
209
+ throw new Error("cannot convert non-string series to strings");
210
+ return new TextDecoder()
211
+ .decode(this.buffer)
212
+ .split("\n")
213
+ .slice(0, -1)
214
+ .map((s) => schema.parse(JSON.parse(s)));
215
+ }
216
+
217
+ /** @returns the time range of this array. */
218
+ get timeRange(): TimeRange {
219
+ validateFieldNotNull("timeRange", this._timeRange);
220
+ return this._timeRange as TimeRange;
221
+ }
222
+
223
+ /** @returns the capacity of the underlying buffer in bytes. */
224
+ get byteCap(): Size {
225
+ return new Size(this.buffer.byteLength);
226
+ }
227
+
228
+ /** @returns the capacity of the underlying buffer in samples. */
229
+ get cap(): number {
230
+ return this.dataType.density.length(this.byteCap);
231
+ }
232
+
233
+ /** @returns the length of the underlying buffer in samples. */
234
+ get byteLength(): Size {
235
+ if (this.writePos === FULL_BUFFER) return this.byteCap;
236
+ return this.dataType.density.size(this.writePos);
237
+ }
238
+
239
+ /** @returns the number of samples in this array. */
240
+ get length(): number {
241
+ if (this.writePos === FULL_BUFFER) return this.data.length;
242
+ return this.writePos;
243
+ }
244
+
245
+ /**
246
+ * Creates a new array with a different data type.
247
+ * @param target the data type to convert to.
248
+ * @param sampleOffset an offset to apply to each sample. This can help with precision
249
+ * issues when converting between data types.
250
+ *
251
+ * WARNING: This method is expensive and copies the entire underlying array. There
252
+ * also may be untimely precision issues when converting between data types.
253
+ */
254
+ convert(target: DataType, sampleOffset: SampleValue = 0): Series {
255
+ if (this.dataType.equals(target)) return this;
256
+ const data = new target.Array(this.length);
257
+ for (let i = 0; i < this.length; i++) {
258
+ data[i] = convertDataType(this.dataType, target, this.data[i], sampleOffset);
259
+ }
260
+ return new Series(
261
+ data.buffer,
262
+ target,
263
+ this._timeRange,
264
+ sampleOffset,
265
+ this.gl.bufferUsage,
266
+ this.alignment,
267
+ );
268
+ }
269
+
270
+ private calcRawMax(): SampleValue {
271
+ if (this.length === 0) return -Infinity;
272
+ if (this.dataType.equals(DataType.TIMESTAMP)) {
273
+ this._max = this.data[this.data.length - 1];
274
+ } else if (this.dataType.usesBigInt) {
275
+ const d = this.data as BigInt64Array;
276
+ this._max = d.reduce((a, b) => (a > b ? a : b));
277
+ } else {
278
+ const d = this.data as Float64Array;
279
+ this._max = d.reduce((a, b) => (a > b ? a : b));
280
+ }
281
+ return this._max;
282
+ }
283
+
284
+ /** @returns the maximum value in the array */
285
+ get max(): SampleValue {
286
+ if (this.writePos === 0) return -Infinity;
287
+ else if (this._max == null) this._max = this.calcRawMax();
288
+ return addSamples(this._max, this.sampleOffset);
289
+ }
290
+
291
+ private calcRawMin(): SampleValue {
292
+ if (this.length === 0) return Infinity;
293
+ if (this.dataType.equals(DataType.TIMESTAMP)) {
294
+ this._min = this.data[0];
295
+ } else if (this.dataType.usesBigInt) {
296
+ const d = this.data as BigInt64Array;
297
+ this._min = d.reduce((a, b) => (a < b ? a : b));
298
+ } else {
299
+ const d = this.data as Float64Array;
300
+ this._min = d.reduce((a, b) => (a < b ? a : b));
301
+ }
302
+ return this._min;
303
+ }
304
+
305
+ /** @returns the minimum value in the array */
306
+ get min(): SampleValue {
307
+ if (this.writePos === 0) return Infinity;
308
+ else if (this._min == null) this._min = this.calcRawMin();
309
+ return addSamples(this._min, this.sampleOffset);
310
+ }
311
+
312
+ /** @returns the bounds of this array. */
313
+ get bounds(): bounds.Bounds {
314
+ return bounds.construct(Number(this.min), Number(this.max));
315
+ }
316
+
317
+ private maybeRecomputeMinMax(update: Series): void {
318
+ if (this._min != null) {
319
+ const min = update._min ?? update.calcRawMin();
320
+ if (min < this._min) this._min = min;
321
+ }
322
+ if (this._max != null) {
323
+ const max = update._max ?? update.calcRawMax();
324
+ if (max > this._max) this._max = max;
325
+ }
326
+ }
327
+
328
+ enrich(): void {
329
+ let _ = this.max;
330
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
331
+ _ = this.min;
332
+ }
333
+
334
+ get range(): SampleValue {
335
+ return addSamples(this.max, -this.min);
336
+ }
337
+
338
+ at(index: number): SampleValue {
339
+ const v = this.data[index];
340
+ if (v == null) return undefined as any;
341
+ return addSamples(v, this.sampleOffset);
342
+ }
343
+
344
+ /**
345
+ * @returns the index of the first sample that is greater than or equal to the given value.
346
+ * The underlying array must be sorted. If it is not, the behavior of this method is undefined.
347
+ * @param value the value to search for.
348
+ */
349
+ binarySearch(value: SampleValue): number {
350
+ let left = 0;
351
+ let right = this.length - 1;
352
+ const cf = compare.newF(value);
353
+ while (left <= right) {
354
+ const mid = Math.floor((left + right) / 2);
355
+ const cmp = cf(this.at(mid), value);
356
+ if (cmp === 0) return mid;
357
+ if (cmp < 0) left = mid + 1;
358
+ else right = mid - 1;
359
+ }
360
+ return left;
361
+ }
362
+
363
+ updateGLBuffer(gl: GLBufferController): void {
364
+ this.gl.control = gl;
365
+ if (!this.dataType.equals(DataType.FLOAT32))
366
+ throw new Error("Only FLOAT32 arrays can be used in WebGL");
367
+ const { buffer, bufferUsage, prevBuffer } = this.gl;
368
+
369
+ // If no buffer has been created yet, create one.
370
+ if (buffer == null) this.gl.buffer = gl.createBuffer();
371
+ // If the current write position is the same as the previous buffer, we're already
372
+ // up date.
373
+ if (this.writePos === prevBuffer) return;
374
+
375
+ // Bind the buffer.
376
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.gl.buffer);
377
+
378
+ // This means we only need to buffer part of the array.
379
+ if (this.writePos !== FULL_BUFFER) {
380
+ if (prevBuffer === 0) {
381
+ gl.bufferData(gl.ARRAY_BUFFER, this.byteCap.valueOf(), gl.STATIC_DRAW);
382
+ }
383
+ const byteOffset = this.dataType.density.size(prevBuffer).valueOf();
384
+ const slice = this.underlyingData.slice(this.gl.prevBuffer, this.writePos);
385
+ gl.bufferSubData(gl.ARRAY_BUFFER, byteOffset, slice.buffer);
386
+ this.gl.prevBuffer = this.writePos;
387
+ } else {
388
+ // This means we can buffer the entire array in a single go.
389
+ gl.bufferData(
390
+ gl.ARRAY_BUFFER,
391
+ this.buffer,
392
+ bufferUsage === "static" ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW,
393
+ );
394
+ this.gl.prevBuffer = FULL_BUFFER;
395
+ }
396
+ }
397
+
398
+ get digest(): SeriesDigest {
399
+ return {
400
+ dataType: this.dataType.toString(),
401
+ sampleOffset: this.sampleOffset,
402
+ alignment: this.alignment,
403
+ timeRange: this._timeRange?.toString(),
404
+ length: this.length,
405
+ };
406
+ }
407
+
408
+ private maybeGarbageCollectGLBuffer(gl: GLBufferController): void {
409
+ if (this.gl.buffer == null) return;
410
+ gl.deleteBuffer(this.gl.buffer);
411
+ this.gl.buffer = null;
412
+ this.gl.prevBuffer = 0;
413
+ }
414
+
415
+ get glBuffer(): WebGLBuffer {
416
+ if (this.gl.buffer == null) throw new Error("gl buffer not initialized");
417
+ if (!(this.gl.prevBuffer === this.writePos)) console.warn("buffer not updated");
418
+ return this.gl.buffer;
419
+ }
420
+
421
+ slice(start: number, end?: number): Series {
422
+ const d = this.data.slice(start, end);
423
+ return new Series(
424
+ d,
425
+ this.dataType,
426
+ TimeRange.ZERO,
427
+ this.sampleOffset,
428
+ this.gl.bufferUsage,
429
+ this.alignment + start,
430
+ );
431
+ }
432
+
433
+ reAlign(alignment: number): Series {
434
+ return new Series(
435
+ this.buffer,
436
+ this.dataType,
437
+ TimeRange.ZERO,
438
+ this.sampleOffset,
439
+ "static",
440
+ alignment,
441
+ );
442
+ }
443
+ }
444
+
445
+ export const addSamples = (a: SampleValue, b: SampleValue): SampleValue => {
446
+ if (typeof a === "bigint" && typeof b === "bigint") return a + b;
447
+ if (typeof a === "number" && typeof b === "number") return a + b;
448
+ return Number(a) + Number(b);
449
+ };