@danielsimonjr/mathts-wasm 0.1.2 → 0.1.3
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/README.md +15 -11
- package/build/bindings/wasm-loader.d.ts +17 -0
- package/build/bindings/wasm-loader.d.ts.map +1 -1
- package/build/bindings/wasm-loader.js +74 -2
- package/build/bindings/wasm-loader.js.map +1 -1
- package/build/mathts-debug.d.ts +632 -0
- package/build/mathts-debug.js +686 -0
- package/build/mathts-debug.wasm +0 -0
- package/build/mathts-debug.wasm.map +1 -1
- package/build/mathts-debug.wat +21765 -1025
- package/build/mathts.d.ts +632 -0
- package/build/mathts.js +686 -0
- package/build/mathts.wasm +0 -0
- package/build/mathts.wat +18590 -839
- package/build/wasm-manifest.json +4 -0
- package/package.json +5 -4
- package/src/bindings/wasm-loader.ts +126 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danielsimonjr/mathts-wasm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "MathTS WebAssembly module - high-performance numerical computing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/release.js",
|
|
@@ -28,10 +28,11 @@
|
|
|
28
28
|
"build:bindings": "tsc -p tsconfig.bindings.json",
|
|
29
29
|
"build": "npm run asbuild && npm run build:bindings",
|
|
30
30
|
"clean": "rm -rf build/",
|
|
31
|
-
"test": "node
|
|
31
|
+
"test": "node tests/run.js && node tests/svd.test.mjs && node tests/special-numtheory.test.mjs && node tests/polynomial.test.mjs && node tests/signal.test.mjs && node tests/linalg.test.mjs && node tests/curvefit.test.mjs && node tests/optimization.test.mjs && node tests/approx.test.mjs && node tests/tensor.test.mjs",
|
|
32
32
|
"benchmark": "node --experimental-wasm-simd benchmarks/run.js"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
+
"@assemblyscript/loader": "^0.28.17",
|
|
35
36
|
"assemblyscript": "^0.27.27",
|
|
36
37
|
"typescript": "^5.3.0"
|
|
37
38
|
},
|
|
@@ -45,11 +46,11 @@
|
|
|
45
46
|
"linear-algebra",
|
|
46
47
|
"simd"
|
|
47
48
|
],
|
|
48
|
-
"author": "
|
|
49
|
+
"author": "Daniel Simon Jr.",
|
|
49
50
|
"license": "MIT",
|
|
50
51
|
"repository": {
|
|
51
52
|
"type": "git",
|
|
52
|
-
"url": "https://github.com/danielsimonjr/
|
|
53
|
+
"url": "https://github.com/danielsimonjr/mathts",
|
|
53
54
|
"directory": "assembly"
|
|
54
55
|
},
|
|
55
56
|
"bugs": {
|
|
@@ -3,8 +3,82 @@
|
|
|
3
3
|
*
|
|
4
4
|
* TypeScript bindings for loading and using the MathTS WASM module.
|
|
5
5
|
* This provides a clean interface for JavaScript/TypeScript consumers.
|
|
6
|
+
*
|
|
7
|
+
* Security: optional SHA-384 integrity check via a sibling
|
|
8
|
+
* `wasm-manifest.json`. See verifyWasm() / loadManifest() below — kept
|
|
9
|
+
* inline rather than imported because the assembly package ships
|
|
10
|
+
* standalone (no functions/ dependency).
|
|
6
11
|
*/
|
|
7
12
|
|
|
13
|
+
interface WasmManifest {
|
|
14
|
+
[fileName: string]: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function sha384Base64(buffer: ArrayBuffer | Uint8Array): Promise<string> {
|
|
18
|
+
const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
19
|
+
const isNode = typeof process !== 'undefined' && (process as any).versions?.node !== undefined;
|
|
20
|
+
let digest: Uint8Array;
|
|
21
|
+
if (isNode) {
|
|
22
|
+
const { createHash } = await import('crypto');
|
|
23
|
+
const h = createHash('sha384');
|
|
24
|
+
h.update(bytes);
|
|
25
|
+
digest = new Uint8Array(h.digest());
|
|
26
|
+
} else {
|
|
27
|
+
// Copy into a fresh ArrayBuffer so SubtleCrypto.digest accepts the
|
|
28
|
+
// bytes regardless of the source backing (ArrayBuffer or SharedArrayBuffer).
|
|
29
|
+
const copy = new ArrayBuffer(bytes.byteLength);
|
|
30
|
+
new Uint8Array(copy).set(bytes);
|
|
31
|
+
const out = await crypto.subtle.digest('SHA-384', copy);
|
|
32
|
+
digest = new Uint8Array(out);
|
|
33
|
+
}
|
|
34
|
+
let bin = '';
|
|
35
|
+
for (let i = 0; i < digest.length; i++) bin += String.fromCharCode(digest[i]);
|
|
36
|
+
const b64 =
|
|
37
|
+
typeof btoa === 'function'
|
|
38
|
+
? btoa(bin)
|
|
39
|
+
: (globalThis as any).Buffer.from(digest).toString('base64');
|
|
40
|
+
return `sha384-${b64}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function loadManifest(wasmPath: string): Promise<WasmManifest | null> {
|
|
44
|
+
const manifestPath = wasmPath.replace(/[^/\\]+$/, 'wasm-manifest.json');
|
|
45
|
+
const isNode = typeof process !== 'undefined' && (process as any).versions?.node !== undefined;
|
|
46
|
+
try {
|
|
47
|
+
if (isNode) {
|
|
48
|
+
const fs = await import('fs');
|
|
49
|
+
const text = fs.readFileSync(manifestPath, 'utf8');
|
|
50
|
+
return JSON.parse(text) as WasmManifest;
|
|
51
|
+
} else {
|
|
52
|
+
const res = await fetch(manifestPath);
|
|
53
|
+
if (!res.ok) return null;
|
|
54
|
+
return (await res.json()) as WasmManifest;
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function verifyWasm(buffer: ArrayBuffer | Uint8Array, wasmPath: string): Promise<void> {
|
|
62
|
+
const manifest = await loadManifest(wasmPath);
|
|
63
|
+
if (!manifest) {
|
|
64
|
+
if (typeof console !== 'undefined') {
|
|
65
|
+
console.warn(
|
|
66
|
+
`[wasm-integrity] no manifest found beside "${wasmPath}"; skipping SHA-384 verification`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const fileName = wasmPath.split(/[/\\]/).pop() || wasmPath;
|
|
72
|
+
const expected = manifest[fileName];
|
|
73
|
+
if (!expected) return;
|
|
74
|
+
const actual = await sha384Base64(buffer);
|
|
75
|
+
if (actual !== expected) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`WASM integrity check failed for "${fileName}": expected ${expected}, got ${actual}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
8
82
|
// Type definitions for the WASM module exports
|
|
9
83
|
export interface MathTSWasmExports {
|
|
10
84
|
// Memory
|
|
@@ -30,11 +104,54 @@ export interface MathTSWasmExports {
|
|
|
30
104
|
array_norm(dataPtr: number): number;
|
|
31
105
|
|
|
32
106
|
// Matrix operations (pointers)
|
|
33
|
-
matrix_multiply(
|
|
107
|
+
matrix_multiply(
|
|
108
|
+
aPtr: number,
|
|
109
|
+
aRows: number,
|
|
110
|
+
aCols: number,
|
|
111
|
+
bPtr: number,
|
|
112
|
+
bCols: number,
|
|
113
|
+
resultPtr: number
|
|
114
|
+
): void;
|
|
34
115
|
matrix_transpose(aPtr: number, rows: number, cols: number, resultPtr: number): void;
|
|
35
116
|
matrix_trace(dataPtr: number, rows: number, cols: number): number;
|
|
36
117
|
matrix_norm_frobenius(dataPtr: number): number;
|
|
37
118
|
|
|
119
|
+
// Dense matrix decompositions — AS exports added in tandem with the
|
|
120
|
+
// Rust crate's `decomposition.rs` so the AS backend can stop falling
|
|
121
|
+
// back to JS for these ops. AS calling convention: pass typed-array
|
|
122
|
+
// *header* references (the AS runtime carries length, no ptr+len args).
|
|
123
|
+
// See `assembly/src/algebra/decomposition.ts` for the kernels.
|
|
124
|
+
matrix_lu_decompose?(
|
|
125
|
+
a: Float64Array,
|
|
126
|
+
n: number,
|
|
127
|
+
l_out: Float64Array,
|
|
128
|
+
u_out: Float64Array,
|
|
129
|
+
perm_out: Int32Array
|
|
130
|
+
): number;
|
|
131
|
+
matrix_qr_decompose?(
|
|
132
|
+
a: Float64Array,
|
|
133
|
+
m: number,
|
|
134
|
+
n: number,
|
|
135
|
+
q_out: Float64Array,
|
|
136
|
+
r_out: Float64Array
|
|
137
|
+
): number;
|
|
138
|
+
matrix_cholesky?(a: Float64Array, n: number, l_out: Float64Array): number;
|
|
139
|
+
matrix_inverse?(a: Float64Array, n: number, result: Float64Array, work: Float64Array): number;
|
|
140
|
+
matrix_determinant?(a: Float64Array, n: number, work: Float64Array): number;
|
|
141
|
+
|
|
142
|
+
// Bitwise operations (Int32Array, elementwise) — AS-backend kernels.
|
|
143
|
+
// The Rust backend ships an equivalent set under the `*Array` /
|
|
144
|
+
// `*ArrayPerElement` naming (declared in
|
|
145
|
+
// functions/src/wasm/WasmLoader.ts's interface); the AS kernels keep
|
|
146
|
+
// the `_i32_array` suffix to match AS export-name discoverability.
|
|
147
|
+
bitAnd_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
|
|
148
|
+
bitOr_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
|
|
149
|
+
bitXor_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
|
|
150
|
+
bitNot_i32_array?(a: Int32Array, result: Int32Array): void;
|
|
151
|
+
leftShift_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
|
|
152
|
+
rightArithShift_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
|
|
153
|
+
rightLogShift_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
|
|
154
|
+
|
|
38
155
|
// Memory management
|
|
39
156
|
__new(size: number, id: number): number;
|
|
40
157
|
__pin(ptr: number): number;
|
|
@@ -58,16 +175,20 @@ export async function loadWasm(source: string | BufferSource): Promise<MathTSWas
|
|
|
58
175
|
if (typeof fetch !== 'undefined') {
|
|
59
176
|
const response = await fetch(source);
|
|
60
177
|
const buffer = await response.arrayBuffer();
|
|
178
|
+
await verifyWasm(buffer, source);
|
|
61
179
|
wasmModule = await WebAssembly.compile(buffer);
|
|
62
180
|
} else {
|
|
63
181
|
// Node.js environment
|
|
64
182
|
const fs = await import('fs');
|
|
65
183
|
const path = await import('path');
|
|
66
|
-
const
|
|
184
|
+
const resolved = path.resolve(source);
|
|
185
|
+
const buffer = fs.readFileSync(resolved);
|
|
186
|
+
await verifyWasm(buffer, resolved);
|
|
67
187
|
wasmModule = await WebAssembly.compile(buffer);
|
|
68
188
|
}
|
|
69
189
|
} else {
|
|
70
|
-
// Load from buffer
|
|
190
|
+
// Load from buffer — caller is responsible for verifying integrity
|
|
191
|
+
// since we have no path to locate a sibling manifest.
|
|
71
192
|
wasmModule = await WebAssembly.compile(source);
|
|
72
193
|
}
|
|
73
194
|
|
|
@@ -214,8 +335,8 @@ export class MathTSWasm {
|
|
|
214
335
|
this.exports.__pin(ptr);
|
|
215
336
|
|
|
216
337
|
const view = new DataView(this.memory.buffer);
|
|
217
|
-
view.setInt32(ptr, ptr + 16, true);
|
|
218
|
-
view.setInt32(ptr + 4, length, true);
|
|
338
|
+
view.setInt32(ptr, ptr + 16, true); // data pointer
|
|
339
|
+
view.setInt32(ptr + 4, length, true); // length
|
|
219
340
|
|
|
220
341
|
const array = new Float64Array(this.memory.buffer, ptr + 16, length);
|
|
221
342
|
return { ptr, array };
|