@danielsimonjr/mathts-functions 0.1.2 → 0.2.2
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/dist/chunk-KX4KB4LV.js +644 -0
- package/dist/index.js +20267 -7022
- package/dist/wasm-bridge-G57UBVKZ.js +18 -0
- package/package.json +7 -7
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/wasm/integrity.ts
|
|
8
|
+
var ALGO = "sha384";
|
|
9
|
+
async function sha384OfBuffer(buffer) {
|
|
10
|
+
const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
11
|
+
const isNode = typeof process !== "undefined" && process.versions?.node !== void 0;
|
|
12
|
+
let digest;
|
|
13
|
+
if (isNode) {
|
|
14
|
+
const { createHash } = await import("crypto");
|
|
15
|
+
const hash = createHash(ALGO);
|
|
16
|
+
hash.update(bytes);
|
|
17
|
+
digest = new Uint8Array(hash.digest());
|
|
18
|
+
} else {
|
|
19
|
+
const copy = new ArrayBuffer(bytes.byteLength);
|
|
20
|
+
new Uint8Array(copy).set(bytes);
|
|
21
|
+
const out = await crypto.subtle.digest("SHA-384", copy);
|
|
22
|
+
digest = new Uint8Array(out);
|
|
23
|
+
}
|
|
24
|
+
let binary = "";
|
|
25
|
+
for (let i = 0; i < digest.length; i++) binary += String.fromCharCode(digest[i]);
|
|
26
|
+
const b64 = typeof btoa === "function" ? btoa(binary) : Buffer.from(digest).toString("base64");
|
|
27
|
+
return `sha384-${b64}`;
|
|
28
|
+
}
|
|
29
|
+
async function loadWasmManifest(wasmPath) {
|
|
30
|
+
const isNode = typeof process !== "undefined" && process.versions?.node !== void 0;
|
|
31
|
+
const manifestPath = wasmPath.replace(/[^/\\]+$/, "wasm-manifest.json");
|
|
32
|
+
try {
|
|
33
|
+
if (isNode) {
|
|
34
|
+
const fs = await import("fs");
|
|
35
|
+
const { promisify } = await import("util");
|
|
36
|
+
const readFile = promisify(fs.readFile);
|
|
37
|
+
const text = await readFile(manifestPath, "utf8");
|
|
38
|
+
return JSON.parse(text);
|
|
39
|
+
} else {
|
|
40
|
+
const res = await fetch(manifestPath);
|
|
41
|
+
if (!res.ok) return null;
|
|
42
|
+
return await res.json();
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function verifyWasmIntegrity(buffer, wasmPath, options = {}) {
|
|
49
|
+
const fileName = wasmPath.split(/[/\\]/).pop() || wasmPath;
|
|
50
|
+
const manifest = options.manifest !== void 0 ? options.manifest : await loadWasmManifest(wasmPath);
|
|
51
|
+
if (!manifest) {
|
|
52
|
+
if (options.required) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`WASM integrity check failed: no manifest at sibling of "${wasmPath}" (required)`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (typeof console !== "undefined") {
|
|
58
|
+
console.warn(
|
|
59
|
+
`[wasm-integrity] no manifest found beside "${wasmPath}"; skipping SHA-384 verification (set options.required=true to fail closed)`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const expected = manifest[fileName];
|
|
65
|
+
if (!expected) {
|
|
66
|
+
if (options.required) {
|
|
67
|
+
throw new Error(`WASM integrity check failed: manifest has no entry for "${fileName}"`);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const actual = await sha384OfBuffer(buffer);
|
|
72
|
+
if (actual !== expected) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`WASM integrity check failed for "${fileName}": expected ${expected}, got ${actual}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/wasm/WasmLoader.ts
|
|
80
|
+
var WasmLoader = class _WasmLoader {
|
|
81
|
+
static instance = null;
|
|
82
|
+
wasmModule = null;
|
|
83
|
+
compiledModule = null;
|
|
84
|
+
loading = null;
|
|
85
|
+
isNode;
|
|
86
|
+
lastMetrics = null;
|
|
87
|
+
// Memory pool for reusable allocations
|
|
88
|
+
float64Pool = [];
|
|
89
|
+
int32Pool = [];
|
|
90
|
+
maxPoolSize = 32;
|
|
91
|
+
poolSizeThreshold = 1024 * 1024;
|
|
92
|
+
// 1MB max per pool entry
|
|
93
|
+
constructor() {
|
|
94
|
+
this.isNode = typeof process !== "undefined" && process.versions?.node !== void 0;
|
|
95
|
+
}
|
|
96
|
+
static getInstance() {
|
|
97
|
+
if (!_WasmLoader.instance) {
|
|
98
|
+
_WasmLoader.instance = new _WasmLoader();
|
|
99
|
+
}
|
|
100
|
+
return _WasmLoader.instance;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Load the WASM module
|
|
104
|
+
*/
|
|
105
|
+
async load(wasmPath) {
|
|
106
|
+
if (this.wasmModule) {
|
|
107
|
+
return this.wasmModule;
|
|
108
|
+
}
|
|
109
|
+
if (this.loading) {
|
|
110
|
+
return this.loading;
|
|
111
|
+
}
|
|
112
|
+
this.loading = this.loadModule(wasmPath);
|
|
113
|
+
this.wasmModule = await this.loading;
|
|
114
|
+
return this.wasmModule;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Precompile the WASM module without instantiation
|
|
118
|
+
* Useful for build-time or startup optimization
|
|
119
|
+
*/
|
|
120
|
+
async precompile(wasmPath) {
|
|
121
|
+
if (this.compiledModule) return;
|
|
122
|
+
const path = wasmPath || await this.getDefaultWasmPath();
|
|
123
|
+
const startTime = performance.now();
|
|
124
|
+
if (this.isNode) {
|
|
125
|
+
const fs = await import("fs");
|
|
126
|
+
const { promisify } = await import("util");
|
|
127
|
+
const readFile = promisify(fs.readFile);
|
|
128
|
+
const buffer = await readFile(path);
|
|
129
|
+
await verifyWasmIntegrity(buffer, path);
|
|
130
|
+
this.compiledModule = await WebAssembly.compile(buffer);
|
|
131
|
+
} else {
|
|
132
|
+
const manifest = await loadWasmManifest(path);
|
|
133
|
+
if (!manifest && typeof WebAssembly.compileStreaming === "function") {
|
|
134
|
+
this.compiledModule = await WebAssembly.compileStreaming(fetch(path));
|
|
135
|
+
} else {
|
|
136
|
+
const response = await fetch(path);
|
|
137
|
+
const buffer = await response.arrayBuffer();
|
|
138
|
+
await verifyWasmIntegrity(buffer, path, { manifest });
|
|
139
|
+
this.compiledModule = await WebAssembly.compile(buffer);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
this.lastMetrics = {
|
|
143
|
+
fileReadMs: 0,
|
|
144
|
+
compileMs: performance.now() - startTime,
|
|
145
|
+
instantiateMs: 0,
|
|
146
|
+
totalMs: performance.now() - startTime,
|
|
147
|
+
fromCache: false
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async loadModule(wasmPath) {
|
|
151
|
+
const path = wasmPath || await this.getDefaultWasmPath();
|
|
152
|
+
const totalStart = performance.now();
|
|
153
|
+
if (this.compiledModule) {
|
|
154
|
+
const instStart = performance.now();
|
|
155
|
+
const instance = await WebAssembly.instantiate(this.compiledModule, this.getImports());
|
|
156
|
+
this.lastMetrics = {
|
|
157
|
+
fileReadMs: 0,
|
|
158
|
+
compileMs: 0,
|
|
159
|
+
instantiateMs: performance.now() - instStart,
|
|
160
|
+
totalMs: performance.now() - totalStart,
|
|
161
|
+
fromCache: true
|
|
162
|
+
};
|
|
163
|
+
return instance.exports;
|
|
164
|
+
}
|
|
165
|
+
if (this.isNode) {
|
|
166
|
+
return this.loadNodeWasm(path, totalStart);
|
|
167
|
+
} else {
|
|
168
|
+
return this.loadBrowserWasm(path, totalStart);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the WASM binary path based on the selected backend.
|
|
173
|
+
* Set MATHTS_WASM_BACKEND=assemblyscript to use the AS binary.
|
|
174
|
+
* Default is Rust (after migration cutover).
|
|
175
|
+
*
|
|
176
|
+
* Filenames were renamed from mathjs.wasm / mathjs-as.wasm during the
|
|
177
|
+
* mathjs-to-MathTS rebrand; the legacy MATHJS_WASM_BACKEND env var
|
|
178
|
+
* still works for one release for backward compatibility.
|
|
179
|
+
*
|
|
180
|
+
* Returns an async result because the Node branch dynamically imports
|
|
181
|
+
* `node:url` (fileURLToPath) so the path is resolved relative to this
|
|
182
|
+
* source file's location rather than process.cwd().
|
|
183
|
+
*/
|
|
184
|
+
async getDefaultWasmPath() {
|
|
185
|
+
const useAS = typeof process !== "undefined" && (process.env?.MATHTS_WASM_BACKEND === "assemblyscript" || process.env?.MATHJS_WASM_BACKEND === "assemblyscript");
|
|
186
|
+
const wasmFile = useAS ? "mathts-as.wasm" : "mathts.wasm";
|
|
187
|
+
const resolvedUrl = new URL(`../../../lib/wasm/${wasmFile}`, import.meta.url);
|
|
188
|
+
if (this.isNode) {
|
|
189
|
+
const { fileURLToPath } = await import("url");
|
|
190
|
+
return fileURLToPath(resolvedUrl);
|
|
191
|
+
}
|
|
192
|
+
return resolvedUrl.href;
|
|
193
|
+
}
|
|
194
|
+
async loadNodeWasm(path, totalStart) {
|
|
195
|
+
const fs = await import("fs");
|
|
196
|
+
const { promisify } = await import("util");
|
|
197
|
+
const readFile = promisify(fs.readFile);
|
|
198
|
+
const readStart = performance.now();
|
|
199
|
+
const buffer = await readFile(path);
|
|
200
|
+
const readEnd = performance.now();
|
|
201
|
+
await verifyWasmIntegrity(buffer, path);
|
|
202
|
+
const compileStart = performance.now();
|
|
203
|
+
this.compiledModule = await WebAssembly.compile(buffer);
|
|
204
|
+
const compileEnd = performance.now();
|
|
205
|
+
const instStart = performance.now();
|
|
206
|
+
const instance = await WebAssembly.instantiate(this.compiledModule, this.getImports());
|
|
207
|
+
const instEnd = performance.now();
|
|
208
|
+
this.lastMetrics = {
|
|
209
|
+
fileReadMs: readEnd - readStart,
|
|
210
|
+
compileMs: compileEnd - compileStart,
|
|
211
|
+
instantiateMs: instEnd - instStart,
|
|
212
|
+
totalMs: performance.now() - totalStart,
|
|
213
|
+
fromCache: false
|
|
214
|
+
};
|
|
215
|
+
return instance.exports;
|
|
216
|
+
}
|
|
217
|
+
async loadBrowserWasm(path, totalStart) {
|
|
218
|
+
const manifest = await loadWasmManifest(path);
|
|
219
|
+
if (!manifest && typeof WebAssembly.instantiateStreaming === "function") {
|
|
220
|
+
const instStart2 = performance.now();
|
|
221
|
+
const result = await WebAssembly.instantiateStreaming(fetch(path), this.getImports());
|
|
222
|
+
this.compiledModule = result.module;
|
|
223
|
+
this.lastMetrics = {
|
|
224
|
+
fileReadMs: 0,
|
|
225
|
+
// Combined with compile in streaming
|
|
226
|
+
compileMs: 0,
|
|
227
|
+
// Combined in streaming
|
|
228
|
+
instantiateMs: performance.now() - instStart2,
|
|
229
|
+
totalMs: performance.now() - totalStart,
|
|
230
|
+
fromCache: false
|
|
231
|
+
};
|
|
232
|
+
return result.instance.exports;
|
|
233
|
+
}
|
|
234
|
+
const readStart = performance.now();
|
|
235
|
+
const response = await fetch(path);
|
|
236
|
+
const buffer = await response.arrayBuffer();
|
|
237
|
+
const readEnd = performance.now();
|
|
238
|
+
await verifyWasmIntegrity(buffer, path, { manifest });
|
|
239
|
+
const compileStart = performance.now();
|
|
240
|
+
this.compiledModule = await WebAssembly.compile(buffer);
|
|
241
|
+
const compileEnd = performance.now();
|
|
242
|
+
const instStart = performance.now();
|
|
243
|
+
const instance = await WebAssembly.instantiate(this.compiledModule, this.getImports());
|
|
244
|
+
const instEnd = performance.now();
|
|
245
|
+
this.lastMetrics = {
|
|
246
|
+
fileReadMs: readEnd - readStart,
|
|
247
|
+
compileMs: compileEnd - compileStart,
|
|
248
|
+
instantiateMs: instEnd - instStart,
|
|
249
|
+
totalMs: performance.now() - totalStart,
|
|
250
|
+
fromCache: false
|
|
251
|
+
};
|
|
252
|
+
return instance.exports;
|
|
253
|
+
}
|
|
254
|
+
getImports() {
|
|
255
|
+
return {
|
|
256
|
+
env: {
|
|
257
|
+
abort: (msg, file, line, column) => {
|
|
258
|
+
console.error("WASM abort", { msg, file, line, column });
|
|
259
|
+
throw new Error("WASM abort");
|
|
260
|
+
},
|
|
261
|
+
seed: () => Date.now()
|
|
262
|
+
},
|
|
263
|
+
Math,
|
|
264
|
+
Date
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get the loaded WASM module
|
|
269
|
+
*/
|
|
270
|
+
getModule() {
|
|
271
|
+
return this.wasmModule;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get the compiled WASM module (for caching/serialization)
|
|
275
|
+
*/
|
|
276
|
+
getCompiledModule() {
|
|
277
|
+
return this.compiledModule;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Check if WASM is loaded
|
|
281
|
+
*/
|
|
282
|
+
isLoaded() {
|
|
283
|
+
return this.wasmModule !== null;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Check if WASM is precompiled
|
|
287
|
+
*/
|
|
288
|
+
isPrecompiled() {
|
|
289
|
+
return this.compiledModule !== null;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Get loading performance metrics
|
|
293
|
+
*/
|
|
294
|
+
getLoadingMetrics() {
|
|
295
|
+
return this.lastMetrics;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Allocate Float64Array in WASM memory
|
|
299
|
+
* Uses memory pooling for frequently reused sizes
|
|
300
|
+
*/
|
|
301
|
+
allocateFloat64Array(data) {
|
|
302
|
+
const module = this.wasmModule;
|
|
303
|
+
if (!module) throw new Error("WASM module not loaded");
|
|
304
|
+
const length = data.length;
|
|
305
|
+
const byteLength = length * 8;
|
|
306
|
+
let ptr;
|
|
307
|
+
if (byteLength <= this.poolSizeThreshold) {
|
|
308
|
+
const poolEntry = this.getFromPool(this.float64Pool, byteLength);
|
|
309
|
+
if (poolEntry) {
|
|
310
|
+
ptr = poolEntry.ptr;
|
|
311
|
+
poolEntry.inUse = true;
|
|
312
|
+
} else {
|
|
313
|
+
ptr = module.__new(byteLength, 2);
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
ptr = module.__new(byteLength, 2);
|
|
317
|
+
}
|
|
318
|
+
const array = new Float64Array(module.memory.buffer, ptr, length);
|
|
319
|
+
array.set(data);
|
|
320
|
+
return { ptr, array };
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Allocate Float64Array without copying data (for output buffers)
|
|
324
|
+
*/
|
|
325
|
+
allocateFloat64ArrayEmpty(length) {
|
|
326
|
+
const module = this.wasmModule;
|
|
327
|
+
if (!module) throw new Error("WASM module not loaded");
|
|
328
|
+
const byteLength = length * 8;
|
|
329
|
+
let ptr;
|
|
330
|
+
if (byteLength <= this.poolSizeThreshold) {
|
|
331
|
+
const poolEntry = this.getFromPool(this.float64Pool, byteLength);
|
|
332
|
+
if (poolEntry) {
|
|
333
|
+
ptr = poolEntry.ptr;
|
|
334
|
+
poolEntry.inUse = true;
|
|
335
|
+
} else {
|
|
336
|
+
ptr = module.__new(byteLength, 2);
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
ptr = module.__new(byteLength, 2);
|
|
340
|
+
}
|
|
341
|
+
const array = new Float64Array(module.memory.buffer, ptr, length);
|
|
342
|
+
return { ptr, array };
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Allocate Int32Array in WASM memory
|
|
346
|
+
* Uses memory pooling for frequently reused sizes
|
|
347
|
+
*/
|
|
348
|
+
allocateInt32Array(data) {
|
|
349
|
+
const module = this.wasmModule;
|
|
350
|
+
if (!module) throw new Error("WASM module not loaded");
|
|
351
|
+
const length = data.length;
|
|
352
|
+
const byteLength = length * 4;
|
|
353
|
+
let ptr;
|
|
354
|
+
if (byteLength <= this.poolSizeThreshold) {
|
|
355
|
+
const poolEntry = this.getFromPool(this.int32Pool, byteLength);
|
|
356
|
+
if (poolEntry) {
|
|
357
|
+
ptr = poolEntry.ptr;
|
|
358
|
+
poolEntry.inUse = true;
|
|
359
|
+
} else {
|
|
360
|
+
ptr = module.__new(byteLength, 1);
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
ptr = module.__new(byteLength, 1);
|
|
364
|
+
}
|
|
365
|
+
const array = new Int32Array(module.memory.buffer, ptr, length);
|
|
366
|
+
array.set(data);
|
|
367
|
+
return { ptr, array };
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Allocate Int32Array without copying data (for output buffers)
|
|
371
|
+
*/
|
|
372
|
+
allocateInt32ArrayEmpty(length) {
|
|
373
|
+
const module = this.wasmModule;
|
|
374
|
+
if (!module) throw new Error("WASM module not loaded");
|
|
375
|
+
const byteLength = length * 4;
|
|
376
|
+
let ptr;
|
|
377
|
+
if (byteLength <= this.poolSizeThreshold) {
|
|
378
|
+
const poolEntry = this.getFromPool(this.int32Pool, byteLength);
|
|
379
|
+
if (poolEntry) {
|
|
380
|
+
ptr = poolEntry.ptr;
|
|
381
|
+
poolEntry.inUse = true;
|
|
382
|
+
} else {
|
|
383
|
+
ptr = module.__new(byteLength, 1);
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
ptr = module.__new(byteLength, 1);
|
|
387
|
+
}
|
|
388
|
+
const array = new Int32Array(module.memory.buffer, ptr, length);
|
|
389
|
+
return { ptr, array };
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get a suitable entry from the memory pool
|
|
393
|
+
*/
|
|
394
|
+
getFromPool(pool, requestedSize) {
|
|
395
|
+
let bestFit = null;
|
|
396
|
+
let bestFitWaste = Infinity;
|
|
397
|
+
for (const entry of pool) {
|
|
398
|
+
if (!entry.inUse && entry.size >= requestedSize) {
|
|
399
|
+
const waste = entry.size - requestedSize;
|
|
400
|
+
if (waste < bestFitWaste && entry.size <= requestedSize * 2) {
|
|
401
|
+
bestFit = entry;
|
|
402
|
+
bestFitWaste = waste;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return bestFit;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Return allocation to pool for reuse
|
|
410
|
+
*/
|
|
411
|
+
release(ptr, isFloat64 = true) {
|
|
412
|
+
const pool = isFloat64 ? this.float64Pool : this.int32Pool;
|
|
413
|
+
const entry = pool.find((e) => e.ptr === ptr);
|
|
414
|
+
if (entry) {
|
|
415
|
+
entry.inUse = false;
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
this.free(ptr);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Free allocated memory (immediate, bypasses pool)
|
|
422
|
+
*/
|
|
423
|
+
free(ptr) {
|
|
424
|
+
const module = this.wasmModule;
|
|
425
|
+
if (!module) return;
|
|
426
|
+
this.float64Pool = this.float64Pool.filter((e) => e.ptr !== ptr);
|
|
427
|
+
this.int32Pool = this.int32Pool.filter((e) => e.ptr !== ptr);
|
|
428
|
+
module.__unpin(ptr);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Clear the memory pool
|
|
432
|
+
*/
|
|
433
|
+
clearPool() {
|
|
434
|
+
const module = this.wasmModule;
|
|
435
|
+
if (!module) return;
|
|
436
|
+
for (const entry of this.float64Pool) {
|
|
437
|
+
module.__unpin(entry.ptr);
|
|
438
|
+
}
|
|
439
|
+
for (const entry of this.int32Pool) {
|
|
440
|
+
module.__unpin(entry.ptr);
|
|
441
|
+
}
|
|
442
|
+
this.float64Pool = [];
|
|
443
|
+
this.int32Pool = [];
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Get pool statistics
|
|
447
|
+
*/
|
|
448
|
+
getPoolStats() {
|
|
449
|
+
const f64InUse = this.float64Pool.filter((e) => e.inUse).length;
|
|
450
|
+
const f64Bytes = this.float64Pool.reduce((sum, e) => sum + e.size, 0);
|
|
451
|
+
const i32InUse = this.int32Pool.filter((e) => e.inUse).length;
|
|
452
|
+
const i32Bytes = this.int32Pool.reduce((sum, e) => sum + e.size, 0);
|
|
453
|
+
return {
|
|
454
|
+
float64: {
|
|
455
|
+
total: this.float64Pool.length,
|
|
456
|
+
inUse: f64InUse,
|
|
457
|
+
totalBytes: f64Bytes
|
|
458
|
+
},
|
|
459
|
+
int32: {
|
|
460
|
+
total: this.int32Pool.length,
|
|
461
|
+
inUse: i32InUse,
|
|
462
|
+
totalBytes: i32Bytes
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Run garbage collection
|
|
468
|
+
*/
|
|
469
|
+
collect() {
|
|
470
|
+
const module = this.wasmModule;
|
|
471
|
+
if (!module) return;
|
|
472
|
+
module.__collect();
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Reset the loader (for testing)
|
|
476
|
+
*/
|
|
477
|
+
reset() {
|
|
478
|
+
this.clearPool();
|
|
479
|
+
this.wasmModule = null;
|
|
480
|
+
this.compiledModule = null;
|
|
481
|
+
this.loading = null;
|
|
482
|
+
this.lastMetrics = null;
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
var wasmLoader = WasmLoader.getInstance();
|
|
486
|
+
|
|
487
|
+
// src/wasm/sort/wasm-bridge.ts
|
|
488
|
+
var WASM_SORT_THRESHOLD = 16384;
|
|
489
|
+
function getWasm() {
|
|
490
|
+
try {
|
|
491
|
+
return wasmLoader.getModule();
|
|
492
|
+
} catch {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function cmpNanLast(a, b) {
|
|
497
|
+
if (a < b) return -1;
|
|
498
|
+
if (a > b) return 1;
|
|
499
|
+
if (a === b) return 0;
|
|
500
|
+
if (Number.isNaN(a) && Number.isNaN(b)) return 0;
|
|
501
|
+
if (Number.isNaN(a)) return 1;
|
|
502
|
+
return -1;
|
|
503
|
+
}
|
|
504
|
+
function sortF64JS(data) {
|
|
505
|
+
const arr = Array.from(data);
|
|
506
|
+
arr.sort(cmpNanLast);
|
|
507
|
+
for (let i = 0; i < arr.length; i++) data[i] = arr[i];
|
|
508
|
+
return data;
|
|
509
|
+
}
|
|
510
|
+
function argsortF64JS(data) {
|
|
511
|
+
const n = data.length;
|
|
512
|
+
const indices = new Int32Array(n);
|
|
513
|
+
for (let i = 0; i < n; i++) indices[i] = i;
|
|
514
|
+
const arr = Array.from(indices);
|
|
515
|
+
arr.sort((i, j) => cmpNanLast(data[i], data[j]));
|
|
516
|
+
for (let k = 0; k < n; k++) indices[k] = arr[k];
|
|
517
|
+
return indices;
|
|
518
|
+
}
|
|
519
|
+
function rankF64JS(data) {
|
|
520
|
+
const n = data.length;
|
|
521
|
+
const argsort = argsortF64JS(data);
|
|
522
|
+
const rank = new Int32Array(n);
|
|
523
|
+
for (let k = 0; k < n; k++) rank[argsort[k]] = k;
|
|
524
|
+
return rank;
|
|
525
|
+
}
|
|
526
|
+
function sortF64Dispatch(data) {
|
|
527
|
+
const n = data.length;
|
|
528
|
+
if (n >= WASM_SORT_THRESHOLD) {
|
|
529
|
+
const wasm = getWasm();
|
|
530
|
+
if (wasm) {
|
|
531
|
+
const rustFn = wasm["sort_f64"];
|
|
532
|
+
if (typeof rustFn === "function") {
|
|
533
|
+
try {
|
|
534
|
+
const alloc = wasmLoader.allocateFloat64Array(data);
|
|
535
|
+
try {
|
|
536
|
+
const ret = rustFn(alloc.ptr, n);
|
|
537
|
+
if (ret >= 0) {
|
|
538
|
+
data.set(alloc.array);
|
|
539
|
+
return data;
|
|
540
|
+
}
|
|
541
|
+
} finally {
|
|
542
|
+
wasmLoader.free(alloc.ptr);
|
|
543
|
+
}
|
|
544
|
+
} catch {
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
const asFn = wasm["sort_f64"];
|
|
548
|
+
if (typeof asFn === "function") {
|
|
549
|
+
try {
|
|
550
|
+
asFn(data);
|
|
551
|
+
return data;
|
|
552
|
+
} catch {
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return sortF64JS(data);
|
|
558
|
+
}
|
|
559
|
+
function argsortF64Dispatch(data) {
|
|
560
|
+
const n = data.length;
|
|
561
|
+
if (n >= WASM_SORT_THRESHOLD) {
|
|
562
|
+
const wasm = getWasm();
|
|
563
|
+
if (wasm) {
|
|
564
|
+
const rustFn = wasm["argsort_f64"];
|
|
565
|
+
if (typeof rustFn === "function") {
|
|
566
|
+
try {
|
|
567
|
+
const inAlloc = wasmLoader.allocateFloat64Array(data);
|
|
568
|
+
const outAlloc = wasmLoader.allocateInt32ArrayEmpty(n);
|
|
569
|
+
try {
|
|
570
|
+
const ret = rustFn(
|
|
571
|
+
inAlloc.ptr,
|
|
572
|
+
n,
|
|
573
|
+
outAlloc.ptr
|
|
574
|
+
);
|
|
575
|
+
if (ret >= 0) {
|
|
576
|
+
return new Int32Array(outAlloc.array);
|
|
577
|
+
}
|
|
578
|
+
} finally {
|
|
579
|
+
wasmLoader.free(inAlloc.ptr);
|
|
580
|
+
wasmLoader.free(outAlloc.ptr);
|
|
581
|
+
}
|
|
582
|
+
} catch {
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
const asFn = wasm["argsort_f64"];
|
|
586
|
+
if (typeof asFn === "function") {
|
|
587
|
+
try {
|
|
588
|
+
return asFn(data);
|
|
589
|
+
} catch {
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return argsortF64JS(data);
|
|
595
|
+
}
|
|
596
|
+
function rankF64Dispatch(data) {
|
|
597
|
+
const n = data.length;
|
|
598
|
+
if (n >= WASM_SORT_THRESHOLD) {
|
|
599
|
+
const wasm = getWasm();
|
|
600
|
+
if (wasm) {
|
|
601
|
+
const rustFn = wasm["rank_f64"];
|
|
602
|
+
if (typeof rustFn === "function") {
|
|
603
|
+
try {
|
|
604
|
+
const inAlloc = wasmLoader.allocateFloat64Array(data);
|
|
605
|
+
const outAlloc = wasmLoader.allocateInt32ArrayEmpty(n);
|
|
606
|
+
try {
|
|
607
|
+
const ret = rustFn(
|
|
608
|
+
inAlloc.ptr,
|
|
609
|
+
n,
|
|
610
|
+
outAlloc.ptr
|
|
611
|
+
);
|
|
612
|
+
if (ret >= 0) {
|
|
613
|
+
return new Int32Array(outAlloc.array);
|
|
614
|
+
}
|
|
615
|
+
} finally {
|
|
616
|
+
wasmLoader.free(inAlloc.ptr);
|
|
617
|
+
wasmLoader.free(outAlloc.ptr);
|
|
618
|
+
}
|
|
619
|
+
} catch {
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const asFn = wasm["rank_f64"];
|
|
623
|
+
if (typeof asFn === "function") {
|
|
624
|
+
try {
|
|
625
|
+
return asFn(data);
|
|
626
|
+
} catch {
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return rankF64JS(data);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export {
|
|
635
|
+
__export,
|
|
636
|
+
wasmLoader,
|
|
637
|
+
WASM_SORT_THRESHOLD,
|
|
638
|
+
sortF64JS,
|
|
639
|
+
argsortF64JS,
|
|
640
|
+
rankF64JS,
|
|
641
|
+
sortF64Dispatch,
|
|
642
|
+
argsortF64Dispatch,
|
|
643
|
+
rankF64Dispatch
|
|
644
|
+
};
|