@synnaxlabs/x 0.11.0 → 0.12.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.
- package/.turbo/turbo-build.log +5 -5
- package/dist/telem/series.d.ts +17 -8
- package/dist/x.cjs.js +26 -13
- package/dist/x.cjs.js.map +1 -1
- package/dist/x.es.js +26 -13
- package/dist/x.es.js.map +1 -1
- package/package.json +3 -3
- package/src/deep/memo.ts +10 -1
- package/src/shallowCopy.ts +10 -1
- package/src/telem/series.spec.ts +37 -19
- package/src/telem/series.ts +30 -14
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synnaxlabs/x",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Common Utilities for Synnax Labs",
|
|
7
7
|
"repository": "https://github.com/synnaxlabs/synnax/tree/main/x/go",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"@vitest/coverage-v8": "^1.2.2",
|
|
27
27
|
"vite": "^5.1.2",
|
|
28
28
|
"vitest": "^1.2.2",
|
|
29
|
-
"@synnaxlabs/
|
|
29
|
+
"@synnaxlabs/vite-plugin": "0.0.1",
|
|
30
30
|
"eslint-config-synnaxlabs": "0.0.1",
|
|
31
|
-
"@synnaxlabs/
|
|
31
|
+
"@synnaxlabs/tsconfig": "0.0.2"
|
|
32
32
|
},
|
|
33
33
|
"main": "dist/x.cjs.js",
|
|
34
34
|
"module": "dist/x.es.js",
|
package/src/deep/memo.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// Copyright 2024 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
|
+
|
|
1
10
|
import { equal } from "@/deep/equal"
|
|
2
11
|
|
|
3
12
|
|
|
@@ -12,4 +21,4 @@ export const memo = <F extends (...args: any[]) => any>(func: F): F => {
|
|
|
12
21
|
return result
|
|
13
22
|
})
|
|
14
23
|
return v as F;
|
|
15
|
-
}
|
|
24
|
+
}
|
package/src/shallowCopy.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
|
|
2
|
+
// Copyright 2024 Synnax Labs, Inc.
|
|
3
|
+
//
|
|
4
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
5
|
+
// licenses/BSL.txt.
|
|
6
|
+
//
|
|
7
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
8
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
9
|
+
// included in the file licenses/APL.txt.
|
|
10
|
+
|
|
2
11
|
export const shallowCopy = <T extends unknown>(obj: T): T => {
|
|
3
12
|
if (Array.isArray(obj)) return [...obj] as T;
|
|
4
13
|
if (typeof obj === "object" && obj !== null) return { ...obj } as T;
|
|
5
14
|
return obj;
|
|
6
|
-
}
|
|
15
|
+
}
|
package/src/telem/series.spec.ts
CHANGED
|
@@ -21,8 +21,8 @@ describe("Series", () => {
|
|
|
21
21
|
expect(a.dataType.toString()).toBe(DataType.FLOAT32.toString());
|
|
22
22
|
expect(a.length).toEqual(3);
|
|
23
23
|
expect(a.byteLength).toEqual(Size.bytes(12));
|
|
24
|
-
expect(a.
|
|
25
|
-
expect(a.
|
|
24
|
+
expect(a.byteCapacity).toEqual(Size.bytes(12));
|
|
25
|
+
expect(a.capacity).toEqual(3);
|
|
26
26
|
const b = new Series({ data: new BigInt64Array([BigInt(1)]) });
|
|
27
27
|
expect(b.dataType.toString()).toBe(DataType.INT64.toString());
|
|
28
28
|
const c = new Series({
|
|
@@ -55,15 +55,15 @@ describe("Series", () => {
|
|
|
55
55
|
|
|
56
56
|
describe("allocation", () => {
|
|
57
57
|
it("should allocate a lazy array", () => {
|
|
58
|
-
const series = Series.alloc({
|
|
59
|
-
expect(series.
|
|
60
|
-
expect(series.
|
|
58
|
+
const series = Series.alloc({ capacity: 10, dataType: DataType.FLOAT32 });
|
|
59
|
+
expect(series.byteCapacity).toEqual(Size.bytes(40));
|
|
60
|
+
expect(series.capacity).toEqual(10);
|
|
61
61
|
expect(series.length).toEqual(0);
|
|
62
62
|
expect(series.byteLength).toEqual(Size.bytes(0));
|
|
63
63
|
});
|
|
64
64
|
it("should throw an error when attempting to allocate an array of lenght 0", () => {
|
|
65
65
|
expect(() => {
|
|
66
|
-
Series.alloc({
|
|
66
|
+
Series.alloc({ capacity: 0, dataType: DataType.FLOAT32 });
|
|
67
67
|
}).toThrow();
|
|
68
68
|
});
|
|
69
69
|
});
|
|
@@ -87,8 +87,26 @@ describe("Series", () => {
|
|
|
87
87
|
});
|
|
88
88
|
expect(series.at(3)).toBeUndefined();
|
|
89
89
|
});
|
|
90
|
+
it("should allow the index to be negative", () => {
|
|
91
|
+
const series = new Series({
|
|
92
|
+
data: new Float32Array([1, 2, 3]),
|
|
93
|
+
dataType: DataType.FLOAT32,
|
|
94
|
+
});
|
|
95
|
+
expect(series.at(-1)).toEqual(3);
|
|
96
|
+
});
|
|
97
|
+
it("should throw an error when the index is out of bounds and require is set to true", () => {
|
|
98
|
+
const series = new Series({
|
|
99
|
+
data: new Float32Array([1, 2, 3]),
|
|
100
|
+
dataType: DataType.FLOAT32,
|
|
101
|
+
});
|
|
102
|
+
expect(() => {
|
|
103
|
+
series.at(3, true);
|
|
104
|
+
}).toThrow();
|
|
105
|
+
});
|
|
90
106
|
});
|
|
91
107
|
|
|
108
|
+
|
|
109
|
+
|
|
92
110
|
describe("slice", () => {
|
|
93
111
|
it("should slice a lazy array", () => {
|
|
94
112
|
const a = new Series({
|
|
@@ -100,14 +118,14 @@ describe("Series", () => {
|
|
|
100
118
|
expect(b.data).toEqual(new Float32Array([2]));
|
|
101
119
|
expect(b.length).toEqual(1);
|
|
102
120
|
expect(b.byteLength).toEqual(Size.bytes(4));
|
|
103
|
-
expect(b.
|
|
104
|
-
expect(b.
|
|
121
|
+
expect(b.byteCapacity).toEqual(Size.bytes(4));
|
|
122
|
+
expect(b.capacity).toEqual(1);
|
|
105
123
|
});
|
|
106
124
|
});
|
|
107
125
|
|
|
108
126
|
describe("min and max", () => {
|
|
109
127
|
it("should return a min and max of zero on an allocated array", () => {
|
|
110
|
-
const series = Series.alloc({
|
|
128
|
+
const series = Series.alloc({ capacity: 10, dataType: DataType.FLOAT32 });
|
|
111
129
|
expect(series.max).toEqual(-Infinity);
|
|
112
130
|
expect(series.min).toEqual(Infinity);
|
|
113
131
|
});
|
|
@@ -154,8 +172,8 @@ describe("Series", () => {
|
|
|
154
172
|
|
|
155
173
|
describe("writing", () => {
|
|
156
174
|
it("should correctly write to an allocated lazy array", () => {
|
|
157
|
-
const series = Series.alloc({
|
|
158
|
-
expect(series.
|
|
175
|
+
const series = Series.alloc({ capacity: 10, dataType: DataType.FLOAT32 });
|
|
176
|
+
expect(series.byteCapacity).toEqual(Size.bytes(40));
|
|
159
177
|
expect(series.length).toEqual(0);
|
|
160
178
|
const writeOne = new Series({ data: new Float32Array([1]) });
|
|
161
179
|
expect(series.write(writeOne)).toEqual(1);
|
|
@@ -165,7 +183,7 @@ describe("Series", () => {
|
|
|
165
183
|
expect(series.length).toEqual(3);
|
|
166
184
|
});
|
|
167
185
|
it("should recompute cached max and min correctly", () => {
|
|
168
|
-
const series = Series.alloc({
|
|
186
|
+
const series = Series.alloc({ capacity: 10, dataType: DataType.FLOAT32 });
|
|
169
187
|
series.enrich();
|
|
170
188
|
const writeTwo = new Series({ data: new Float32Array([2, 3]) });
|
|
171
189
|
series.write(writeTwo);
|
|
@@ -174,7 +192,7 @@ describe("Series", () => {
|
|
|
174
192
|
});
|
|
175
193
|
it("should correctly adjust the sample offset of a written array", () => {
|
|
176
194
|
const series = Series.alloc({
|
|
177
|
-
|
|
195
|
+
capacity: 2,
|
|
178
196
|
dataType: DataType.FLOAT32,
|
|
179
197
|
timeRange: TimeRange.ZERO,
|
|
180
198
|
sampleOffset: -3,
|
|
@@ -200,7 +218,7 @@ describe("Series", () => {
|
|
|
200
218
|
expect(ts.timeRange).toEqual(
|
|
201
219
|
new TimeRange(TimeStamp.seconds(1), TimeStamp.seconds(6)),
|
|
202
220
|
);
|
|
203
|
-
expect(ts.
|
|
221
|
+
expect(ts.capacity).toEqual(5);
|
|
204
222
|
expect(ts.length).toEqual(5);
|
|
205
223
|
expect(ts.dataType.toString()).toEqual(DataType.TIMESTAMP.toString());
|
|
206
224
|
expect(ts.data).toEqual(
|
|
@@ -233,7 +251,7 @@ describe("Series", () => {
|
|
|
233
251
|
expect(buf).toEqual(new Float32Array([1, 2, 3]));
|
|
234
252
|
});
|
|
235
253
|
it("should correctly update a buffer when writing to an allocated array", () => {
|
|
236
|
-
const series = Series.alloc({
|
|
254
|
+
const series = Series.alloc({ capacity: 10, dataType: DataType.FLOAT32 });
|
|
237
255
|
const controller = new MockGLBufferController();
|
|
238
256
|
series.updateGLBuffer(controller);
|
|
239
257
|
expect(controller.createBufferMock).toHaveBeenCalledTimes(1);
|
|
@@ -249,7 +267,7 @@ describe("Series", () => {
|
|
|
249
267
|
expect(controller.bufferDataMock).toHaveBeenCalledTimes(1);
|
|
250
268
|
expect(controller.bufferSubDataMock).toHaveBeenCalledTimes(1);
|
|
251
269
|
buf = controller.buffers[series.glBuffer as number];
|
|
252
|
-
expect(buf.byteLength).toEqual(series.
|
|
270
|
+
expect(buf.byteLength).toEqual(series.byteCapacity.valueOf());
|
|
253
271
|
expect(new Float32Array(buf)[0]).toEqual(1);
|
|
254
272
|
const writeTwo = new Series({ data: new Float32Array([2, 3]) });
|
|
255
273
|
series.write(writeTwo);
|
|
@@ -257,7 +275,7 @@ describe("Series", () => {
|
|
|
257
275
|
expect(controller.bufferDataMock).not.toHaveBeenCalledTimes(2);
|
|
258
276
|
expect(controller.bufferSubDataMock).toHaveBeenCalledTimes(2);
|
|
259
277
|
buf = controller.buffers[series.glBuffer as number];
|
|
260
|
-
expect(buf.byteLength).toEqual(series.
|
|
278
|
+
expect(buf.byteLength).toEqual(series.byteCapacity.valueOf());
|
|
261
279
|
expect(new Float32Array(buf)[0]).toEqual(1);
|
|
262
280
|
expect(new Float32Array(buf)[1]).toEqual(2);
|
|
263
281
|
expect(new Float32Array(buf)[2]).toEqual(3);
|
|
@@ -286,7 +304,7 @@ describe("Series", () => {
|
|
|
286
304
|
series.acquire(controller);
|
|
287
305
|
expect(controller.createBufferMock).toHaveBeenCalledTimes(2);
|
|
288
306
|
const buf = controller.buffers[series.glBuffer as number];
|
|
289
|
-
expect(buf.byteLength).toEqual(series.
|
|
307
|
+
expect(buf.byteLength).toEqual(series.byteCapacity.valueOf());
|
|
290
308
|
});
|
|
291
309
|
});
|
|
292
310
|
|
|
@@ -341,7 +359,7 @@ describe("Series", () => {
|
|
|
341
359
|
|
|
342
360
|
describe("binarySearch", () => {
|
|
343
361
|
it("should correctly binary search a pre-allocated array", () => {
|
|
344
|
-
const series = Series.alloc({
|
|
362
|
+
const series = Series.alloc({ capacity: 10, dataType: DataType.FLOAT32 });
|
|
345
363
|
const writeOne = new Series({ data: new Float32Array([1, 2, 3, 4, 5]) });
|
|
346
364
|
series.write(writeOne);
|
|
347
365
|
expect(series.binarySearch(3)).toEqual(2);
|
package/src/telem/series.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface SeriesProps extends BaseSeriesProps {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export interface SeriesAllocProps extends BaseSeriesProps {
|
|
65
|
-
|
|
65
|
+
capacity: number;
|
|
66
66
|
dataType: CrudeDataType;
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -106,7 +106,7 @@ export class Series {
|
|
|
106
106
|
/** Tracks the number of entities currently using this array. */
|
|
107
107
|
private _refCount: number = 0;
|
|
108
108
|
|
|
109
|
-
static alloc({ length, dataType, ...props }: SeriesAllocProps): Series {
|
|
109
|
+
static alloc({ capacity: length, dataType, ...props }: SeriesAllocProps): Series {
|
|
110
110
|
if (length === 0)
|
|
111
111
|
throw new Error("[Series] - cannot allocate an array of length 0");
|
|
112
112
|
const data = new new DataType(dataType).Array(length);
|
|
@@ -188,13 +188,21 @@ export class Series {
|
|
|
188
188
|
throw new Error("cannot release an array with a negative reference count");
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Writes the given series to this series. If the series being written exceeds the
|
|
193
|
+
* remaining of series being written to, only the portion that fits will be written.
|
|
194
|
+
* @param other the series to write to this series. The data type of the series written
|
|
195
|
+
* must be the same as the data type of the series being written to.
|
|
196
|
+
* @returns the number of samples written. If the entire series fits, this value is
|
|
197
|
+
* equal to the length of the series being written.
|
|
198
|
+
*/
|
|
191
199
|
write(other: Series): number {
|
|
192
200
|
if (!other.dataType.equals(this.dataType))
|
|
193
201
|
throw new Error("buffer must be of the same type as this array");
|
|
194
202
|
|
|
195
203
|
// We've filled the entire underlying buffer
|
|
196
204
|
if (this.writePos === FULL_BUFFER) return 0;
|
|
197
|
-
const available = this.
|
|
205
|
+
const available = this.capacity - this.writePos;
|
|
198
206
|
|
|
199
207
|
const toWrite = available < other.length ? other.slice(0, available) : other;
|
|
200
208
|
this.underlyingData.set(toWrite.data as any, this.writePos);
|
|
@@ -256,19 +264,19 @@ export class Series {
|
|
|
256
264
|
return this._timeRange!;
|
|
257
265
|
}
|
|
258
266
|
|
|
259
|
-
/** @returns the capacity of the
|
|
260
|
-
get
|
|
267
|
+
/** @returns the capacity of the series in bytes. */
|
|
268
|
+
get byteCapacity(): Size {
|
|
261
269
|
return new Size(this.buffer.byteLength);
|
|
262
270
|
}
|
|
263
271
|
|
|
264
|
-
/** @returns the capacity of the
|
|
265
|
-
get
|
|
266
|
-
return this.dataType.density.length(this.
|
|
272
|
+
/** @returns the capacity of the series in samples. */
|
|
273
|
+
get capacity(): number {
|
|
274
|
+
return this.dataType.density.length(this.byteCapacity);
|
|
267
275
|
}
|
|
268
276
|
|
|
269
|
-
/** @returns the length of the
|
|
277
|
+
/** @returns the length of the series in bytes. */
|
|
270
278
|
get byteLength(): Size {
|
|
271
|
-
if (this.writePos === FULL_BUFFER) return this.
|
|
279
|
+
if (this.writePos === FULL_BUFFER) return this.byteCapacity;
|
|
272
280
|
return this.dataType.density.size(this.writePos);
|
|
273
281
|
}
|
|
274
282
|
|
|
@@ -371,9 +379,17 @@ export class Series {
|
|
|
371
379
|
return addSamples(this.max, -this.min);
|
|
372
380
|
}
|
|
373
381
|
|
|
374
|
-
at(index: number): SampleValue
|
|
382
|
+
at(index: number, required: true): SampleValue;
|
|
383
|
+
|
|
384
|
+
at(index: number, required?: false): SampleValue | undefined;
|
|
385
|
+
|
|
386
|
+
at(index: number, required?: boolean): SampleValue | undefined {
|
|
387
|
+
if (index < 0) index = this.length + index;
|
|
375
388
|
const v = this.data[index];
|
|
376
|
-
if (v == null)
|
|
389
|
+
if (v == null) {
|
|
390
|
+
if (required) throw new Error(`[series] - no value at index ${index}`);
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
377
393
|
return addSamples(v, this.sampleOffset);
|
|
378
394
|
}
|
|
379
395
|
|
|
@@ -388,7 +404,7 @@ export class Series {
|
|
|
388
404
|
const cf = compare.newF(value);
|
|
389
405
|
while (left <= right) {
|
|
390
406
|
const mid = Math.floor((left + right) / 2);
|
|
391
|
-
const cmp = cf(this.at(mid), value);
|
|
407
|
+
const cmp = cf(this.at(mid, true), value);
|
|
392
408
|
if (cmp === 0) return mid;
|
|
393
409
|
if (cmp < 0) left = mid + 1;
|
|
394
410
|
else right = mid - 1;
|
|
@@ -414,7 +430,7 @@ export class Series {
|
|
|
414
430
|
// This means we only need to buffer part of the array.
|
|
415
431
|
if (this.writePos !== FULL_BUFFER) {
|
|
416
432
|
if (prevBuffer === 0) {
|
|
417
|
-
gl.bufferData(gl.ARRAY_BUFFER, this.
|
|
433
|
+
gl.bufferData(gl.ARRAY_BUFFER, this.byteCapacity.valueOf(), gl.STATIC_DRAW);
|
|
418
434
|
}
|
|
419
435
|
const byteOffset = this.dataType.density.size(prevBuffer).valueOf();
|
|
420
436
|
const slice = this.underlyingData.slice(this.gl.prevBuffer, this.writePos);
|