kenqtade commited on
Commit
0865492
·
1 Parent(s): 9fae9b4

Use ken-q org namespace for project Spaces

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. AGENTS.md +1 -1
  2. README.md +1 -1
  3. node_modules/.bin/hfjs +1 -0
  4. node_modules/.package-lock.json +123 -0
  5. node_modules/@huggingface/blake3-jit/LICENSE +22 -0
  6. node_modules/@huggingface/blake3-jit/README.md +56 -0
  7. node_modules/@huggingface/blake3-jit/dist/commonjs/compress.d.ts +37 -0
  8. node_modules/@huggingface/blake3-jit/dist/commonjs/compress.js +919 -0
  9. node_modules/@huggingface/blake3-jit/dist/commonjs/constants.d.ts +34 -0
  10. node_modules/@huggingface/blake3-jit/dist/commonjs/constants.js +56 -0
  11. node_modules/@huggingface/blake3-jit/dist/commonjs/hash.d.ts +28 -0
  12. node_modules/@huggingface/blake3-jit/dist/commonjs/hash.js +1038 -0
  13. node_modules/@huggingface/blake3-jit/dist/commonjs/hasher.d.ts +108 -0
  14. node_modules/@huggingface/blake3-jit/dist/commonjs/hasher.js +396 -0
  15. node_modules/@huggingface/blake3-jit/dist/commonjs/index.d.ts +82 -0
  16. node_modules/@huggingface/blake3-jit/dist/commonjs/index.js +109 -0
  17. node_modules/@huggingface/blake3-jit/dist/commonjs/package.json +3 -0
  18. node_modules/@huggingface/blake3-jit/dist/commonjs/utils.d.ts +90 -0
  19. node_modules/@huggingface/blake3-jit/dist/commonjs/utils.js +226 -0
  20. node_modules/@huggingface/blake3-jit/dist/commonjs/wasm-simd.d.ts +97 -0
  21. node_modules/@huggingface/blake3-jit/dist/commonjs/wasm-simd.js +948 -0
  22. node_modules/@huggingface/blake3-jit/dist/esm/compress.d.ts +37 -0
  23. node_modules/@huggingface/blake3-jit/dist/esm/compress.js +916 -0
  24. node_modules/@huggingface/blake3-jit/dist/esm/constants.d.ts +34 -0
  25. node_modules/@huggingface/blake3-jit/dist/esm/constants.js +53 -0
  26. node_modules/@huggingface/blake3-jit/dist/esm/hash.d.ts +28 -0
  27. node_modules/@huggingface/blake3-jit/dist/esm/hash.js +1033 -0
  28. node_modules/@huggingface/blake3-jit/dist/esm/hasher.d.ts +108 -0
  29. node_modules/@huggingface/blake3-jit/dist/esm/hasher.js +391 -0
  30. node_modules/@huggingface/blake3-jit/dist/esm/index.d.ts +82 -0
  31. node_modules/@huggingface/blake3-jit/dist/esm/index.js +98 -0
  32. node_modules/@huggingface/blake3-jit/dist/esm/package.json +3 -0
  33. node_modules/@huggingface/blake3-jit/dist/esm/utils.d.ts +90 -0
  34. node_modules/@huggingface/blake3-jit/dist/esm/utils.js +214 -0
  35. node_modules/@huggingface/blake3-jit/dist/esm/wasm-simd.d.ts +97 -0
  36. node_modules/@huggingface/blake3-jit/dist/esm/wasm-simd.js +936 -0
  37. node_modules/@huggingface/blake3-jit/package.json +49 -0
  38. node_modules/@huggingface/blake3-jit/src/compress.ts +954 -0
  39. node_modules/@huggingface/blake3-jit/src/constants.ts +59 -0
  40. node_modules/@huggingface/blake3-jit/src/hash.ts +1318 -0
  41. node_modules/@huggingface/blake3-jit/src/hasher.ts +576 -0
  42. node_modules/@huggingface/blake3-jit/src/index.ts +106 -0
  43. node_modules/@huggingface/blake3-jit/src/utils.ts +253 -0
  44. node_modules/@huggingface/blake3-jit/src/wasm-simd.ts +1087 -0
  45. node_modules/@huggingface/hub/LICENSE +21 -0
  46. node_modules/@huggingface/hub/README.md +228 -0
  47. node_modules/@huggingface/hub/dist/FileBlob-RUOT7DBI.mjs +91 -0
  48. node_modules/@huggingface/hub/dist/browser/FileBlob-7MRLQ6TG.mjs +0 -0
  49. node_modules/@huggingface/hub/dist/browser/FileBlob-YC2EPDW4.js +1 -0
  50. node_modules/@huggingface/hub/dist/browser/index.js +0 -0
AGENTS.md CHANGED
@@ -50,5 +50,5 @@ When the user asks to start or work on a project:
50
  - Each project lives in its OWN HF Space
51
  - Use `python3` and the `huggingface_hub` library for all HF API calls
52
  - If python3 or huggingface_hub is not installed, install with pip
53
- - Ask the user for the namespace/username to use for project Spaces
54
  - One project at a time unless explicitly asked to multitask
 
50
  - Each project lives in its OWN HF Space
51
  - Use `python3` and the `huggingface_hub` library for all HF API calls
52
  - If python3 or huggingface_hub is not installed, install with pip
53
+ - Default namespace for all project Spaces is `ken-q`
54
  - One project at a time unless explicitly asked to multitask
README.md CHANGED
@@ -20,4 +20,4 @@ An AI coding agent running in your browser via Hugging Face Spaces, powered by N
20
 
21
  ## Usage
22
 
23
- Visit `https://kenqtade-opencode-home.hf.space` and start coding.
 
20
 
21
  ## Usage
22
 
23
+ Visit `https://ken-q-opencode-home.hf.space` and start coding.
node_modules/.bin/hfjs ADDED
@@ -0,0 +1 @@
 
 
1
+ ../@huggingface/hub/dist/cli.js
node_modules/.package-lock.json ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "opencode-space",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "node_modules/@huggingface/blake3-jit": {
8
+ "version": "0.0.2",
9
+ "resolved": "https://registry.npmjs.org/@huggingface/blake3-jit/-/blake3-jit-0.0.2.tgz",
10
+ "integrity": "sha512-Bq7B5qabyjrJfhBsl85Jd2QBtf+HzRD7h7A9GfN2lzrrsABhOa5evVPgzoCTxR7Ub0QFj7YDK1YkYRWBU25+2w==",
11
+ "license": "MIT"
12
+ },
13
+ "node_modules/@huggingface/hub": {
14
+ "version": "2.13.2",
15
+ "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-2.13.2.tgz",
16
+ "integrity": "sha512-I1Pbn9UcBHCt0M/SbZkKzYr3JVnbJp4YIFv74eVRH2Y22yT9Jpm6DtfM74tcjNLbC7SyFZ433znUw0szdFd4Fg==",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "@huggingface/tasks": "^0.21.13",
20
+ "@huggingface/xetchunk-wasm": "^0.1.0"
21
+ },
22
+ "bin": {
23
+ "hfjs": "dist/cli.js"
24
+ },
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "optionalDependencies": {
29
+ "cli-progress": "^3.12.0"
30
+ }
31
+ },
32
+ "node_modules/@huggingface/tasks": {
33
+ "version": "0.21.20",
34
+ "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.21.20.tgz",
35
+ "integrity": "sha512-pGw7pQI8TYSbrOVfNRdrYeISDEYOvQzOCEBceYjEKOH2wOmooc6eGfbYRhAALDgNKZCSISPYYFszRxHta3FaNw==",
36
+ "license": "MIT"
37
+ },
38
+ "node_modules/@huggingface/xetchunk-wasm": {
39
+ "version": "0.1.0",
40
+ "resolved": "https://registry.npmjs.org/@huggingface/xetchunk-wasm/-/xetchunk-wasm-0.1.0.tgz",
41
+ "integrity": "sha512-wWpp2qwPgf9kv1KLJjcDUk/OrpDOsFoQ3Qpz0U5LGn20csoymBf8eneOv6wm/GzPBzlWac1OYiR0aa1vT6aM2Q==",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "@huggingface/blake3-jit": "0.0.2",
45
+ "gearhash-jit": "1.0.2"
46
+ }
47
+ },
48
+ "node_modules/ansi-regex": {
49
+ "version": "5.0.1",
50
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
51
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
52
+ "license": "MIT",
53
+ "optional": true,
54
+ "engines": {
55
+ "node": ">=8"
56
+ }
57
+ },
58
+ "node_modules/cli-progress": {
59
+ "version": "3.12.0",
60
+ "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
61
+ "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
62
+ "license": "MIT",
63
+ "optional": true,
64
+ "dependencies": {
65
+ "string-width": "^4.2.3"
66
+ },
67
+ "engines": {
68
+ "node": ">=4"
69
+ }
70
+ },
71
+ "node_modules/emoji-regex": {
72
+ "version": "8.0.0",
73
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
74
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
75
+ "license": "MIT",
76
+ "optional": true
77
+ },
78
+ "node_modules/gearhash-jit": {
79
+ "version": "1.0.2",
80
+ "resolved": "https://registry.npmjs.org/gearhash-jit/-/gearhash-jit-1.0.2.tgz",
81
+ "integrity": "sha512-UhzJL4KXSdqAKepy/tZwmi2Rcy0YMmtiC4DQS4SURCuIWdh8ECZtnXK2ePRMLigfB61hRKdLK/Vgg2bSw73izQ==",
82
+ "license": "MIT"
83
+ },
84
+ "node_modules/is-fullwidth-code-point": {
85
+ "version": "3.0.0",
86
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
87
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
88
+ "license": "MIT",
89
+ "optional": true,
90
+ "engines": {
91
+ "node": ">=8"
92
+ }
93
+ },
94
+ "node_modules/string-width": {
95
+ "version": "4.2.3",
96
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
97
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
98
+ "license": "MIT",
99
+ "optional": true,
100
+ "dependencies": {
101
+ "emoji-regex": "^8.0.0",
102
+ "is-fullwidth-code-point": "^3.0.0",
103
+ "strip-ansi": "^6.0.1"
104
+ },
105
+ "engines": {
106
+ "node": ">=8"
107
+ }
108
+ },
109
+ "node_modules/strip-ansi": {
110
+ "version": "6.0.1",
111
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
112
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
113
+ "license": "MIT",
114
+ "optional": true,
115
+ "dependencies": {
116
+ "ansi-regex": "^5.0.1"
117
+ },
118
+ "engines": {
119
+ "node": ">=8"
120
+ }
121
+ }
122
+ }
123
+ }
node_modules/@huggingface/blake3-jit/LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Original work Copyright (c) LongYinan <github@lyn.one> (https://github.com/Brooooooklyn/blake3-jit)
4
+ Modified work Copyright (c) Hugging Face
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
node_modules/@huggingface/blake3-jit/README.md ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @huggingface/blake3-jit
2
+
3
+ Temporary fork of [`blake3-jit`](https://github.com/Brooooooklyn/blake3-jit) by [@Brooooooklyn](https://github.com/Brooooooklyn) with performance enhancements for [Hugging Face Xet](https://huggingface.co/docs/hub/xet) content-defined chunking.
4
+
5
+ This package will be deprecated once upstream `blake3-jit` exposes these changes.
6
+
7
+ ## Changes from upstream
8
+
9
+ 1. **`Hasher.reset()` method** — resets the hasher to process a new message with the same key/flags, reusing all internal buffers (zero allocations per hash).
10
+
11
+ 2. **Pre-allocated internal buffers** — `parentBlock`, `parentCv`, `chunkCv`, `outWords`, and `finalizeCv` are allocated once in the constructor and reused across `update`/`finalize` calls, significantly reducing GC pressure in hot loops.
12
+
13
+ 3. **`ChunkState.resetTo()` method** — allows reusing `ChunkState` instances instead of allocating new ones per chunk.
14
+
15
+ 4. **Removed `Uint32Array` view fast-path** in `ChunkState.update` — the byte-by-byte `readLittleEndianWordsFull` path was empirically faster and avoids `RangeError` on unaligned offsets.
16
+
17
+ 5. **Dual ESM/CJS output via `tshy`** — the upstream package is ESM-only; this fork uses [`tshy`](https://github.com/isaacs/tshy) to produce both ESM and CommonJS builds, required for compatibility with Node.js CJS consumers.
18
+
19
+ These changes are also available as a patch file at [`packages/xetchunk-wasm/patches/blake3-jit.patch`](../xetchunk-wasm/patches/blake3-jit.patch) (applicable to the upstream dist bundle).
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @huggingface/blake3-jit
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```typescript
30
+ import { hash, Hasher } from "@huggingface/blake3-jit";
31
+
32
+ // One-shot hashing
33
+ const digest = hash(new Uint8Array([1, 2, 3]));
34
+
35
+ // Incremental hashing with reset (zero-alloc reuse)
36
+ const hasher = new Hasher();
37
+ hasher.update(chunk1);
38
+ const hash1 = hasher.finalize();
39
+
40
+ hasher.reset();
41
+ hasher.update(chunk2);
42
+ const hash2 = hasher.finalize();
43
+
44
+ // Keyed hashing (MAC)
45
+ const mac = Hasher.newKeyed(key).update(message).finalize();
46
+ ```
47
+
48
+ ## Upstream
49
+
50
+ This is a fork of [blake3-jit](https://github.com/Brooooooklyn/blake3-jit) — a high-performance BLAKE3 implementation with runtime JIT WASM SIMD, created by [LongYinan (@Brooooooklyn)](https://github.com/Brooooooklyn).
51
+
52
+ See the upstream repository for full documentation, benchmarks, and architecture details.
53
+
54
+ ## License
55
+
56
+ MIT — see [LICENSE](./LICENSE)
node_modules/@huggingface/blake3-jit/dist/commonjs/compress.d.ts ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Compression Function - Highly Optimized
3
+ *
4
+ * Optimization techniques applied (from Fleek Network case study):
5
+ * 1. Use 16 SMI variables for state instead of TypedArray
6
+ * 2. Use 16 SMI variables for message words
7
+ * 3. Fully inlined G function (no function call overhead)
8
+ * 4. Use `| 0` for integer coercion (forces V8 to use 32-bit ALU)
9
+ * 5. Hardcoded permutation swaps using only 2 temporary variables
10
+ * 6. Offset-based access pattern (avoid creating new views)
11
+ *
12
+ * The compression function takes:
13
+ * - cv: 8-word chaining value
14
+ * - block: 16-word message block (64 bytes)
15
+ * - counter: 64-bit block counter
16
+ * - blockLen: number of input bytes in this block
17
+ * - flags: domain separation flags
18
+ *
19
+ * And outputs 8 or 16 words depending on whether this is a root node.
20
+ */
21
+ /**
22
+ * Compress a single block.
23
+ *
24
+ * This is the hot path - every optimization matters here.
25
+ *
26
+ * @param cv - Chaining value array
27
+ * @param cvOff - Offset into cv
28
+ * @param block - Message block words
29
+ * @param blockOff - Offset into block
30
+ * @param out - Output array (8 or 16 words)
31
+ * @param outOff - Offset into out
32
+ * @param full - If true, output all 16 words (for XOF); if false, output 8 words
33
+ * @param counter - 64-bit block counter
34
+ * @param blockLen - Number of bytes in this block (0-64)
35
+ * @param flags - Domain separation flags
36
+ */
37
+ export declare function compress(cv: Uint32Array, cvOff: number, block: Uint32Array, blockOff: number, out: Uint32Array, outOff: number, full: boolean, counter: number, blockLen: number, flags: number): void;
node_modules/@huggingface/blake3-jit/dist/commonjs/compress.js ADDED
@@ -0,0 +1,919 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ /**
3
+ * BLAKE3 Compression Function - Highly Optimized
4
+ *
5
+ * Optimization techniques applied (from Fleek Network case study):
6
+ * 1. Use 16 SMI variables for state instead of TypedArray
7
+ * 2. Use 16 SMI variables for message words
8
+ * 3. Fully inlined G function (no function call overhead)
9
+ * 4. Use `| 0` for integer coercion (forces V8 to use 32-bit ALU)
10
+ * 5. Hardcoded permutation swaps using only 2 temporary variables
11
+ * 6. Offset-based access pattern (avoid creating new views)
12
+ *
13
+ * The compression function takes:
14
+ * - cv: 8-word chaining value
15
+ * - block: 16-word message block (64 bytes)
16
+ * - counter: 64-bit block counter
17
+ * - blockLen: number of input bytes in this block
18
+ * - flags: domain separation flags
19
+ *
20
+ * And outputs 8 or 16 words depending on whether this is a root node.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.compress = compress;
24
+ /**
25
+ * Compress a single block.
26
+ *
27
+ * This is the hot path - every optimization matters here.
28
+ *
29
+ * @param cv - Chaining value array
30
+ * @param cvOff - Offset into cv
31
+ * @param block - Message block words
32
+ * @param blockOff - Offset into block
33
+ * @param out - Output array (8 or 16 words)
34
+ * @param outOff - Offset into out
35
+ * @param full - If true, output all 16 words (for XOF); if false, output 8 words
36
+ * @param counter - 64-bit block counter
37
+ * @param blockLen - Number of bytes in this block (0-64)
38
+ * @param flags - Domain separation flags
39
+ */
40
+ function compress(cv, cvOff, block, blockOff, out, outOff, full, counter, blockLen, flags) {
41
+ // Load message words into SMI variables for maximum performance
42
+ // V8 optimizes SMI arithmetic directly with the ALU
43
+ let m0 = block[blockOff] | 0;
44
+ let m1 = block[blockOff + 1] | 0;
45
+ let m2 = block[blockOff + 2] | 0;
46
+ let m3 = block[blockOff + 3] | 0;
47
+ let m4 = block[blockOff + 4] | 0;
48
+ let m5 = block[blockOff + 5] | 0;
49
+ let m6 = block[blockOff + 6] | 0;
50
+ let m7 = block[blockOff + 7] | 0;
51
+ let m8 = block[blockOff + 8] | 0;
52
+ let m9 = block[blockOff + 9] | 0;
53
+ let m10 = block[blockOff + 10] | 0;
54
+ let m11 = block[blockOff + 11] | 0;
55
+ let m12 = block[blockOff + 12] | 0;
56
+ let m13 = block[blockOff + 13] | 0;
57
+ let m14 = block[blockOff + 14] | 0;
58
+ let m15 = block[blockOff + 15] | 0;
59
+ // Initialize state: first 8 words from chaining value
60
+ let s0 = cv[cvOff] | 0;
61
+ let s1 = cv[cvOff + 1] | 0;
62
+ let s2 = cv[cvOff + 2] | 0;
63
+ let s3 = cv[cvOff + 3] | 0;
64
+ let s4 = cv[cvOff + 4] | 0;
65
+ let s5 = cv[cvOff + 5] | 0;
66
+ let s6 = cv[cvOff + 6] | 0;
67
+ let s7 = cv[cvOff + 7] | 0;
68
+ // Words 8-11: IV constants
69
+ let s8 = 0x6a09e667;
70
+ let s9 = 0xbb67ae85;
71
+ let s10 = 0x3c6ef372;
72
+ let s11 = 0xa54ff53a;
73
+ // Words 12-15: counter, blockLen, flags
74
+ // Note: counter is 64-bit, split into low and high 32-bit words
75
+ let s12 = counter | 0;
76
+ let s13 = (counter / 0x100000000) | 0;
77
+ let s14 = blockLen | 0;
78
+ let s15 = flags | 0;
79
+ // ============================================================
80
+ // 7 rounds of mixing
81
+ // Each round consists of 4 column G functions and 4 diagonal G functions
82
+ // followed by a message word permutation (except for round 7)
83
+ // ============================================================
84
+ // ROUND 1 (message schedule: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
85
+ // Column G functions
86
+ // G(0, 4, 8, 12) with m0, m1
87
+ s0 = (((s0 + s4) | 0) + m0) | 0;
88
+ s12 ^= s0;
89
+ s12 = (s12 >>> 16) | (s12 << 16);
90
+ s8 = (s8 + s12) | 0;
91
+ s4 ^= s8;
92
+ s4 = (s4 >>> 12) | (s4 << 20);
93
+ s0 = (((s0 + s4) | 0) + m1) | 0;
94
+ s12 ^= s0;
95
+ s12 = (s12 >>> 8) | (s12 << 24);
96
+ s8 = (s8 + s12) | 0;
97
+ s4 ^= s8;
98
+ s4 = (s4 >>> 7) | (s4 << 25);
99
+ // G(1, 5, 9, 13) with m2, m3
100
+ s1 = (((s1 + s5) | 0) + m2) | 0;
101
+ s13 ^= s1;
102
+ s13 = (s13 >>> 16) | (s13 << 16);
103
+ s9 = (s9 + s13) | 0;
104
+ s5 ^= s9;
105
+ s5 = (s5 >>> 12) | (s5 << 20);
106
+ s1 = (((s1 + s5) | 0) + m3) | 0;
107
+ s13 ^= s1;
108
+ s13 = (s13 >>> 8) | (s13 << 24);
109
+ s9 = (s9 + s13) | 0;
110
+ s5 ^= s9;
111
+ s5 = (s5 >>> 7) | (s5 << 25);
112
+ // G(2, 6, 10, 14) with m4, m5
113
+ s2 = (((s2 + s6) | 0) + m4) | 0;
114
+ s14 ^= s2;
115
+ s14 = (s14 >>> 16) | (s14 << 16);
116
+ s10 = (s10 + s14) | 0;
117
+ s6 ^= s10;
118
+ s6 = (s6 >>> 12) | (s6 << 20);
119
+ s2 = (((s2 + s6) | 0) + m5) | 0;
120
+ s14 ^= s2;
121
+ s14 = (s14 >>> 8) | (s14 << 24);
122
+ s10 = (s10 + s14) | 0;
123
+ s6 ^= s10;
124
+ s6 = (s6 >>> 7) | (s6 << 25);
125
+ // G(3, 7, 11, 15) with m6, m7
126
+ s3 = (((s3 + s7) | 0) + m6) | 0;
127
+ s15 ^= s3;
128
+ s15 = (s15 >>> 16) | (s15 << 16);
129
+ s11 = (s11 + s15) | 0;
130
+ s7 ^= s11;
131
+ s7 = (s7 >>> 12) | (s7 << 20);
132
+ s3 = (((s3 + s7) | 0) + m7) | 0;
133
+ s15 ^= s3;
134
+ s15 = (s15 >>> 8) | (s15 << 24);
135
+ s11 = (s11 + s15) | 0;
136
+ s7 ^= s11;
137
+ s7 = (s7 >>> 7) | (s7 << 25);
138
+ // Diagonal G functions
139
+ // G(0, 5, 10, 15) with m8, m9
140
+ s0 = (((s0 + s5) | 0) + m8) | 0;
141
+ s15 ^= s0;
142
+ s15 = (s15 >>> 16) | (s15 << 16);
143
+ s10 = (s10 + s15) | 0;
144
+ s5 ^= s10;
145
+ s5 = (s5 >>> 12) | (s5 << 20);
146
+ s0 = (((s0 + s5) | 0) + m9) | 0;
147
+ s15 ^= s0;
148
+ s15 = (s15 >>> 8) | (s15 << 24);
149
+ s10 = (s10 + s15) | 0;
150
+ s5 ^= s10;
151
+ s5 = (s5 >>> 7) | (s5 << 25);
152
+ // G(1, 6, 11, 12) with m10, m11
153
+ s1 = (((s1 + s6) | 0) + m10) | 0;
154
+ s12 ^= s1;
155
+ s12 = (s12 >>> 16) | (s12 << 16);
156
+ s11 = (s11 + s12) | 0;
157
+ s6 ^= s11;
158
+ s6 = (s6 >>> 12) | (s6 << 20);
159
+ s1 = (((s1 + s6) | 0) + m11) | 0;
160
+ s12 ^= s1;
161
+ s12 = (s12 >>> 8) | (s12 << 24);
162
+ s11 = (s11 + s12) | 0;
163
+ s6 ^= s11;
164
+ s6 = (s6 >>> 7) | (s6 << 25);
165
+ // G(2, 7, 8, 13) with m12, m13
166
+ s2 = (((s2 + s7) | 0) + m12) | 0;
167
+ s13 ^= s2;
168
+ s13 = (s13 >>> 16) | (s13 << 16);
169
+ s8 = (s8 + s13) | 0;
170
+ s7 ^= s8;
171
+ s7 = (s7 >>> 12) | (s7 << 20);
172
+ s2 = (((s2 + s7) | 0) + m13) | 0;
173
+ s13 ^= s2;
174
+ s13 = (s13 >>> 8) | (s13 << 24);
175
+ s8 = (s8 + s13) | 0;
176
+ s7 ^= s8;
177
+ s7 = (s7 >>> 7) | (s7 << 25);
178
+ // G(3, 4, 9, 14) with m14, m15
179
+ s3 = (((s3 + s4) | 0) + m14) | 0;
180
+ s14 ^= s3;
181
+ s14 = (s14 >>> 16) | (s14 << 16);
182
+ s9 = (s9 + s14) | 0;
183
+ s4 ^= s9;
184
+ s4 = (s4 >>> 12) | (s4 << 20);
185
+ s3 = (((s3 + s4) | 0) + m15) | 0;
186
+ s14 ^= s3;
187
+ s14 = (s14 >>> 8) | (s14 << 24);
188
+ s9 = (s9 + s14) | 0;
189
+ s4 ^= s9;
190
+ s4 = (s4 >>> 7) | (s4 << 25);
191
+ // Permute message words for round 2
192
+ // Permutation: [2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8]
193
+ // Using 2 temps for the two cycles in the permutation
194
+ {
195
+ const t0 = m0, t1 = m1;
196
+ m0 = m2;
197
+ m2 = m3;
198
+ m3 = m10;
199
+ m10 = m12;
200
+ m12 = m9;
201
+ m9 = m11;
202
+ m11 = m5;
203
+ m5 = t0;
204
+ m1 = m6;
205
+ m6 = m4;
206
+ m4 = m7;
207
+ m7 = m13;
208
+ m13 = m14;
209
+ m14 = m15;
210
+ m15 = m8;
211
+ m8 = t1;
212
+ }
213
+ // ROUND 2 (message schedule: 2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8)
214
+ s0 = (((s0 + s4) | 0) + m0) | 0;
215
+ s12 ^= s0;
216
+ s12 = (s12 >>> 16) | (s12 << 16);
217
+ s8 = (s8 + s12) | 0;
218
+ s4 ^= s8;
219
+ s4 = (s4 >>> 12) | (s4 << 20);
220
+ s0 = (((s0 + s4) | 0) + m1) | 0;
221
+ s12 ^= s0;
222
+ s12 = (s12 >>> 8) | (s12 << 24);
223
+ s8 = (s8 + s12) | 0;
224
+ s4 ^= s8;
225
+ s4 = (s4 >>> 7) | (s4 << 25);
226
+ s1 = (((s1 + s5) | 0) + m2) | 0;
227
+ s13 ^= s1;
228
+ s13 = (s13 >>> 16) | (s13 << 16);
229
+ s9 = (s9 + s13) | 0;
230
+ s5 ^= s9;
231
+ s5 = (s5 >>> 12) | (s5 << 20);
232
+ s1 = (((s1 + s5) | 0) + m3) | 0;
233
+ s13 ^= s1;
234
+ s13 = (s13 >>> 8) | (s13 << 24);
235
+ s9 = (s9 + s13) | 0;
236
+ s5 ^= s9;
237
+ s5 = (s5 >>> 7) | (s5 << 25);
238
+ s2 = (((s2 + s6) | 0) + m4) | 0;
239
+ s14 ^= s2;
240
+ s14 = (s14 >>> 16) | (s14 << 16);
241
+ s10 = (s10 + s14) | 0;
242
+ s6 ^= s10;
243
+ s6 = (s6 >>> 12) | (s6 << 20);
244
+ s2 = (((s2 + s6) | 0) + m5) | 0;
245
+ s14 ^= s2;
246
+ s14 = (s14 >>> 8) | (s14 << 24);
247
+ s10 = (s10 + s14) | 0;
248
+ s6 ^= s10;
249
+ s6 = (s6 >>> 7) | (s6 << 25);
250
+ s3 = (((s3 + s7) | 0) + m6) | 0;
251
+ s15 ^= s3;
252
+ s15 = (s15 >>> 16) | (s15 << 16);
253
+ s11 = (s11 + s15) | 0;
254
+ s7 ^= s11;
255
+ s7 = (s7 >>> 12) | (s7 << 20);
256
+ s3 = (((s3 + s7) | 0) + m7) | 0;
257
+ s15 ^= s3;
258
+ s15 = (s15 >>> 8) | (s15 << 24);
259
+ s11 = (s11 + s15) | 0;
260
+ s7 ^= s11;
261
+ s7 = (s7 >>> 7) | (s7 << 25);
262
+ s0 = (((s0 + s5) | 0) + m8) | 0;
263
+ s15 ^= s0;
264
+ s15 = (s15 >>> 16) | (s15 << 16);
265
+ s10 = (s10 + s15) | 0;
266
+ s5 ^= s10;
267
+ s5 = (s5 >>> 12) | (s5 << 20);
268
+ s0 = (((s0 + s5) | 0) + m9) | 0;
269
+ s15 ^= s0;
270
+ s15 = (s15 >>> 8) | (s15 << 24);
271
+ s10 = (s10 + s15) | 0;
272
+ s5 ^= s10;
273
+ s5 = (s5 >>> 7) | (s5 << 25);
274
+ s1 = (((s1 + s6) | 0) + m10) | 0;
275
+ s12 ^= s1;
276
+ s12 = (s12 >>> 16) | (s12 << 16);
277
+ s11 = (s11 + s12) | 0;
278
+ s6 ^= s11;
279
+ s6 = (s6 >>> 12) | (s6 << 20);
280
+ s1 = (((s1 + s6) | 0) + m11) | 0;
281
+ s12 ^= s1;
282
+ s12 = (s12 >>> 8) | (s12 << 24);
283
+ s11 = (s11 + s12) | 0;
284
+ s6 ^= s11;
285
+ s6 = (s6 >>> 7) | (s6 << 25);
286
+ s2 = (((s2 + s7) | 0) + m12) | 0;
287
+ s13 ^= s2;
288
+ s13 = (s13 >>> 16) | (s13 << 16);
289
+ s8 = (s8 + s13) | 0;
290
+ s7 ^= s8;
291
+ s7 = (s7 >>> 12) | (s7 << 20);
292
+ s2 = (((s2 + s7) | 0) + m13) | 0;
293
+ s13 ^= s2;
294
+ s13 = (s13 >>> 8) | (s13 << 24);
295
+ s8 = (s8 + s13) | 0;
296
+ s7 ^= s8;
297
+ s7 = (s7 >>> 7) | (s7 << 25);
298
+ s3 = (((s3 + s4) | 0) + m14) | 0;
299
+ s14 ^= s3;
300
+ s14 = (s14 >>> 16) | (s14 << 16);
301
+ s9 = (s9 + s14) | 0;
302
+ s4 ^= s9;
303
+ s4 = (s4 >>> 12) | (s4 << 20);
304
+ s3 = (((s3 + s4) | 0) + m15) | 0;
305
+ s14 ^= s3;
306
+ s14 = (s14 >>> 8) | (s14 << 24);
307
+ s9 = (s9 + s14) | 0;
308
+ s4 ^= s9;
309
+ s4 = (s4 >>> 7) | (s4 << 25);
310
+ // Permute for round 3
311
+ {
312
+ const t0 = m0, t1 = m1;
313
+ m0 = m2;
314
+ m2 = m3;
315
+ m3 = m10;
316
+ m10 = m12;
317
+ m12 = m9;
318
+ m9 = m11;
319
+ m11 = m5;
320
+ m5 = t0;
321
+ m1 = m6;
322
+ m6 = m4;
323
+ m4 = m7;
324
+ m7 = m13;
325
+ m13 = m14;
326
+ m14 = m15;
327
+ m15 = m8;
328
+ m8 = t1;
329
+ }
330
+ // ROUND 3 (message schedule: 3,4,10,12,13,2,7,14,6,5,9,0,11,15,8,1)
331
+ s0 = (((s0 + s4) | 0) + m0) | 0;
332
+ s12 ^= s0;
333
+ s12 = (s12 >>> 16) | (s12 << 16);
334
+ s8 = (s8 + s12) | 0;
335
+ s4 ^= s8;
336
+ s4 = (s4 >>> 12) | (s4 << 20);
337
+ s0 = (((s0 + s4) | 0) + m1) | 0;
338
+ s12 ^= s0;
339
+ s12 = (s12 >>> 8) | (s12 << 24);
340
+ s8 = (s8 + s12) | 0;
341
+ s4 ^= s8;
342
+ s4 = (s4 >>> 7) | (s4 << 25);
343
+ s1 = (((s1 + s5) | 0) + m2) | 0;
344
+ s13 ^= s1;
345
+ s13 = (s13 >>> 16) | (s13 << 16);
346
+ s9 = (s9 + s13) | 0;
347
+ s5 ^= s9;
348
+ s5 = (s5 >>> 12) | (s5 << 20);
349
+ s1 = (((s1 + s5) | 0) + m3) | 0;
350
+ s13 ^= s1;
351
+ s13 = (s13 >>> 8) | (s13 << 24);
352
+ s9 = (s9 + s13) | 0;
353
+ s5 ^= s9;
354
+ s5 = (s5 >>> 7) | (s5 << 25);
355
+ s2 = (((s2 + s6) | 0) + m4) | 0;
356
+ s14 ^= s2;
357
+ s14 = (s14 >>> 16) | (s14 << 16);
358
+ s10 = (s10 + s14) | 0;
359
+ s6 ^= s10;
360
+ s6 = (s6 >>> 12) | (s6 << 20);
361
+ s2 = (((s2 + s6) | 0) + m5) | 0;
362
+ s14 ^= s2;
363
+ s14 = (s14 >>> 8) | (s14 << 24);
364
+ s10 = (s10 + s14) | 0;
365
+ s6 ^= s10;
366
+ s6 = (s6 >>> 7) | (s6 << 25);
367
+ s3 = (((s3 + s7) | 0) + m6) | 0;
368
+ s15 ^= s3;
369
+ s15 = (s15 >>> 16) | (s15 << 16);
370
+ s11 = (s11 + s15) | 0;
371
+ s7 ^= s11;
372
+ s7 = (s7 >>> 12) | (s7 << 20);
373
+ s3 = (((s3 + s7) | 0) + m7) | 0;
374
+ s15 ^= s3;
375
+ s15 = (s15 >>> 8) | (s15 << 24);
376
+ s11 = (s11 + s15) | 0;
377
+ s7 ^= s11;
378
+ s7 = (s7 >>> 7) | (s7 << 25);
379
+ s0 = (((s0 + s5) | 0) + m8) | 0;
380
+ s15 ^= s0;
381
+ s15 = (s15 >>> 16) | (s15 << 16);
382
+ s10 = (s10 + s15) | 0;
383
+ s5 ^= s10;
384
+ s5 = (s5 >>> 12) | (s5 << 20);
385
+ s0 = (((s0 + s5) | 0) + m9) | 0;
386
+ s15 ^= s0;
387
+ s15 = (s15 >>> 8) | (s15 << 24);
388
+ s10 = (s10 + s15) | 0;
389
+ s5 ^= s10;
390
+ s5 = (s5 >>> 7) | (s5 << 25);
391
+ s1 = (((s1 + s6) | 0) + m10) | 0;
392
+ s12 ^= s1;
393
+ s12 = (s12 >>> 16) | (s12 << 16);
394
+ s11 = (s11 + s12) | 0;
395
+ s6 ^= s11;
396
+ s6 = (s6 >>> 12) | (s6 << 20);
397
+ s1 = (((s1 + s6) | 0) + m11) | 0;
398
+ s12 ^= s1;
399
+ s12 = (s12 >>> 8) | (s12 << 24);
400
+ s11 = (s11 + s12) | 0;
401
+ s6 ^= s11;
402
+ s6 = (s6 >>> 7) | (s6 << 25);
403
+ s2 = (((s2 + s7) | 0) + m12) | 0;
404
+ s13 ^= s2;
405
+ s13 = (s13 >>> 16) | (s13 << 16);
406
+ s8 = (s8 + s13) | 0;
407
+ s7 ^= s8;
408
+ s7 = (s7 >>> 12) | (s7 << 20);
409
+ s2 = (((s2 + s7) | 0) + m13) | 0;
410
+ s13 ^= s2;
411
+ s13 = (s13 >>> 8) | (s13 << 24);
412
+ s8 = (s8 + s13) | 0;
413
+ s7 ^= s8;
414
+ s7 = (s7 >>> 7) | (s7 << 25);
415
+ s3 = (((s3 + s4) | 0) + m14) | 0;
416
+ s14 ^= s3;
417
+ s14 = (s14 >>> 16) | (s14 << 16);
418
+ s9 = (s9 + s14) | 0;
419
+ s4 ^= s9;
420
+ s4 = (s4 >>> 12) | (s4 << 20);
421
+ s3 = (((s3 + s4) | 0) + m15) | 0;
422
+ s14 ^= s3;
423
+ s14 = (s14 >>> 8) | (s14 << 24);
424
+ s9 = (s9 + s14) | 0;
425
+ s4 ^= s9;
426
+ s4 = (s4 >>> 7) | (s4 << 25);
427
+ // Permute for round 4
428
+ {
429
+ const t0 = m0, t1 = m1;
430
+ m0 = m2;
431
+ m2 = m3;
432
+ m3 = m10;
433
+ m10 = m12;
434
+ m12 = m9;
435
+ m9 = m11;
436
+ m11 = m5;
437
+ m5 = t0;
438
+ m1 = m6;
439
+ m6 = m4;
440
+ m4 = m7;
441
+ m7 = m13;
442
+ m13 = m14;
443
+ m14 = m15;
444
+ m15 = m8;
445
+ m8 = t1;
446
+ }
447
+ // ROUND 4 (message schedule: 10,7,12,9,14,3,13,15,4,0,11,2,5,8,1,6)
448
+ s0 = (((s0 + s4) | 0) + m0) | 0;
449
+ s12 ^= s0;
450
+ s12 = (s12 >>> 16) | (s12 << 16);
451
+ s8 = (s8 + s12) | 0;
452
+ s4 ^= s8;
453
+ s4 = (s4 >>> 12) | (s4 << 20);
454
+ s0 = (((s0 + s4) | 0) + m1) | 0;
455
+ s12 ^= s0;
456
+ s12 = (s12 >>> 8) | (s12 << 24);
457
+ s8 = (s8 + s12) | 0;
458
+ s4 ^= s8;
459
+ s4 = (s4 >>> 7) | (s4 << 25);
460
+ s1 = (((s1 + s5) | 0) + m2) | 0;
461
+ s13 ^= s1;
462
+ s13 = (s13 >>> 16) | (s13 << 16);
463
+ s9 = (s9 + s13) | 0;
464
+ s5 ^= s9;
465
+ s5 = (s5 >>> 12) | (s5 << 20);
466
+ s1 = (((s1 + s5) | 0) + m3) | 0;
467
+ s13 ^= s1;
468
+ s13 = (s13 >>> 8) | (s13 << 24);
469
+ s9 = (s9 + s13) | 0;
470
+ s5 ^= s9;
471
+ s5 = (s5 >>> 7) | (s5 << 25);
472
+ s2 = (((s2 + s6) | 0) + m4) | 0;
473
+ s14 ^= s2;
474
+ s14 = (s14 >>> 16) | (s14 << 16);
475
+ s10 = (s10 + s14) | 0;
476
+ s6 ^= s10;
477
+ s6 = (s6 >>> 12) | (s6 << 20);
478
+ s2 = (((s2 + s6) | 0) + m5) | 0;
479
+ s14 ^= s2;
480
+ s14 = (s14 >>> 8) | (s14 << 24);
481
+ s10 = (s10 + s14) | 0;
482
+ s6 ^= s10;
483
+ s6 = (s6 >>> 7) | (s6 << 25);
484
+ s3 = (((s3 + s7) | 0) + m6) | 0;
485
+ s15 ^= s3;
486
+ s15 = (s15 >>> 16) | (s15 << 16);
487
+ s11 = (s11 + s15) | 0;
488
+ s7 ^= s11;
489
+ s7 = (s7 >>> 12) | (s7 << 20);
490
+ s3 = (((s3 + s7) | 0) + m7) | 0;
491
+ s15 ^= s3;
492
+ s15 = (s15 >>> 8) | (s15 << 24);
493
+ s11 = (s11 + s15) | 0;
494
+ s7 ^= s11;
495
+ s7 = (s7 >>> 7) | (s7 << 25);
496
+ s0 = (((s0 + s5) | 0) + m8) | 0;
497
+ s15 ^= s0;
498
+ s15 = (s15 >>> 16) | (s15 << 16);
499
+ s10 = (s10 + s15) | 0;
500
+ s5 ^= s10;
501
+ s5 = (s5 >>> 12) | (s5 << 20);
502
+ s0 = (((s0 + s5) | 0) + m9) | 0;
503
+ s15 ^= s0;
504
+ s15 = (s15 >>> 8) | (s15 << 24);
505
+ s10 = (s10 + s15) | 0;
506
+ s5 ^= s10;
507
+ s5 = (s5 >>> 7) | (s5 << 25);
508
+ s1 = (((s1 + s6) | 0) + m10) | 0;
509
+ s12 ^= s1;
510
+ s12 = (s12 >>> 16) | (s12 << 16);
511
+ s11 = (s11 + s12) | 0;
512
+ s6 ^= s11;
513
+ s6 = (s6 >>> 12) | (s6 << 20);
514
+ s1 = (((s1 + s6) | 0) + m11) | 0;
515
+ s12 ^= s1;
516
+ s12 = (s12 >>> 8) | (s12 << 24);
517
+ s11 = (s11 + s12) | 0;
518
+ s6 ^= s11;
519
+ s6 = (s6 >>> 7) | (s6 << 25);
520
+ s2 = (((s2 + s7) | 0) + m12) | 0;
521
+ s13 ^= s2;
522
+ s13 = (s13 >>> 16) | (s13 << 16);
523
+ s8 = (s8 + s13) | 0;
524
+ s7 ^= s8;
525
+ s7 = (s7 >>> 12) | (s7 << 20);
526
+ s2 = (((s2 + s7) | 0) + m13) | 0;
527
+ s13 ^= s2;
528
+ s13 = (s13 >>> 8) | (s13 << 24);
529
+ s8 = (s8 + s13) | 0;
530
+ s7 ^= s8;
531
+ s7 = (s7 >>> 7) | (s7 << 25);
532
+ s3 = (((s3 + s4) | 0) + m14) | 0;
533
+ s14 ^= s3;
534
+ s14 = (s14 >>> 16) | (s14 << 16);
535
+ s9 = (s9 + s14) | 0;
536
+ s4 ^= s9;
537
+ s4 = (s4 >>> 12) | (s4 << 20);
538
+ s3 = (((s3 + s4) | 0) + m15) | 0;
539
+ s14 ^= s3;
540
+ s14 = (s14 >>> 8) | (s14 << 24);
541
+ s9 = (s9 + s14) | 0;
542
+ s4 ^= s9;
543
+ s4 = (s4 >>> 7) | (s4 << 25);
544
+ // Permute for round 5
545
+ {
546
+ const t0 = m0, t1 = m1;
547
+ m0 = m2;
548
+ m2 = m3;
549
+ m3 = m10;
550
+ m10 = m12;
551
+ m12 = m9;
552
+ m9 = m11;
553
+ m11 = m5;
554
+ m5 = t0;
555
+ m1 = m6;
556
+ m6 = m4;
557
+ m4 = m7;
558
+ m7 = m13;
559
+ m13 = m14;
560
+ m14 = m15;
561
+ m15 = m8;
562
+ m8 = t1;
563
+ }
564
+ // ROUND 5 (message schedule: 12,13,9,11,15,10,14,8,7,2,5,3,0,1,6,4)
565
+ s0 = (((s0 + s4) | 0) + m0) | 0;
566
+ s12 ^= s0;
567
+ s12 = (s12 >>> 16) | (s12 << 16);
568
+ s8 = (s8 + s12) | 0;
569
+ s4 ^= s8;
570
+ s4 = (s4 >>> 12) | (s4 << 20);
571
+ s0 = (((s0 + s4) | 0) + m1) | 0;
572
+ s12 ^= s0;
573
+ s12 = (s12 >>> 8) | (s12 << 24);
574
+ s8 = (s8 + s12) | 0;
575
+ s4 ^= s8;
576
+ s4 = (s4 >>> 7) | (s4 << 25);
577
+ s1 = (((s1 + s5) | 0) + m2) | 0;
578
+ s13 ^= s1;
579
+ s13 = (s13 >>> 16) | (s13 << 16);
580
+ s9 = (s9 + s13) | 0;
581
+ s5 ^= s9;
582
+ s5 = (s5 >>> 12) | (s5 << 20);
583
+ s1 = (((s1 + s5) | 0) + m3) | 0;
584
+ s13 ^= s1;
585
+ s13 = (s13 >>> 8) | (s13 << 24);
586
+ s9 = (s9 + s13) | 0;
587
+ s5 ^= s9;
588
+ s5 = (s5 >>> 7) | (s5 << 25);
589
+ s2 = (((s2 + s6) | 0) + m4) | 0;
590
+ s14 ^= s2;
591
+ s14 = (s14 >>> 16) | (s14 << 16);
592
+ s10 = (s10 + s14) | 0;
593
+ s6 ^= s10;
594
+ s6 = (s6 >>> 12) | (s6 << 20);
595
+ s2 = (((s2 + s6) | 0) + m5) | 0;
596
+ s14 ^= s2;
597
+ s14 = (s14 >>> 8) | (s14 << 24);
598
+ s10 = (s10 + s14) | 0;
599
+ s6 ^= s10;
600
+ s6 = (s6 >>> 7) | (s6 << 25);
601
+ s3 = (((s3 + s7) | 0) + m6) | 0;
602
+ s15 ^= s3;
603
+ s15 = (s15 >>> 16) | (s15 << 16);
604
+ s11 = (s11 + s15) | 0;
605
+ s7 ^= s11;
606
+ s7 = (s7 >>> 12) | (s7 << 20);
607
+ s3 = (((s3 + s7) | 0) + m7) | 0;
608
+ s15 ^= s3;
609
+ s15 = (s15 >>> 8) | (s15 << 24);
610
+ s11 = (s11 + s15) | 0;
611
+ s7 ^= s11;
612
+ s7 = (s7 >>> 7) | (s7 << 25);
613
+ s0 = (((s0 + s5) | 0) + m8) | 0;
614
+ s15 ^= s0;
615
+ s15 = (s15 >>> 16) | (s15 << 16);
616
+ s10 = (s10 + s15) | 0;
617
+ s5 ^= s10;
618
+ s5 = (s5 >>> 12) | (s5 << 20);
619
+ s0 = (((s0 + s5) | 0) + m9) | 0;
620
+ s15 ^= s0;
621
+ s15 = (s15 >>> 8) | (s15 << 24);
622
+ s10 = (s10 + s15) | 0;
623
+ s5 ^= s10;
624
+ s5 = (s5 >>> 7) | (s5 << 25);
625
+ s1 = (((s1 + s6) | 0) + m10) | 0;
626
+ s12 ^= s1;
627
+ s12 = (s12 >>> 16) | (s12 << 16);
628
+ s11 = (s11 + s12) | 0;
629
+ s6 ^= s11;
630
+ s6 = (s6 >>> 12) | (s6 << 20);
631
+ s1 = (((s1 + s6) | 0) + m11) | 0;
632
+ s12 ^= s1;
633
+ s12 = (s12 >>> 8) | (s12 << 24);
634
+ s11 = (s11 + s12) | 0;
635
+ s6 ^= s11;
636
+ s6 = (s6 >>> 7) | (s6 << 25);
637
+ s2 = (((s2 + s7) | 0) + m12) | 0;
638
+ s13 ^= s2;
639
+ s13 = (s13 >>> 16) | (s13 << 16);
640
+ s8 = (s8 + s13) | 0;
641
+ s7 ^= s8;
642
+ s7 = (s7 >>> 12) | (s7 << 20);
643
+ s2 = (((s2 + s7) | 0) + m13) | 0;
644
+ s13 ^= s2;
645
+ s13 = (s13 >>> 8) | (s13 << 24);
646
+ s8 = (s8 + s13) | 0;
647
+ s7 ^= s8;
648
+ s7 = (s7 >>> 7) | (s7 << 25);
649
+ s3 = (((s3 + s4) | 0) + m14) | 0;
650
+ s14 ^= s3;
651
+ s14 = (s14 >>> 16) | (s14 << 16);
652
+ s9 = (s9 + s14) | 0;
653
+ s4 ^= s9;
654
+ s4 = (s4 >>> 12) | (s4 << 20);
655
+ s3 = (((s3 + s4) | 0) + m15) | 0;
656
+ s14 ^= s3;
657
+ s14 = (s14 >>> 8) | (s14 << 24);
658
+ s9 = (s9 + s14) | 0;
659
+ s4 ^= s9;
660
+ s4 = (s4 >>> 7) | (s4 << 25);
661
+ // Permute for round 6
662
+ {
663
+ const t0 = m0, t1 = m1;
664
+ m0 = m2;
665
+ m2 = m3;
666
+ m3 = m10;
667
+ m10 = m12;
668
+ m12 = m9;
669
+ m9 = m11;
670
+ m11 = m5;
671
+ m5 = t0;
672
+ m1 = m6;
673
+ m6 = m4;
674
+ m4 = m7;
675
+ m7 = m13;
676
+ m13 = m14;
677
+ m14 = m15;
678
+ m15 = m8;
679
+ m8 = t1;
680
+ }
681
+ // ROUND 6 (message schedule: 9,14,11,5,8,12,15,1,13,3,0,10,2,6,4,7)
682
+ s0 = (((s0 + s4) | 0) + m0) | 0;
683
+ s12 ^= s0;
684
+ s12 = (s12 >>> 16) | (s12 << 16);
685
+ s8 = (s8 + s12) | 0;
686
+ s4 ^= s8;
687
+ s4 = (s4 >>> 12) | (s4 << 20);
688
+ s0 = (((s0 + s4) | 0) + m1) | 0;
689
+ s12 ^= s0;
690
+ s12 = (s12 >>> 8) | (s12 << 24);
691
+ s8 = (s8 + s12) | 0;
692
+ s4 ^= s8;
693
+ s4 = (s4 >>> 7) | (s4 << 25);
694
+ s1 = (((s1 + s5) | 0) + m2) | 0;
695
+ s13 ^= s1;
696
+ s13 = (s13 >>> 16) | (s13 << 16);
697
+ s9 = (s9 + s13) | 0;
698
+ s5 ^= s9;
699
+ s5 = (s5 >>> 12) | (s5 << 20);
700
+ s1 = (((s1 + s5) | 0) + m3) | 0;
701
+ s13 ^= s1;
702
+ s13 = (s13 >>> 8) | (s13 << 24);
703
+ s9 = (s9 + s13) | 0;
704
+ s5 ^= s9;
705
+ s5 = (s5 >>> 7) | (s5 << 25);
706
+ s2 = (((s2 + s6) | 0) + m4) | 0;
707
+ s14 ^= s2;
708
+ s14 = (s14 >>> 16) | (s14 << 16);
709
+ s10 = (s10 + s14) | 0;
710
+ s6 ^= s10;
711
+ s6 = (s6 >>> 12) | (s6 << 20);
712
+ s2 = (((s2 + s6) | 0) + m5) | 0;
713
+ s14 ^= s2;
714
+ s14 = (s14 >>> 8) | (s14 << 24);
715
+ s10 = (s10 + s14) | 0;
716
+ s6 ^= s10;
717
+ s6 = (s6 >>> 7) | (s6 << 25);
718
+ s3 = (((s3 + s7) | 0) + m6) | 0;
719
+ s15 ^= s3;
720
+ s15 = (s15 >>> 16) | (s15 << 16);
721
+ s11 = (s11 + s15) | 0;
722
+ s7 ^= s11;
723
+ s7 = (s7 >>> 12) | (s7 << 20);
724
+ s3 = (((s3 + s7) | 0) + m7) | 0;
725
+ s15 ^= s3;
726
+ s15 = (s15 >>> 8) | (s15 << 24);
727
+ s11 = (s11 + s15) | 0;
728
+ s7 ^= s11;
729
+ s7 = (s7 >>> 7) | (s7 << 25);
730
+ s0 = (((s0 + s5) | 0) + m8) | 0;
731
+ s15 ^= s0;
732
+ s15 = (s15 >>> 16) | (s15 << 16);
733
+ s10 = (s10 + s15) | 0;
734
+ s5 ^= s10;
735
+ s5 = (s5 >>> 12) | (s5 << 20);
736
+ s0 = (((s0 + s5) | 0) + m9) | 0;
737
+ s15 ^= s0;
738
+ s15 = (s15 >>> 8) | (s15 << 24);
739
+ s10 = (s10 + s15) | 0;
740
+ s5 ^= s10;
741
+ s5 = (s5 >>> 7) | (s5 << 25);
742
+ s1 = (((s1 + s6) | 0) + m10) | 0;
743
+ s12 ^= s1;
744
+ s12 = (s12 >>> 16) | (s12 << 16);
745
+ s11 = (s11 + s12) | 0;
746
+ s6 ^= s11;
747
+ s6 = (s6 >>> 12) | (s6 << 20);
748
+ s1 = (((s1 + s6) | 0) + m11) | 0;
749
+ s12 ^= s1;
750
+ s12 = (s12 >>> 8) | (s12 << 24);
751
+ s11 = (s11 + s12) | 0;
752
+ s6 ^= s11;
753
+ s6 = (s6 >>> 7) | (s6 << 25);
754
+ s2 = (((s2 + s7) | 0) + m12) | 0;
755
+ s13 ^= s2;
756
+ s13 = (s13 >>> 16) | (s13 << 16);
757
+ s8 = (s8 + s13) | 0;
758
+ s7 ^= s8;
759
+ s7 = (s7 >>> 12) | (s7 << 20);
760
+ s2 = (((s2 + s7) | 0) + m13) | 0;
761
+ s13 ^= s2;
762
+ s13 = (s13 >>> 8) | (s13 << 24);
763
+ s8 = (s8 + s13) | 0;
764
+ s7 ^= s8;
765
+ s7 = (s7 >>> 7) | (s7 << 25);
766
+ s3 = (((s3 + s4) | 0) + m14) | 0;
767
+ s14 ^= s3;
768
+ s14 = (s14 >>> 16) | (s14 << 16);
769
+ s9 = (s9 + s14) | 0;
770
+ s4 ^= s9;
771
+ s4 = (s4 >>> 12) | (s4 << 20);
772
+ s3 = (((s3 + s4) | 0) + m15) | 0;
773
+ s14 ^= s3;
774
+ s14 = (s14 >>> 8) | (s14 << 24);
775
+ s9 = (s9 + s14) | 0;
776
+ s4 ^= s9;
777
+ s4 = (s4 >>> 7) | (s4 << 25);
778
+ // Permute for round 7
779
+ {
780
+ const t0 = m0, t1 = m1;
781
+ m0 = m2;
782
+ m2 = m3;
783
+ m3 = m10;
784
+ m10 = m12;
785
+ m12 = m9;
786
+ m9 = m11;
787
+ m11 = m5;
788
+ m5 = t0;
789
+ m1 = m6;
790
+ m6 = m4;
791
+ m4 = m7;
792
+ m7 = m13;
793
+ m13 = m14;
794
+ m14 = m15;
795
+ m15 = m8;
796
+ m8 = t1;
797
+ }
798
+ // ROUND 7 (message schedule: 11,15,5,0,1,9,8,6,14,10,2,12,3,4,7,13)
799
+ s0 = (((s0 + s4) | 0) + m0) | 0;
800
+ s12 ^= s0;
801
+ s12 = (s12 >>> 16) | (s12 << 16);
802
+ s8 = (s8 + s12) | 0;
803
+ s4 ^= s8;
804
+ s4 = (s4 >>> 12) | (s4 << 20);
805
+ s0 = (((s0 + s4) | 0) + m1) | 0;
806
+ s12 ^= s0;
807
+ s12 = (s12 >>> 8) | (s12 << 24);
808
+ s8 = (s8 + s12) | 0;
809
+ s4 ^= s8;
810
+ s4 = (s4 >>> 7) | (s4 << 25);
811
+ s1 = (((s1 + s5) | 0) + m2) | 0;
812
+ s13 ^= s1;
813
+ s13 = (s13 >>> 16) | (s13 << 16);
814
+ s9 = (s9 + s13) | 0;
815
+ s5 ^= s9;
816
+ s5 = (s5 >>> 12) | (s5 << 20);
817
+ s1 = (((s1 + s5) | 0) + m3) | 0;
818
+ s13 ^= s1;
819
+ s13 = (s13 >>> 8) | (s13 << 24);
820
+ s9 = (s9 + s13) | 0;
821
+ s5 ^= s9;
822
+ s5 = (s5 >>> 7) | (s5 << 25);
823
+ s2 = (((s2 + s6) | 0) + m4) | 0;
824
+ s14 ^= s2;
825
+ s14 = (s14 >>> 16) | (s14 << 16);
826
+ s10 = (s10 + s14) | 0;
827
+ s6 ^= s10;
828
+ s6 = (s6 >>> 12) | (s6 << 20);
829
+ s2 = (((s2 + s6) | 0) + m5) | 0;
830
+ s14 ^= s2;
831
+ s14 = (s14 >>> 8) | (s14 << 24);
832
+ s10 = (s10 + s14) | 0;
833
+ s6 ^= s10;
834
+ s6 = (s6 >>> 7) | (s6 << 25);
835
+ s3 = (((s3 + s7) | 0) + m6) | 0;
836
+ s15 ^= s3;
837
+ s15 = (s15 >>> 16) | (s15 << 16);
838
+ s11 = (s11 + s15) | 0;
839
+ s7 ^= s11;
840
+ s7 = (s7 >>> 12) | (s7 << 20);
841
+ s3 = (((s3 + s7) | 0) + m7) | 0;
842
+ s15 ^= s3;
843
+ s15 = (s15 >>> 8) | (s15 << 24);
844
+ s11 = (s11 + s15) | 0;
845
+ s7 ^= s11;
846
+ s7 = (s7 >>> 7) | (s7 << 25);
847
+ s0 = (((s0 + s5) | 0) + m8) | 0;
848
+ s15 ^= s0;
849
+ s15 = (s15 >>> 16) | (s15 << 16);
850
+ s10 = (s10 + s15) | 0;
851
+ s5 ^= s10;
852
+ s5 = (s5 >>> 12) | (s5 << 20);
853
+ s0 = (((s0 + s5) | 0) + m9) | 0;
854
+ s15 ^= s0;
855
+ s15 = (s15 >>> 8) | (s15 << 24);
856
+ s10 = (s10 + s15) | 0;
857
+ s5 ^= s10;
858
+ s5 = (s5 >>> 7) | (s5 << 25);
859
+ s1 = (((s1 + s6) | 0) + m10) | 0;
860
+ s12 ^= s1;
861
+ s12 = (s12 >>> 16) | (s12 << 16);
862
+ s11 = (s11 + s12) | 0;
863
+ s6 ^= s11;
864
+ s6 = (s6 >>> 12) | (s6 << 20);
865
+ s1 = (((s1 + s6) | 0) + m11) | 0;
866
+ s12 ^= s1;
867
+ s12 = (s12 >>> 8) | (s12 << 24);
868
+ s11 = (s11 + s12) | 0;
869
+ s6 ^= s11;
870
+ s6 = (s6 >>> 7) | (s6 << 25);
871
+ s2 = (((s2 + s7) | 0) + m12) | 0;
872
+ s13 ^= s2;
873
+ s13 = (s13 >>> 16) | (s13 << 16);
874
+ s8 = (s8 + s13) | 0;
875
+ s7 ^= s8;
876
+ s7 = (s7 >>> 12) | (s7 << 20);
877
+ s2 = (((s2 + s7) | 0) + m13) | 0;
878
+ s13 ^= s2;
879
+ s13 = (s13 >>> 8) | (s13 << 24);
880
+ s8 = (s8 + s13) | 0;
881
+ s7 ^= s8;
882
+ s7 = (s7 >>> 7) | (s7 << 25);
883
+ s3 = (((s3 + s4) | 0) + m14) | 0;
884
+ s14 ^= s3;
885
+ s14 = (s14 >>> 16) | (s14 << 16);
886
+ s9 = (s9 + s14) | 0;
887
+ s4 ^= s9;
888
+ s4 = (s4 >>> 12) | (s4 << 20);
889
+ s3 = (((s3 + s4) | 0) + m15) | 0;
890
+ s14 ^= s3;
891
+ s14 = (s14 >>> 8) | (s14 << 24);
892
+ s9 = (s9 + s14) | 0;
893
+ s4 ^= s9;
894
+ s4 = (s4 >>> 7) | (s4 << 25);
895
+ // ============================================================
896
+ // Final XOR and output
897
+ // ============================================================
898
+ // If full output needed (XOF mode), write words 8-15 first
899
+ // (written first in case out === cv)
900
+ if (full) {
901
+ out[outOff + 8] = s8 ^ cv[cvOff];
902
+ out[outOff + 9] = s9 ^ cv[cvOff + 1];
903
+ out[outOff + 10] = s10 ^ cv[cvOff + 2];
904
+ out[outOff + 11] = s11 ^ cv[cvOff + 3];
905
+ out[outOff + 12] = s12 ^ cv[cvOff + 4];
906
+ out[outOff + 13] = s13 ^ cv[cvOff + 5];
907
+ out[outOff + 14] = s14 ^ cv[cvOff + 6];
908
+ out[outOff + 15] = s15 ^ cv[cvOff + 7];
909
+ }
910
+ // Standard output: XOR state[0..7] with state[8..15]
911
+ out[outOff] = s0 ^ s8;
912
+ out[outOff + 1] = s1 ^ s9;
913
+ out[outOff + 2] = s2 ^ s10;
914
+ out[outOff + 3] = s3 ^ s11;
915
+ out[outOff + 4] = s4 ^ s12;
916
+ out[outOff + 5] = s5 ^ s13;
917
+ out[outOff + 6] = s6 ^ s14;
918
+ out[outOff + 7] = s7 ^ s15;
919
+ }
node_modules/@huggingface/blake3-jit/dist/commonjs/constants.d.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Constants
3
+ *
4
+ * IV values are the same as SHA-256: first 32 bits of the fractional parts
5
+ * of the square roots of the first 8 primes (2..19)
6
+ */
7
+ export declare const IV: Uint32Array<ArrayBuffer>;
8
+ export declare const CHUNK_START = 1;
9
+ export declare const CHUNK_END: number;
10
+ export declare const PARENT: number;
11
+ export declare const ROOT: number;
12
+ export declare const KEYED_HASH: number;
13
+ export declare const DERIVE_KEY_CONTEXT: number;
14
+ export declare const DERIVE_KEY_MATERIAL: number;
15
+ export declare const OUT_LEN = 32;
16
+ export declare const KEY_LEN = 32;
17
+ export declare const BLOCK_LEN = 64;
18
+ export declare const CHUNK_LEN = 1024;
19
+ export declare const MAX_DEPTH = 54;
20
+ /**
21
+ * Precomputed message word permutations for all 7 rounds.
22
+ *
23
+ * The base permutation is: [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8]
24
+ * Each subsequent permutation is the previous one with this permutation applied.
25
+ *
26
+ * These are the indices into the message block for each round.
27
+ * By precomputing these, we avoid runtime permutation overhead.
28
+ */
29
+ export declare const MSG_SCHEDULE: ReadonlyArray<ReadonlyArray<number>>;
30
+ /**
31
+ * Flattened permutation table for compress function optimization.
32
+ * This enables direct indexed access: PERMUTATIONS[round * 16 + index]
33
+ */
34
+ export declare const PERMUTATIONS: Uint8Array<ArrayBuffer>;
node_modules/@huggingface/blake3-jit/dist/commonjs/constants.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ /**
3
+ * BLAKE3 Constants
4
+ *
5
+ * IV values are the same as SHA-256: first 32 bits of the fractional parts
6
+ * of the square roots of the first 8 primes (2..19)
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PERMUTATIONS = exports.MSG_SCHEDULE = exports.MAX_DEPTH = exports.CHUNK_LEN = exports.BLOCK_LEN = exports.KEY_LEN = exports.OUT_LEN = exports.DERIVE_KEY_MATERIAL = exports.DERIVE_KEY_CONTEXT = exports.KEYED_HASH = exports.ROOT = exports.PARENT = exports.CHUNK_END = exports.CHUNK_START = exports.IV = void 0;
10
+ // Initialization Vector (same as SHA-256)
11
+ exports.IV = new Uint32Array([
12
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
13
+ ]);
14
+ // Domain separation flags
15
+ exports.CHUNK_START = 1;
16
+ exports.CHUNK_END = 1 << 1;
17
+ exports.PARENT = 1 << 2;
18
+ exports.ROOT = 1 << 3;
19
+ exports.KEYED_HASH = 1 << 4;
20
+ exports.DERIVE_KEY_CONTEXT = 1 << 5;
21
+ exports.DERIVE_KEY_MATERIAL = 1 << 6;
22
+ // Size constants
23
+ exports.OUT_LEN = 32;
24
+ exports.KEY_LEN = 32;
25
+ exports.BLOCK_LEN = 64;
26
+ exports.CHUNK_LEN = 1024;
27
+ // Maximum depth of the CV stack (supports up to 2^54 bytes input)
28
+ exports.MAX_DEPTH = 54;
29
+ /**
30
+ * Precomputed message word permutations for all 7 rounds.
31
+ *
32
+ * The base permutation is: [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8]
33
+ * Each subsequent permutation is the previous one with this permutation applied.
34
+ *
35
+ * These are the indices into the message block for each round.
36
+ * By precomputing these, we avoid runtime permutation overhead.
37
+ */
38
+ exports.MSG_SCHEDULE = [
39
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
40
+ [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8],
41
+ [3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1],
42
+ [10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6],
43
+ [12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4],
44
+ [9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7],
45
+ [11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13],
46
+ ];
47
+ /**
48
+ * Flattened permutation table for compress function optimization.
49
+ * This enables direct indexed access: PERMUTATIONS[round * 16 + index]
50
+ */
51
+ exports.PERMUTATIONS = new Uint8Array([
52
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9,
53
+ 14, 15, 8, 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1, 10, 7, 12, 9, 14, 3, 13, 15, 4,
54
+ 0, 11, 2, 5, 8, 1, 6, 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4, 9, 14, 11, 5, 8, 12,
55
+ 15, 1, 13, 3, 0, 10, 2, 6, 4, 7, 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13,
56
+ ]);
node_modules/@huggingface/blake3-jit/dist/commonjs/hash.d.ts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hash Function - Simple one-shot API
3
+ *
4
+ * This provides a simple hash() function optimized for different input sizes.
5
+ * For small inputs, uses pure JS. For large inputs, uses WASM SIMD.
6
+ */
7
+ /**
8
+ * Hash input data and return the result.
9
+ * Automatically uses WASM SIMD for large inputs when available.
10
+ *
11
+ * @param input - Data to hash
12
+ * @param outputLength - Number of bytes to output (default: 32)
13
+ * @returns The hash output
14
+ */
15
+ export declare function hash(input: Uint8Array, outputLength?: number): Uint8Array;
16
+ /**
17
+ * Pre-warm SIMD initialization (call early to avoid latency later).
18
+ */
19
+ export declare function warmupSimd(): boolean;
20
+ /**
21
+ * Hash input data directly into a caller-provided output buffer.
22
+ * Zero-allocation for the common 32-byte case - ideal for performance-critical code.
23
+ *
24
+ * @param input - Data to hash
25
+ * @param output - Pre-allocated output buffer (must be at least outputLength bytes)
26
+ * @param outputLength - Number of bytes to output (default: 32, max: output.length)
27
+ */
28
+ export declare function hashInto(input: Uint8Array, output: Uint8Array, outputLength?: number): void;
node_modules/@huggingface/blake3-jit/dist/commonjs/hash.js ADDED
@@ -0,0 +1,1038 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ /**
3
+ * BLAKE3 Hash Function - Simple one-shot API
4
+ *
5
+ * This provides a simple hash() function optimized for different input sizes.
6
+ * For small inputs, uses pure JS. For large inputs, uses WASM SIMD.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.hash = hash;
10
+ exports.warmupSimd = warmupSimd;
11
+ exports.hashInto = hashInto;
12
+ const compress_js_1 = require("./compress.js");
13
+ const constants_js_1 = require("./constants.js");
14
+ const utils_js_1 = require("./utils.js");
15
+ const wasm_simd_js_1 = require("./wasm-simd.js");
16
+ // Pre-allocated buffers for reuse (single-threaded optimization)
17
+ let blockWords = null;
18
+ // ===== Contiguous Hyper CV Stack (Optimization #6) =====
19
+ // Maximum tree depth for practical inputs (2^64 chunks = essentially unlimited)
20
+ // Fixed allocation at module load - no runtime allocation
21
+ const CV_STACK_DEPTH = 64;
22
+ const HYPER_CV_STACK = new Uint32Array(CV_STACK_DEPTH * 8); // 64 CVs × 8 words = 512 words
23
+ // Pre-computed offsets for the first few stack levels (hot path optimization)
24
+ // Note: These can be used for further optimization if needed
25
+ // const CV_STACK_OFF_0 = 0;
26
+ // const CV_STACK_OFF_1 = 8;
27
+ // const CV_STACK_OFF_2 = 16;
28
+ // const CV_STACK_OFF_3 = 24;
29
+ // ===== Pre-allocated CV Pool with Views (avoids subarray() in hot paths) =====
30
+ const CV_POOL_SIZE = 64;
31
+ const CV_POOL = new Uint32Array(CV_POOL_SIZE * 8); // 64 CVs × 8 words = 512 words
32
+ const CV_VIEWS = [];
33
+ for (let i = 0; i < CV_POOL_SIZE; i++) {
34
+ CV_VIEWS.push(CV_POOL.subarray(i * 8, i * 8 + 8));
35
+ }
36
+ // SIMD initialization state
37
+ let simdAvailable = false;
38
+ // Threshold for switching to SIMD (must be > 1 chunk to benefit from parallelism)
39
+ const SIMD_THRESHOLD = 4 * constants_js_1.CHUNK_LEN; // 4KB - need at least 4 chunks for SIMD benefit
40
+ /**
41
+ * Initialize SIMD synchronously (lazy).
42
+ */
43
+ function ensureSimdSync() {
44
+ if (simdAvailable)
45
+ return true;
46
+ simdAvailable = (0, wasm_simd_js_1.initSimdSync)();
47
+ return simdAvailable;
48
+ }
49
+ // Reusable buffer for SIMD chunk CVs (4 chunks × 8 words)
50
+ const simdChunkCvs = new Uint32Array(32);
51
+ // ===== Module-level reusable buffers (single-threaded safe) =====
52
+ // These eliminate heap allocations in hot paths
53
+ // For hashChunkWithWords() and hashChunkRoot()
54
+ const reusableTempCv = new Uint32Array(8);
55
+ // For hashPureJS()
56
+ const reusableChunkCv = new Uint32Array(8);
57
+ const reusablePureParentBlock = new Uint32Array(16);
58
+ const reusablePureParentCv = new Uint32Array(8);
59
+ // For hashSimd() - use flat array for 4 chunk CVs (access via subarray)
60
+ const reusableSimdCvs = new Uint32Array(32); // 4 × 8 words flat
61
+ // For hashSimd() parent compression
62
+ const reusableSimdParentBlock = new Uint32Array(16);
63
+ const reusableSimdParentCv = new Uint32Array(8);
64
+ // For hashSimd() parameters - TypedArrays instead of JS arrays
65
+ const reusableOffsets = new Uint32Array(4);
66
+ const reusableCounters = new Uint32Array(4);
67
+ const reusableBlockLens = new Uint32Array(4);
68
+ const reusableFlags = new Uint32Array(4);
69
+ // Reusable output buffer for common 32-byte hash (eliminates allocations)
70
+ const reusableOut8 = new Uint32Array(8); // Standard 32-byte output
71
+ // Pre-created view to avoid allocation in hot path (Task 1 optimization)
72
+ const reusableOut8View = new Uint8Array(reusableOut8.buffer, 0, 32);
73
+ // ===== Unrolled CV Copy Helper (Task 7 optimization) =====
74
+ // V8 will inline this - avoids loop overhead in hot paths
75
+ function copyCV8(src, srcOff, dst, dstOff) {
76
+ dst[dstOff] = src[srcOff];
77
+ dst[dstOff + 1] = src[srcOff + 1];
78
+ dst[dstOff + 2] = src[srcOff + 2];
79
+ dst[dstOff + 3] = src[srcOff + 3];
80
+ dst[dstOff + 4] = src[srcOff + 4];
81
+ dst[dstOff + 5] = src[srcOff + 5];
82
+ dst[dstOff + 6] = src[srcOff + 6];
83
+ dst[dstOff + 7] = src[srcOff + 7];
84
+ }
85
+ /**
86
+ * Transpose 4 blocks (64 bytes each) into SIMD memory layout.
87
+ * The SIMD compress4x expects: [m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...]
88
+ * where m{i}_{j} is message word i from block j.
89
+ *
90
+ * OPTIMIZED: Processes all 4 blocks together for each word position,
91
+ * writing 4 consecutive u32s at once for better cache locality.
92
+ *
93
+ * @param inputWords - Pre-created Uint32Array view of input (null if unaligned/non-LE).
94
+ * Created once per hash call to avoid allocation in hot loop.
95
+ */
96
+ function transposeBlocksToSimd(input, offsets, // Starting offsets for each of 4 blocks
97
+ blockLens, // Length of each block (0-64 bytes)
98
+ mem32, blockCount, // 1-4 blocks
99
+ inputWords) {
100
+ // Fast path: all blocks are full 64-byte blocks with aligned LE input
101
+ const allFull = blockCount === 4 &&
102
+ blockLens[0] === 64 &&
103
+ blockLens[1] === 64 &&
104
+ blockLens[2] === 64 &&
105
+ blockLens[3] === 64;
106
+ if (allFull &&
107
+ inputWords &&
108
+ offsets[0] % 4 === 0 &&
109
+ offsets[1] % 4 === 0 &&
110
+ offsets[2] % 4 === 0 &&
111
+ offsets[3] % 4 === 0) {
112
+ // Ultra-fast path: process all 4 blocks together, write 4 consecutive u32s per word
113
+ const wordOff0 = offsets[0] >>> 2;
114
+ const wordOff1 = offsets[1] >>> 2;
115
+ const wordOff2 = offsets[2] >>> 2;
116
+ const wordOff3 = offsets[3] >>> 2;
117
+ for (let w = 0; w < 16; w++) {
118
+ const dstBase = w * 4;
119
+ mem32[dstBase] = inputWords[wordOff0 + w];
120
+ mem32[dstBase + 1] = inputWords[wordOff1 + w];
121
+ mem32[dstBase + 2] = inputWords[wordOff2 + w];
122
+ mem32[dstBase + 3] = inputWords[wordOff3 + w];
123
+ }
124
+ return;
125
+ }
126
+ // Standard path: process each block independently (handles partial blocks)
127
+ for (let b = 0; b < blockCount; b++) {
128
+ const len = blockLens[b];
129
+ const off = offsets[b];
130
+ if (len === 64) {
131
+ // Full block
132
+ if (inputWords && off % 4 === 0) {
133
+ // Direct Uint32Array access for aligned LE blocks
134
+ const wordOff = off >>> 2;
135
+ for (let w = 0; w < 16; w++) {
136
+ mem32[w * 4 + b] = inputWords[wordOff + w];
137
+ }
138
+ }
139
+ else {
140
+ // Byte-by-byte reconstruction
141
+ for (let w = 0; w < 16; w++) {
142
+ const srcOff = off + w * 4;
143
+ mem32[w * 4 + b] =
144
+ input[srcOff] |
145
+ (input[srcOff + 1] << 8) |
146
+ (input[srcOff + 2] << 16) |
147
+ (input[srcOff + 3] << 24);
148
+ }
149
+ }
150
+ }
151
+ else if (len === 0) {
152
+ // Zero block
153
+ for (let w = 0; w < 16; w++) {
154
+ mem32[w * 4 + b] = 0;
155
+ }
156
+ }
157
+ else {
158
+ // Partial block - handle word by word
159
+ for (let w = 0; w < 16; w++) {
160
+ const wordOff = w * 4;
161
+ if (wordOff >= len) {
162
+ mem32[w * 4 + b] = 0;
163
+ }
164
+ else if (wordOff + 4 <= len) {
165
+ const srcOff = off + wordOff;
166
+ mem32[w * 4 + b] =
167
+ input[srcOff] |
168
+ (input[srcOff + 1] << 8) |
169
+ (input[srcOff + 2] << 16) |
170
+ (input[srcOff + 3] << 24);
171
+ }
172
+ else {
173
+ // Partial word at end of block
174
+ let word = 0;
175
+ for (let i = 0; i < len - wordOff; i++) {
176
+ word |= input[off + wordOff + i] << (i * 8);
177
+ }
178
+ mem32[w * 4 + b] = word;
179
+ }
180
+ }
181
+ }
182
+ }
183
+ // Zero unused block slots
184
+ for (let b = blockCount; b < 4; b++) {
185
+ for (let w = 0; w < 16; w++) {
186
+ mem32[w * 4 + b] = 0;
187
+ }
188
+ }
189
+ }
190
+ /**
191
+ * Transpose 4 full chunks (4 × 16 blocks = 64 blocks) into batch SIMD memory.
192
+ * This is used for the batched compressChunks4x function that processes
193
+ * all 16 blocks in a single WASM call.
194
+ *
195
+ * Memory layout: BATCH_BLOCK_WORDS has 16 positions, each with 16 v128 values.
196
+ * Position p, word w: mem32[(p * 64) + (w * 4) + lane]
197
+ *
198
+ * OPTIMIZED: Processes all 4 chunks together for each (pos, word) pair,
199
+ * writing 4 consecutive u32s at once for better cache locality.
200
+ *
201
+ * @param input - Input data (must have at least 4 full chunks = 4096 bytes)
202
+ * @param chunkOffsets - Starting offsets for each of 4 chunks
203
+ * @param mem32 - WASM memory view
204
+ * @param inputWords - Pre-created Uint32Array view (null if unaligned)
205
+ */
206
+ function transposeBatchToSimd(input, chunkOffsets, mem32, inputWords) {
207
+ const BATCH_BASE = wasm_simd_js_1.SIMD_MEMORY.BATCH_BLOCK_WORDS / 4;
208
+ // Get base word offsets for each chunk (pre-computed for fast path)
209
+ const chunk0WordBase = chunkOffsets[0] >>> 2;
210
+ const chunk1WordBase = chunkOffsets[1] >>> 2;
211
+ const chunk2WordBase = chunkOffsets[2] >>> 2;
212
+ const chunk3WordBase = chunkOffsets[3] >>> 2;
213
+ // Fast path: all chunks aligned and LE - process 4 consecutive u32s at once
214
+ if (inputWords && chunkOffsets[0] % 4 === 0) {
215
+ for (let pos = 0; pos < 16; pos++) {
216
+ const posBase = BATCH_BASE + pos * 64; // 16 words × 4 lanes = 64
217
+ const blockWordOff = pos * 16; // 16 words per block (64 bytes / 4)
218
+ // Process all 16 words, writing 4 chunks at a time (cache-friendly: 16 bytes per write group)
219
+ for (let w = 0; w < 16; w++) {
220
+ const dstBase = posBase + w * 4;
221
+ // Read word w from all 4 chunks at positions that become consecutive in output
222
+ mem32[dstBase] = inputWords[chunk0WordBase + blockWordOff + w];
223
+ mem32[dstBase + 1] = inputWords[chunk1WordBase + blockWordOff + w];
224
+ mem32[dstBase + 2] = inputWords[chunk2WordBase + blockWordOff + w];
225
+ mem32[dstBase + 3] = inputWords[chunk3WordBase + blockWordOff + w];
226
+ }
227
+ }
228
+ }
229
+ else {
230
+ // Slow path: byte-by-byte reconstruction, still cache-friendly write pattern
231
+ for (let pos = 0; pos < 16; pos++) {
232
+ const posBase = BATCH_BASE + pos * 64;
233
+ const blockByteOff = pos * 64; // 64 bytes per block
234
+ for (let w = 0; w < 16; w++) {
235
+ const dstBase = posBase + w * 4;
236
+ const wordByteOff = w * 4;
237
+ // Chunk 0
238
+ const off0 = chunkOffsets[0] + blockByteOff + wordByteOff;
239
+ mem32[dstBase] =
240
+ input[off0] | (input[off0 + 1] << 8) | (input[off0 + 2] << 16) | (input[off0 + 3] << 24);
241
+ // Chunk 1
242
+ const off1 = chunkOffsets[1] + blockByteOff + wordByteOff;
243
+ mem32[dstBase + 1] =
244
+ input[off1] | (input[off1 + 1] << 8) | (input[off1 + 2] << 16) | (input[off1 + 3] << 24);
245
+ // Chunk 2
246
+ const off2 = chunkOffsets[2] + blockByteOff + wordByteOff;
247
+ mem32[dstBase + 2] =
248
+ input[off2] | (input[off2 + 1] << 8) | (input[off2 + 2] << 16) | (input[off2 + 3] << 24);
249
+ // Chunk 3
250
+ const off3 = chunkOffsets[3] + blockByteOff + wordByteOff;
251
+ mem32[dstBase + 3] =
252
+ input[off3] | (input[off3 + 1] << 8) | (input[off3 + 2] << 16) | (input[off3 + 3] << 24);
253
+ }
254
+ }
255
+ }
256
+ }
257
+ // Pre-computed memory offsets for SIMD operations (single-block mode)
258
+ const SIMD_CV_BASE = wasm_simd_js_1.SIMD_MEMORY.CHAINING_VALUES / 4;
259
+ const SIMD_OUT_BASE = wasm_simd_js_1.SIMD_MEMORY.OUTPUT / 4;
260
+ const SIMD_COUNTER_LOW_BASE = wasm_simd_js_1.SIMD_MEMORY.COUNTER_LOW / 4;
261
+ const SIMD_COUNTER_HIGH_BASE = wasm_simd_js_1.SIMD_MEMORY.COUNTER_HIGH / 4;
262
+ const SIMD_BLOCK_LEN_BASE = wasm_simd_js_1.SIMD_MEMORY.BLOCK_LEN / 4;
263
+ // Pre-computed memory offsets for batch SIMD operations (16-block mode)
264
+ const BATCH_CV_BASE = wasm_simd_js_1.SIMD_MEMORY.BATCH_CV / 4;
265
+ const BATCH_COUNTER_LOW_BASE = wasm_simd_js_1.SIMD_MEMORY.BATCH_COUNTER_LOW / 4;
266
+ const BATCH_FLAGS_BASE_OFFSET = wasm_simd_js_1.SIMD_MEMORY.BATCH_FLAGS_BASE / 4;
267
+ const BATCH_OUTPUT_BASE = wasm_simd_js_1.SIMD_MEMORY.BATCH_OUTPUT / 4;
268
+ // Reusable arrays for batch processing
269
+ const batchChunkOffsets = new Uint32Array(4);
270
+ const SIMD_FLAGS_BASE = wasm_simd_js_1.SIMD_MEMORY.FLAGS / 4;
271
+ /**
272
+ * Set up chaining values in SIMD memory (transposed layout).
273
+ * Optimized: unrolled loops for common case of 4 chunks.
274
+ * cvs is flat: [cv0_word0..cv0_word7, cv1_word0..cv1_word7, ...]
275
+ */
276
+ function setupSimdCvs(cvs, // Flat array: 4 × 8 words
277
+ mem32, count) {
278
+ // Unrolled for 4 chunks (common case)
279
+ if (count === 4) {
280
+ for (let w = 0; w < 8; w++) {
281
+ const base = SIMD_CV_BASE + w * 4;
282
+ mem32[base] = cvs[w]; // cv0[w]
283
+ mem32[base + 1] = cvs[8 + w]; // cv1[w]
284
+ mem32[base + 2] = cvs[16 + w]; // cv2[w]
285
+ mem32[base + 3] = cvs[24 + w]; // cv3[w]
286
+ }
287
+ }
288
+ else {
289
+ for (let w = 0; w < 8; w++) {
290
+ const base = SIMD_CV_BASE + w * 4;
291
+ for (let c = 0; c < count; c++) {
292
+ mem32[base + c] = cvs[c * 8 + w];
293
+ }
294
+ for (let c = count; c < 4; c++) {
295
+ mem32[base + c] = 0;
296
+ }
297
+ }
298
+ }
299
+ }
300
+ /**
301
+ * Set up SIMD parameters (counters, flags, block lengths).
302
+ */
303
+ function setupSimdParams(mem32, counters, blockLens, flagsArr, count) {
304
+ // Most chunk counters fit in 32 bits, so counter high is usually 0
305
+ for (let i = 0; i < count; i++) {
306
+ mem32[SIMD_COUNTER_LOW_BASE + i] = counters[i];
307
+ mem32[SIMD_COUNTER_HIGH_BASE + i] = 0; // Assume counters fit in 32 bits
308
+ mem32[SIMD_BLOCK_LEN_BASE + i] = blockLens[i];
309
+ mem32[SIMD_FLAGS_BASE + i] = flagsArr[i];
310
+ }
311
+ // Zero unused slots
312
+ for (let i = count; i < 4; i++) {
313
+ mem32[SIMD_COUNTER_LOW_BASE + i] = 0;
314
+ mem32[SIMD_COUNTER_HIGH_BASE + i] = 0;
315
+ mem32[SIMD_BLOCK_LEN_BASE + i] = 0;
316
+ mem32[SIMD_FLAGS_BASE + i] = 0;
317
+ }
318
+ }
319
+ /**
320
+ * Read output CVs from SIMD memory (untranspose).
321
+ */
322
+ function readSimdOutputCvs(mem32, outputCvs, // Flat array: 4 × 8 words
323
+ count) {
324
+ // Unrolled for 4 chunks (common case)
325
+ if (count === 4) {
326
+ for (let w = 0; w < 8; w++) {
327
+ const base = SIMD_OUT_BASE + w * 4;
328
+ outputCvs[w] = mem32[base];
329
+ outputCvs[8 + w] = mem32[base + 1];
330
+ outputCvs[16 + w] = mem32[base + 2];
331
+ outputCvs[24 + w] = mem32[base + 3];
332
+ }
333
+ }
334
+ else {
335
+ for (let w = 0; w < 8; w++) {
336
+ const base = SIMD_OUT_BASE + w * 4;
337
+ for (let c = 0; c < count; c++) {
338
+ outputCvs[c * 8 + w] = mem32[base + c];
339
+ }
340
+ }
341
+ }
342
+ }
343
+ function getBlockWords() {
344
+ if (!blockWords) {
345
+ blockWords = new Uint32Array(16);
346
+ }
347
+ return blockWords;
348
+ }
349
+ /**
350
+ * Hash a single chunk (up to 1024 bytes) with pre-created inputWords view.
351
+ * This is the optimized version that avoids creating Uint32Array views per chunk.
352
+ * (Fleek optimization Step 8)
353
+ */
354
+ function hashChunkWithWords(input, inputWords, // Pre-created view of entire input
355
+ inputOffset, inputLen, chunkCounter, flags, cv, cvOffset) {
356
+ // Use reusable temporary CV for intermediate blocks (single-threaded safe)
357
+ reusableTempCv.set(constants_js_1.IV);
358
+ // Process full blocks
359
+ const fullBlocks = inputLen >>> 6; // inputLen / 64
360
+ const remainder = inputLen & 63; // inputLen % 64
361
+ // Calculate word offset for this chunk within the pre-created view
362
+ const chunkWordOffset = inputOffset >>> 2;
363
+ // Fast path for full chunks with aligned little-endian input
364
+ if (inputWords && remainder === 0 && inputLen === constants_js_1.CHUNK_LEN) {
365
+ // All 16 blocks are full, use fast path exclusively
366
+ let wordOff = chunkWordOffset;
367
+ // Block 0 (CHUNK_START)
368
+ (0, compress_js_1.compress)(reusableTempCv, 0, inputWords, wordOff, reusableTempCv, 0, false, chunkCounter, constants_js_1.BLOCK_LEN, flags | constants_js_1.CHUNK_START);
369
+ wordOff += 16;
370
+ // Blocks 1-14 (no special flags)
371
+ for (let i = 1; i < 15; i++) {
372
+ (0, compress_js_1.compress)(reusableTempCv, 0, inputWords, wordOff, reusableTempCv, 0, false, chunkCounter, constants_js_1.BLOCK_LEN, flags);
373
+ wordOff += 16;
374
+ }
375
+ // Block 15 (CHUNK_END)
376
+ (0, compress_js_1.compress)(reusableTempCv, 0, inputWords, wordOff, reusableTempCv, 0, false, chunkCounter, constants_js_1.BLOCK_LEN, flags | constants_js_1.CHUNK_END);
377
+ cv.set(reusableTempCv, cvOffset);
378
+ return;
379
+ }
380
+ // Slower path for partial chunks or non-aligned input
381
+ const totalBlocks = fullBlocks + (remainder > 0 ? 1 : 0);
382
+ const block = getBlockWords();
383
+ for (let blockIdx = 0; blockIdx < totalBlocks; blockIdx++) {
384
+ const isFirst = blockIdx === 0;
385
+ const isLast = blockIdx === totalBlocks - 1;
386
+ const blockStart = blockIdx << 6;
387
+ const blockLen = isLast && remainder > 0 ? remainder : constants_js_1.BLOCK_LEN;
388
+ // Determine flags for this block
389
+ let blockFlags = flags;
390
+ if (isFirst)
391
+ blockFlags |= constants_js_1.CHUNK_START;
392
+ if (isLast)
393
+ blockFlags |= constants_js_1.CHUNK_END;
394
+ // Load block words
395
+ if (isLast && remainder > 0) {
396
+ // Partial final block - need zero padding
397
+ (0, utils_js_1.readLittleEndianWordsPartial)(input, inputOffset + blockStart, blockLen, block);
398
+ }
399
+ else if (inputWords && chunkWordOffset + (blockStart >>> 2) + 16 <= inputWords.length) {
400
+ // Fast path: use pre-created view directly
401
+ (0, compress_js_1.compress)(reusableTempCv, 0, inputWords, chunkWordOffset + (blockStart >>> 2), reusableTempCv, 0, false, chunkCounter, blockLen, blockFlags);
402
+ continue;
403
+ }
404
+ else {
405
+ (0, utils_js_1.readLittleEndianWordsFull)(input, inputOffset + blockStart, block);
406
+ }
407
+ (0, compress_js_1.compress)(reusableTempCv, 0, block, 0, reusableTempCv, 0, false, chunkCounter, blockLen, blockFlags);
408
+ }
409
+ // Copy result to output
410
+ cv.set(reusableTempCv, cvOffset);
411
+ }
412
+ /**
413
+ * Hash input using pure JavaScript.
414
+ * Handles the full Merkle tree construction.
415
+ */
416
+ function hashPureJS(input, outputLen) {
417
+ const inputLen = input.length;
418
+ // Special case: empty input
419
+ if (inputLen === 0) {
420
+ const block = getBlockWords();
421
+ block.fill(0);
422
+ // Use reusable output buffer for common 32-byte case
423
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
424
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, block, 0, out, 0, outputLen > 32, 0, 0, constants_js_1.CHUNK_START | constants_js_1.CHUNK_END | constants_js_1.ROOT);
425
+ // Return result - use pre-created view for common 32-byte case
426
+ if (outputLen === 32 && utils_js_1.IS_LITTLE_ENDIAN) {
427
+ return reusableOut8View.slice();
428
+ }
429
+ const result = new Uint8Array(outputLen);
430
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
431
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
432
+ }
433
+ else {
434
+ (0, utils_js_1.writeLittleEndianBytesPartial)(out, 0, result, 0, outputLen);
435
+ }
436
+ return result;
437
+ }
438
+ // Calculate number of chunks
439
+ const numChunks = Math.ceil(inputLen / constants_js_1.CHUNK_LEN);
440
+ // Single chunk optimization
441
+ if (numChunks === 1) {
442
+ // Use reusable output buffer for common 32-byte case
443
+ const cv = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
444
+ hashChunkRoot(input, 0, inputLen, 0, 0, cv, outputLen > 32);
445
+ // Return result - use pre-created view for common 32-byte case
446
+ if (outputLen === 32 && utils_js_1.IS_LITTLE_ENDIAN) {
447
+ return reusableOut8View.slice();
448
+ }
449
+ const result = new Uint8Array(outputLen);
450
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
451
+ result.set(new Uint8Array(cv.buffer, 0, outputLen));
452
+ }
453
+ else {
454
+ (0, utils_js_1.writeLittleEndianBytesPartial)(cv, 0, result, 0, outputLen);
455
+ }
456
+ return result;
457
+ }
458
+ // Multiple chunks - need Merkle tree
459
+ // Use the global contiguous CV stack (no allocation)
460
+ const stack = HYPER_CV_STACK;
461
+ let stackLen = 0;
462
+ // Use reusable buffers (single-threaded safe)
463
+ const chunkCv = reusableChunkCv;
464
+ const parentBlock = reusablePureParentBlock;
465
+ const parentCv = reusablePureParentCv;
466
+ // Create Uint32Array view ONCE for entire input (Fleek optimization Step 8)
467
+ // This avoids creating views inside each chunk/block processing
468
+ let inputWords = null;
469
+ const canUseFastPath = utils_js_1.IS_LITTLE_ENDIAN && input.byteOffset % 4 === 0;
470
+ if (canUseFastPath) {
471
+ inputWords = new Uint32Array(input.buffer, input.byteOffset, inputLen >>> 2);
472
+ }
473
+ // Determine how many full chunks we have
474
+ const fullChunks = inputLen >>> 10; // inputLen / 1024
475
+ const lastChunkLen = inputLen & 1023; // inputLen % 1024
476
+ // Process all full chunks with fast path (inlined for performance)
477
+ if (canUseFastPath && inputWords) {
478
+ for (let chunkIdx = 0; chunkIdx < fullChunks; chunkIdx++) {
479
+ // Inline chunk processing for full chunks
480
+ chunkCv.set(constants_js_1.IV);
481
+ let wordOff = chunkIdx << 8; // chunkIdx * 256 (CHUNK_LEN/4)
482
+ // Block 0 (CHUNK_START)
483
+ (0, compress_js_1.compress)(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, constants_js_1.BLOCK_LEN, constants_js_1.CHUNK_START);
484
+ wordOff += 16;
485
+ // Blocks 1-14 (no special flags)
486
+ for (let b = 1; b < 15; b++) {
487
+ (0, compress_js_1.compress)(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, constants_js_1.BLOCK_LEN, 0);
488
+ wordOff += 16;
489
+ }
490
+ // Block 15 (CHUNK_END)
491
+ (0, compress_js_1.compress)(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, constants_js_1.BLOCK_LEN, constants_js_1.CHUNK_END);
492
+ // Merge completed subtrees (avoid subarray by using index math)
493
+ let totalChunks = chunkIdx + 1;
494
+ let cvSrcOff = 0;
495
+ let cvSrc = chunkCv;
496
+ // Check if this is the last chunk overall
497
+ const isLastChunk = chunkIdx === fullChunks - 1 && lastChunkLen === 0;
498
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
499
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
500
+ if (stackLen === 1 && isLastChunk) {
501
+ break;
502
+ }
503
+ stackLen--;
504
+ const stackOff = stackLen * 8;
505
+ // Copy left CV from stack to parentBlock[0..7] (unrolled)
506
+ copyCV8(stack, stackOff, parentBlock, 0);
507
+ // Copy current CV to parentBlock[8..15] (unrolled)
508
+ copyCV8(cvSrc, cvSrcOff, parentBlock, 8);
509
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, parentCv, 0, false, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT);
510
+ cvSrc = parentCv;
511
+ cvSrcOff = 0;
512
+ totalChunks >>>= 1;
513
+ }
514
+ // Push CV to stack (unrolled)
515
+ const stackOff = stackLen * 8;
516
+ copyCV8(cvSrc, cvSrcOff, stack, stackOff);
517
+ stackLen++;
518
+ }
519
+ // Process last partial chunk if any
520
+ if (lastChunkLen > 0) {
521
+ hashChunkWithWords(input, inputWords, fullChunks * constants_js_1.CHUNK_LEN, lastChunkLen, fullChunks, 0, chunkCv, 0);
522
+ let totalChunks = fullChunks + 1;
523
+ let newCv = chunkCv;
524
+ let newCvOffset = 0;
525
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
526
+ // Skip final merge; this IS the last chunk, let finalization handle ROOT flag
527
+ if (stackLen === 1) {
528
+ break;
529
+ }
530
+ stackLen--;
531
+ const stackOff = stackLen * 8;
532
+ // Copy from stack to parentBlock[0..7] (unrolled)
533
+ copyCV8(stack, stackOff, parentBlock, 0);
534
+ // Copy from newCv to parentBlock[8..15] (unrolled)
535
+ copyCV8(newCv, newCvOffset, parentBlock, 8);
536
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, parentCv, 0, false, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT);
537
+ newCv = parentCv;
538
+ newCvOffset = 0;
539
+ totalChunks >>>= 1;
540
+ }
541
+ // Push CV to stack (unrolled)
542
+ const pushOff = stackLen * 8;
543
+ copyCV8(newCv, newCvOffset, stack, pushOff);
544
+ stackLen++;
545
+ }
546
+ }
547
+ else {
548
+ // Slow path for unaligned or big-endian
549
+ for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
550
+ const chunkStart = chunkIdx * constants_js_1.CHUNK_LEN;
551
+ const chunkLen = Math.min(constants_js_1.CHUNK_LEN, inputLen - chunkStart);
552
+ hashChunkWithWords(input, inputWords, chunkStart, chunkLen, chunkIdx, 0, chunkCv, 0);
553
+ // Merge completed subtrees
554
+ let totalChunks = chunkIdx + 1;
555
+ let newCv = chunkCv;
556
+ let newCvOffset = 0;
557
+ // Check if this is the last chunk
558
+ const isLastChunk = chunkIdx === numChunks - 1;
559
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
560
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
561
+ if (stackLen === 1 && isLastChunk) {
562
+ break;
563
+ }
564
+ stackLen--;
565
+ const stackOff = stackLen * 8;
566
+ // Copy from stack to parentBlock[0..7] (unrolled)
567
+ copyCV8(stack, stackOff, parentBlock, 0);
568
+ // Copy from newCv to parentBlock[8..15] (unrolled)
569
+ copyCV8(newCv, newCvOffset, parentBlock, 8);
570
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, parentCv, 0, false, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT);
571
+ newCv = parentCv;
572
+ newCvOffset = 0;
573
+ totalChunks >>>= 1;
574
+ }
575
+ // Push CV to stack (unrolled)
576
+ const pushOff = stackLen * 8;
577
+ copyCV8(newCv, newCvOffset, stack, pushOff);
578
+ stackLen++;
579
+ }
580
+ }
581
+ // Finalize: merge remaining stack entries
582
+ while (stackLen > 1) {
583
+ stackLen--;
584
+ const rightOff = stackLen * 8;
585
+ stackLen--;
586
+ const leftOff = stackLen * 8;
587
+ // Copy left CV to parentBlock[0..7] and right CV to parentBlock[8..15] (unrolled)
588
+ copyCV8(stack, leftOff, parentBlock, 0);
589
+ copyCV8(stack, rightOff, parentBlock, 8);
590
+ if (stackLen === 0) {
591
+ // This is the root - use reusable output buffer for common 32-byte case
592
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
593
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, out, 0, outputLen > 32, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT | constants_js_1.ROOT);
594
+ // Return result - use pre-created view for common 32-byte case
595
+ if (outputLen === 32 && utils_js_1.IS_LITTLE_ENDIAN) {
596
+ return reusableOut8View.slice();
597
+ }
598
+ const result = new Uint8Array(outputLen);
599
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
600
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
601
+ }
602
+ else {
603
+ (0, utils_js_1.writeLittleEndianBytesPartial)(out, 0, result, 0, outputLen);
604
+ }
605
+ return result;
606
+ }
607
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, parentCv, 0, false, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT);
608
+ // Push to stack (unrolled)
609
+ copyCV8(parentCv, 0, stack, stackLen * 8);
610
+ stackLen++;
611
+ }
612
+ // Single entry in stack - this is the root
613
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
614
+ const lastBlock = getBlockWords();
615
+ lastBlock.fill(0);
616
+ // Copy first 8 words from stack (unrolled)
617
+ copyCV8(stack, 0, lastBlock, 0);
618
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, lastBlock, 0, out, 0, outputLen > 32, 0, constants_js_1.BLOCK_LEN, constants_js_1.ROOT);
619
+ // Return result - use pre-created view for common 32-byte case
620
+ if (outputLen === 32 && utils_js_1.IS_LITTLE_ENDIAN) {
621
+ return reusableOut8View.slice();
622
+ }
623
+ const result = new Uint8Array(outputLen);
624
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
625
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
626
+ }
627
+ else {
628
+ (0, utils_js_1.writeLittleEndianBytesPartial)(out, 0, result, 0, outputLen);
629
+ }
630
+ return result;
631
+ }
632
+ /**
633
+ * Hash a single chunk that is also the root (single chunk input).
634
+ */
635
+ function hashChunkRoot(input, inputOffset, inputLen, chunkCounter, flags, out, fullOutput) {
636
+ // Use reusable tempCv (single-threaded safe)
637
+ reusableTempCv.set(constants_js_1.IV);
638
+ const block = getBlockWords();
639
+ // Process full blocks
640
+ const fullBlocks = inputLen >>> 6;
641
+ const remainder = inputLen & 63;
642
+ const totalBlocks = fullBlocks + (remainder > 0 ? 1 : 0) || 1; // At least 1 block
643
+ // Create a Uint32Array view if possible
644
+ let inputWords = null;
645
+ if (utils_js_1.IS_LITTLE_ENDIAN && (input.byteOffset + inputOffset) % 4 === 0 && inputLen >= 4) {
646
+ inputWords = new Uint32Array(input.buffer, input.byteOffset + inputOffset, inputLen >>> 2);
647
+ }
648
+ for (let blockIdx = 0; blockIdx < totalBlocks; blockIdx++) {
649
+ const isFirst = blockIdx === 0;
650
+ const isLast = blockIdx === totalBlocks - 1;
651
+ const blockStart = blockIdx << 6;
652
+ const blockLen = isLast ? remainder || (inputLen > 0 ? constants_js_1.BLOCK_LEN : 0) : constants_js_1.BLOCK_LEN;
653
+ // Determine flags
654
+ let blockFlags = flags;
655
+ if (isFirst)
656
+ blockFlags |= constants_js_1.CHUNK_START;
657
+ if (isLast)
658
+ blockFlags |= constants_js_1.CHUNK_END | constants_js_1.ROOT;
659
+ // Load block
660
+ if (isLast && remainder > 0) {
661
+ (0, utils_js_1.readLittleEndianWordsPartial)(input, inputOffset + blockStart, blockLen, block);
662
+ }
663
+ else if (inputLen === 0) {
664
+ block.fill(0);
665
+ }
666
+ else if (inputWords && (blockStart >>> 2) + 16 <= inputWords.length) {
667
+ // Fast path
668
+ (0, compress_js_1.compress)(reusableTempCv, 0, inputWords, blockStart >>> 2, isLast ? out : reusableTempCv, 0, isLast && fullOutput, chunkCounter, blockLen, blockFlags);
669
+ continue;
670
+ }
671
+ else {
672
+ (0, utils_js_1.readLittleEndianWordsFull)(input, inputOffset + blockStart, block);
673
+ }
674
+ (0, compress_js_1.compress)(reusableTempCv, 0, block, 0, isLast ? out : reusableTempCv, 0, isLast && fullOutput, chunkCounter, blockLen, blockFlags);
675
+ }
676
+ }
677
+ /**
678
+ * Hash using WASM SIMD - processes 4 chunks in parallel.
679
+ * Falls back to pure JS if SIMD fails.
680
+ */
681
+ function hashSimd(input, outputLen) {
682
+ const mem = (0, wasm_simd_js_1.getSimdMemory)();
683
+ if (!mem) {
684
+ return hashPureJS(input, outputLen);
685
+ }
686
+ const { view32 } = mem;
687
+ const inputLen = input.length;
688
+ const numChunks = Math.ceil(inputLen / constants_js_1.CHUNK_LEN);
689
+ // For small inputs, pure JS is faster (no transpose overhead)
690
+ if (numChunks < 4) {
691
+ return hashPureJS(input, outputLen);
692
+ }
693
+ // Try to use WASM arena buffers (zero JS heap allocation)
694
+ // Falls back to JS buffers if arena not available
695
+ const arena = (0, wasm_simd_js_1.getArenaBuffers)();
696
+ const useWasmParent = arena !== null; // Use WASM parent compress when arena available
697
+ let stack;
698
+ let tempCvs;
699
+ let parentBlock;
700
+ let parentCv;
701
+ if (arena) {
702
+ // Use WASM-backed arena buffers
703
+ stack = arena.cvStack;
704
+ tempCvs = arena.tempCvs;
705
+ parentBlock = arena.parentBlock;
706
+ parentCv = arena.chunkCv;
707
+ }
708
+ else {
709
+ // Fallback to JS heap buffers - use global contiguous stack (no allocation)
710
+ stack = HYPER_CV_STACK;
711
+ tempCvs = reusableSimdCvs;
712
+ parentBlock = reusableSimdParentBlock;
713
+ parentCv = reusableSimdParentCv;
714
+ }
715
+ let stackLen = 0;
716
+ // Use TypedArrays instead of JS arrays for block parameters
717
+ const offsets = reusableOffsets;
718
+ const counters = reusableCounters;
719
+ const blockLens = reusableBlockLens;
720
+ const flagsArr = reusableFlags;
721
+ // Create Uint32Array view once for entire hash call (optimization: avoid allocation in hot loop)
722
+ const inputWords = utils_js_1.IS_LITTLE_ENDIAN && input.byteOffset % 4 === 0
723
+ ? new Uint32Array(input.buffer, input.byteOffset, input.byteLength >>> 2)
724
+ : null;
725
+ // Calculate number of full chunks (1024 bytes each)
726
+ const numFullChunks = inputLen >>> 10; // inputLen / 1024
727
+ // Process chunks in groups of 4
728
+ let chunkIdx = 0;
729
+ while (chunkIdx < numChunks) {
730
+ const groupSize = Math.min(4, numChunks - chunkIdx);
731
+ // === BATCH FAST PATH: 4 full chunks ===
732
+ // Use compressChunks4x for groups of exactly 4 full chunks
733
+ // This reduces 16 WASM calls to 1 per group
734
+ const canUseBatchPath = groupSize === 4 && chunkIdx + 4 <= numFullChunks;
735
+ if (canUseBatchPath) {
736
+ // Set up chunk offsets for batch transpose
737
+ batchChunkOffsets[0] = chunkIdx * constants_js_1.CHUNK_LEN;
738
+ batchChunkOffsets[1] = (chunkIdx + 1) * constants_js_1.CHUNK_LEN;
739
+ batchChunkOffsets[2] = (chunkIdx + 2) * constants_js_1.CHUNK_LEN;
740
+ batchChunkOffsets[3] = (chunkIdx + 3) * constants_js_1.CHUNK_LEN;
741
+ // Transpose all 64 blocks (4 chunks × 16 blocks) at once
742
+ transposeBatchToSimd(input, batchChunkOffsets, view32, inputWords);
743
+ // Set up initial CVs (IV) in batch memory - transposed layout
744
+ for (let w = 0; w < 8; w++) {
745
+ const ivWord = constants_js_1.IV[w];
746
+ const base = BATCH_CV_BASE + w * 4;
747
+ view32[base] = ivWord;
748
+ view32[base + 1] = ivWord;
749
+ view32[base + 2] = ivWord;
750
+ view32[base + 3] = ivWord;
751
+ }
752
+ // Set up counters in batch memory
753
+ view32[BATCH_COUNTER_LOW_BASE] = chunkIdx;
754
+ view32[BATCH_COUNTER_LOW_BASE + 1] = chunkIdx + 1;
755
+ view32[BATCH_COUNTER_LOW_BASE + 2] = chunkIdx + 2;
756
+ view32[BATCH_COUNTER_LOW_BASE + 3] = chunkIdx + 3;
757
+ // Set up base flags (0 - no keyed hashing)
758
+ view32[BATCH_FLAGS_BASE_OFFSET] = 0;
759
+ view32[BATCH_FLAGS_BASE_OFFSET + 1] = 0;
760
+ view32[BATCH_FLAGS_BASE_OFFSET + 2] = 0;
761
+ view32[BATCH_FLAGS_BASE_OFFSET + 3] = 0;
762
+ // Run batched compress (16 blocks × 4 chunks in one call!)
763
+ (0, wasm_simd_js_1.runCompressChunks4x)();
764
+ // Read output CVs from batch output - untranspose to tempCvs
765
+ for (let w = 0; w < 8; w++) {
766
+ const base = BATCH_OUTPUT_BASE + w * 4;
767
+ tempCvs[w] = view32[base]; // chunk 0
768
+ tempCvs[8 + w] = view32[base + 1]; // chunk 1
769
+ tempCvs[16 + w] = view32[base + 2]; // chunk 2
770
+ tempCvs[24 + w] = view32[base + 3]; // chunk 3
771
+ }
772
+ }
773
+ else {
774
+ // === STANDARD PATH: block-by-block processing ===
775
+ // Used for partial chunks or groups < 4
776
+ // Initialize CVs for this group to IV (flat array: 4 × 8 words)
777
+ for (let g = 0; g < groupSize; g++) {
778
+ const base = g * 8;
779
+ tempCvs[base] = constants_js_1.IV[0];
780
+ tempCvs[base + 1] = constants_js_1.IV[1];
781
+ tempCvs[base + 2] = constants_js_1.IV[2];
782
+ tempCvs[base + 3] = constants_js_1.IV[3];
783
+ tempCvs[base + 4] = constants_js_1.IV[4];
784
+ tempCvs[base + 5] = constants_js_1.IV[5];
785
+ tempCvs[base + 6] = constants_js_1.IV[6];
786
+ tempCvs[base + 7] = constants_js_1.IV[7];
787
+ }
788
+ // Process all 16 blocks of each chunk in this group
789
+ for (let blockIdx = 0; blockIdx < 16; blockIdx++) {
790
+ // Calculate block offsets and parameters (reuse arrays)
791
+ for (let g = 0; g < groupSize; g++) {
792
+ const thisChunkIdx = chunkIdx + g;
793
+ const chunkStart = thisChunkIdx * constants_js_1.CHUNK_LEN;
794
+ const chunkLen = Math.min(constants_js_1.CHUNK_LEN, inputLen - chunkStart);
795
+ const thisBlockStart = chunkStart + blockIdx * constants_js_1.BLOCK_LEN;
796
+ // Determine block length for this specific block
797
+ const blockStartInChunk = blockIdx * constants_js_1.BLOCK_LEN;
798
+ let thisBlockLen = constants_js_1.BLOCK_LEN;
799
+ if (blockStartInChunk >= chunkLen) {
800
+ thisBlockLen = 0;
801
+ }
802
+ else if (blockStartInChunk + constants_js_1.BLOCK_LEN > chunkLen) {
803
+ thisBlockLen = chunkLen - blockStartInChunk;
804
+ }
805
+ offsets[g] = thisBlockStart;
806
+ counters[g] = thisChunkIdx;
807
+ // Determine flags
808
+ let flags = 0;
809
+ if (blockIdx === 0)
810
+ flags |= constants_js_1.CHUNK_START;
811
+ const totalBlocksInChunk = Math.ceil(chunkLen / constants_js_1.BLOCK_LEN) || 1;
812
+ if (blockIdx === totalBlocksInChunk - 1)
813
+ flags |= constants_js_1.CHUNK_END;
814
+ blockLens[g] = thisBlockLen;
815
+ flagsArr[g] = flags;
816
+ }
817
+ // Check if any blocks need processing
818
+ if (blockLens[0] === 0 && blockLens[1] === 0 && blockLens[2] === 0 && blockLens[3] === 0)
819
+ continue;
820
+ // Transpose blocks into SIMD memory (pass pre-created view to avoid allocation)
821
+ transposeBlocksToSimd(input, offsets, blockLens, view32, groupSize, inputWords);
822
+ // Set up CVs in SIMD memory
823
+ setupSimdCvs(tempCvs, view32, groupSize);
824
+ // Set up parameters
825
+ setupSimdParams(view32, counters, blockLens, flagsArr, groupSize);
826
+ // Run SIMD compress
827
+ (0, wasm_simd_js_1.runCompress4x)();
828
+ // Read output CVs back
829
+ readSimdOutputCvs(view32, simdChunkCvs, groupSize);
830
+ // Update tempCvs - copy from simdChunkCvs (both are flat 32-word arrays)
831
+ // simdChunkCvs layout matches tempCvs: [cv0_w0..cv0_w7, cv1_w0..cv1_w7, ...]
832
+ // IMPORTANT: Only update CVs for chunks that had data in this block!
833
+ // Skipping this check would corrupt CVs for partial chunks after their final block.
834
+ for (let g = 0; g < groupSize; g++) {
835
+ if (blockLens[g] === 0)
836
+ continue; // Don't update CV for chunks with no data in this block
837
+ const base = g * 8;
838
+ tempCvs[base] = simdChunkCvs[base];
839
+ tempCvs[base + 1] = simdChunkCvs[base + 1];
840
+ tempCvs[base + 2] = simdChunkCvs[base + 2];
841
+ tempCvs[base + 3] = simdChunkCvs[base + 3];
842
+ tempCvs[base + 4] = simdChunkCvs[base + 4];
843
+ tempCvs[base + 5] = simdChunkCvs[base + 5];
844
+ tempCvs[base + 6] = simdChunkCvs[base + 6];
845
+ tempCvs[base + 7] = simdChunkCvs[base + 7];
846
+ }
847
+ }
848
+ }
849
+ // Merge each chunk's CV into the Merkle tree
850
+ for (let g = 0; g < groupSize; g++) {
851
+ const thisChunkIdx = chunkIdx + g;
852
+ // Merge completed subtrees
853
+ let totalChunks = thisChunkIdx + 1;
854
+ // Track newCv source - either from tempCvs or parentCv
855
+ let newCvBase = g * 8; // Offset into tempCvs
856
+ let newCvSrc = tempCvs;
857
+ // Check if this is the last chunk
858
+ const isLastChunk = thisChunkIdx === numChunks - 1;
859
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
860
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
861
+ if (stackLen === 1 && isLastChunk) {
862
+ break;
863
+ }
864
+ // Pop left child
865
+ stackLen--;
866
+ const stackOff = stackLen * 8;
867
+ // Copy from stack to parentBlock[0..7] (unrolled)
868
+ copyCV8(stack, stackOff, parentBlock, 0);
869
+ // Copy from newCv source to parentBlock[8..15] (unrolled)
870
+ copyCV8(newCvSrc, newCvBase, parentBlock, 8);
871
+ if (useWasmParent) {
872
+ // WASM parent compress - data already in arena buffers
873
+ (0, wasm_simd_js_1.runCompressParent)();
874
+ }
875
+ else {
876
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, parentCv, 0, false, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT);
877
+ }
878
+ newCvSrc = parentCv;
879
+ newCvBase = 0;
880
+ totalChunks >>>= 1;
881
+ }
882
+ // Push to stack (unrolled)
883
+ const pushOff = stackLen * 8;
884
+ copyCV8(newCvSrc, newCvBase, stack, pushOff);
885
+ stackLen++;
886
+ }
887
+ chunkIdx += groupSize;
888
+ }
889
+ // Finalize: merge remaining stack entries
890
+ while (stackLen > 1) {
891
+ stackLen--;
892
+ const rightOff = stackLen * 8;
893
+ stackLen--;
894
+ const leftOff = stackLen * 8;
895
+ // Copy left CV to parentBlock[0..7] and right CV to parentBlock[8..15] (unrolled)
896
+ copyCV8(stack, leftOff, parentBlock, 0);
897
+ copyCV8(stack, rightOff, parentBlock, 8);
898
+ if (stackLen === 0) {
899
+ // This is the root - use reusable output buffer
900
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
901
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, out, 0, outputLen > 32, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT | constants_js_1.ROOT);
902
+ // Return result - use pre-created view for common 32-byte case
903
+ if (outputLen === 32 && utils_js_1.IS_LITTLE_ENDIAN) {
904
+ return reusableOut8View.slice();
905
+ }
906
+ const result = new Uint8Array(outputLen);
907
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
908
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
909
+ }
910
+ else {
911
+ (0, utils_js_1.writeLittleEndianBytesPartial)(out, 0, result, 0, outputLen);
912
+ }
913
+ return result;
914
+ }
915
+ if (useWasmParent) {
916
+ // WASM parent compress - data already in arena buffers
917
+ (0, wasm_simd_js_1.runCompressParent)();
918
+ }
919
+ else {
920
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, parentBlock, 0, parentCv, 0, false, 0, constants_js_1.BLOCK_LEN, constants_js_1.PARENT);
921
+ }
922
+ // Push to stack (unrolled)
923
+ copyCV8(parentCv, 0, stack, stackLen * 8);
924
+ stackLen++;
925
+ }
926
+ // Single entry in stack - finalize as root
927
+ if (stackLen === 1) {
928
+ const block = getBlockWords();
929
+ block.fill(0);
930
+ // Copy first 8 words from stack (unrolled)
931
+ copyCV8(stack, 0, block, 0);
932
+ // Use reusable output buffer
933
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
934
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, block, 0, out, 0, outputLen > 32, 0, constants_js_1.BLOCK_LEN, constants_js_1.ROOT);
935
+ // Return result - use pre-created view for common 32-byte case
936
+ if (outputLen === 32 && utils_js_1.IS_LITTLE_ENDIAN) {
937
+ return reusableOut8View.slice();
938
+ }
939
+ const result = new Uint8Array(outputLen);
940
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
941
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
942
+ }
943
+ else {
944
+ (0, utils_js_1.writeLittleEndianBytesPartial)(out, 0, result, 0, outputLen);
945
+ }
946
+ return result;
947
+ }
948
+ // Should not reach here
949
+ return hashPureJS(input, outputLen);
950
+ }
951
+ /**
952
+ * Hash input data and return the result.
953
+ * Automatically uses WASM SIMD for large inputs when available.
954
+ *
955
+ * @param input - Data to hash
956
+ * @param outputLength - Number of bytes to output (default: 32)
957
+ * @returns The hash output
958
+ */
959
+ function hash(input, outputLength = constants_js_1.OUT_LEN) {
960
+ // For large inputs, use SIMD for ~1.5x performance improvement
961
+ if (input.length >= SIMD_THRESHOLD && ensureSimdSync()) {
962
+ return hashSimd(input, outputLength);
963
+ }
964
+ return hashPureJS(input, outputLength);
965
+ }
966
+ /**
967
+ * Pre-warm SIMD initialization (call early to avoid latency later).
968
+ */
969
+ function warmupSimd() {
970
+ return ensureSimdSync();
971
+ }
972
+ /**
973
+ * Hash input data directly into a caller-provided output buffer.
974
+ * Zero-allocation for the common 32-byte case - ideal for performance-critical code.
975
+ *
976
+ * @param input - Data to hash
977
+ * @param output - Pre-allocated output buffer (must be at least outputLength bytes)
978
+ * @param outputLength - Number of bytes to output (default: 32, max: output.length)
979
+ */
980
+ function hashInto(input, output, outputLength = constants_js_1.OUT_LEN) {
981
+ // Validate output buffer
982
+ if (output.length < outputLength) {
983
+ throw new Error(`Output buffer too small: ${output.length} < ${outputLength}`);
984
+ }
985
+ // For large inputs, use SIMD for ~1.5x performance improvement
986
+ if (input.length >= SIMD_THRESHOLD && ensureSimdSync()) {
987
+ hashSimdInto(input, output, outputLength);
988
+ return;
989
+ }
990
+ hashPureJSInto(input, output, outputLength);
991
+ }
992
+ /**
993
+ * Internal: Hash using pure JS, writing directly to output buffer.
994
+ */
995
+ function hashPureJSInto(input, output, outputLen) {
996
+ const inputLen = input.length;
997
+ // Special case: empty input
998
+ if (inputLen === 0) {
999
+ const block = getBlockWords();
1000
+ block.fill(0);
1001
+ const out = outputLen <= 32 ? reusableOut8 : new Uint32Array(16);
1002
+ (0, compress_js_1.compress)(constants_js_1.IV, 0, block, 0, out, 0, outputLen > 32, 0, 0, constants_js_1.CHUNK_START | constants_js_1.CHUNK_END | constants_js_1.ROOT);
1003
+ // Copy result to output
1004
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
1005
+ output.set(new Uint8Array(out.buffer, out.byteOffset, outputLen));
1006
+ }
1007
+ else {
1008
+ (0, utils_js_1.writeLittleEndianBytesPartial)(out, 0, output, 0, outputLen);
1009
+ }
1010
+ return;
1011
+ }
1012
+ // Calculate number of chunks
1013
+ const numChunks = Math.ceil(inputLen / constants_js_1.CHUNK_LEN);
1014
+ // Single chunk optimization
1015
+ if (numChunks === 1) {
1016
+ const cv = outputLen <= 32 ? reusableOut8 : new Uint32Array(16);
1017
+ hashChunkRoot(input, 0, inputLen, 0, 0, cv, outputLen > 32);
1018
+ // Copy result to output
1019
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
1020
+ output.set(new Uint8Array(cv.buffer, cv.byteOffset, outputLen));
1021
+ }
1022
+ else {
1023
+ (0, utils_js_1.writeLittleEndianBytesPartial)(cv, 0, output, 0, outputLen);
1024
+ }
1025
+ return;
1026
+ }
1027
+ // Multiple chunks - delegate to hashPureJS and copy result
1028
+ const result = hashPureJS(input, outputLen);
1029
+ output.set(result);
1030
+ }
1031
+ /**
1032
+ * Internal: Hash using SIMD, writing directly to output buffer.
1033
+ */
1034
+ function hashSimdInto(input, output, outputLen) {
1035
+ // Delegate to hashSimd and copy result (SIMD path already optimized)
1036
+ const result = hashSimd(input, outputLen);
1037
+ output.set(result);
1038
+ }
node_modules/@huggingface/blake3-jit/dist/commonjs/hasher.d.ts ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hasher - Incremental hashing with support for all modes
3
+ *
4
+ * Supports:
5
+ * - Regular hashing
6
+ * - Keyed hashing (MAC)
7
+ * - Key derivation (derive_key)
8
+ * - XOF (eXtendable Output Function) mode
9
+ */
10
+ /**
11
+ * Output state for XOF (eXtendable Output Function) mode.
12
+ * Allows reading arbitrary amounts of output.
13
+ */
14
+ export declare class XofReader {
15
+ private inputCv;
16
+ private blockWords;
17
+ private counter;
18
+ private blockLen;
19
+ private flags;
20
+ private outputBlock;
21
+ private outputBlockOffset;
22
+ constructor(inputCv: Uint32Array, blockWords: Uint32Array, counter: number, blockLen: number, flags: number);
23
+ /**
24
+ * Read the next `length` bytes of output.
25
+ */
26
+ read(length: number): Uint8Array;
27
+ }
28
+ /**
29
+ * Main BLAKE3 Hasher class.
30
+ *
31
+ * Usage:
32
+ * const hasher = new Hasher();
33
+ * hasher.update(data);
34
+ * const hash = hasher.finalize();
35
+ *
36
+ * Or with chaining:
37
+ * const hash = new Hasher().update(data).finalize();
38
+ */
39
+ export declare class Hasher {
40
+ private chunkState;
41
+ private keyWords;
42
+ private cvStack;
43
+ private cvStackLen;
44
+ private flags;
45
+ private parentBlock;
46
+ private parentCv;
47
+ private chunkCv;
48
+ private outWords;
49
+ private finalizeCv;
50
+ /**
51
+ * Create a new Hasher.
52
+ *
53
+ * @param keyWords - Initial key words (IV for regular hashing)
54
+ * @param flags - Domain separation flags
55
+ */
56
+ constructor(keyWords?: Uint32Array, flags?: number);
57
+ /**
58
+ * Reset the hasher to process a new message with the same key/flags.
59
+ * Reuses all internal buffers — zero allocations.
60
+ */
61
+ reset(): this;
62
+ /**
63
+ * Create a new keyed hasher (MAC).
64
+ *
65
+ * @param key - 32-byte key
66
+ */
67
+ static newKeyed(key: Uint8Array): Hasher;
68
+ /**
69
+ * Create a new key derivation hasher.
70
+ *
71
+ * @param context - Context string for domain separation
72
+ */
73
+ static newDeriveKey(context: string): Hasher;
74
+ /**
75
+ * Push a chaining value onto the stack.
76
+ */
77
+ private pushCv;
78
+ /**
79
+ * Pop a chaining value from the stack.
80
+ */
81
+ private popCv;
82
+ /**
83
+ * Add a chunk's chaining value and merge completed subtrees.
84
+ */
85
+ private addChunkCv;
86
+ /**
87
+ * Update the hasher with input data.
88
+ *
89
+ * @param input - Data to hash
90
+ * @returns this (for chaining)
91
+ */
92
+ update(input: Uint8Array): this;
93
+ /**
94
+ * Get the output parameters (for XOF mode or finalization).
95
+ */
96
+ private finalizeOutput;
97
+ /**
98
+ * Finalize the hash and return the result.
99
+ *
100
+ * @param outputLength - Number of bytes to output (default: 32)
101
+ * @returns The hash output
102
+ */
103
+ finalize(outputLength?: number): Uint8Array;
104
+ /**
105
+ * Finalize and return an XOF reader for arbitrary-length output.
106
+ */
107
+ finalizeXof(): XofReader;
108
+ }
node_modules/@huggingface/blake3-jit/dist/commonjs/hasher.js ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ /**
3
+ * BLAKE3 Hasher - Incremental hashing with support for all modes
4
+ *
5
+ * Supports:
6
+ * - Regular hashing
7
+ * - Keyed hashing (MAC)
8
+ * - Key derivation (derive_key)
9
+ * - XOF (eXtendable Output Function) mode
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.Hasher = exports.XofReader = void 0;
13
+ const compress_js_1 = require("./compress.js");
14
+ const constants_js_1 = require("./constants.js");
15
+ const utils_js_1 = require("./utils.js");
16
+ /**
17
+ * Output state for XOF (eXtendable Output Function) mode.
18
+ * Allows reading arbitrary amounts of output.
19
+ */
20
+ class XofReader {
21
+ inputCv;
22
+ blockWords;
23
+ counter;
24
+ blockLen;
25
+ flags;
26
+ outputBlock;
27
+ outputBlockOffset;
28
+ constructor(inputCv, blockWords, counter, blockLen, flags) {
29
+ this.inputCv = inputCv;
30
+ this.blockWords = blockWords;
31
+ this.counter = counter;
32
+ this.blockLen = blockLen;
33
+ this.flags = flags | constants_js_1.ROOT;
34
+ this.outputBlock = new Uint32Array(16);
35
+ this.outputBlockOffset = 64; // Forces generation on first read
36
+ }
37
+ /**
38
+ * Read the next `length` bytes of output.
39
+ */
40
+ read(length) {
41
+ const output = new Uint8Array(length);
42
+ let outputOffset = 0;
43
+ while (outputOffset < length) {
44
+ // Generate new output block if needed
45
+ if (this.outputBlockOffset >= 64) {
46
+ (0, compress_js_1.compress)(this.inputCv, 0, this.blockWords, 0, this.outputBlock, 0, true, // full 64-byte output
47
+ this.counter++, this.blockLen, this.flags);
48
+ this.outputBlockOffset = 0;
49
+ }
50
+ // Copy bytes from output block
51
+ const available = 64 - this.outputBlockOffset;
52
+ const toCopy = Math.min(available, length - outputOffset);
53
+ // Optimized copy using writeLittleEndianBytesPartial
54
+ const wordOffset = this.outputBlockOffset >>> 2;
55
+ const byteWithinWord = this.outputBlockOffset & 3;
56
+ if (byteWithinWord === 0 && toCopy >= 4) {
57
+ // Aligned copy - can use word-at-a-time
58
+ const fullWords = toCopy >>> 2;
59
+ (0, utils_js_1.writeLittleEndianBytesPartial)(this.outputBlock, wordOffset, output, outputOffset, fullWords << 2);
60
+ const bytesCopied = fullWords << 2;
61
+ outputOffset += bytesCopied;
62
+ this.outputBlockOffset += bytesCopied;
63
+ }
64
+ else {
65
+ // Byte-by-byte for unaligned access
66
+ for (let i = 0; i < toCopy; i++) {
67
+ const wordIdx = (this.outputBlockOffset + i) >>> 2;
68
+ const byteIdx = (this.outputBlockOffset + i) & 3;
69
+ output[outputOffset + i] = (this.outputBlock[wordIdx] >>> (byteIdx << 3)) & 0xff;
70
+ }
71
+ outputOffset += toCopy;
72
+ this.outputBlockOffset += toCopy;
73
+ }
74
+ }
75
+ return output;
76
+ }
77
+ }
78
+ exports.XofReader = XofReader;
79
+ /**
80
+ * Chunk state for processing input data.
81
+ * Each chunk is 1024 bytes and produces an 8-word chaining value.
82
+ */
83
+ class ChunkState {
84
+ chainingValue;
85
+ chunkCounter;
86
+ blockWords;
87
+ blockLen;
88
+ blocksCompressed;
89
+ flags;
90
+ constructor(keyWords, chunkCounter, flags) {
91
+ this.chainingValue = new Uint32Array(keyWords);
92
+ this.chunkCounter = chunkCounter;
93
+ this.blockWords = new Uint32Array(16);
94
+ this.blockLen = 0;
95
+ this.blocksCompressed = 0;
96
+ this.flags = flags;
97
+ }
98
+ resetTo(keyWords, chunkCounter, flags) {
99
+ this.chainingValue.set(keyWords);
100
+ this.chunkCounter = chunkCounter;
101
+ this.blockLen = 0;
102
+ this.blocksCompressed = 0;
103
+ this.flags = flags;
104
+ }
105
+ /**
106
+ * Get the flags for the current block.
107
+ */
108
+ startFlag() {
109
+ return this.blocksCompressed === 0 ? constants_js_1.CHUNK_START : 0;
110
+ }
111
+ /**
112
+ * Update the chunk state with input data.
113
+ * Returns the number of bytes consumed.
114
+ */
115
+ update(input, inputOffset, inputLen) {
116
+ let consumed = 0;
117
+ while (inputLen > 0) {
118
+ // If we have a full block, compress it
119
+ if (this.blockLen === constants_js_1.BLOCK_LEN) {
120
+ (0, compress_js_1.compress)(this.chainingValue, 0, this.blockWords, 0, this.chainingValue, 0, false, this.chunkCounter, constants_js_1.BLOCK_LEN, this.flags | this.startFlag());
121
+ this.blocksCompressed++;
122
+ this.blockLen = 0;
123
+ }
124
+ // Fill the block buffer
125
+ const want = constants_js_1.BLOCK_LEN - this.blockLen;
126
+ const take = Math.min(want, inputLen);
127
+ if (this.blockLen === 0 && take === constants_js_1.BLOCK_LEN) {
128
+ (0, utils_js_1.readLittleEndianWordsFull)(input, inputOffset, this.blockWords);
129
+ }
130
+ else {
131
+ // Partial block - byte-by-byte into correct position
132
+ for (let i = 0; i < take; i++) {
133
+ const pos = this.blockLen + i;
134
+ const wordIdx = pos >>> 2;
135
+ const byteIdx = pos & 3;
136
+ if (byteIdx === 0) {
137
+ this.blockWords[wordIdx] = input[inputOffset + i];
138
+ }
139
+ else {
140
+ this.blockWords[wordIdx] |= input[inputOffset + i] << (byteIdx << 3);
141
+ }
142
+ }
143
+ }
144
+ this.blockLen += take;
145
+ inputOffset += take;
146
+ inputLen -= take;
147
+ consumed += take;
148
+ }
149
+ return consumed;
150
+ }
151
+ /**
152
+ * Finalize this chunk and return its output.
153
+ * Returns 8 words (chaining value) or 16 words (if root).
154
+ */
155
+ output() {
156
+ // Zero-pad unused words in blockWords to avoid stale data from previous blocks
157
+ // This is necessary when a partial block follows a full block within the same chunk
158
+ const usedWords = (this.blockLen + 3) >>> 2; // ceil(blockLen / 4)
159
+ for (let i = usedWords; i < 16; i++) {
160
+ this.blockWords[i] = 0;
161
+ }
162
+ return {
163
+ inputCv: this.chainingValue,
164
+ blockWords: this.blockWords,
165
+ blockLen: this.blockLen,
166
+ counter: this.chunkCounter,
167
+ flags: this.flags | this.startFlag() | constants_js_1.CHUNK_END,
168
+ };
169
+ }
170
+ /**
171
+ * Get the number of bytes in this chunk.
172
+ */
173
+ len() {
174
+ return this.blocksCompressed * constants_js_1.BLOCK_LEN + this.blockLen;
175
+ }
176
+ }
177
+ /**
178
+ * Main BLAKE3 Hasher class.
179
+ *
180
+ * Usage:
181
+ * const hasher = new Hasher();
182
+ * hasher.update(data);
183
+ * const hash = hasher.finalize();
184
+ *
185
+ * Or with chaining:
186
+ * const hash = new Hasher().update(data).finalize();
187
+ */
188
+ class Hasher {
189
+ chunkState;
190
+ keyWords;
191
+ cvStack;
192
+ cvStackLen;
193
+ flags;
194
+ parentBlock;
195
+ parentCv;
196
+ chunkCv;
197
+ outWords;
198
+ finalizeCv;
199
+ /**
200
+ * Create a new Hasher.
201
+ *
202
+ * @param keyWords - Initial key words (IV for regular hashing)
203
+ * @param flags - Domain separation flags
204
+ */
205
+ constructor(keyWords, flags) {
206
+ this.keyWords = keyWords ? new Uint32Array(keyWords) : new Uint32Array(constants_js_1.IV);
207
+ this.flags = flags ?? 0;
208
+ this.chunkState = new ChunkState(this.keyWords, 0, this.flags);
209
+ this.cvStack = new Uint32Array(constants_js_1.MAX_DEPTH * 8);
210
+ this.cvStackLen = 0;
211
+ this.parentBlock = new Uint32Array(16);
212
+ this.parentCv = new Uint32Array(8);
213
+ this.chunkCv = new Uint32Array(8);
214
+ this.outWords = new Uint32Array(16);
215
+ this.finalizeCv = new Uint32Array(8);
216
+ }
217
+ /**
218
+ * Reset the hasher to process a new message with the same key/flags.
219
+ * Reuses all internal buffers — zero allocations.
220
+ */
221
+ reset() {
222
+ this.chunkState.resetTo(this.keyWords, 0, this.flags);
223
+ this.cvStackLen = 0;
224
+ return this;
225
+ }
226
+ /**
227
+ * Create a new keyed hasher (MAC).
228
+ *
229
+ * @param key - 32-byte key
230
+ */
231
+ static newKeyed(key) {
232
+ if (key.length !== constants_js_1.KEY_LEN) {
233
+ throw new Error(`Key must be ${constants_js_1.KEY_LEN} bytes, got ${key.length}`);
234
+ }
235
+ const keyWords = new Uint32Array(8);
236
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
237
+ const view = new Uint32Array(key.buffer, key.byteOffset, 8);
238
+ keyWords.set(view);
239
+ }
240
+ else {
241
+ for (let i = 0; i < 8; i++) {
242
+ const off = i * 4;
243
+ keyWords[i] = key[off] | (key[off + 1] << 8) | (key[off + 2] << 16) | (key[off + 3] << 24);
244
+ }
245
+ }
246
+ return new Hasher(keyWords, constants_js_1.KEYED_HASH);
247
+ }
248
+ /**
249
+ * Create a new key derivation hasher.
250
+ *
251
+ * @param context - Context string for domain separation
252
+ */
253
+ static newDeriveKey(context) {
254
+ // First, hash the context string with DERIVE_KEY_CONTEXT flag
255
+ const contextBytes = (0, utils_js_1.encodeUTF8)(context);
256
+ const contextHasher = new Hasher(new Uint32Array(constants_js_1.IV), constants_js_1.DERIVE_KEY_CONTEXT);
257
+ contextHasher.update(contextBytes);
258
+ // Get the context key
259
+ const contextKey = new Uint32Array(8);
260
+ const output = contextHasher.finalizeOutput();
261
+ (0, compress_js_1.compress)(output.inputCv, 0, output.blockWords, 0, contextKey, 0, false, output.counter, output.blockLen, output.flags | constants_js_1.ROOT);
262
+ // Return a hasher initialized with the context key
263
+ return new Hasher(contextKey, constants_js_1.DERIVE_KEY_MATERIAL);
264
+ }
265
+ /**
266
+ * Push a chaining value onto the stack.
267
+ */
268
+ pushCv(cv, cvOffset) {
269
+ this.cvStack.set(cv.subarray(cvOffset, cvOffset + 8), this.cvStackLen * 8);
270
+ this.cvStackLen++;
271
+ }
272
+ /**
273
+ * Pop a chaining value from the stack.
274
+ */
275
+ popCv(out, outOffset) {
276
+ this.cvStackLen--;
277
+ out.set(this.cvStack.subarray(this.cvStackLen * 8, (this.cvStackLen + 1) * 8), outOffset);
278
+ }
279
+ /**
280
+ * Add a chunk's chaining value and merge completed subtrees.
281
+ */
282
+ addChunkCv(newCv, newCvOffset, totalChunks) {
283
+ const parentBlock = this.parentBlock;
284
+ const parentCv = this.parentCv;
285
+ while ((totalChunks & 1) === 0) {
286
+ // Pop left child, new CV is right child
287
+ this.popCv(parentBlock, 0);
288
+ parentBlock.set(newCv.subarray(newCvOffset, newCvOffset + 8), 8);
289
+ (0, compress_js_1.compress)(this.keyWords, 0, parentBlock, 0, parentCv, 0, false, 0, constants_js_1.BLOCK_LEN, this.flags | constants_js_1.PARENT);
290
+ newCv = parentCv;
291
+ newCvOffset = 0;
292
+ totalChunks >>>= 1;
293
+ }
294
+ this.pushCv(newCv, newCvOffset);
295
+ }
296
+ /**
297
+ * Update the hasher with input data.
298
+ *
299
+ * @param input - Data to hash
300
+ * @returns this (for chaining)
301
+ */
302
+ update(input) {
303
+ let inputOffset = 0;
304
+ let inputLen = input.length;
305
+ // Fill the current chunk
306
+ while (inputLen > 0) {
307
+ // If current chunk is full, finalize it and start a new one
308
+ if (this.chunkState.len() === constants_js_1.CHUNK_LEN) {
309
+ const output = this.chunkState.output();
310
+ const chunkCv = this.chunkCv;
311
+ (0, compress_js_1.compress)(output.inputCv, 0, output.blockWords, 0, chunkCv, 0, false, output.counter, output.blockLen, output.flags);
312
+ const totalChunks = this.chunkState.chunkCounter + 1;
313
+ this.addChunkCv(chunkCv, 0, totalChunks);
314
+ this.chunkState.resetTo(this.keyWords, totalChunks, this.flags);
315
+ }
316
+ // Fill the current chunk
317
+ const want = constants_js_1.CHUNK_LEN - this.chunkState.len();
318
+ const take = Math.min(want, inputLen);
319
+ this.chunkState.update(input, inputOffset, take);
320
+ inputOffset += take;
321
+ inputLen -= take;
322
+ }
323
+ return this;
324
+ }
325
+ /**
326
+ * Get the output parameters (for XOF mode or finalization).
327
+ */
328
+ finalizeOutput() {
329
+ let output = this.chunkState.output();
330
+ let parentBlock = this.parentBlock;
331
+ let cv = this.finalizeCv;
332
+ // If there are chunks on the stack, merge them
333
+ if (this.cvStackLen > 0) {
334
+ // First compress the current chunk
335
+ (0, compress_js_1.compress)(output.inputCv, 0, output.blockWords, 0, cv, 0, false, output.counter, output.blockLen, output.flags);
336
+ // Merge with parent nodes from stack
337
+ while (this.cvStackLen > 0) {
338
+ this.cvStackLen--;
339
+ parentBlock.set(this.cvStack.subarray(this.cvStackLen * 8, (this.cvStackLen + 1) * 8), 0);
340
+ parentBlock.set(cv, 8);
341
+ if (this.cvStackLen > 0) {
342
+ (0, compress_js_1.compress)(this.keyWords, 0, parentBlock, 0, cv, 0, false, 0, constants_js_1.BLOCK_LEN, this.flags | constants_js_1.PARENT);
343
+ }
344
+ else {
345
+ // This is the root - return output params
346
+ return {
347
+ inputCv: this.keyWords,
348
+ blockWords: parentBlock,
349
+ blockLen: constants_js_1.BLOCK_LEN,
350
+ counter: 0,
351
+ flags: this.flags | constants_js_1.PARENT,
352
+ };
353
+ }
354
+ }
355
+ }
356
+ // Single chunk case
357
+ return output;
358
+ }
359
+ /**
360
+ * Finalize the hash and return the result.
361
+ *
362
+ * @param outputLength - Number of bytes to output (default: 32)
363
+ * @returns The hash output
364
+ */
365
+ finalize(outputLength = constants_js_1.OUT_LEN) {
366
+ const output = this.finalizeOutput();
367
+ const result = new Uint8Array(outputLength);
368
+ if (outputLength <= 64) {
369
+ const outWords = this.outWords;
370
+ (0, compress_js_1.compress)(output.inputCv, 0, output.blockWords, 0, outWords, 0, outputLength > 32, // full output if > 32 bytes
371
+ output.counter, output.blockLen, output.flags | constants_js_1.ROOT);
372
+ if (utils_js_1.IS_LITTLE_ENDIAN) {
373
+ const outBytes = new Uint8Array(outWords.buffer);
374
+ result.set(outBytes.subarray(0, outputLength));
375
+ }
376
+ else {
377
+ (0, utils_js_1.writeLittleEndianBytesPartial)(outWords, 0, result, 0, outputLength);
378
+ }
379
+ }
380
+ else {
381
+ // Multiple blocks - use XOF
382
+ const xof = this.finalizeXof();
383
+ const full = xof.read(outputLength);
384
+ result.set(full);
385
+ }
386
+ return result;
387
+ }
388
+ /**
389
+ * Finalize and return an XOF reader for arbitrary-length output.
390
+ */
391
+ finalizeXof() {
392
+ const output = this.finalizeOutput();
393
+ return new XofReader(new Uint32Array(output.inputCv), new Uint32Array(output.blockWords), output.counter, output.blockLen, output.flags);
394
+ }
395
+ }
396
+ exports.Hasher = Hasher;
node_modules/@huggingface/blake3-jit/dist/commonjs/index.d.ts ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 - The fastest pure JavaScript implementation
3
+ *
4
+ * Features:
5
+ * - All 3 modes: hash, keyed (MAC), derive_key
6
+ * - XOF (eXtendable Output Function) support
7
+ * - Automatic WASM SIMD acceleration for large inputs
8
+ * - Zero dependencies
9
+ * - Tree-shakeable exports
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { hash, createKeyed, createDeriveKey } from 'blake3-jit';
14
+ *
15
+ * // Simple hashing
16
+ * const digest = hash(new Uint8Array([1, 2, 3]));
17
+ *
18
+ * // Keyed hashing (MAC)
19
+ * const mac = createKeyed(key).update(data).finalize();
20
+ *
21
+ * // Key derivation
22
+ * const derived = createDeriveKey("my context").update(material).finalize(64);
23
+ * ```
24
+ */
25
+ export { Hasher, XofReader } from "./hasher.js";
26
+ export { hash, hashInto, warmupSimd } from "./hash.js";
27
+ import { Hasher } from "./hasher.js";
28
+ /**
29
+ * Create a new keyed hasher (MAC).
30
+ *
31
+ * @param key - 32-byte key
32
+ * @returns A new Hasher configured for keyed hashing
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const key = new Uint8Array(32); // Your 32-byte key
37
+ * crypto.getRandomValues(key);
38
+ *
39
+ * const mac = createKeyed(key)
40
+ * .update(message)
41
+ * .finalize();
42
+ * ```
43
+ */
44
+ export declare function createKeyed(key: Uint8Array): Hasher;
45
+ /**
46
+ * Create a new key derivation hasher.
47
+ *
48
+ * @param context - Context string for domain separation
49
+ * @returns A new Hasher configured for key derivation
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const derivedKey = createDeriveKey("my-app encryption key v1")
54
+ * .update(inputKeyMaterial)
55
+ * .finalize(32);
56
+ * ```
57
+ */
58
+ export declare function createDeriveKey(context: string): Hasher;
59
+ /**
60
+ * Create a new regular hasher for incremental hashing.
61
+ *
62
+ * @returns A new Hasher
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const hasher = createHasher();
67
+ * hasher.update(chunk1);
68
+ * hasher.update(chunk2);
69
+ * const digest = hasher.finalize();
70
+ * ```
71
+ */
72
+ export declare function createHasher(): Hasher;
73
+ import { hash, hashInto } from "./hash.js";
74
+ declare const _default: {
75
+ hash: typeof hash;
76
+ hashInto: typeof hashInto;
77
+ Hasher: typeof Hasher;
78
+ createHasher: typeof createHasher;
79
+ createKeyed: typeof createKeyed;
80
+ createDeriveKey: typeof createDeriveKey;
81
+ };
82
+ export default _default;
node_modules/@huggingface/blake3-jit/dist/commonjs/index.js ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ /**
3
+ * BLAKE3 - The fastest pure JavaScript implementation
4
+ *
5
+ * Features:
6
+ * - All 3 modes: hash, keyed (MAC), derive_key
7
+ * - XOF (eXtendable Output Function) support
8
+ * - Automatic WASM SIMD acceleration for large inputs
9
+ * - Zero dependencies
10
+ * - Tree-shakeable exports
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { hash, createKeyed, createDeriveKey } from 'blake3-jit';
15
+ *
16
+ * // Simple hashing
17
+ * const digest = hash(new Uint8Array([1, 2, 3]));
18
+ *
19
+ * // Keyed hashing (MAC)
20
+ * const mac = createKeyed(key).update(data).finalize();
21
+ *
22
+ * // Key derivation
23
+ * const derived = createDeriveKey("my context").update(material).finalize(64);
24
+ * ```
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.warmupSimd = exports.hashInto = exports.hash = exports.XofReader = exports.Hasher = void 0;
28
+ exports.createKeyed = createKeyed;
29
+ exports.createDeriveKey = createDeriveKey;
30
+ exports.createHasher = createHasher;
31
+ // Core exports
32
+ var hasher_js_1 = require("./hasher.js");
33
+ Object.defineProperty(exports, "Hasher", { enumerable: true, get: function () { return hasher_js_1.Hasher; } });
34
+ Object.defineProperty(exports, "XofReader", { enumerable: true, get: function () { return hasher_js_1.XofReader; } });
35
+ var hash_js_1 = require("./hash.js");
36
+ Object.defineProperty(exports, "hash", { enumerable: true, get: function () { return hash_js_1.hash; } });
37
+ Object.defineProperty(exports, "hashInto", { enumerable: true, get: function () { return hash_js_1.hashInto; } });
38
+ Object.defineProperty(exports, "warmupSimd", { enumerable: true, get: function () { return hash_js_1.warmupSimd; } });
39
+ // Convenience imports
40
+ const hasher_js_2 = require("./hasher.js");
41
+ /**
42
+ * Create a new keyed hasher (MAC).
43
+ *
44
+ * @param key - 32-byte key
45
+ * @returns A new Hasher configured for keyed hashing
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const key = new Uint8Array(32); // Your 32-byte key
50
+ * crypto.getRandomValues(key);
51
+ *
52
+ * const mac = createKeyed(key)
53
+ * .update(message)
54
+ * .finalize();
55
+ * ```
56
+ */
57
+ function createKeyed(key) {
58
+ return hasher_js_2.Hasher.newKeyed(key);
59
+ }
60
+ /**
61
+ * Create a new key derivation hasher.
62
+ *
63
+ * @param context - Context string for domain separation
64
+ * @returns A new Hasher configured for key derivation
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const derivedKey = createDeriveKey("my-app encryption key v1")
69
+ * .update(inputKeyMaterial)
70
+ * .finalize(32);
71
+ * ```
72
+ */
73
+ function createDeriveKey(context) {
74
+ return hasher_js_2.Hasher.newDeriveKey(context);
75
+ }
76
+ /**
77
+ * Create a new regular hasher for incremental hashing.
78
+ *
79
+ * @returns A new Hasher
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const hasher = createHasher();
84
+ * hasher.update(chunk1);
85
+ * hasher.update(chunk2);
86
+ * const digest = hasher.finalize();
87
+ * ```
88
+ */
89
+ function createHasher() {
90
+ return new hasher_js_2.Hasher();
91
+ }
92
+ // Import for default export
93
+ const hash_js_2 = require("./hash.js");
94
+ // Pre-warm SIMD in browser environments (non-blocking)
95
+ // This avoids initialization latency on first large hash
96
+ if (typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined") {
97
+ queueMicrotask(() => {
98
+ (0, hash_js_2.warmupSimd)();
99
+ });
100
+ }
101
+ // Default export for convenience
102
+ exports.default = {
103
+ hash: hash_js_2.hash,
104
+ hashInto: hash_js_2.hashInto,
105
+ Hasher: hasher_js_2.Hasher,
106
+ createHasher,
107
+ createKeyed,
108
+ createDeriveKey,
109
+ };
node_modules/@huggingface/blake3-jit/dist/commonjs/package.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "type": "commonjs"
3
+ }
node_modules/@huggingface/blake3-jit/dist/commonjs/utils.d.ts ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Utility Functions
3
+ *
4
+ * Optimized for little-endian systems (most user-facing systems).
5
+ * BLAKE3 is little-endian friendly - on little-endian systems we can
6
+ * create Uint32Array views directly over input buffers.
7
+ */
8
+ /**
9
+ * Detect system endianness at module load time.
10
+ * On little-endian systems, the byte 0x01 will be at index 0.
11
+ */
12
+ export declare const IS_LITTLE_ENDIAN: boolean;
13
+ /**
14
+ * Read 16 little-endian 32-bit words from a byte array into a Uint32Array.
15
+ * This is only needed on big-endian systems.
16
+ *
17
+ * @param input - Source byte array
18
+ * @param offset - Starting byte offset in input
19
+ * @param words - Destination Uint32Array (must have at least 16 elements)
20
+ */
21
+ export declare function readLittleEndianWordsFull(input: Uint8Array, offset: number, words: Uint32Array): void;
22
+ /**
23
+ * Read N little-endian 32-bit words from a byte array.
24
+ * Handles partial reads (for final blocks).
25
+ *
26
+ * @param input - Source byte array
27
+ * @param offset - Starting byte offset
28
+ * @param words - Destination Uint32Array
29
+ * @param wordCount - Number of words to read
30
+ */
31
+ export declare function readLittleEndianWords(input: Uint8Array, offset: number, words: Uint32Array, wordCount: number): void;
32
+ /**
33
+ * Read a partial block with zero padding.
34
+ * Used for the final block when input length is not a multiple of 64.
35
+ *
36
+ * @param input - Source byte array
37
+ * @param offset - Starting byte offset
38
+ * @param length - Number of bytes to read (< 64)
39
+ * @param words - Destination Uint32Array (must have 16 elements)
40
+ */
41
+ export declare function readLittleEndianWordsPartial(input: Uint8Array, offset: number, length: number, words: Uint32Array): void;
42
+ /**
43
+ * Write 8 little-endian 32-bit words to a byte array.
44
+ *
45
+ * @param words - Source Uint32Array
46
+ * @param wordOffset - Starting word offset in source
47
+ * @param output - Destination byte array
48
+ * @param byteOffset - Starting byte offset in destination
49
+ */
50
+ export declare function writeLittleEndianWords(words: Uint32Array, wordOffset: number, output: Uint8Array, byteOffset: number): void;
51
+ /**
52
+ * Write N bytes from 32-bit words to output.
53
+ * Used for variable-length output (XOF mode).
54
+ *
55
+ * @param words - Source Uint32Array
56
+ * @param wordOffset - Starting word offset
57
+ * @param output - Destination byte array
58
+ * @param byteOffset - Starting byte offset in destination
59
+ * @param byteCount - Number of bytes to write
60
+ */
61
+ export declare function writeLittleEndianBytesPartial(words: Uint32Array, wordOffset: number, output: Uint8Array, byteOffset: number, byteCount: number): void;
62
+ /**
63
+ * Encode a UTF-8 string to Uint8Array.
64
+ * Used for derive_key context strings.
65
+ */
66
+ export declare function encodeUTF8(str: string): Uint8Array;
67
+ /**
68
+ * Count trailing zero bits in a 32-bit number using De Bruijn multiplication.
69
+ * This is O(1) and branchless for non-zero inputs.
70
+ *
71
+ * For Merkle tree merge: ctz32(chunkCounter) tells us how many merges to do.
72
+ */
73
+ export declare function ctz32(n: number): number;
74
+ /**
75
+ * Count trailing zero bits in a 64-bit number.
76
+ * Used to determine how many parent nodes to compute after adding a chunk.
77
+ *
78
+ * Note: JavaScript bitwise ops work on 32-bit signed integers,
79
+ * so we need to handle 64-bit numbers carefully.
80
+ */
81
+ export declare function countTrailingZeros(n: number): number;
82
+ /**
83
+ * Create a Uint32Array view of a Uint8Array.
84
+ * Only works correctly on little-endian systems when the offset is 4-byte aligned.
85
+ *
86
+ * @param arr - Source byte array
87
+ * @param byteOffset - Starting byte offset (must be 4-byte aligned)
88
+ * @param wordLength - Number of 32-bit words
89
+ */
90
+ export declare function uint32View(arr: Uint8Array, byteOffset: number, wordLength: number): Uint32Array;
node_modules/@huggingface/blake3-jit/dist/commonjs/utils.js ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ /**
3
+ * BLAKE3 Utility Functions
4
+ *
5
+ * Optimized for little-endian systems (most user-facing systems).
6
+ * BLAKE3 is little-endian friendly - on little-endian systems we can
7
+ * create Uint32Array views directly over input buffers.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.IS_LITTLE_ENDIAN = void 0;
11
+ exports.readLittleEndianWordsFull = readLittleEndianWordsFull;
12
+ exports.readLittleEndianWords = readLittleEndianWords;
13
+ exports.readLittleEndianWordsPartial = readLittleEndianWordsPartial;
14
+ exports.writeLittleEndianWords = writeLittleEndianWords;
15
+ exports.writeLittleEndianBytesPartial = writeLittleEndianBytesPartial;
16
+ exports.encodeUTF8 = encodeUTF8;
17
+ exports.ctz32 = ctz32;
18
+ exports.countTrailingZeros = countTrailingZeros;
19
+ exports.uint32View = uint32View;
20
+ /**
21
+ * Detect system endianness at module load time.
22
+ * On little-endian systems, the byte 0x01 will be at index 0.
23
+ */
24
+ exports.IS_LITTLE_ENDIAN = new Uint8Array(new Uint32Array([0x01020304]).buffer)[0] === 0x04;
25
+ /**
26
+ * Read 16 little-endian 32-bit words from a byte array into a Uint32Array.
27
+ * This is only needed on big-endian systems.
28
+ *
29
+ * @param input - Source byte array
30
+ * @param offset - Starting byte offset in input
31
+ * @param words - Destination Uint32Array (must have at least 16 elements)
32
+ */
33
+ function readLittleEndianWordsFull(input, offset, words) {
34
+ for (let i = 0; i < 16; ++i, offset += 4) {
35
+ words[i] =
36
+ input[offset] |
37
+ (input[offset + 1] << 8) |
38
+ (input[offset + 2] << 16) |
39
+ (input[offset + 3] << 24);
40
+ }
41
+ }
42
+ /**
43
+ * Read N little-endian 32-bit words from a byte array.
44
+ * Handles partial reads (for final blocks).
45
+ *
46
+ * @param input - Source byte array
47
+ * @param offset - Starting byte offset
48
+ * @param words - Destination Uint32Array
49
+ * @param wordCount - Number of words to read
50
+ */
51
+ function readLittleEndianWords(input, offset, words, wordCount) {
52
+ for (let i = 0; i < wordCount; ++i, offset += 4) {
53
+ words[i] =
54
+ input[offset] |
55
+ (input[offset + 1] << 8) |
56
+ (input[offset + 2] << 16) |
57
+ (input[offset + 3] << 24);
58
+ }
59
+ }
60
+ /**
61
+ * Read a partial block with zero padding.
62
+ * Used for the final block when input length is not a multiple of 64.
63
+ *
64
+ * @param input - Source byte array
65
+ * @param offset - Starting byte offset
66
+ * @param length - Number of bytes to read (< 64)
67
+ * @param words - Destination Uint32Array (must have 16 elements)
68
+ */
69
+ function readLittleEndianWordsPartial(input, offset, length, words) {
70
+ // Zero out all words first
71
+ words.fill(0);
72
+ // Read full words
73
+ const fullWords = length >>> 2;
74
+ let i = 0;
75
+ for (; i < fullWords; ++i, offset += 4) {
76
+ words[i] =
77
+ input[offset] |
78
+ (input[offset + 1] << 8) |
79
+ (input[offset + 2] << 16) |
80
+ (input[offset + 3] << 24);
81
+ }
82
+ // Handle remaining bytes (0-3)
83
+ const remaining = length & 3;
84
+ if (remaining > 0) {
85
+ let word = input[offset];
86
+ if (remaining > 1)
87
+ word |= input[offset + 1] << 8;
88
+ if (remaining > 2)
89
+ word |= input[offset + 2] << 16;
90
+ words[i] = word;
91
+ }
92
+ }
93
+ /**
94
+ * Write 8 little-endian 32-bit words to a byte array.
95
+ *
96
+ * @param words - Source Uint32Array
97
+ * @param wordOffset - Starting word offset in source
98
+ * @param output - Destination byte array
99
+ * @param byteOffset - Starting byte offset in destination
100
+ */
101
+ function writeLittleEndianWords(words, wordOffset, output, byteOffset) {
102
+ for (let i = 0; i < 8; ++i, byteOffset += 4) {
103
+ const w = words[wordOffset + i];
104
+ output[byteOffset] = w & 0xff;
105
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
106
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
107
+ output[byteOffset + 3] = (w >>> 24) & 0xff;
108
+ }
109
+ }
110
+ /**
111
+ * Write N bytes from 32-bit words to output.
112
+ * Used for variable-length output (XOF mode).
113
+ *
114
+ * @param words - Source Uint32Array
115
+ * @param wordOffset - Starting word offset
116
+ * @param output - Destination byte array
117
+ * @param byteOffset - Starting byte offset in destination
118
+ * @param byteCount - Number of bytes to write
119
+ */
120
+ function writeLittleEndianBytesPartial(words, wordOffset, output, byteOffset, byteCount) {
121
+ const fullWords = byteCount >>> 2;
122
+ let i = 0;
123
+ // Write full words
124
+ for (; i < fullWords; ++i, byteOffset += 4) {
125
+ const w = words[wordOffset + i];
126
+ output[byteOffset] = w & 0xff;
127
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
128
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
129
+ output[byteOffset + 3] = (w >>> 24) & 0xff;
130
+ }
131
+ // Write remaining bytes
132
+ const remaining = byteCount & 3;
133
+ if (remaining > 0) {
134
+ const w = words[wordOffset + i];
135
+ output[byteOffset] = w & 0xff;
136
+ if (remaining > 1)
137
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
138
+ if (remaining > 2)
139
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
140
+ }
141
+ }
142
+ /**
143
+ * Encode a UTF-8 string to Uint8Array.
144
+ * Used for derive_key context strings.
145
+ */
146
+ function encodeUTF8(str) {
147
+ if (typeof TextEncoder !== "undefined") {
148
+ return new TextEncoder().encode(str);
149
+ }
150
+ // Fallback for older environments
151
+ const bytes = [];
152
+ for (let i = 0; i < str.length; i++) {
153
+ let c = str.charCodeAt(i);
154
+ if (c < 0x80) {
155
+ bytes.push(c);
156
+ }
157
+ else if (c < 0x800) {
158
+ bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
159
+ }
160
+ else if (c < 0xd800 || c >= 0xe000) {
161
+ bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
162
+ }
163
+ else {
164
+ // Surrogate pair
165
+ i++;
166
+ c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
167
+ bytes.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
168
+ }
169
+ }
170
+ return new Uint8Array(bytes);
171
+ }
172
+ /**
173
+ * De Bruijn lookup table for O(1) trailing zero count.
174
+ * The expression (n & -n) isolates the lowest set bit.
175
+ * Multiplying by the De Bruijn constant maps each power of 2 to a unique 5-bit index.
176
+ */
177
+ const CTZ32_TABLE = new Uint8Array([
178
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12,
179
+ 18, 6, 11, 5, 10, 9,
180
+ ]);
181
+ /**
182
+ * Count trailing zero bits in a 32-bit number using De Bruijn multiplication.
183
+ * This is O(1) and branchless for non-zero inputs.
184
+ *
185
+ * For Merkle tree merge: ctz32(chunkCounter) tells us how many merges to do.
186
+ */
187
+ function ctz32(n) {
188
+ if (n === 0)
189
+ return 32;
190
+ // Use unsigned right shift to handle negative numbers correctly
191
+ return CTZ32_TABLE[(((n & -n) * 0x077cb531) >>> 27) & 31];
192
+ }
193
+ /**
194
+ * Count trailing zero bits in a 64-bit number.
195
+ * Used to determine how many parent nodes to compute after adding a chunk.
196
+ *
197
+ * Note: JavaScript bitwise ops work on 32-bit signed integers,
198
+ * so we need to handle 64-bit numbers carefully.
199
+ */
200
+ function countTrailingZeros(n) {
201
+ if (n === 0)
202
+ return 64;
203
+ // For numbers that fit in 32 bits
204
+ const low = n | 0;
205
+ if (low !== 0) {
206
+ // Use Math.clz32 trick: ctz(x) = 31 - clz32(x & -x) for non-zero x
207
+ return 31 - Math.clz32(low & -low);
208
+ }
209
+ // High 32 bits
210
+ const high = (n / 0x100000000) | 0;
211
+ if (high !== 0) {
212
+ return 32 + (31 - Math.clz32(high & -high));
213
+ }
214
+ return 64;
215
+ }
216
+ /**
217
+ * Create a Uint32Array view of a Uint8Array.
218
+ * Only works correctly on little-endian systems when the offset is 4-byte aligned.
219
+ *
220
+ * @param arr - Source byte array
221
+ * @param byteOffset - Starting byte offset (must be 4-byte aligned)
222
+ * @param wordLength - Number of 32-bit words
223
+ */
224
+ function uint32View(arr, byteOffset, wordLength) {
225
+ return new Uint32Array(arr.buffer, arr.byteOffset + byteOffset, wordLength);
226
+ }
node_modules/@huggingface/blake3-jit/dist/commonjs/wasm-simd.d.ts ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 WASM SIMD - Runtime bytecode generation
3
+ *
4
+ * Generates WebAssembly SIMD bytecode at runtime to process 4 compress
5
+ * operations in parallel using 128-bit SIMD vectors (i32x4).
6
+ *
7
+ * Key insight: One i32x4.add instruction performs 4 parallel additions,
8
+ * giving us 4x throughput for the same number of instructions.
9
+ *
10
+ * Memory layout (all values are transposed for SIMD access):
11
+ * 0-511: 4 x 16 message words (m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...)
12
+ * 512-639: 4 x 8 chaining values
13
+ * 640-767: 4 x 8 output values
14
+ * 768-783: 4 x counter low
15
+ * 784-799: 4 x counter high
16
+ * 800-815: 4 x block length
17
+ * 816-831: 4 x flags
18
+ */
19
+ /**
20
+ * Check if WASM SIMD is supported.
21
+ */
22
+ export declare function isSimdSupported(): boolean;
23
+ export declare function initSimdSync(): boolean;
24
+ /**
25
+ * Memory offsets for SIMD data layout
26
+ *
27
+ * WASM Arena Pattern: All working buffers live in WASM memory (64KB page)
28
+ * This eliminates JS heap allocations during hashing operations.
29
+ */
30
+ export declare const SIMD_MEMORY: {
31
+ readonly BLOCK_WORDS: 0;
32
+ readonly CHAINING_VALUES: 512;
33
+ readonly OUTPUT: 640;
34
+ readonly COUNTER_LOW: 768;
35
+ readonly COUNTER_HIGH: 784;
36
+ readonly BLOCK_LEN: 800;
37
+ readonly FLAGS: 816;
38
+ readonly BATCH_BLOCK_WORDS: 832;
39
+ readonly BATCH_CV: 4928;
40
+ readonly BATCH_COUNTER_LOW: 5056;
41
+ readonly BATCH_FLAGS_BASE: 5072;
42
+ readonly BATCH_OUTPUT: 5088;
43
+ readonly CV_STACK: 5216;
44
+ readonly PARENT_BLOCK: 7264;
45
+ readonly CHUNK_CV: 7328;
46
+ readonly TEMP_CVS: 7360;
47
+ };
48
+ /**
49
+ * Get the WASM memory views for writing input data.
50
+ */
51
+ export declare function getSimdMemory(): {
52
+ view: Uint8Array;
53
+ view32: Uint32Array;
54
+ } | null;
55
+ /**
56
+ * Get the arena buffers for Merkle tree operations.
57
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
58
+ */
59
+ export declare function getArenaBuffers(): {
60
+ cvStack: Uint32Array;
61
+ parentBlock: Uint32Array;
62
+ chunkCv: Uint32Array;
63
+ tempCvs: Uint32Array;
64
+ } | null;
65
+ /**
66
+ * Get the batch arena buffers for chunk-level batched operations.
67
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
68
+ */
69
+ export declare function getBatchArenaBuffers(): {
70
+ blockWords: Uint32Array;
71
+ cv: Uint32Array;
72
+ counterLow: Uint32Array;
73
+ flagsBase: Uint32Array;
74
+ output: Uint32Array;
75
+ } | null;
76
+ /**
77
+ * Run the compress4x function.
78
+ * Data must already be set up in WASM memory.
79
+ */
80
+ export declare function runCompress4x(): void;
81
+ /**
82
+ * Run the compressChunks4x function.
83
+ * Processes 4 full chunks (16 blocks each) in a single WASM call.
84
+ * Data must already be set up in batch arena buffers.
85
+ */
86
+ export declare function runCompressChunks4x(): void;
87
+ /**
88
+ * Run the compressParent function.
89
+ * Compresses a parent node: reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
90
+ * Data must already be set up in arena buffers (PARENT_BLOCK at offset 7264).
91
+ * Output is written to CHUNK_CV at offset 7328.
92
+ */
93
+ export declare function runCompressParent(): void;
94
+ /**
95
+ * Check if SIMD is initialized and ready.
96
+ */
97
+ export declare function isSimdReady(): boolean;
node_modules/@huggingface/blake3-jit/dist/commonjs/wasm-simd.js ADDED
@@ -0,0 +1,948 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ /**
3
+ * BLAKE3 WASM SIMD - Runtime bytecode generation
4
+ *
5
+ * Generates WebAssembly SIMD bytecode at runtime to process 4 compress
6
+ * operations in parallel using 128-bit SIMD vectors (i32x4).
7
+ *
8
+ * Key insight: One i32x4.add instruction performs 4 parallel additions,
9
+ * giving us 4x throughput for the same number of instructions.
10
+ *
11
+ * Memory layout (all values are transposed for SIMD access):
12
+ * 0-511: 4 x 16 message words (m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...)
13
+ * 512-639: 4 x 8 chaining values
14
+ * 640-767: 4 x 8 output values
15
+ * 768-783: 4 x counter low
16
+ * 784-799: 4 x counter high
17
+ * 800-815: 4 x block length
18
+ * 816-831: 4 x flags
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.SIMD_MEMORY = void 0;
22
+ exports.isSimdSupported = isSimdSupported;
23
+ exports.initSimdSync = initSimdSync;
24
+ exports.getSimdMemory = getSimdMemory;
25
+ exports.getArenaBuffers = getArenaBuffers;
26
+ exports.getBatchArenaBuffers = getBatchArenaBuffers;
27
+ exports.runCompress4x = runCompress4x;
28
+ exports.runCompressChunks4x = runCompressChunks4x;
29
+ exports.runCompressParent = runCompressParent;
30
+ exports.isSimdReady = isSimdReady;
31
+ // LEB128 encoding with minimum 2 bytes
32
+ // This fixes a V8 quirk where single-byte values 64-127 cause issues
33
+ // when followed by certain SIMD instructions
34
+ function toLebU32Min2(n) {
35
+ // Always use at least 2 bytes
36
+ return [(n & 0x7f) | 0x80, (n >>> 7) & 0x7f];
37
+ }
38
+ // LEB128 encoding padded to exactly 5 bytes (for backpatching)
39
+ // Uses continuation bits for all but the last byte
40
+ function toLebU32Padded5(n) {
41
+ return [
42
+ (n & 0x7f) | 0x80,
43
+ ((n >>> 7) & 0x7f) | 0x80,
44
+ ((n >>> 14) & 0x7f) | 0x80,
45
+ ((n >>> 21) & 0x7f) | 0x80,
46
+ (n >>> 28) & 0x0f, // Last byte has no continuation bit
47
+ ];
48
+ }
49
+ // Signed LEB128 encoding for i32 constants (handles full 32-bit range)
50
+ // WASM i32.const uses signed LEB128 immediate
51
+ function toSignedLeb128_i32(n) {
52
+ const bytes = [];
53
+ // Treat as signed 32-bit integer
54
+ let value = n | 0;
55
+ let more = true;
56
+ while (more) {
57
+ let byte = value & 0x7f;
58
+ // Arithmetic right shift preserves sign
59
+ value >>= 7;
60
+ // Check if we're done:
61
+ // - If value is 0 and sign bit of byte is clear, we're done
62
+ // - If value is -1 and sign bit of byte is set, we're done
63
+ if ((value === 0 && (byte & 0x40) === 0) || (value === -1 && (byte & 0x40) !== 0)) {
64
+ more = false;
65
+ }
66
+ else {
67
+ byte |= 0x80;
68
+ }
69
+ bytes.push(byte);
70
+ }
71
+ return bytes;
72
+ }
73
+ // Precomputed message access order for all 7 rounds
74
+ const MSG_ACCESS_ORDER = [
75
+ // Round 1: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
76
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
77
+ // Round 2: 2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8
78
+ 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8,
79
+ // Round 3: 3,4,10,12,13,2,7,14,6,5,9,0,11,15,8,1
80
+ 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1,
81
+ // Round 4: 10,7,12,9,14,3,13,15,4,0,11,2,5,8,1,6
82
+ 10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6,
83
+ // Round 5: 12,13,9,11,15,10,14,8,7,2,5,3,0,1,6,4
84
+ 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4,
85
+ // Round 6: 9,14,11,5,8,12,15,1,13,3,0,10,2,6,4,7
86
+ 9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7,
87
+ // Round 7: 11,15,5,0,1,9,8,6,14,10,2,12,3,4,7,13
88
+ 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13,
89
+ ];
90
+ // BLAKE3 Constants (used in generated WASM code)
91
+ // CHUNK_START = 1, CHUNK_END = 2 are embedded directly in WASM bytecode
92
+ /**
93
+ * Generate the WASM module bytecode with compress4x, compressChunks4x, and compressParent functions.
94
+ */
95
+ function generateWasmBytes() {
96
+ const code = [];
97
+ // Helper to append bytes
98
+ function put(bytes) {
99
+ code.push(...bytes);
100
+ }
101
+ // WASM module header
102
+ put([0x00, 0x61, 0x73, 0x6d]); // Magic
103
+ put([0x01, 0x00, 0x00, 0x00]); // Version
104
+ // Section 1: Types
105
+ put([0x01]); // Section ID
106
+ put([0x04]); // Section size
107
+ put([0x01]); // 1 type
108
+ put([0x60, 0x00, 0x00]); // func () -> ()
109
+ // Section 2: Imports (memory from JS)
110
+ put([0x02]); // Section ID
111
+ put([0x0b]); // Section size
112
+ put([0x01]); // 1 import
113
+ put([0x02, 0x6a, 0x73]); // "js"
114
+ put([0x03, 0x6d, 0x65, 0x6d]); // "mem"
115
+ put([0x02, 0x00, 0x01]); // memory min=1, no max
116
+ // Section 3: Functions
117
+ put([0x03]); // Section ID
118
+ put([0x04]); // Section size (3 functions = 4 bytes)
119
+ put([0x03]); // 3 functions
120
+ put([0x00]); // Function 0: type index 0
121
+ put([0x00]); // Function 1: type index 0
122
+ put([0x00]); // Function 2: type index 0
123
+ // Section 7: Exports
124
+ // Size calculation: 1 (count) + (1+10+1+1) + (1+16+1+1) + (1+14+1+1) = 1 + 13 + 19 + 17 = 50 bytes
125
+ put([0x07]); // Section ID
126
+ put([0x32]); // Section size (50 bytes)
127
+ put([0x03]); // 3 exports
128
+ // "compress4x" -> func 0
129
+ put([0x0a]); // name length
130
+ put([0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x34, 0x78]); // "compress4x"
131
+ put([0x00, 0x00]); // func index 0
132
+ // "compressChunks4x" -> func 1
133
+ put([0x10]); // name length (16)
134
+ put([
135
+ 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x34, 0x78,
136
+ ]); // "compressChunks4x"
137
+ put([0x00, 0x01]); // func index 1
138
+ // "compressParent" -> func 2
139
+ put([0x0e]); // name length (14)
140
+ put([0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74]); // "compressParent"
141
+ put([0x00, 0x02]); // func index 2
142
+ // Section 10: Code
143
+ put([0x0a]); // Section ID
144
+ // Reserve 5 bytes for section size (LEB128 u32)
145
+ const sectionSizeOffset = code.length;
146
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
147
+ put([0x03]); // 3 functions
148
+ // === Function 0: compress4x ===
149
+ // Reserve 5 bytes for function size
150
+ const funcSizeOffset = code.length;
151
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
152
+ const funcBodyStart = code.length;
153
+ // Local declarations: 32 v128 locals
154
+ // Variables $0-$15: message words (m0-m15)
155
+ // Variables $16-$31: state words (s0-s15)
156
+ put([0x01]); // 1 local declaration
157
+ put([0x20, 0x7b]); // 32 x v128
158
+ // ===== Function body =====
159
+ // Load message words from memory (offset 0-255)
160
+ // Each v128 is 16 bytes, so m[i] is at offset i*16
161
+ // Note: we use toLebU32Min2 to avoid V8 quirk with single-byte values 64-127
162
+ for (let i = 0; i < 16; i++) {
163
+ put([0x41, ...toLebU32Min2(i * 16)]); // i32.const offset (2+ byte LEB128)
164
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
165
+ put([0x21, i]); // local.set $i
166
+ }
167
+ // Load chaining values (offset 512-639)
168
+ // cv[i] at offset 512 + i*16
169
+ for (let i = 0; i < 8; i++) {
170
+ put([0x41, ...toLebU32Min2(512 + i * 16)]); // i32.const offset
171
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
172
+ put([0x21, 16 + i]); // local.set $(16+i)
173
+ }
174
+ // Initialize state[8-15] from IV and parameters
175
+ // s8-s11 = IV[0-3]
176
+ const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a];
177
+ for (let i = 0; i < 4; i++) {
178
+ // Create v128 constant with all lanes set to IV[i]
179
+ const ivBytes = [];
180
+ for (let j = 0; j < 4; j++) {
181
+ ivBytes.push(IV[i] & 0xff);
182
+ ivBytes.push((IV[i] >>> 8) & 0xff);
183
+ ivBytes.push((IV[i] >>> 16) & 0xff);
184
+ ivBytes.push((IV[i] >>> 24) & 0xff);
185
+ }
186
+ put([0xfd, 0x0c, ...ivBytes]); // v128.const
187
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
188
+ }
189
+ // s12 = counter_low (offset 768)
190
+ put([0x41, ...toLebU32Min2(768)]); // i32.const
191
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
192
+ put([0x21, 28]); // local.set $28 -> s12
193
+ // s13 = counter_high (offset 784)
194
+ put([0x41, ...toLebU32Min2(784)]); // i32.const
195
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
196
+ put([0x21, 29]); // local.set $29 -> s13
197
+ // s14 = block_len (offset 800)
198
+ put([0x41, ...toLebU32Min2(800)]); // i32.const
199
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
200
+ put([0x21, 30]); // local.set $30 -> s14
201
+ // s15 = flags (offset 816)
202
+ put([0x41, ...toLebU32Min2(816)]); // i32.const
203
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
204
+ put([0x21, 31]); // local.set $31 -> s15
205
+ // ===== 7 rounds of mixing =====
206
+ let msgIdx = 0; // Index into MSG_ACCESS_ORDER
207
+ // Helper to generate G function (inlined)
208
+ // G(a, b, c, d) with two message words
209
+ function g(a, b, c, d) {
210
+ const mx = MSG_ACCESS_ORDER[msgIdx++];
211
+ const my = MSG_ACCESS_ORDER[msgIdx++];
212
+ // Variables: a,b,c,d are state indices (16-31), mx,my are message indices (0-15)
213
+ // First half of G
214
+ // s[a] = s[a] + s[b] + m[mx]
215
+ put([0x20, 16 + a]); // local.get s[a]
216
+ put([0x20, 16 + b]); // local.get s[b]
217
+ put([0xfd, 0xae, 0x01]); // i32x4.add
218
+ put([0x20, mx]); // local.get m[mx]
219
+ put([0xfd, 0xae, 0x01]); // i32x4.add
220
+ put([0x21, 16 + a]); // local.set s[a]
221
+ // s[d] = rotr(s[d] ^ s[a], 16) - using i8x16.shuffle (single instruction vs shift+or)
222
+ // ROTR16 pattern: [2,3,0,1, 6,7,4,5, 10,11,8,9, 14,15,12,13]
223
+ put([0x20, 16 + d]); // local.get s[d]
224
+ put([0x20, 16 + a]); // local.get s[a]
225
+ put([0xfd, 0x51]); // v128.xor
226
+ put([0x22, 16 + d]); // local.tee s[d]
227
+ put([0x20, 16 + d]); // local.get s[d] (second operand for shuffle)
228
+ put([0xfd, 0x0d, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13]); // i8x16.shuffle ROTR16
229
+ put([0x21, 16 + d]); // local.set s[d]
230
+ // s[c] = s[c] + s[d]
231
+ put([0x20, 16 + c]); // local.get s[c]
232
+ put([0x20, 16 + d]); // local.get s[d]
233
+ put([0xfd, 0xae, 0x01]); // i32x4.add
234
+ put([0x21, 16 + c]); // local.set s[c]
235
+ // s[b] = (s[b] ^ s[c]) >>> 12
236
+ put([0x20, 16 + b]); // local.get s[b]
237
+ put([0x20, 16 + c]); // local.get s[c]
238
+ put([0xfd, 0x51]); // v128.xor
239
+ put([0x22, 16 + b]); // local.tee s[b]
240
+ put([0x41, 0x0c]); // i32.const 12
241
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
242
+ put([0x20, 16 + b]); // local.get s[b]
243
+ put([0x41, 0x14]); // i32.const 20
244
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
245
+ put([0xfd, 0x50]); // v128.or
246
+ put([0x21, 16 + b]); // local.set s[b]
247
+ // Second half of G
248
+ // s[a] = s[a] + s[b] + m[my]
249
+ put([0x20, 16 + a]); // local.get s[a]
250
+ put([0x20, 16 + b]); // local.get s[b]
251
+ put([0xfd, 0xae, 0x01]); // i32x4.add
252
+ put([0x20, my]); // local.get m[my]
253
+ put([0xfd, 0xae, 0x01]); // i32x4.add
254
+ put([0x21, 16 + a]); // local.set s[a]
255
+ // s[d] = rotr(s[d] ^ s[a], 8) - using i8x16.shuffle (single instruction vs shift+or)
256
+ // ROTR8 pattern: [1,2,3,0, 5,6,7,4, 9,10,11,8, 13,14,15,12]
257
+ put([0x20, 16 + d]); // local.get s[d]
258
+ put([0x20, 16 + a]); // local.get s[a]
259
+ put([0xfd, 0x51]); // v128.xor
260
+ put([0x22, 16 + d]); // local.tee s[d]
261
+ put([0x20, 16 + d]); // local.get s[d] (second operand for shuffle)
262
+ put([0xfd, 0x0d, 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12]); // i8x16.shuffle ROTR8
263
+ put([0x21, 16 + d]); // local.set s[d]
264
+ // s[c] = s[c] + s[d]
265
+ put([0x20, 16 + c]); // local.get s[c]
266
+ put([0x20, 16 + d]); // local.get s[d]
267
+ put([0xfd, 0xae, 0x01]); // i32x4.add
268
+ put([0x21, 16 + c]); // local.set s[c]
269
+ // s[b] = (s[b] ^ s[c]) >>> 7
270
+ put([0x20, 16 + b]); // local.get s[b]
271
+ put([0x20, 16 + c]); // local.get s[c]
272
+ put([0xfd, 0x51]); // v128.xor
273
+ put([0x22, 16 + b]); // local.tee s[b]
274
+ put([0x41, 0x07]); // i32.const 7
275
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
276
+ put([0x20, 16 + b]); // local.get s[b]
277
+ put([0x41, 0x19]); // i32.const 25
278
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
279
+ put([0xfd, 0x50]); // v128.or
280
+ put([0x21, 16 + b]); // local.set s[b]
281
+ }
282
+ // Generate all 7 rounds
283
+ for (let round = 0; round < 7; round++) {
284
+ // Column mixing
285
+ g(0, 4, 8, 12);
286
+ g(1, 5, 9, 13);
287
+ g(2, 6, 10, 14);
288
+ g(3, 7, 11, 15);
289
+ // Diagonal mixing
290
+ g(0, 5, 10, 15);
291
+ g(1, 6, 11, 12);
292
+ g(2, 7, 8, 13);
293
+ g(3, 4, 9, 14);
294
+ }
295
+ // ===== Final XOR and store output =====
296
+ // out[i] = s[i] ^ s[i+8] for i in 0..7
297
+ // Store at offset 640-767
298
+ for (let i = 0; i < 8; i++) {
299
+ put([0x41, ...toLebU32Min2(640 + i * 16)]); // i32.const offset
300
+ put([0x20, 16 + i]); // local.get s[i]
301
+ put([0x20, 24 + i]); // local.get s[i+8]
302
+ put([0xfd, 0x51]); // v128.xor
303
+ put([0xfd, 0x0b, 0x02, 0x00]); // v128.store align=4
304
+ }
305
+ // End of function
306
+ put([0x0b]); // end
307
+ // Fill in function 0 size using padded LEB128
308
+ const funcBodySize = code.length - funcBodyStart;
309
+ const funcSizeBytes = toLebU32Padded5(funcBodySize);
310
+ for (let i = 0; i < 5; i++) {
311
+ code[funcSizeOffset + i] = funcSizeBytes[i];
312
+ }
313
+ // === Function 1: compressChunks4x ===
314
+ // Reserve 5 bytes for function size
315
+ const func1SizeOffset = code.length;
316
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
317
+ const func1BodyStart = code.length;
318
+ // Generate the compressChunks4x function body
319
+ const compressChunksBody = generateCompressChunks4xBody();
320
+ put(compressChunksBody);
321
+ // Fill in function 1 size using padded LEB128
322
+ const func1BodySize = code.length - func1BodyStart;
323
+ const func1SizeBytes = toLebU32Padded5(func1BodySize);
324
+ for (let i = 0; i < 5; i++) {
325
+ code[func1SizeOffset + i] = func1SizeBytes[i];
326
+ }
327
+ // === Function 2: compressParent ===
328
+ // Reserve 5 bytes for function size
329
+ const func2SizeOffset = code.length;
330
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
331
+ const func2BodyStart = code.length;
332
+ // Generate the compressParent function body
333
+ const compressParentBody = generateCompressParentBody();
334
+ put(compressParentBody);
335
+ // Fill in function 2 size using padded LEB128
336
+ const func2BodySize = code.length - func2BodyStart;
337
+ const func2SizeBytes = toLebU32Padded5(func2BodySize);
338
+ for (let i = 0; i < 5; i++) {
339
+ code[func2SizeOffset + i] = func2SizeBytes[i];
340
+ }
341
+ // Fill in section size using padded LEB128
342
+ const sectionSize = code.length - sectionSizeOffset - 5;
343
+ const sectionSizeBytes = toLebU32Padded5(sectionSize);
344
+ for (let i = 0; i < 5; i++) {
345
+ code[sectionSizeOffset + i] = sectionSizeBytes[i];
346
+ }
347
+ return new Uint8Array(code);
348
+ }
349
+ /**
350
+ * Generate compressChunks4x WASM function body.
351
+ * Processes all 16 blocks of 4 chunks in a single call.
352
+ */
353
+ function generateCompressChunks4xBody() {
354
+ const code = [];
355
+ function put(bytes) {
356
+ code.push(...bytes);
357
+ }
358
+ // Local declarations: 32 v128 locals + 1 i32 for position
359
+ // Locals $0-$15: message words (reloaded each iteration)
360
+ // Locals $16-$31: state words (s0-s15)
361
+ // Local $32: position counter (i32)
362
+ put([0x02]); // 2 local declarations
363
+ put([0x20, 0x7b]); // 32 x v128
364
+ put([0x01, 0x7f]); // 1 x i32
365
+ const BATCH_BLOCK_WORDS = exports.SIMD_MEMORY.BATCH_BLOCK_WORDS;
366
+ const BATCH_CV = exports.SIMD_MEMORY.BATCH_CV;
367
+ const BATCH_COUNTER_LOW = exports.SIMD_MEMORY.BATCH_COUNTER_LOW;
368
+ const BATCH_FLAGS_BASE = exports.SIMD_MEMORY.BATCH_FLAGS_BASE;
369
+ const BATCH_OUTPUT = exports.SIMD_MEMORY.BATCH_OUTPUT;
370
+ // IV constants (same as compress4x)
371
+ const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a];
372
+ // Load initial CVs from BATCH_CV into locals $16-$23
373
+ for (let i = 0; i < 8; i++) {
374
+ put([0x41, ...toLebU32Min2(BATCH_CV + i * 16)]); // i32.const offset
375
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
376
+ put([0x21, 16 + i]); // local.set $(16+i) -> s0-s7
377
+ }
378
+ // Initialize $32 (pos) = 0
379
+ put([0x41, 0x00]); // i32.const 0
380
+ put([0x21, 0x20]); // local.set $32
381
+ // block $done
382
+ put([0x02, 0x40]); // block void
383
+ // loop $continue
384
+ put([0x03, 0x40]); // loop void
385
+ // === Load message words for position $pos ===
386
+ // offset = BATCH_BLOCK_WORDS + pos * 256 + word * 16
387
+ for (let w = 0; w < 16; w++) {
388
+ put([0x20, 0x20]); // local.get $32 (pos)
389
+ put([0x41, ...toLebU32Min2(256)]); // i32.const 256
390
+ put([0x6c]); // i32.mul
391
+ put([0x41, ...toLebU32Min2(BATCH_BLOCK_WORDS + w * 16)]); // i32.const base + word*16
392
+ put([0x6a]); // i32.add
393
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
394
+ put([0x21, w]); // local.set $w
395
+ }
396
+ // === Initialize state[8-15] ===
397
+ // s8-s11 = IV[0-3]
398
+ for (let i = 0; i < 4; i++) {
399
+ const ivBytes = [];
400
+ for (let j = 0; j < 4; j++) {
401
+ ivBytes.push(IV[i] & 0xff);
402
+ ivBytes.push((IV[i] >>> 8) & 0xff);
403
+ ivBytes.push((IV[i] >>> 16) & 0xff);
404
+ ivBytes.push((IV[i] >>> 24) & 0xff);
405
+ }
406
+ put([0xfd, 0x0c, ...ivBytes]); // v128.const
407
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
408
+ }
409
+ // s12 = counter_low (from BATCH_COUNTER_LOW)
410
+ put([0x41, ...toLebU32Min2(BATCH_COUNTER_LOW)]); // i32.const
411
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
412
+ put([0x21, 28]); // local.set $28 -> s12
413
+ // s13 = 0 (counter high - assume fits in 32 bits)
414
+ put([0xfd, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // v128.const 0
415
+ put([0x21, 29]); // local.set $29 -> s13
416
+ // s14 = 64 (block_len = 64 for full blocks)
417
+ const blockLen64 = [];
418
+ for (let j = 0; j < 4; j++) {
419
+ blockLen64.push(64, 0, 0, 0); // 64 in little-endian
420
+ }
421
+ put([0xfd, 0x0c, ...blockLen64]); // v128.const [64,64,64,64]
422
+ put([0x21, 30]); // local.set $30 -> s14
423
+ // s15 = flags = base_flags | (pos == 0 ? 1 : 0) | (pos == 15 ? 2 : 0)
424
+ // First load base flags
425
+ put([0x41, ...toLebU32Min2(BATCH_FLAGS_BASE)]); // i32.const
426
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load base flags
427
+ // Compute position-dependent bits
428
+ // CHUNK_START (1) if pos == 0
429
+ put([0x20, 0x20]); // local.get $32 (pos)
430
+ put([0x45]); // i32.eqz -> 1 if pos==0, 0 otherwise
431
+ // CHUNK_END (2) if pos == 15
432
+ put([0x20, 0x20]); // local.get $32 (pos)
433
+ put([0x41, 0x0f]); // i32.const 15
434
+ put([0x46]); // i32.eq -> 1 if pos==15, 0 otherwise
435
+ put([0x41, 0x01]); // i32.const 1 (shift amount)
436
+ put([0x74]); // i32.shl -> 2 if pos==15, 0 otherwise
437
+ // OR the two bits together
438
+ put([0x72]); // i32.or -> combined position bits
439
+ // Splat to v128 and OR with base flags (stack: base_flags, bits)
440
+ put([0xfd, 0x11]); // i32x4.splat
441
+ put([0xfd, 0x50]); // v128.or
442
+ put([0x21, 31]); // local.set $31 -> s15
443
+ // === 7 rounds of mixing ===
444
+ let msgIdx = 0;
445
+ function g(a, b, c, d) {
446
+ const mx = MSG_ACCESS_ORDER[msgIdx++];
447
+ const my = MSG_ACCESS_ORDER[msgIdx++];
448
+ // First half of G: s[a] = s[a] + s[b] + m[mx]
449
+ put([0x20, 16 + a]); // local.get s[a]
450
+ put([0x20, 16 + b]); // local.get s[b]
451
+ put([0xfd, 0xae, 0x01]); // i32x4.add
452
+ put([0x20, mx]); // local.get m[mx]
453
+ put([0xfd, 0xae, 0x01]); // i32x4.add
454
+ put([0x21, 16 + a]); // local.set s[a]
455
+ // s[d] = rotr(s[d] ^ s[a], 16) - byte shuffle
456
+ put([0x20, 16 + d]); // local.get s[d]
457
+ put([0x20, 16 + a]); // local.get s[a]
458
+ put([0xfd, 0x51]); // v128.xor
459
+ put([0x22, 16 + d]); // local.tee s[d]
460
+ put([0x20, 16 + d]); // local.get s[d]
461
+ put([0xfd, 0x0d, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13]); // i8x16.shuffle ROTR16
462
+ put([0x21, 16 + d]); // local.set s[d]
463
+ // s[c] = s[c] + s[d]
464
+ put([0x20, 16 + c]); // local.get s[c]
465
+ put([0x20, 16 + d]); // local.get s[d]
466
+ put([0xfd, 0xae, 0x01]); // i32x4.add
467
+ put([0x21, 16 + c]); // local.set s[c]
468
+ // s[b] = rotr(s[b] ^ s[c], 12)
469
+ put([0x20, 16 + b]); // local.get s[b]
470
+ put([0x20, 16 + c]); // local.get s[c]
471
+ put([0xfd, 0x51]); // v128.xor
472
+ put([0x22, 16 + b]); // local.tee s[b]
473
+ put([0x41, 0x0c]); // i32.const 12
474
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
475
+ put([0x20, 16 + b]); // local.get s[b]
476
+ put([0x41, 0x14]); // i32.const 20
477
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
478
+ put([0xfd, 0x50]); // v128.or
479
+ put([0x21, 16 + b]); // local.set s[b]
480
+ // Second half: s[a] = s[a] + s[b] + m[my]
481
+ put([0x20, 16 + a]); // local.get s[a]
482
+ put([0x20, 16 + b]); // local.get s[b]
483
+ put([0xfd, 0xae, 0x01]); // i32x4.add
484
+ put([0x20, my]); // local.get m[my]
485
+ put([0xfd, 0xae, 0x01]); // i32x4.add
486
+ put([0x21, 16 + a]); // local.set s[a]
487
+ // s[d] = rotr(s[d] ^ s[a], 8) - byte shuffle
488
+ put([0x20, 16 + d]); // local.get s[d]
489
+ put([0x20, 16 + a]); // local.get s[a]
490
+ put([0xfd, 0x51]); // v128.xor
491
+ put([0x22, 16 + d]); // local.tee s[d]
492
+ put([0x20, 16 + d]); // local.get s[d]
493
+ put([0xfd, 0x0d, 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12]); // i8x16.shuffle ROTR8
494
+ put([0x21, 16 + d]); // local.set s[d]
495
+ // s[c] = s[c] + s[d]
496
+ put([0x20, 16 + c]); // local.get s[c]
497
+ put([0x20, 16 + d]); // local.get s[d]
498
+ put([0xfd, 0xae, 0x01]); // i32x4.add
499
+ put([0x21, 16 + c]); // local.set s[c]
500
+ // s[b] = rotr(s[b] ^ s[c], 7)
501
+ put([0x20, 16 + b]); // local.get s[b]
502
+ put([0x20, 16 + c]); // local.get s[c]
503
+ put([0xfd, 0x51]); // v128.xor
504
+ put([0x22, 16 + b]); // local.tee s[b]
505
+ put([0x41, 0x07]); // i32.const 7
506
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
507
+ put([0x20, 16 + b]); // local.get s[b]
508
+ put([0x41, 0x19]); // i32.const 25
509
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
510
+ put([0xfd, 0x50]); // v128.or
511
+ put([0x21, 16 + b]); // local.set s[b]
512
+ }
513
+ // Generate all 7 rounds
514
+ for (let round = 0; round < 7; round++) {
515
+ // Column mixing
516
+ g(0, 4, 8, 12);
517
+ g(1, 5, 9, 13);
518
+ g(2, 6, 10, 14);
519
+ g(3, 7, 11, 15);
520
+ // Diagonal mixing
521
+ g(0, 5, 10, 15);
522
+ g(1, 6, 11, 12);
523
+ g(2, 7, 8, 13);
524
+ g(3, 4, 9, 14);
525
+ }
526
+ // === Update CVs: cv[i] = s[i] ^ s[i+8] ===
527
+ // Store back to state locals $16-$23 (the CV positions)
528
+ for (let i = 0; i < 8; i++) {
529
+ put([0x20, 16 + i]); // local.get s[i]
530
+ put([0x20, 24 + i]); // local.get s[i+8]
531
+ put([0xfd, 0x51]); // v128.xor
532
+ put([0x21, 16 + i]); // local.set $(16+i) - update CV
533
+ }
534
+ // === Loop control: pos++, continue if pos < 16 ===
535
+ put([0x20, 0x20]); // local.get $32 (pos)
536
+ put([0x41, 0x01]); // i32.const 1
537
+ put([0x6a]); // i32.add
538
+ put([0x22, 0x20]); // local.tee $32 (pos)
539
+ put([0x41, 0x10]); // i32.const 16
540
+ put([0x49]); // i32.lt_u
541
+ put([0x0d, 0x00]); // br_if 0 (continue loop)
542
+ // end loop
543
+ put([0x0b]); // end
544
+ // end block
545
+ put([0x0b]); // end
546
+ // === Store final CVs to BATCH_OUTPUT ===
547
+ for (let i = 0; i < 8; i++) {
548
+ put([0x41, ...toLebU32Min2(BATCH_OUTPUT + i * 16)]); // i32.const offset
549
+ put([0x20, 16 + i]); // local.get $(16+i) - CV word
550
+ put([0xfd, 0x0b, 0x02, 0x00]); // v128.store align=4
551
+ }
552
+ // end function
553
+ put([0x0b]); // end
554
+ return code;
555
+ }
556
+ /**
557
+ * Generate compressParent WASM function body.
558
+ * Performs a single parent node compression using scalar i32 operations.
559
+ * Reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
560
+ * Uses IV, counter=0, blockLen=64, flags=PARENT(4).
561
+ */
562
+ function generateCompressParentBody() {
563
+ const code = [];
564
+ function put(bytes) {
565
+ code.push(...bytes);
566
+ }
567
+ // Local declarations: 32 i32 locals for state (s0-s15) and message (m0-m15)
568
+ put([0x01]); // 1 local declaration
569
+ put([0x20, 0x7f]); // 32 x i32
570
+ // Message word indices: 0-15, State indices: 16-31
571
+ // Locals $0-$15: message words (m0-m15)
572
+ // Locals $16-$31: state words (s0-s15)
573
+ const PARENT_BLOCK_OFFSET = exports.SIMD_MEMORY.PARENT_BLOCK;
574
+ const CHUNK_CV_OFFSET = exports.SIMD_MEMORY.CHUNK_CV;
575
+ // BLAKE3 IV
576
+ const IV = [
577
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
578
+ ];
579
+ // Load message words from PARENT_BLOCK (16 words at offset 7264)
580
+ for (let i = 0; i < 16; i++) {
581
+ put([0x41, ...toLebU32Min2(PARENT_BLOCK_OFFSET + i * 4)]); // i32.const offset
582
+ put([0x28, 0x02, 0x00]); // i32.load align=4 offset=0
583
+ put([0x21, i]); // local.set $i (m0-m15)
584
+ }
585
+ // Initialize state s0-s7 = IV[0-7]
586
+ for (let i = 0; i < 8; i++) {
587
+ put([0x41, ...toSignedLeb128_i32(IV[i])]); // i32.const IV[i]
588
+ put([0x21, 16 + i]); // local.set $(16+i) -> s0-s7
589
+ }
590
+ // Initialize state s8-s11 = IV[0-3]
591
+ for (let i = 0; i < 4; i++) {
592
+ put([0x41, ...toSignedLeb128_i32(IV[i])]); // i32.const IV[i]
593
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
594
+ }
595
+ // s12 = counter_low = 0
596
+ put([0x41, 0x00]); // i32.const 0
597
+ put([0x21, 28]); // local.set $28 -> s12
598
+ // s13 = counter_high = 0
599
+ put([0x41, 0x00]); // i32.const 0
600
+ put([0x21, 29]); // local.set $29 -> s13
601
+ // s14 = block_len = 64
602
+ // Note: 0x40 alone is -64 in signed LEB128 (bit 6 is sign bit)
603
+ // For 64, we need [0xC0, 0x00] to avoid sign extension
604
+ put([0x41, 0xc0, 0x00]); // i32.const 64
605
+ put([0x21, 30]); // local.set $30 -> s14
606
+ // s15 = flags = PARENT = 4
607
+ put([0x41, 0x04]); // i32.const 4
608
+ put([0x21, 31]); // local.set $31 -> s15
609
+ // Helper to generate scalar G function (inlined)
610
+ // G(a, b, c, d, mx, my) where a,b,c,d are state indices 0-15, mx,my are message indices 0-15
611
+ function g(a, b, c, d, mx, my) {
612
+ const sa = 16 + a, sb = 16 + b, sc = 16 + c, sd = 16 + d;
613
+ // s[a] = (s[a] + s[b] + m[mx]) >>> 0
614
+ put([0x20, sa]); // local.get s[a]
615
+ put([0x20, sb]); // local.get s[b]
616
+ put([0x6a]); // i32.add
617
+ put([0x20, mx]); // local.get m[mx]
618
+ put([0x6a]); // i32.add
619
+ put([0x21, sa]); // local.set s[a]
620
+ // s[d] = rotr(s[d] ^ s[a], 16)
621
+ put([0x20, sd]); // local.get s[d]
622
+ put([0x20, sa]); // local.get s[a]
623
+ put([0x73]); // i32.xor
624
+ put([0x41, 0x10]); // i32.const 16
625
+ put([0x78]); // i32.rotr
626
+ put([0x21, sd]); // local.set s[d]
627
+ // s[c] = (s[c] + s[d]) >>> 0
628
+ put([0x20, sc]); // local.get s[c]
629
+ put([0x20, sd]); // local.get s[d]
630
+ put([0x6a]); // i32.add
631
+ put([0x21, sc]); // local.set s[c]
632
+ // s[b] = rotr(s[b] ^ s[c], 12)
633
+ put([0x20, sb]); // local.get s[b]
634
+ put([0x20, sc]); // local.get s[c]
635
+ put([0x73]); // i32.xor
636
+ put([0x41, 0x0c]); // i32.const 12
637
+ put([0x78]); // i32.rotr
638
+ put([0x21, sb]); // local.set s[b]
639
+ // s[a] = (s[a] + s[b] + m[my]) >>> 0
640
+ put([0x20, sa]); // local.get s[a]
641
+ put([0x20, sb]); // local.get s[b]
642
+ put([0x6a]); // i32.add
643
+ put([0x20, my]); // local.get m[my]
644
+ put([0x6a]); // i32.add
645
+ put([0x21, sa]); // local.set s[a]
646
+ // s[d] = rotr(s[d] ^ s[a], 8)
647
+ put([0x20, sd]); // local.get s[d]
648
+ put([0x20, sa]); // local.get s[a]
649
+ put([0x73]); // i32.xor
650
+ put([0x41, 0x08]); // i32.const 8
651
+ put([0x78]); // i32.rotr
652
+ put([0x21, sd]); // local.set s[d]
653
+ // s[c] = (s[c] + s[d]) >>> 0
654
+ put([0x20, sc]); // local.get s[c]
655
+ put([0x20, sd]); // local.get s[d]
656
+ put([0x6a]); // i32.add
657
+ put([0x21, sc]); // local.set s[c]
658
+ // s[b] = rotr(s[b] ^ s[c], 7)
659
+ put([0x20, sb]); // local.get s[b]
660
+ put([0x20, sc]); // local.get s[c]
661
+ put([0x73]); // i32.xor
662
+ put([0x41, 0x07]); // i32.const 7
663
+ put([0x78]); // i32.rotr
664
+ put([0x21, sb]); // local.set s[b]
665
+ }
666
+ // 7 rounds of mixing with permuted message schedule
667
+ let msgIdx = 0;
668
+ for (let round = 0; round < 7; round++) {
669
+ // Column mixing
670
+ g(0, 4, 8, 12, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
671
+ msgIdx += 2;
672
+ g(1, 5, 9, 13, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
673
+ msgIdx += 2;
674
+ g(2, 6, 10, 14, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
675
+ msgIdx += 2;
676
+ g(3, 7, 11, 15, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
677
+ msgIdx += 2;
678
+ // Diagonal mixing
679
+ g(0, 5, 10, 15, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
680
+ msgIdx += 2;
681
+ g(1, 6, 11, 12, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
682
+ msgIdx += 2;
683
+ g(2, 7, 8, 13, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
684
+ msgIdx += 2;
685
+ g(3, 4, 9, 14, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
686
+ msgIdx += 2;
687
+ }
688
+ // Store output: out[i] = s[i] ^ s[i+8] for i in 0..7
689
+ for (let i = 0; i < 8; i++) {
690
+ put([0x41, ...toLebU32Min2(CHUNK_CV_OFFSET + i * 4)]); // i32.const offset
691
+ put([0x20, 16 + i]); // local.get s[i]
692
+ put([0x20, 24 + i]); // local.get s[i+8]
693
+ put([0x73]); // i32.xor
694
+ put([0x36, 0x02, 0x00]); // i32.store align=4 offset=0
695
+ }
696
+ // end function
697
+ put([0x0b]); // end
698
+ return code;
699
+ }
700
+ // Cached WASM instance
701
+ let wasmInstance = null;
702
+ let wasmMemory = null;
703
+ let wasmCompress4x = null;
704
+ let wasmCompressChunks4x = null;
705
+ let wasmCompressParent = null;
706
+ let wasmMemoryView = null;
707
+ let wasmMemoryView32 = null;
708
+ /**
709
+ * Check if WASM SIMD is supported.
710
+ */
711
+ function isSimdSupported() {
712
+ try {
713
+ // Minimal WASM module with v128.const instruction to test SIMD support
714
+ const simdTest = new Uint8Array([
715
+ 0x00,
716
+ 0x61,
717
+ 0x73,
718
+ 0x6d, // magic: \0asm
719
+ 0x01,
720
+ 0x00,
721
+ 0x00,
722
+ 0x00, // version: 1
723
+ // Type section (id=1): () -> v128
724
+ 0x01, // section id = 1 (type)
725
+ 0x05, // section length = 5
726
+ 0x01, // 1 type
727
+ 0x60,
728
+ 0x00,
729
+ 0x01,
730
+ 0x7b, // func () -> v128
731
+ // Function section (id=3)
732
+ 0x03, // section id = 3 (function)
733
+ 0x02, // section length = 2
734
+ 0x01, // 1 function
735
+ 0x00, // type index 0
736
+ // Code section (id=10) with v128.const
737
+ 0x0a, // section id = 10 (code)
738
+ 0x16, // section length = 22
739
+ 0x01, // 1 function body
740
+ 0x14, // body length = 20
741
+ 0x00, // 0 locals
742
+ 0xfd,
743
+ 0x0c, // v128.const opcode
744
+ 0x00,
745
+ 0x00,
746
+ 0x00,
747
+ 0x00,
748
+ 0x00,
749
+ 0x00,
750
+ 0x00,
751
+ 0x00,
752
+ 0x00,
753
+ 0x00,
754
+ 0x00,
755
+ 0x00,
756
+ 0x00,
757
+ 0x00,
758
+ 0x00,
759
+ 0x00,
760
+ 0x0b, // end
761
+ ]);
762
+ return WebAssembly.validate(simdTest);
763
+ }
764
+ catch {
765
+ return false;
766
+ }
767
+ }
768
+ /**
769
+ * Set up arena views over WASM memory.
770
+ * Called after WASM memory is allocated.
771
+ */
772
+ function setupArenaViews() {
773
+ if (!wasmMemory)
774
+ return;
775
+ const buffer = wasmMemory.buffer;
776
+ // Create TypedArray views over WASM memory for arena buffers
777
+ // These views are backed by WASM memory, eliminating JS heap allocation
778
+ arenaCvStack = new Uint32Array(buffer, exports.SIMD_MEMORY.CV_STACK, 64 * 8); // 64 levels × 8 words
779
+ arenaParentBlock = new Uint32Array(buffer, exports.SIMD_MEMORY.PARENT_BLOCK, 16); // 16 words
780
+ arenaChunkCv = new Uint32Array(buffer, exports.SIMD_MEMORY.CHUNK_CV, 8); // 8 words
781
+ arenaTempCvs = new Uint32Array(buffer, exports.SIMD_MEMORY.TEMP_CVS, 32); // 4 × 8 words
782
+ // Batch mode views
783
+ // 16 positions × 16 v128 words = 16 × 64 u32 words = 1024 words per position? No...
784
+ // In u32 terms: 16 positions × 16 words × 4 lanes = 1024 u32 values total
785
+ arenaBatchBlockWords = new Uint32Array(buffer, exports.SIMD_MEMORY.BATCH_BLOCK_WORDS, 16 * 16 * 4); // 16 pos × 16 words × 4 lanes
786
+ arenaBatchCv = new Uint32Array(buffer, exports.SIMD_MEMORY.BATCH_CV, 32); // 4 × 8 words
787
+ arenaBatchCounterLow = new Uint32Array(buffer, exports.SIMD_MEMORY.BATCH_COUNTER_LOW, 4); // 4 words
788
+ arenaBatchFlagsBase = new Uint32Array(buffer, exports.SIMD_MEMORY.BATCH_FLAGS_BASE, 4); // 4 words
789
+ arenaBatchOutput = new Uint32Array(buffer, exports.SIMD_MEMORY.BATCH_OUTPUT, 32); // 4 × 8 words
790
+ }
791
+ /**
792
+ * Initialize the WASM SIMD module synchronously.
793
+ * Call this once before using compress4x.
794
+ */
795
+ // Cache generated WASM bytes to avoid regenerating on each init
796
+ let cachedWasmBytes = null;
797
+ function initSimdSync() {
798
+ if (wasmInstance)
799
+ return true;
800
+ if (!isSimdSupported()) {
801
+ return false;
802
+ }
803
+ try {
804
+ const wasmBytes = cachedWasmBytes || generateWasmBytes();
805
+ cachedWasmBytes = wasmBytes;
806
+ wasmMemory = new WebAssembly.Memory({ initial: 1 });
807
+ const importObject = {
808
+ js: { mem: wasmMemory },
809
+ };
810
+ const module = new WebAssembly.Module(wasmBytes.buffer);
811
+ wasmInstance = new WebAssembly.Instance(module, importObject);
812
+ wasmCompress4x = wasmInstance.exports.compress4x;
813
+ wasmCompressChunks4x = wasmInstance.exports.compressChunks4x;
814
+ wasmCompressParent = wasmInstance.exports.compressParent;
815
+ wasmMemoryView = new Uint8Array(wasmMemory.buffer);
816
+ wasmMemoryView32 = new Uint32Array(wasmMemory.buffer);
817
+ // Set up arena views for Merkle tree operations
818
+ setupArenaViews();
819
+ return true;
820
+ }
821
+ catch (e) {
822
+ console.warn("Failed to initialize WASM SIMD:", e);
823
+ return false;
824
+ }
825
+ }
826
+ /**
827
+ * Memory offsets for SIMD data layout
828
+ *
829
+ * WASM Arena Pattern: All working buffers live in WASM memory (64KB page)
830
+ * This eliminates JS heap allocations during hashing operations.
831
+ */
832
+ exports.SIMD_MEMORY = {
833
+ // SIMD compress4x working area (used by WASM code) - single block
834
+ BLOCK_WORDS: 0, // 4 x 16 words = 512 bytes (transposed layout)
835
+ CHAINING_VALUES: 512, // 4 x 8 words = 128 bytes
836
+ OUTPUT: 640, // 4 x 8 words = 128 bytes
837
+ COUNTER_LOW: 768, // 4 words = 16 bytes
838
+ COUNTER_HIGH: 784, // 4 words = 16 bytes
839
+ BLOCK_LEN: 800, // 4 words = 16 bytes
840
+ FLAGS: 816, // 4 words = 16 bytes
841
+ // End of single-block SIMD working area: 832 bytes
842
+ // SIMD compressChunks4x working area - 16 blocks batched
843
+ // Each block position has 16 v128 values (one per message word) = 256 bytes
844
+ // 16 block positions = 16 × 256 = 4096 bytes
845
+ BATCH_BLOCK_WORDS: 832, // 16 positions × 256 bytes = 4096 bytes (transposed), ends at 4928
846
+ BATCH_CV: 4928, // 4 × 8 words × 4 bytes = 128 bytes (working CVs), ends at 5056
847
+ BATCH_COUNTER_LOW: 5056, // 4 words × 4 bytes = 16 bytes (per-chunk counters), ends at 5072
848
+ BATCH_FLAGS_BASE: 5072, // 4 words × 4 bytes = 16 bytes (base flags, no START/END), ends at 5088
849
+ BATCH_OUTPUT: 5088, // 4 × 8 words × 4 bytes = 128 bytes (final output), ends at 5216
850
+ // End of batch working area: 5216 bytes
851
+ // WASM Arena: JS working buffers (accessed via TypedArray views)
852
+ CV_STACK: 5216, // 64 levels × 8 words × 4 bytes = 2048 bytes, ends at 7264
853
+ PARENT_BLOCK: 7264, // 16 words × 4 bytes = 64 bytes, ends at 7328
854
+ CHUNK_CV: 7328, // 8 words × 4 bytes = 32 bytes, ends at 7360
855
+ TEMP_CVS: 7360, // 4 × 8 words × 4 bytes = 128 bytes, ends at 7488
856
+ // Total arena usage: ~7488 bytes (fits comfortably in 64KB page)
857
+ };
858
+ // Arena views - created once when SIMD initializes
859
+ let arenaCvStack = null;
860
+ let arenaParentBlock = null;
861
+ let arenaChunkCv = null;
862
+ let arenaTempCvs = null;
863
+ // Batch mode arena views
864
+ let arenaBatchBlockWords = null;
865
+ let arenaBatchCv = null;
866
+ let arenaBatchCounterLow = null;
867
+ let arenaBatchFlagsBase = null;
868
+ let arenaBatchOutput = null;
869
+ /**
870
+ * Get the WASM memory views for writing input data.
871
+ */
872
+ function getSimdMemory() {
873
+ if (!wasmMemoryView || !wasmMemoryView32)
874
+ return null;
875
+ return { view: wasmMemoryView, view32: wasmMemoryView32 };
876
+ }
877
+ /**
878
+ * Get the arena buffers for Merkle tree operations.
879
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
880
+ */
881
+ function getArenaBuffers() {
882
+ if (!arenaCvStack || !arenaParentBlock || !arenaChunkCv || !arenaTempCvs)
883
+ return null;
884
+ return {
885
+ cvStack: arenaCvStack,
886
+ parentBlock: arenaParentBlock,
887
+ chunkCv: arenaChunkCv,
888
+ tempCvs: arenaTempCvs,
889
+ };
890
+ }
891
+ /**
892
+ * Get the batch arena buffers for chunk-level batched operations.
893
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
894
+ */
895
+ function getBatchArenaBuffers() {
896
+ if (!arenaBatchBlockWords ||
897
+ !arenaBatchCv ||
898
+ !arenaBatchCounterLow ||
899
+ !arenaBatchFlagsBase ||
900
+ !arenaBatchOutput)
901
+ return null;
902
+ return {
903
+ blockWords: arenaBatchBlockWords,
904
+ cv: arenaBatchCv,
905
+ counterLow: arenaBatchCounterLow,
906
+ flagsBase: arenaBatchFlagsBase,
907
+ output: arenaBatchOutput,
908
+ };
909
+ }
910
+ /**
911
+ * Run the compress4x function.
912
+ * Data must already be set up in WASM memory.
913
+ */
914
+ function runCompress4x() {
915
+ if (!wasmCompress4x) {
916
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
917
+ }
918
+ wasmCompress4x();
919
+ }
920
+ /**
921
+ * Run the compressChunks4x function.
922
+ * Processes 4 full chunks (16 blocks each) in a single WASM call.
923
+ * Data must already be set up in batch arena buffers.
924
+ */
925
+ function runCompressChunks4x() {
926
+ if (!wasmCompressChunks4x) {
927
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
928
+ }
929
+ wasmCompressChunks4x();
930
+ }
931
+ /**
932
+ * Run the compressParent function.
933
+ * Compresses a parent node: reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
934
+ * Data must already be set up in arena buffers (PARENT_BLOCK at offset 7264).
935
+ * Output is written to CHUNK_CV at offset 7328.
936
+ */
937
+ function runCompressParent() {
938
+ if (!wasmCompressParent) {
939
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
940
+ }
941
+ wasmCompressParent();
942
+ }
943
+ /**
944
+ * Check if SIMD is initialized and ready.
945
+ */
946
+ function isSimdReady() {
947
+ return wasmCompress4x !== null;
948
+ }
node_modules/@huggingface/blake3-jit/dist/esm/compress.d.ts ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Compression Function - Highly Optimized
3
+ *
4
+ * Optimization techniques applied (from Fleek Network case study):
5
+ * 1. Use 16 SMI variables for state instead of TypedArray
6
+ * 2. Use 16 SMI variables for message words
7
+ * 3. Fully inlined G function (no function call overhead)
8
+ * 4. Use `| 0` for integer coercion (forces V8 to use 32-bit ALU)
9
+ * 5. Hardcoded permutation swaps using only 2 temporary variables
10
+ * 6. Offset-based access pattern (avoid creating new views)
11
+ *
12
+ * The compression function takes:
13
+ * - cv: 8-word chaining value
14
+ * - block: 16-word message block (64 bytes)
15
+ * - counter: 64-bit block counter
16
+ * - blockLen: number of input bytes in this block
17
+ * - flags: domain separation flags
18
+ *
19
+ * And outputs 8 or 16 words depending on whether this is a root node.
20
+ */
21
+ /**
22
+ * Compress a single block.
23
+ *
24
+ * This is the hot path - every optimization matters here.
25
+ *
26
+ * @param cv - Chaining value array
27
+ * @param cvOff - Offset into cv
28
+ * @param block - Message block words
29
+ * @param blockOff - Offset into block
30
+ * @param out - Output array (8 or 16 words)
31
+ * @param outOff - Offset into out
32
+ * @param full - If true, output all 16 words (for XOF); if false, output 8 words
33
+ * @param counter - 64-bit block counter
34
+ * @param blockLen - Number of bytes in this block (0-64)
35
+ * @param flags - Domain separation flags
36
+ */
37
+ export declare function compress(cv: Uint32Array, cvOff: number, block: Uint32Array, blockOff: number, out: Uint32Array, outOff: number, full: boolean, counter: number, blockLen: number, flags: number): void;
node_modules/@huggingface/blake3-jit/dist/esm/compress.js ADDED
@@ -0,0 +1,916 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Compression Function - Highly Optimized
3
+ *
4
+ * Optimization techniques applied (from Fleek Network case study):
5
+ * 1. Use 16 SMI variables for state instead of TypedArray
6
+ * 2. Use 16 SMI variables for message words
7
+ * 3. Fully inlined G function (no function call overhead)
8
+ * 4. Use `| 0` for integer coercion (forces V8 to use 32-bit ALU)
9
+ * 5. Hardcoded permutation swaps using only 2 temporary variables
10
+ * 6. Offset-based access pattern (avoid creating new views)
11
+ *
12
+ * The compression function takes:
13
+ * - cv: 8-word chaining value
14
+ * - block: 16-word message block (64 bytes)
15
+ * - counter: 64-bit block counter
16
+ * - blockLen: number of input bytes in this block
17
+ * - flags: domain separation flags
18
+ *
19
+ * And outputs 8 or 16 words depending on whether this is a root node.
20
+ */
21
+ /**
22
+ * Compress a single block.
23
+ *
24
+ * This is the hot path - every optimization matters here.
25
+ *
26
+ * @param cv - Chaining value array
27
+ * @param cvOff - Offset into cv
28
+ * @param block - Message block words
29
+ * @param blockOff - Offset into block
30
+ * @param out - Output array (8 or 16 words)
31
+ * @param outOff - Offset into out
32
+ * @param full - If true, output all 16 words (for XOF); if false, output 8 words
33
+ * @param counter - 64-bit block counter
34
+ * @param blockLen - Number of bytes in this block (0-64)
35
+ * @param flags - Domain separation flags
36
+ */
37
+ export function compress(cv, cvOff, block, blockOff, out, outOff, full, counter, blockLen, flags) {
38
+ // Load message words into SMI variables for maximum performance
39
+ // V8 optimizes SMI arithmetic directly with the ALU
40
+ let m0 = block[blockOff] | 0;
41
+ let m1 = block[blockOff + 1] | 0;
42
+ let m2 = block[blockOff + 2] | 0;
43
+ let m3 = block[blockOff + 3] | 0;
44
+ let m4 = block[blockOff + 4] | 0;
45
+ let m5 = block[blockOff + 5] | 0;
46
+ let m6 = block[blockOff + 6] | 0;
47
+ let m7 = block[blockOff + 7] | 0;
48
+ let m8 = block[blockOff + 8] | 0;
49
+ let m9 = block[blockOff + 9] | 0;
50
+ let m10 = block[blockOff + 10] | 0;
51
+ let m11 = block[blockOff + 11] | 0;
52
+ let m12 = block[blockOff + 12] | 0;
53
+ let m13 = block[blockOff + 13] | 0;
54
+ let m14 = block[blockOff + 14] | 0;
55
+ let m15 = block[blockOff + 15] | 0;
56
+ // Initialize state: first 8 words from chaining value
57
+ let s0 = cv[cvOff] | 0;
58
+ let s1 = cv[cvOff + 1] | 0;
59
+ let s2 = cv[cvOff + 2] | 0;
60
+ let s3 = cv[cvOff + 3] | 0;
61
+ let s4 = cv[cvOff + 4] | 0;
62
+ let s5 = cv[cvOff + 5] | 0;
63
+ let s6 = cv[cvOff + 6] | 0;
64
+ let s7 = cv[cvOff + 7] | 0;
65
+ // Words 8-11: IV constants
66
+ let s8 = 0x6a09e667;
67
+ let s9 = 0xbb67ae85;
68
+ let s10 = 0x3c6ef372;
69
+ let s11 = 0xa54ff53a;
70
+ // Words 12-15: counter, blockLen, flags
71
+ // Note: counter is 64-bit, split into low and high 32-bit words
72
+ let s12 = counter | 0;
73
+ let s13 = (counter / 0x100000000) | 0;
74
+ let s14 = blockLen | 0;
75
+ let s15 = flags | 0;
76
+ // ============================================================
77
+ // 7 rounds of mixing
78
+ // Each round consists of 4 column G functions and 4 diagonal G functions
79
+ // followed by a message word permutation (except for round 7)
80
+ // ============================================================
81
+ // ROUND 1 (message schedule: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
82
+ // Column G functions
83
+ // G(0, 4, 8, 12) with m0, m1
84
+ s0 = (((s0 + s4) | 0) + m0) | 0;
85
+ s12 ^= s0;
86
+ s12 = (s12 >>> 16) | (s12 << 16);
87
+ s8 = (s8 + s12) | 0;
88
+ s4 ^= s8;
89
+ s4 = (s4 >>> 12) | (s4 << 20);
90
+ s0 = (((s0 + s4) | 0) + m1) | 0;
91
+ s12 ^= s0;
92
+ s12 = (s12 >>> 8) | (s12 << 24);
93
+ s8 = (s8 + s12) | 0;
94
+ s4 ^= s8;
95
+ s4 = (s4 >>> 7) | (s4 << 25);
96
+ // G(1, 5, 9, 13) with m2, m3
97
+ s1 = (((s1 + s5) | 0) + m2) | 0;
98
+ s13 ^= s1;
99
+ s13 = (s13 >>> 16) | (s13 << 16);
100
+ s9 = (s9 + s13) | 0;
101
+ s5 ^= s9;
102
+ s5 = (s5 >>> 12) | (s5 << 20);
103
+ s1 = (((s1 + s5) | 0) + m3) | 0;
104
+ s13 ^= s1;
105
+ s13 = (s13 >>> 8) | (s13 << 24);
106
+ s9 = (s9 + s13) | 0;
107
+ s5 ^= s9;
108
+ s5 = (s5 >>> 7) | (s5 << 25);
109
+ // G(2, 6, 10, 14) with m4, m5
110
+ s2 = (((s2 + s6) | 0) + m4) | 0;
111
+ s14 ^= s2;
112
+ s14 = (s14 >>> 16) | (s14 << 16);
113
+ s10 = (s10 + s14) | 0;
114
+ s6 ^= s10;
115
+ s6 = (s6 >>> 12) | (s6 << 20);
116
+ s2 = (((s2 + s6) | 0) + m5) | 0;
117
+ s14 ^= s2;
118
+ s14 = (s14 >>> 8) | (s14 << 24);
119
+ s10 = (s10 + s14) | 0;
120
+ s6 ^= s10;
121
+ s6 = (s6 >>> 7) | (s6 << 25);
122
+ // G(3, 7, 11, 15) with m6, m7
123
+ s3 = (((s3 + s7) | 0) + m6) | 0;
124
+ s15 ^= s3;
125
+ s15 = (s15 >>> 16) | (s15 << 16);
126
+ s11 = (s11 + s15) | 0;
127
+ s7 ^= s11;
128
+ s7 = (s7 >>> 12) | (s7 << 20);
129
+ s3 = (((s3 + s7) | 0) + m7) | 0;
130
+ s15 ^= s3;
131
+ s15 = (s15 >>> 8) | (s15 << 24);
132
+ s11 = (s11 + s15) | 0;
133
+ s7 ^= s11;
134
+ s7 = (s7 >>> 7) | (s7 << 25);
135
+ // Diagonal G functions
136
+ // G(0, 5, 10, 15) with m8, m9
137
+ s0 = (((s0 + s5) | 0) + m8) | 0;
138
+ s15 ^= s0;
139
+ s15 = (s15 >>> 16) | (s15 << 16);
140
+ s10 = (s10 + s15) | 0;
141
+ s5 ^= s10;
142
+ s5 = (s5 >>> 12) | (s5 << 20);
143
+ s0 = (((s0 + s5) | 0) + m9) | 0;
144
+ s15 ^= s0;
145
+ s15 = (s15 >>> 8) | (s15 << 24);
146
+ s10 = (s10 + s15) | 0;
147
+ s5 ^= s10;
148
+ s5 = (s5 >>> 7) | (s5 << 25);
149
+ // G(1, 6, 11, 12) with m10, m11
150
+ s1 = (((s1 + s6) | 0) + m10) | 0;
151
+ s12 ^= s1;
152
+ s12 = (s12 >>> 16) | (s12 << 16);
153
+ s11 = (s11 + s12) | 0;
154
+ s6 ^= s11;
155
+ s6 = (s6 >>> 12) | (s6 << 20);
156
+ s1 = (((s1 + s6) | 0) + m11) | 0;
157
+ s12 ^= s1;
158
+ s12 = (s12 >>> 8) | (s12 << 24);
159
+ s11 = (s11 + s12) | 0;
160
+ s6 ^= s11;
161
+ s6 = (s6 >>> 7) | (s6 << 25);
162
+ // G(2, 7, 8, 13) with m12, m13
163
+ s2 = (((s2 + s7) | 0) + m12) | 0;
164
+ s13 ^= s2;
165
+ s13 = (s13 >>> 16) | (s13 << 16);
166
+ s8 = (s8 + s13) | 0;
167
+ s7 ^= s8;
168
+ s7 = (s7 >>> 12) | (s7 << 20);
169
+ s2 = (((s2 + s7) | 0) + m13) | 0;
170
+ s13 ^= s2;
171
+ s13 = (s13 >>> 8) | (s13 << 24);
172
+ s8 = (s8 + s13) | 0;
173
+ s7 ^= s8;
174
+ s7 = (s7 >>> 7) | (s7 << 25);
175
+ // G(3, 4, 9, 14) with m14, m15
176
+ s3 = (((s3 + s4) | 0) + m14) | 0;
177
+ s14 ^= s3;
178
+ s14 = (s14 >>> 16) | (s14 << 16);
179
+ s9 = (s9 + s14) | 0;
180
+ s4 ^= s9;
181
+ s4 = (s4 >>> 12) | (s4 << 20);
182
+ s3 = (((s3 + s4) | 0) + m15) | 0;
183
+ s14 ^= s3;
184
+ s14 = (s14 >>> 8) | (s14 << 24);
185
+ s9 = (s9 + s14) | 0;
186
+ s4 ^= s9;
187
+ s4 = (s4 >>> 7) | (s4 << 25);
188
+ // Permute message words for round 2
189
+ // Permutation: [2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8]
190
+ // Using 2 temps for the two cycles in the permutation
191
+ {
192
+ const t0 = m0, t1 = m1;
193
+ m0 = m2;
194
+ m2 = m3;
195
+ m3 = m10;
196
+ m10 = m12;
197
+ m12 = m9;
198
+ m9 = m11;
199
+ m11 = m5;
200
+ m5 = t0;
201
+ m1 = m6;
202
+ m6 = m4;
203
+ m4 = m7;
204
+ m7 = m13;
205
+ m13 = m14;
206
+ m14 = m15;
207
+ m15 = m8;
208
+ m8 = t1;
209
+ }
210
+ // ROUND 2 (message schedule: 2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8)
211
+ s0 = (((s0 + s4) | 0) + m0) | 0;
212
+ s12 ^= s0;
213
+ s12 = (s12 >>> 16) | (s12 << 16);
214
+ s8 = (s8 + s12) | 0;
215
+ s4 ^= s8;
216
+ s4 = (s4 >>> 12) | (s4 << 20);
217
+ s0 = (((s0 + s4) | 0) + m1) | 0;
218
+ s12 ^= s0;
219
+ s12 = (s12 >>> 8) | (s12 << 24);
220
+ s8 = (s8 + s12) | 0;
221
+ s4 ^= s8;
222
+ s4 = (s4 >>> 7) | (s4 << 25);
223
+ s1 = (((s1 + s5) | 0) + m2) | 0;
224
+ s13 ^= s1;
225
+ s13 = (s13 >>> 16) | (s13 << 16);
226
+ s9 = (s9 + s13) | 0;
227
+ s5 ^= s9;
228
+ s5 = (s5 >>> 12) | (s5 << 20);
229
+ s1 = (((s1 + s5) | 0) + m3) | 0;
230
+ s13 ^= s1;
231
+ s13 = (s13 >>> 8) | (s13 << 24);
232
+ s9 = (s9 + s13) | 0;
233
+ s5 ^= s9;
234
+ s5 = (s5 >>> 7) | (s5 << 25);
235
+ s2 = (((s2 + s6) | 0) + m4) | 0;
236
+ s14 ^= s2;
237
+ s14 = (s14 >>> 16) | (s14 << 16);
238
+ s10 = (s10 + s14) | 0;
239
+ s6 ^= s10;
240
+ s6 = (s6 >>> 12) | (s6 << 20);
241
+ s2 = (((s2 + s6) | 0) + m5) | 0;
242
+ s14 ^= s2;
243
+ s14 = (s14 >>> 8) | (s14 << 24);
244
+ s10 = (s10 + s14) | 0;
245
+ s6 ^= s10;
246
+ s6 = (s6 >>> 7) | (s6 << 25);
247
+ s3 = (((s3 + s7) | 0) + m6) | 0;
248
+ s15 ^= s3;
249
+ s15 = (s15 >>> 16) | (s15 << 16);
250
+ s11 = (s11 + s15) | 0;
251
+ s7 ^= s11;
252
+ s7 = (s7 >>> 12) | (s7 << 20);
253
+ s3 = (((s3 + s7) | 0) + m7) | 0;
254
+ s15 ^= s3;
255
+ s15 = (s15 >>> 8) | (s15 << 24);
256
+ s11 = (s11 + s15) | 0;
257
+ s7 ^= s11;
258
+ s7 = (s7 >>> 7) | (s7 << 25);
259
+ s0 = (((s0 + s5) | 0) + m8) | 0;
260
+ s15 ^= s0;
261
+ s15 = (s15 >>> 16) | (s15 << 16);
262
+ s10 = (s10 + s15) | 0;
263
+ s5 ^= s10;
264
+ s5 = (s5 >>> 12) | (s5 << 20);
265
+ s0 = (((s0 + s5) | 0) + m9) | 0;
266
+ s15 ^= s0;
267
+ s15 = (s15 >>> 8) | (s15 << 24);
268
+ s10 = (s10 + s15) | 0;
269
+ s5 ^= s10;
270
+ s5 = (s5 >>> 7) | (s5 << 25);
271
+ s1 = (((s1 + s6) | 0) + m10) | 0;
272
+ s12 ^= s1;
273
+ s12 = (s12 >>> 16) | (s12 << 16);
274
+ s11 = (s11 + s12) | 0;
275
+ s6 ^= s11;
276
+ s6 = (s6 >>> 12) | (s6 << 20);
277
+ s1 = (((s1 + s6) | 0) + m11) | 0;
278
+ s12 ^= s1;
279
+ s12 = (s12 >>> 8) | (s12 << 24);
280
+ s11 = (s11 + s12) | 0;
281
+ s6 ^= s11;
282
+ s6 = (s6 >>> 7) | (s6 << 25);
283
+ s2 = (((s2 + s7) | 0) + m12) | 0;
284
+ s13 ^= s2;
285
+ s13 = (s13 >>> 16) | (s13 << 16);
286
+ s8 = (s8 + s13) | 0;
287
+ s7 ^= s8;
288
+ s7 = (s7 >>> 12) | (s7 << 20);
289
+ s2 = (((s2 + s7) | 0) + m13) | 0;
290
+ s13 ^= s2;
291
+ s13 = (s13 >>> 8) | (s13 << 24);
292
+ s8 = (s8 + s13) | 0;
293
+ s7 ^= s8;
294
+ s7 = (s7 >>> 7) | (s7 << 25);
295
+ s3 = (((s3 + s4) | 0) + m14) | 0;
296
+ s14 ^= s3;
297
+ s14 = (s14 >>> 16) | (s14 << 16);
298
+ s9 = (s9 + s14) | 0;
299
+ s4 ^= s9;
300
+ s4 = (s4 >>> 12) | (s4 << 20);
301
+ s3 = (((s3 + s4) | 0) + m15) | 0;
302
+ s14 ^= s3;
303
+ s14 = (s14 >>> 8) | (s14 << 24);
304
+ s9 = (s9 + s14) | 0;
305
+ s4 ^= s9;
306
+ s4 = (s4 >>> 7) | (s4 << 25);
307
+ // Permute for round 3
308
+ {
309
+ const t0 = m0, t1 = m1;
310
+ m0 = m2;
311
+ m2 = m3;
312
+ m3 = m10;
313
+ m10 = m12;
314
+ m12 = m9;
315
+ m9 = m11;
316
+ m11 = m5;
317
+ m5 = t0;
318
+ m1 = m6;
319
+ m6 = m4;
320
+ m4 = m7;
321
+ m7 = m13;
322
+ m13 = m14;
323
+ m14 = m15;
324
+ m15 = m8;
325
+ m8 = t1;
326
+ }
327
+ // ROUND 3 (message schedule: 3,4,10,12,13,2,7,14,6,5,9,0,11,15,8,1)
328
+ s0 = (((s0 + s4) | 0) + m0) | 0;
329
+ s12 ^= s0;
330
+ s12 = (s12 >>> 16) | (s12 << 16);
331
+ s8 = (s8 + s12) | 0;
332
+ s4 ^= s8;
333
+ s4 = (s4 >>> 12) | (s4 << 20);
334
+ s0 = (((s0 + s4) | 0) + m1) | 0;
335
+ s12 ^= s0;
336
+ s12 = (s12 >>> 8) | (s12 << 24);
337
+ s8 = (s8 + s12) | 0;
338
+ s4 ^= s8;
339
+ s4 = (s4 >>> 7) | (s4 << 25);
340
+ s1 = (((s1 + s5) | 0) + m2) | 0;
341
+ s13 ^= s1;
342
+ s13 = (s13 >>> 16) | (s13 << 16);
343
+ s9 = (s9 + s13) | 0;
344
+ s5 ^= s9;
345
+ s5 = (s5 >>> 12) | (s5 << 20);
346
+ s1 = (((s1 + s5) | 0) + m3) | 0;
347
+ s13 ^= s1;
348
+ s13 = (s13 >>> 8) | (s13 << 24);
349
+ s9 = (s9 + s13) | 0;
350
+ s5 ^= s9;
351
+ s5 = (s5 >>> 7) | (s5 << 25);
352
+ s2 = (((s2 + s6) | 0) + m4) | 0;
353
+ s14 ^= s2;
354
+ s14 = (s14 >>> 16) | (s14 << 16);
355
+ s10 = (s10 + s14) | 0;
356
+ s6 ^= s10;
357
+ s6 = (s6 >>> 12) | (s6 << 20);
358
+ s2 = (((s2 + s6) | 0) + m5) | 0;
359
+ s14 ^= s2;
360
+ s14 = (s14 >>> 8) | (s14 << 24);
361
+ s10 = (s10 + s14) | 0;
362
+ s6 ^= s10;
363
+ s6 = (s6 >>> 7) | (s6 << 25);
364
+ s3 = (((s3 + s7) | 0) + m6) | 0;
365
+ s15 ^= s3;
366
+ s15 = (s15 >>> 16) | (s15 << 16);
367
+ s11 = (s11 + s15) | 0;
368
+ s7 ^= s11;
369
+ s7 = (s7 >>> 12) | (s7 << 20);
370
+ s3 = (((s3 + s7) | 0) + m7) | 0;
371
+ s15 ^= s3;
372
+ s15 = (s15 >>> 8) | (s15 << 24);
373
+ s11 = (s11 + s15) | 0;
374
+ s7 ^= s11;
375
+ s7 = (s7 >>> 7) | (s7 << 25);
376
+ s0 = (((s0 + s5) | 0) + m8) | 0;
377
+ s15 ^= s0;
378
+ s15 = (s15 >>> 16) | (s15 << 16);
379
+ s10 = (s10 + s15) | 0;
380
+ s5 ^= s10;
381
+ s5 = (s5 >>> 12) | (s5 << 20);
382
+ s0 = (((s0 + s5) | 0) + m9) | 0;
383
+ s15 ^= s0;
384
+ s15 = (s15 >>> 8) | (s15 << 24);
385
+ s10 = (s10 + s15) | 0;
386
+ s5 ^= s10;
387
+ s5 = (s5 >>> 7) | (s5 << 25);
388
+ s1 = (((s1 + s6) | 0) + m10) | 0;
389
+ s12 ^= s1;
390
+ s12 = (s12 >>> 16) | (s12 << 16);
391
+ s11 = (s11 + s12) | 0;
392
+ s6 ^= s11;
393
+ s6 = (s6 >>> 12) | (s6 << 20);
394
+ s1 = (((s1 + s6) | 0) + m11) | 0;
395
+ s12 ^= s1;
396
+ s12 = (s12 >>> 8) | (s12 << 24);
397
+ s11 = (s11 + s12) | 0;
398
+ s6 ^= s11;
399
+ s6 = (s6 >>> 7) | (s6 << 25);
400
+ s2 = (((s2 + s7) | 0) + m12) | 0;
401
+ s13 ^= s2;
402
+ s13 = (s13 >>> 16) | (s13 << 16);
403
+ s8 = (s8 + s13) | 0;
404
+ s7 ^= s8;
405
+ s7 = (s7 >>> 12) | (s7 << 20);
406
+ s2 = (((s2 + s7) | 0) + m13) | 0;
407
+ s13 ^= s2;
408
+ s13 = (s13 >>> 8) | (s13 << 24);
409
+ s8 = (s8 + s13) | 0;
410
+ s7 ^= s8;
411
+ s7 = (s7 >>> 7) | (s7 << 25);
412
+ s3 = (((s3 + s4) | 0) + m14) | 0;
413
+ s14 ^= s3;
414
+ s14 = (s14 >>> 16) | (s14 << 16);
415
+ s9 = (s9 + s14) | 0;
416
+ s4 ^= s9;
417
+ s4 = (s4 >>> 12) | (s4 << 20);
418
+ s3 = (((s3 + s4) | 0) + m15) | 0;
419
+ s14 ^= s3;
420
+ s14 = (s14 >>> 8) | (s14 << 24);
421
+ s9 = (s9 + s14) | 0;
422
+ s4 ^= s9;
423
+ s4 = (s4 >>> 7) | (s4 << 25);
424
+ // Permute for round 4
425
+ {
426
+ const t0 = m0, t1 = m1;
427
+ m0 = m2;
428
+ m2 = m3;
429
+ m3 = m10;
430
+ m10 = m12;
431
+ m12 = m9;
432
+ m9 = m11;
433
+ m11 = m5;
434
+ m5 = t0;
435
+ m1 = m6;
436
+ m6 = m4;
437
+ m4 = m7;
438
+ m7 = m13;
439
+ m13 = m14;
440
+ m14 = m15;
441
+ m15 = m8;
442
+ m8 = t1;
443
+ }
444
+ // ROUND 4 (message schedule: 10,7,12,9,14,3,13,15,4,0,11,2,5,8,1,6)
445
+ s0 = (((s0 + s4) | 0) + m0) | 0;
446
+ s12 ^= s0;
447
+ s12 = (s12 >>> 16) | (s12 << 16);
448
+ s8 = (s8 + s12) | 0;
449
+ s4 ^= s8;
450
+ s4 = (s4 >>> 12) | (s4 << 20);
451
+ s0 = (((s0 + s4) | 0) + m1) | 0;
452
+ s12 ^= s0;
453
+ s12 = (s12 >>> 8) | (s12 << 24);
454
+ s8 = (s8 + s12) | 0;
455
+ s4 ^= s8;
456
+ s4 = (s4 >>> 7) | (s4 << 25);
457
+ s1 = (((s1 + s5) | 0) + m2) | 0;
458
+ s13 ^= s1;
459
+ s13 = (s13 >>> 16) | (s13 << 16);
460
+ s9 = (s9 + s13) | 0;
461
+ s5 ^= s9;
462
+ s5 = (s5 >>> 12) | (s5 << 20);
463
+ s1 = (((s1 + s5) | 0) + m3) | 0;
464
+ s13 ^= s1;
465
+ s13 = (s13 >>> 8) | (s13 << 24);
466
+ s9 = (s9 + s13) | 0;
467
+ s5 ^= s9;
468
+ s5 = (s5 >>> 7) | (s5 << 25);
469
+ s2 = (((s2 + s6) | 0) + m4) | 0;
470
+ s14 ^= s2;
471
+ s14 = (s14 >>> 16) | (s14 << 16);
472
+ s10 = (s10 + s14) | 0;
473
+ s6 ^= s10;
474
+ s6 = (s6 >>> 12) | (s6 << 20);
475
+ s2 = (((s2 + s6) | 0) + m5) | 0;
476
+ s14 ^= s2;
477
+ s14 = (s14 >>> 8) | (s14 << 24);
478
+ s10 = (s10 + s14) | 0;
479
+ s6 ^= s10;
480
+ s6 = (s6 >>> 7) | (s6 << 25);
481
+ s3 = (((s3 + s7) | 0) + m6) | 0;
482
+ s15 ^= s3;
483
+ s15 = (s15 >>> 16) | (s15 << 16);
484
+ s11 = (s11 + s15) | 0;
485
+ s7 ^= s11;
486
+ s7 = (s7 >>> 12) | (s7 << 20);
487
+ s3 = (((s3 + s7) | 0) + m7) | 0;
488
+ s15 ^= s3;
489
+ s15 = (s15 >>> 8) | (s15 << 24);
490
+ s11 = (s11 + s15) | 0;
491
+ s7 ^= s11;
492
+ s7 = (s7 >>> 7) | (s7 << 25);
493
+ s0 = (((s0 + s5) | 0) + m8) | 0;
494
+ s15 ^= s0;
495
+ s15 = (s15 >>> 16) | (s15 << 16);
496
+ s10 = (s10 + s15) | 0;
497
+ s5 ^= s10;
498
+ s5 = (s5 >>> 12) | (s5 << 20);
499
+ s0 = (((s0 + s5) | 0) + m9) | 0;
500
+ s15 ^= s0;
501
+ s15 = (s15 >>> 8) | (s15 << 24);
502
+ s10 = (s10 + s15) | 0;
503
+ s5 ^= s10;
504
+ s5 = (s5 >>> 7) | (s5 << 25);
505
+ s1 = (((s1 + s6) | 0) + m10) | 0;
506
+ s12 ^= s1;
507
+ s12 = (s12 >>> 16) | (s12 << 16);
508
+ s11 = (s11 + s12) | 0;
509
+ s6 ^= s11;
510
+ s6 = (s6 >>> 12) | (s6 << 20);
511
+ s1 = (((s1 + s6) | 0) + m11) | 0;
512
+ s12 ^= s1;
513
+ s12 = (s12 >>> 8) | (s12 << 24);
514
+ s11 = (s11 + s12) | 0;
515
+ s6 ^= s11;
516
+ s6 = (s6 >>> 7) | (s6 << 25);
517
+ s2 = (((s2 + s7) | 0) + m12) | 0;
518
+ s13 ^= s2;
519
+ s13 = (s13 >>> 16) | (s13 << 16);
520
+ s8 = (s8 + s13) | 0;
521
+ s7 ^= s8;
522
+ s7 = (s7 >>> 12) | (s7 << 20);
523
+ s2 = (((s2 + s7) | 0) + m13) | 0;
524
+ s13 ^= s2;
525
+ s13 = (s13 >>> 8) | (s13 << 24);
526
+ s8 = (s8 + s13) | 0;
527
+ s7 ^= s8;
528
+ s7 = (s7 >>> 7) | (s7 << 25);
529
+ s3 = (((s3 + s4) | 0) + m14) | 0;
530
+ s14 ^= s3;
531
+ s14 = (s14 >>> 16) | (s14 << 16);
532
+ s9 = (s9 + s14) | 0;
533
+ s4 ^= s9;
534
+ s4 = (s4 >>> 12) | (s4 << 20);
535
+ s3 = (((s3 + s4) | 0) + m15) | 0;
536
+ s14 ^= s3;
537
+ s14 = (s14 >>> 8) | (s14 << 24);
538
+ s9 = (s9 + s14) | 0;
539
+ s4 ^= s9;
540
+ s4 = (s4 >>> 7) | (s4 << 25);
541
+ // Permute for round 5
542
+ {
543
+ const t0 = m0, t1 = m1;
544
+ m0 = m2;
545
+ m2 = m3;
546
+ m3 = m10;
547
+ m10 = m12;
548
+ m12 = m9;
549
+ m9 = m11;
550
+ m11 = m5;
551
+ m5 = t0;
552
+ m1 = m6;
553
+ m6 = m4;
554
+ m4 = m7;
555
+ m7 = m13;
556
+ m13 = m14;
557
+ m14 = m15;
558
+ m15 = m8;
559
+ m8 = t1;
560
+ }
561
+ // ROUND 5 (message schedule: 12,13,9,11,15,10,14,8,7,2,5,3,0,1,6,4)
562
+ s0 = (((s0 + s4) | 0) + m0) | 0;
563
+ s12 ^= s0;
564
+ s12 = (s12 >>> 16) | (s12 << 16);
565
+ s8 = (s8 + s12) | 0;
566
+ s4 ^= s8;
567
+ s4 = (s4 >>> 12) | (s4 << 20);
568
+ s0 = (((s0 + s4) | 0) + m1) | 0;
569
+ s12 ^= s0;
570
+ s12 = (s12 >>> 8) | (s12 << 24);
571
+ s8 = (s8 + s12) | 0;
572
+ s4 ^= s8;
573
+ s4 = (s4 >>> 7) | (s4 << 25);
574
+ s1 = (((s1 + s5) | 0) + m2) | 0;
575
+ s13 ^= s1;
576
+ s13 = (s13 >>> 16) | (s13 << 16);
577
+ s9 = (s9 + s13) | 0;
578
+ s5 ^= s9;
579
+ s5 = (s5 >>> 12) | (s5 << 20);
580
+ s1 = (((s1 + s5) | 0) + m3) | 0;
581
+ s13 ^= s1;
582
+ s13 = (s13 >>> 8) | (s13 << 24);
583
+ s9 = (s9 + s13) | 0;
584
+ s5 ^= s9;
585
+ s5 = (s5 >>> 7) | (s5 << 25);
586
+ s2 = (((s2 + s6) | 0) + m4) | 0;
587
+ s14 ^= s2;
588
+ s14 = (s14 >>> 16) | (s14 << 16);
589
+ s10 = (s10 + s14) | 0;
590
+ s6 ^= s10;
591
+ s6 = (s6 >>> 12) | (s6 << 20);
592
+ s2 = (((s2 + s6) | 0) + m5) | 0;
593
+ s14 ^= s2;
594
+ s14 = (s14 >>> 8) | (s14 << 24);
595
+ s10 = (s10 + s14) | 0;
596
+ s6 ^= s10;
597
+ s6 = (s6 >>> 7) | (s6 << 25);
598
+ s3 = (((s3 + s7) | 0) + m6) | 0;
599
+ s15 ^= s3;
600
+ s15 = (s15 >>> 16) | (s15 << 16);
601
+ s11 = (s11 + s15) | 0;
602
+ s7 ^= s11;
603
+ s7 = (s7 >>> 12) | (s7 << 20);
604
+ s3 = (((s3 + s7) | 0) + m7) | 0;
605
+ s15 ^= s3;
606
+ s15 = (s15 >>> 8) | (s15 << 24);
607
+ s11 = (s11 + s15) | 0;
608
+ s7 ^= s11;
609
+ s7 = (s7 >>> 7) | (s7 << 25);
610
+ s0 = (((s0 + s5) | 0) + m8) | 0;
611
+ s15 ^= s0;
612
+ s15 = (s15 >>> 16) | (s15 << 16);
613
+ s10 = (s10 + s15) | 0;
614
+ s5 ^= s10;
615
+ s5 = (s5 >>> 12) | (s5 << 20);
616
+ s0 = (((s0 + s5) | 0) + m9) | 0;
617
+ s15 ^= s0;
618
+ s15 = (s15 >>> 8) | (s15 << 24);
619
+ s10 = (s10 + s15) | 0;
620
+ s5 ^= s10;
621
+ s5 = (s5 >>> 7) | (s5 << 25);
622
+ s1 = (((s1 + s6) | 0) + m10) | 0;
623
+ s12 ^= s1;
624
+ s12 = (s12 >>> 16) | (s12 << 16);
625
+ s11 = (s11 + s12) | 0;
626
+ s6 ^= s11;
627
+ s6 = (s6 >>> 12) | (s6 << 20);
628
+ s1 = (((s1 + s6) | 0) + m11) | 0;
629
+ s12 ^= s1;
630
+ s12 = (s12 >>> 8) | (s12 << 24);
631
+ s11 = (s11 + s12) | 0;
632
+ s6 ^= s11;
633
+ s6 = (s6 >>> 7) | (s6 << 25);
634
+ s2 = (((s2 + s7) | 0) + m12) | 0;
635
+ s13 ^= s2;
636
+ s13 = (s13 >>> 16) | (s13 << 16);
637
+ s8 = (s8 + s13) | 0;
638
+ s7 ^= s8;
639
+ s7 = (s7 >>> 12) | (s7 << 20);
640
+ s2 = (((s2 + s7) | 0) + m13) | 0;
641
+ s13 ^= s2;
642
+ s13 = (s13 >>> 8) | (s13 << 24);
643
+ s8 = (s8 + s13) | 0;
644
+ s7 ^= s8;
645
+ s7 = (s7 >>> 7) | (s7 << 25);
646
+ s3 = (((s3 + s4) | 0) + m14) | 0;
647
+ s14 ^= s3;
648
+ s14 = (s14 >>> 16) | (s14 << 16);
649
+ s9 = (s9 + s14) | 0;
650
+ s4 ^= s9;
651
+ s4 = (s4 >>> 12) | (s4 << 20);
652
+ s3 = (((s3 + s4) | 0) + m15) | 0;
653
+ s14 ^= s3;
654
+ s14 = (s14 >>> 8) | (s14 << 24);
655
+ s9 = (s9 + s14) | 0;
656
+ s4 ^= s9;
657
+ s4 = (s4 >>> 7) | (s4 << 25);
658
+ // Permute for round 6
659
+ {
660
+ const t0 = m0, t1 = m1;
661
+ m0 = m2;
662
+ m2 = m3;
663
+ m3 = m10;
664
+ m10 = m12;
665
+ m12 = m9;
666
+ m9 = m11;
667
+ m11 = m5;
668
+ m5 = t0;
669
+ m1 = m6;
670
+ m6 = m4;
671
+ m4 = m7;
672
+ m7 = m13;
673
+ m13 = m14;
674
+ m14 = m15;
675
+ m15 = m8;
676
+ m8 = t1;
677
+ }
678
+ // ROUND 6 (message schedule: 9,14,11,5,8,12,15,1,13,3,0,10,2,6,4,7)
679
+ s0 = (((s0 + s4) | 0) + m0) | 0;
680
+ s12 ^= s0;
681
+ s12 = (s12 >>> 16) | (s12 << 16);
682
+ s8 = (s8 + s12) | 0;
683
+ s4 ^= s8;
684
+ s4 = (s4 >>> 12) | (s4 << 20);
685
+ s0 = (((s0 + s4) | 0) + m1) | 0;
686
+ s12 ^= s0;
687
+ s12 = (s12 >>> 8) | (s12 << 24);
688
+ s8 = (s8 + s12) | 0;
689
+ s4 ^= s8;
690
+ s4 = (s4 >>> 7) | (s4 << 25);
691
+ s1 = (((s1 + s5) | 0) + m2) | 0;
692
+ s13 ^= s1;
693
+ s13 = (s13 >>> 16) | (s13 << 16);
694
+ s9 = (s9 + s13) | 0;
695
+ s5 ^= s9;
696
+ s5 = (s5 >>> 12) | (s5 << 20);
697
+ s1 = (((s1 + s5) | 0) + m3) | 0;
698
+ s13 ^= s1;
699
+ s13 = (s13 >>> 8) | (s13 << 24);
700
+ s9 = (s9 + s13) | 0;
701
+ s5 ^= s9;
702
+ s5 = (s5 >>> 7) | (s5 << 25);
703
+ s2 = (((s2 + s6) | 0) + m4) | 0;
704
+ s14 ^= s2;
705
+ s14 = (s14 >>> 16) | (s14 << 16);
706
+ s10 = (s10 + s14) | 0;
707
+ s6 ^= s10;
708
+ s6 = (s6 >>> 12) | (s6 << 20);
709
+ s2 = (((s2 + s6) | 0) + m5) | 0;
710
+ s14 ^= s2;
711
+ s14 = (s14 >>> 8) | (s14 << 24);
712
+ s10 = (s10 + s14) | 0;
713
+ s6 ^= s10;
714
+ s6 = (s6 >>> 7) | (s6 << 25);
715
+ s3 = (((s3 + s7) | 0) + m6) | 0;
716
+ s15 ^= s3;
717
+ s15 = (s15 >>> 16) | (s15 << 16);
718
+ s11 = (s11 + s15) | 0;
719
+ s7 ^= s11;
720
+ s7 = (s7 >>> 12) | (s7 << 20);
721
+ s3 = (((s3 + s7) | 0) + m7) | 0;
722
+ s15 ^= s3;
723
+ s15 = (s15 >>> 8) | (s15 << 24);
724
+ s11 = (s11 + s15) | 0;
725
+ s7 ^= s11;
726
+ s7 = (s7 >>> 7) | (s7 << 25);
727
+ s0 = (((s0 + s5) | 0) + m8) | 0;
728
+ s15 ^= s0;
729
+ s15 = (s15 >>> 16) | (s15 << 16);
730
+ s10 = (s10 + s15) | 0;
731
+ s5 ^= s10;
732
+ s5 = (s5 >>> 12) | (s5 << 20);
733
+ s0 = (((s0 + s5) | 0) + m9) | 0;
734
+ s15 ^= s0;
735
+ s15 = (s15 >>> 8) | (s15 << 24);
736
+ s10 = (s10 + s15) | 0;
737
+ s5 ^= s10;
738
+ s5 = (s5 >>> 7) | (s5 << 25);
739
+ s1 = (((s1 + s6) | 0) + m10) | 0;
740
+ s12 ^= s1;
741
+ s12 = (s12 >>> 16) | (s12 << 16);
742
+ s11 = (s11 + s12) | 0;
743
+ s6 ^= s11;
744
+ s6 = (s6 >>> 12) | (s6 << 20);
745
+ s1 = (((s1 + s6) | 0) + m11) | 0;
746
+ s12 ^= s1;
747
+ s12 = (s12 >>> 8) | (s12 << 24);
748
+ s11 = (s11 + s12) | 0;
749
+ s6 ^= s11;
750
+ s6 = (s6 >>> 7) | (s6 << 25);
751
+ s2 = (((s2 + s7) | 0) + m12) | 0;
752
+ s13 ^= s2;
753
+ s13 = (s13 >>> 16) | (s13 << 16);
754
+ s8 = (s8 + s13) | 0;
755
+ s7 ^= s8;
756
+ s7 = (s7 >>> 12) | (s7 << 20);
757
+ s2 = (((s2 + s7) | 0) + m13) | 0;
758
+ s13 ^= s2;
759
+ s13 = (s13 >>> 8) | (s13 << 24);
760
+ s8 = (s8 + s13) | 0;
761
+ s7 ^= s8;
762
+ s7 = (s7 >>> 7) | (s7 << 25);
763
+ s3 = (((s3 + s4) | 0) + m14) | 0;
764
+ s14 ^= s3;
765
+ s14 = (s14 >>> 16) | (s14 << 16);
766
+ s9 = (s9 + s14) | 0;
767
+ s4 ^= s9;
768
+ s4 = (s4 >>> 12) | (s4 << 20);
769
+ s3 = (((s3 + s4) | 0) + m15) | 0;
770
+ s14 ^= s3;
771
+ s14 = (s14 >>> 8) | (s14 << 24);
772
+ s9 = (s9 + s14) | 0;
773
+ s4 ^= s9;
774
+ s4 = (s4 >>> 7) | (s4 << 25);
775
+ // Permute for round 7
776
+ {
777
+ const t0 = m0, t1 = m1;
778
+ m0 = m2;
779
+ m2 = m3;
780
+ m3 = m10;
781
+ m10 = m12;
782
+ m12 = m9;
783
+ m9 = m11;
784
+ m11 = m5;
785
+ m5 = t0;
786
+ m1 = m6;
787
+ m6 = m4;
788
+ m4 = m7;
789
+ m7 = m13;
790
+ m13 = m14;
791
+ m14 = m15;
792
+ m15 = m8;
793
+ m8 = t1;
794
+ }
795
+ // ROUND 7 (message schedule: 11,15,5,0,1,9,8,6,14,10,2,12,3,4,7,13)
796
+ s0 = (((s0 + s4) | 0) + m0) | 0;
797
+ s12 ^= s0;
798
+ s12 = (s12 >>> 16) | (s12 << 16);
799
+ s8 = (s8 + s12) | 0;
800
+ s4 ^= s8;
801
+ s4 = (s4 >>> 12) | (s4 << 20);
802
+ s0 = (((s0 + s4) | 0) + m1) | 0;
803
+ s12 ^= s0;
804
+ s12 = (s12 >>> 8) | (s12 << 24);
805
+ s8 = (s8 + s12) | 0;
806
+ s4 ^= s8;
807
+ s4 = (s4 >>> 7) | (s4 << 25);
808
+ s1 = (((s1 + s5) | 0) + m2) | 0;
809
+ s13 ^= s1;
810
+ s13 = (s13 >>> 16) | (s13 << 16);
811
+ s9 = (s9 + s13) | 0;
812
+ s5 ^= s9;
813
+ s5 = (s5 >>> 12) | (s5 << 20);
814
+ s1 = (((s1 + s5) | 0) + m3) | 0;
815
+ s13 ^= s1;
816
+ s13 = (s13 >>> 8) | (s13 << 24);
817
+ s9 = (s9 + s13) | 0;
818
+ s5 ^= s9;
819
+ s5 = (s5 >>> 7) | (s5 << 25);
820
+ s2 = (((s2 + s6) | 0) + m4) | 0;
821
+ s14 ^= s2;
822
+ s14 = (s14 >>> 16) | (s14 << 16);
823
+ s10 = (s10 + s14) | 0;
824
+ s6 ^= s10;
825
+ s6 = (s6 >>> 12) | (s6 << 20);
826
+ s2 = (((s2 + s6) | 0) + m5) | 0;
827
+ s14 ^= s2;
828
+ s14 = (s14 >>> 8) | (s14 << 24);
829
+ s10 = (s10 + s14) | 0;
830
+ s6 ^= s10;
831
+ s6 = (s6 >>> 7) | (s6 << 25);
832
+ s3 = (((s3 + s7) | 0) + m6) | 0;
833
+ s15 ^= s3;
834
+ s15 = (s15 >>> 16) | (s15 << 16);
835
+ s11 = (s11 + s15) | 0;
836
+ s7 ^= s11;
837
+ s7 = (s7 >>> 12) | (s7 << 20);
838
+ s3 = (((s3 + s7) | 0) + m7) | 0;
839
+ s15 ^= s3;
840
+ s15 = (s15 >>> 8) | (s15 << 24);
841
+ s11 = (s11 + s15) | 0;
842
+ s7 ^= s11;
843
+ s7 = (s7 >>> 7) | (s7 << 25);
844
+ s0 = (((s0 + s5) | 0) + m8) | 0;
845
+ s15 ^= s0;
846
+ s15 = (s15 >>> 16) | (s15 << 16);
847
+ s10 = (s10 + s15) | 0;
848
+ s5 ^= s10;
849
+ s5 = (s5 >>> 12) | (s5 << 20);
850
+ s0 = (((s0 + s5) | 0) + m9) | 0;
851
+ s15 ^= s0;
852
+ s15 = (s15 >>> 8) | (s15 << 24);
853
+ s10 = (s10 + s15) | 0;
854
+ s5 ^= s10;
855
+ s5 = (s5 >>> 7) | (s5 << 25);
856
+ s1 = (((s1 + s6) | 0) + m10) | 0;
857
+ s12 ^= s1;
858
+ s12 = (s12 >>> 16) | (s12 << 16);
859
+ s11 = (s11 + s12) | 0;
860
+ s6 ^= s11;
861
+ s6 = (s6 >>> 12) | (s6 << 20);
862
+ s1 = (((s1 + s6) | 0) + m11) | 0;
863
+ s12 ^= s1;
864
+ s12 = (s12 >>> 8) | (s12 << 24);
865
+ s11 = (s11 + s12) | 0;
866
+ s6 ^= s11;
867
+ s6 = (s6 >>> 7) | (s6 << 25);
868
+ s2 = (((s2 + s7) | 0) + m12) | 0;
869
+ s13 ^= s2;
870
+ s13 = (s13 >>> 16) | (s13 << 16);
871
+ s8 = (s8 + s13) | 0;
872
+ s7 ^= s8;
873
+ s7 = (s7 >>> 12) | (s7 << 20);
874
+ s2 = (((s2 + s7) | 0) + m13) | 0;
875
+ s13 ^= s2;
876
+ s13 = (s13 >>> 8) | (s13 << 24);
877
+ s8 = (s8 + s13) | 0;
878
+ s7 ^= s8;
879
+ s7 = (s7 >>> 7) | (s7 << 25);
880
+ s3 = (((s3 + s4) | 0) + m14) | 0;
881
+ s14 ^= s3;
882
+ s14 = (s14 >>> 16) | (s14 << 16);
883
+ s9 = (s9 + s14) | 0;
884
+ s4 ^= s9;
885
+ s4 = (s4 >>> 12) | (s4 << 20);
886
+ s3 = (((s3 + s4) | 0) + m15) | 0;
887
+ s14 ^= s3;
888
+ s14 = (s14 >>> 8) | (s14 << 24);
889
+ s9 = (s9 + s14) | 0;
890
+ s4 ^= s9;
891
+ s4 = (s4 >>> 7) | (s4 << 25);
892
+ // ============================================================
893
+ // Final XOR and output
894
+ // ============================================================
895
+ // If full output needed (XOF mode), write words 8-15 first
896
+ // (written first in case out === cv)
897
+ if (full) {
898
+ out[outOff + 8] = s8 ^ cv[cvOff];
899
+ out[outOff + 9] = s9 ^ cv[cvOff + 1];
900
+ out[outOff + 10] = s10 ^ cv[cvOff + 2];
901
+ out[outOff + 11] = s11 ^ cv[cvOff + 3];
902
+ out[outOff + 12] = s12 ^ cv[cvOff + 4];
903
+ out[outOff + 13] = s13 ^ cv[cvOff + 5];
904
+ out[outOff + 14] = s14 ^ cv[cvOff + 6];
905
+ out[outOff + 15] = s15 ^ cv[cvOff + 7];
906
+ }
907
+ // Standard output: XOR state[0..7] with state[8..15]
908
+ out[outOff] = s0 ^ s8;
909
+ out[outOff + 1] = s1 ^ s9;
910
+ out[outOff + 2] = s2 ^ s10;
911
+ out[outOff + 3] = s3 ^ s11;
912
+ out[outOff + 4] = s4 ^ s12;
913
+ out[outOff + 5] = s5 ^ s13;
914
+ out[outOff + 6] = s6 ^ s14;
915
+ out[outOff + 7] = s7 ^ s15;
916
+ }
node_modules/@huggingface/blake3-jit/dist/esm/constants.d.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Constants
3
+ *
4
+ * IV values are the same as SHA-256: first 32 bits of the fractional parts
5
+ * of the square roots of the first 8 primes (2..19)
6
+ */
7
+ export declare const IV: Uint32Array<ArrayBuffer>;
8
+ export declare const CHUNK_START = 1;
9
+ export declare const CHUNK_END: number;
10
+ export declare const PARENT: number;
11
+ export declare const ROOT: number;
12
+ export declare const KEYED_HASH: number;
13
+ export declare const DERIVE_KEY_CONTEXT: number;
14
+ export declare const DERIVE_KEY_MATERIAL: number;
15
+ export declare const OUT_LEN = 32;
16
+ export declare const KEY_LEN = 32;
17
+ export declare const BLOCK_LEN = 64;
18
+ export declare const CHUNK_LEN = 1024;
19
+ export declare const MAX_DEPTH = 54;
20
+ /**
21
+ * Precomputed message word permutations for all 7 rounds.
22
+ *
23
+ * The base permutation is: [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8]
24
+ * Each subsequent permutation is the previous one with this permutation applied.
25
+ *
26
+ * These are the indices into the message block for each round.
27
+ * By precomputing these, we avoid runtime permutation overhead.
28
+ */
29
+ export declare const MSG_SCHEDULE: ReadonlyArray<ReadonlyArray<number>>;
30
+ /**
31
+ * Flattened permutation table for compress function optimization.
32
+ * This enables direct indexed access: PERMUTATIONS[round * 16 + index]
33
+ */
34
+ export declare const PERMUTATIONS: Uint8Array<ArrayBuffer>;
node_modules/@huggingface/blake3-jit/dist/esm/constants.js ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Constants
3
+ *
4
+ * IV values are the same as SHA-256: first 32 bits of the fractional parts
5
+ * of the square roots of the first 8 primes (2..19)
6
+ */
7
+ // Initialization Vector (same as SHA-256)
8
+ export const IV = new Uint32Array([
9
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
10
+ ]);
11
+ // Domain separation flags
12
+ export const CHUNK_START = 1;
13
+ export const CHUNK_END = 1 << 1;
14
+ export const PARENT = 1 << 2;
15
+ export const ROOT = 1 << 3;
16
+ export const KEYED_HASH = 1 << 4;
17
+ export const DERIVE_KEY_CONTEXT = 1 << 5;
18
+ export const DERIVE_KEY_MATERIAL = 1 << 6;
19
+ // Size constants
20
+ export const OUT_LEN = 32;
21
+ export const KEY_LEN = 32;
22
+ export const BLOCK_LEN = 64;
23
+ export const CHUNK_LEN = 1024;
24
+ // Maximum depth of the CV stack (supports up to 2^54 bytes input)
25
+ export const MAX_DEPTH = 54;
26
+ /**
27
+ * Precomputed message word permutations for all 7 rounds.
28
+ *
29
+ * The base permutation is: [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8]
30
+ * Each subsequent permutation is the previous one with this permutation applied.
31
+ *
32
+ * These are the indices into the message block for each round.
33
+ * By precomputing these, we avoid runtime permutation overhead.
34
+ */
35
+ export const MSG_SCHEDULE = [
36
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
37
+ [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8],
38
+ [3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1],
39
+ [10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6],
40
+ [12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4],
41
+ [9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7],
42
+ [11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13],
43
+ ];
44
+ /**
45
+ * Flattened permutation table for compress function optimization.
46
+ * This enables direct indexed access: PERMUTATIONS[round * 16 + index]
47
+ */
48
+ export const PERMUTATIONS = new Uint8Array([
49
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9,
50
+ 14, 15, 8, 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1, 10, 7, 12, 9, 14, 3, 13, 15, 4,
51
+ 0, 11, 2, 5, 8, 1, 6, 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4, 9, 14, 11, 5, 8, 12,
52
+ 15, 1, 13, 3, 0, 10, 2, 6, 4, 7, 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13,
53
+ ]);
node_modules/@huggingface/blake3-jit/dist/esm/hash.d.ts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hash Function - Simple one-shot API
3
+ *
4
+ * This provides a simple hash() function optimized for different input sizes.
5
+ * For small inputs, uses pure JS. For large inputs, uses WASM SIMD.
6
+ */
7
+ /**
8
+ * Hash input data and return the result.
9
+ * Automatically uses WASM SIMD for large inputs when available.
10
+ *
11
+ * @param input - Data to hash
12
+ * @param outputLength - Number of bytes to output (default: 32)
13
+ * @returns The hash output
14
+ */
15
+ export declare function hash(input: Uint8Array, outputLength?: number): Uint8Array;
16
+ /**
17
+ * Pre-warm SIMD initialization (call early to avoid latency later).
18
+ */
19
+ export declare function warmupSimd(): boolean;
20
+ /**
21
+ * Hash input data directly into a caller-provided output buffer.
22
+ * Zero-allocation for the common 32-byte case - ideal for performance-critical code.
23
+ *
24
+ * @param input - Data to hash
25
+ * @param output - Pre-allocated output buffer (must be at least outputLength bytes)
26
+ * @param outputLength - Number of bytes to output (default: 32, max: output.length)
27
+ */
28
+ export declare function hashInto(input: Uint8Array, output: Uint8Array, outputLength?: number): void;
node_modules/@huggingface/blake3-jit/dist/esm/hash.js ADDED
@@ -0,0 +1,1033 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hash Function - Simple one-shot API
3
+ *
4
+ * This provides a simple hash() function optimized for different input sizes.
5
+ * For small inputs, uses pure JS. For large inputs, uses WASM SIMD.
6
+ */
7
+ import { compress } from "./compress.js";
8
+ import { IV, CHUNK_START, CHUNK_END, PARENT, ROOT, BLOCK_LEN, CHUNK_LEN, OUT_LEN, } from "./constants.js";
9
+ import { IS_LITTLE_ENDIAN, readLittleEndianWordsFull, readLittleEndianWordsPartial, writeLittleEndianBytesPartial, } from "./utils.js";
10
+ import { initSimdSync, getSimdMemory, getArenaBuffers, runCompress4x, runCompressChunks4x, runCompressParent, SIMD_MEMORY, } from "./wasm-simd.js";
11
+ // Pre-allocated buffers for reuse (single-threaded optimization)
12
+ let blockWords = null;
13
+ // ===== Contiguous Hyper CV Stack (Optimization #6) =====
14
+ // Maximum tree depth for practical inputs (2^64 chunks = essentially unlimited)
15
+ // Fixed allocation at module load - no runtime allocation
16
+ const CV_STACK_DEPTH = 64;
17
+ const HYPER_CV_STACK = new Uint32Array(CV_STACK_DEPTH * 8); // 64 CVs × 8 words = 512 words
18
+ // Pre-computed offsets for the first few stack levels (hot path optimization)
19
+ // Note: These can be used for further optimization if needed
20
+ // const CV_STACK_OFF_0 = 0;
21
+ // const CV_STACK_OFF_1 = 8;
22
+ // const CV_STACK_OFF_2 = 16;
23
+ // const CV_STACK_OFF_3 = 24;
24
+ // ===== Pre-allocated CV Pool with Views (avoids subarray() in hot paths) =====
25
+ const CV_POOL_SIZE = 64;
26
+ const CV_POOL = new Uint32Array(CV_POOL_SIZE * 8); // 64 CVs × 8 words = 512 words
27
+ const CV_VIEWS = [];
28
+ for (let i = 0; i < CV_POOL_SIZE; i++) {
29
+ CV_VIEWS.push(CV_POOL.subarray(i * 8, i * 8 + 8));
30
+ }
31
+ // SIMD initialization state
32
+ let simdAvailable = false;
33
+ // Threshold for switching to SIMD (must be > 1 chunk to benefit from parallelism)
34
+ const SIMD_THRESHOLD = 4 * CHUNK_LEN; // 4KB - need at least 4 chunks for SIMD benefit
35
+ /**
36
+ * Initialize SIMD synchronously (lazy).
37
+ */
38
+ function ensureSimdSync() {
39
+ if (simdAvailable)
40
+ return true;
41
+ simdAvailable = initSimdSync();
42
+ return simdAvailable;
43
+ }
44
+ // Reusable buffer for SIMD chunk CVs (4 chunks × 8 words)
45
+ const simdChunkCvs = new Uint32Array(32);
46
+ // ===== Module-level reusable buffers (single-threaded safe) =====
47
+ // These eliminate heap allocations in hot paths
48
+ // For hashChunkWithWords() and hashChunkRoot()
49
+ const reusableTempCv = new Uint32Array(8);
50
+ // For hashPureJS()
51
+ const reusableChunkCv = new Uint32Array(8);
52
+ const reusablePureParentBlock = new Uint32Array(16);
53
+ const reusablePureParentCv = new Uint32Array(8);
54
+ // For hashSimd() - use flat array for 4 chunk CVs (access via subarray)
55
+ const reusableSimdCvs = new Uint32Array(32); // 4 × 8 words flat
56
+ // For hashSimd() parent compression
57
+ const reusableSimdParentBlock = new Uint32Array(16);
58
+ const reusableSimdParentCv = new Uint32Array(8);
59
+ // For hashSimd() parameters - TypedArrays instead of JS arrays
60
+ const reusableOffsets = new Uint32Array(4);
61
+ const reusableCounters = new Uint32Array(4);
62
+ const reusableBlockLens = new Uint32Array(4);
63
+ const reusableFlags = new Uint32Array(4);
64
+ // Reusable output buffer for common 32-byte hash (eliminates allocations)
65
+ const reusableOut8 = new Uint32Array(8); // Standard 32-byte output
66
+ // Pre-created view to avoid allocation in hot path (Task 1 optimization)
67
+ const reusableOut8View = new Uint8Array(reusableOut8.buffer, 0, 32);
68
+ // ===== Unrolled CV Copy Helper (Task 7 optimization) =====
69
+ // V8 will inline this - avoids loop overhead in hot paths
70
+ function copyCV8(src, srcOff, dst, dstOff) {
71
+ dst[dstOff] = src[srcOff];
72
+ dst[dstOff + 1] = src[srcOff + 1];
73
+ dst[dstOff + 2] = src[srcOff + 2];
74
+ dst[dstOff + 3] = src[srcOff + 3];
75
+ dst[dstOff + 4] = src[srcOff + 4];
76
+ dst[dstOff + 5] = src[srcOff + 5];
77
+ dst[dstOff + 6] = src[srcOff + 6];
78
+ dst[dstOff + 7] = src[srcOff + 7];
79
+ }
80
+ /**
81
+ * Transpose 4 blocks (64 bytes each) into SIMD memory layout.
82
+ * The SIMD compress4x expects: [m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...]
83
+ * where m{i}_{j} is message word i from block j.
84
+ *
85
+ * OPTIMIZED: Processes all 4 blocks together for each word position,
86
+ * writing 4 consecutive u32s at once for better cache locality.
87
+ *
88
+ * @param inputWords - Pre-created Uint32Array view of input (null if unaligned/non-LE).
89
+ * Created once per hash call to avoid allocation in hot loop.
90
+ */
91
+ function transposeBlocksToSimd(input, offsets, // Starting offsets for each of 4 blocks
92
+ blockLens, // Length of each block (0-64 bytes)
93
+ mem32, blockCount, // 1-4 blocks
94
+ inputWords) {
95
+ // Fast path: all blocks are full 64-byte blocks with aligned LE input
96
+ const allFull = blockCount === 4 &&
97
+ blockLens[0] === 64 &&
98
+ blockLens[1] === 64 &&
99
+ blockLens[2] === 64 &&
100
+ blockLens[3] === 64;
101
+ if (allFull &&
102
+ inputWords &&
103
+ offsets[0] % 4 === 0 &&
104
+ offsets[1] % 4 === 0 &&
105
+ offsets[2] % 4 === 0 &&
106
+ offsets[3] % 4 === 0) {
107
+ // Ultra-fast path: process all 4 blocks together, write 4 consecutive u32s per word
108
+ const wordOff0 = offsets[0] >>> 2;
109
+ const wordOff1 = offsets[1] >>> 2;
110
+ const wordOff2 = offsets[2] >>> 2;
111
+ const wordOff3 = offsets[3] >>> 2;
112
+ for (let w = 0; w < 16; w++) {
113
+ const dstBase = w * 4;
114
+ mem32[dstBase] = inputWords[wordOff0 + w];
115
+ mem32[dstBase + 1] = inputWords[wordOff1 + w];
116
+ mem32[dstBase + 2] = inputWords[wordOff2 + w];
117
+ mem32[dstBase + 3] = inputWords[wordOff3 + w];
118
+ }
119
+ return;
120
+ }
121
+ // Standard path: process each block independently (handles partial blocks)
122
+ for (let b = 0; b < blockCount; b++) {
123
+ const len = blockLens[b];
124
+ const off = offsets[b];
125
+ if (len === 64) {
126
+ // Full block
127
+ if (inputWords && off % 4 === 0) {
128
+ // Direct Uint32Array access for aligned LE blocks
129
+ const wordOff = off >>> 2;
130
+ for (let w = 0; w < 16; w++) {
131
+ mem32[w * 4 + b] = inputWords[wordOff + w];
132
+ }
133
+ }
134
+ else {
135
+ // Byte-by-byte reconstruction
136
+ for (let w = 0; w < 16; w++) {
137
+ const srcOff = off + w * 4;
138
+ mem32[w * 4 + b] =
139
+ input[srcOff] |
140
+ (input[srcOff + 1] << 8) |
141
+ (input[srcOff + 2] << 16) |
142
+ (input[srcOff + 3] << 24);
143
+ }
144
+ }
145
+ }
146
+ else if (len === 0) {
147
+ // Zero block
148
+ for (let w = 0; w < 16; w++) {
149
+ mem32[w * 4 + b] = 0;
150
+ }
151
+ }
152
+ else {
153
+ // Partial block - handle word by word
154
+ for (let w = 0; w < 16; w++) {
155
+ const wordOff = w * 4;
156
+ if (wordOff >= len) {
157
+ mem32[w * 4 + b] = 0;
158
+ }
159
+ else if (wordOff + 4 <= len) {
160
+ const srcOff = off + wordOff;
161
+ mem32[w * 4 + b] =
162
+ input[srcOff] |
163
+ (input[srcOff + 1] << 8) |
164
+ (input[srcOff + 2] << 16) |
165
+ (input[srcOff + 3] << 24);
166
+ }
167
+ else {
168
+ // Partial word at end of block
169
+ let word = 0;
170
+ for (let i = 0; i < len - wordOff; i++) {
171
+ word |= input[off + wordOff + i] << (i * 8);
172
+ }
173
+ mem32[w * 4 + b] = word;
174
+ }
175
+ }
176
+ }
177
+ }
178
+ // Zero unused block slots
179
+ for (let b = blockCount; b < 4; b++) {
180
+ for (let w = 0; w < 16; w++) {
181
+ mem32[w * 4 + b] = 0;
182
+ }
183
+ }
184
+ }
185
+ /**
186
+ * Transpose 4 full chunks (4 × 16 blocks = 64 blocks) into batch SIMD memory.
187
+ * This is used for the batched compressChunks4x function that processes
188
+ * all 16 blocks in a single WASM call.
189
+ *
190
+ * Memory layout: BATCH_BLOCK_WORDS has 16 positions, each with 16 v128 values.
191
+ * Position p, word w: mem32[(p * 64) + (w * 4) + lane]
192
+ *
193
+ * OPTIMIZED: Processes all 4 chunks together for each (pos, word) pair,
194
+ * writing 4 consecutive u32s at once for better cache locality.
195
+ *
196
+ * @param input - Input data (must have at least 4 full chunks = 4096 bytes)
197
+ * @param chunkOffsets - Starting offsets for each of 4 chunks
198
+ * @param mem32 - WASM memory view
199
+ * @param inputWords - Pre-created Uint32Array view (null if unaligned)
200
+ */
201
+ function transposeBatchToSimd(input, chunkOffsets, mem32, inputWords) {
202
+ const BATCH_BASE = SIMD_MEMORY.BATCH_BLOCK_WORDS / 4;
203
+ // Get base word offsets for each chunk (pre-computed for fast path)
204
+ const chunk0WordBase = chunkOffsets[0] >>> 2;
205
+ const chunk1WordBase = chunkOffsets[1] >>> 2;
206
+ const chunk2WordBase = chunkOffsets[2] >>> 2;
207
+ const chunk3WordBase = chunkOffsets[3] >>> 2;
208
+ // Fast path: all chunks aligned and LE - process 4 consecutive u32s at once
209
+ if (inputWords && chunkOffsets[0] % 4 === 0) {
210
+ for (let pos = 0; pos < 16; pos++) {
211
+ const posBase = BATCH_BASE + pos * 64; // 16 words × 4 lanes = 64
212
+ const blockWordOff = pos * 16; // 16 words per block (64 bytes / 4)
213
+ // Process all 16 words, writing 4 chunks at a time (cache-friendly: 16 bytes per write group)
214
+ for (let w = 0; w < 16; w++) {
215
+ const dstBase = posBase + w * 4;
216
+ // Read word w from all 4 chunks at positions that become consecutive in output
217
+ mem32[dstBase] = inputWords[chunk0WordBase + blockWordOff + w];
218
+ mem32[dstBase + 1] = inputWords[chunk1WordBase + blockWordOff + w];
219
+ mem32[dstBase + 2] = inputWords[chunk2WordBase + blockWordOff + w];
220
+ mem32[dstBase + 3] = inputWords[chunk3WordBase + blockWordOff + w];
221
+ }
222
+ }
223
+ }
224
+ else {
225
+ // Slow path: byte-by-byte reconstruction, still cache-friendly write pattern
226
+ for (let pos = 0; pos < 16; pos++) {
227
+ const posBase = BATCH_BASE + pos * 64;
228
+ const blockByteOff = pos * 64; // 64 bytes per block
229
+ for (let w = 0; w < 16; w++) {
230
+ const dstBase = posBase + w * 4;
231
+ const wordByteOff = w * 4;
232
+ // Chunk 0
233
+ const off0 = chunkOffsets[0] + blockByteOff + wordByteOff;
234
+ mem32[dstBase] =
235
+ input[off0] | (input[off0 + 1] << 8) | (input[off0 + 2] << 16) | (input[off0 + 3] << 24);
236
+ // Chunk 1
237
+ const off1 = chunkOffsets[1] + blockByteOff + wordByteOff;
238
+ mem32[dstBase + 1] =
239
+ input[off1] | (input[off1 + 1] << 8) | (input[off1 + 2] << 16) | (input[off1 + 3] << 24);
240
+ // Chunk 2
241
+ const off2 = chunkOffsets[2] + blockByteOff + wordByteOff;
242
+ mem32[dstBase + 2] =
243
+ input[off2] | (input[off2 + 1] << 8) | (input[off2 + 2] << 16) | (input[off2 + 3] << 24);
244
+ // Chunk 3
245
+ const off3 = chunkOffsets[3] + blockByteOff + wordByteOff;
246
+ mem32[dstBase + 3] =
247
+ input[off3] | (input[off3 + 1] << 8) | (input[off3 + 2] << 16) | (input[off3 + 3] << 24);
248
+ }
249
+ }
250
+ }
251
+ }
252
+ // Pre-computed memory offsets for SIMD operations (single-block mode)
253
+ const SIMD_CV_BASE = SIMD_MEMORY.CHAINING_VALUES / 4;
254
+ const SIMD_OUT_BASE = SIMD_MEMORY.OUTPUT / 4;
255
+ const SIMD_COUNTER_LOW_BASE = SIMD_MEMORY.COUNTER_LOW / 4;
256
+ const SIMD_COUNTER_HIGH_BASE = SIMD_MEMORY.COUNTER_HIGH / 4;
257
+ const SIMD_BLOCK_LEN_BASE = SIMD_MEMORY.BLOCK_LEN / 4;
258
+ // Pre-computed memory offsets for batch SIMD operations (16-block mode)
259
+ const BATCH_CV_BASE = SIMD_MEMORY.BATCH_CV / 4;
260
+ const BATCH_COUNTER_LOW_BASE = SIMD_MEMORY.BATCH_COUNTER_LOW / 4;
261
+ const BATCH_FLAGS_BASE_OFFSET = SIMD_MEMORY.BATCH_FLAGS_BASE / 4;
262
+ const BATCH_OUTPUT_BASE = SIMD_MEMORY.BATCH_OUTPUT / 4;
263
+ // Reusable arrays for batch processing
264
+ const batchChunkOffsets = new Uint32Array(4);
265
+ const SIMD_FLAGS_BASE = SIMD_MEMORY.FLAGS / 4;
266
+ /**
267
+ * Set up chaining values in SIMD memory (transposed layout).
268
+ * Optimized: unrolled loops for common case of 4 chunks.
269
+ * cvs is flat: [cv0_word0..cv0_word7, cv1_word0..cv1_word7, ...]
270
+ */
271
+ function setupSimdCvs(cvs, // Flat array: 4 × 8 words
272
+ mem32, count) {
273
+ // Unrolled for 4 chunks (common case)
274
+ if (count === 4) {
275
+ for (let w = 0; w < 8; w++) {
276
+ const base = SIMD_CV_BASE + w * 4;
277
+ mem32[base] = cvs[w]; // cv0[w]
278
+ mem32[base + 1] = cvs[8 + w]; // cv1[w]
279
+ mem32[base + 2] = cvs[16 + w]; // cv2[w]
280
+ mem32[base + 3] = cvs[24 + w]; // cv3[w]
281
+ }
282
+ }
283
+ else {
284
+ for (let w = 0; w < 8; w++) {
285
+ const base = SIMD_CV_BASE + w * 4;
286
+ for (let c = 0; c < count; c++) {
287
+ mem32[base + c] = cvs[c * 8 + w];
288
+ }
289
+ for (let c = count; c < 4; c++) {
290
+ mem32[base + c] = 0;
291
+ }
292
+ }
293
+ }
294
+ }
295
+ /**
296
+ * Set up SIMD parameters (counters, flags, block lengths).
297
+ */
298
+ function setupSimdParams(mem32, counters, blockLens, flagsArr, count) {
299
+ // Most chunk counters fit in 32 bits, so counter high is usually 0
300
+ for (let i = 0; i < count; i++) {
301
+ mem32[SIMD_COUNTER_LOW_BASE + i] = counters[i];
302
+ mem32[SIMD_COUNTER_HIGH_BASE + i] = 0; // Assume counters fit in 32 bits
303
+ mem32[SIMD_BLOCK_LEN_BASE + i] = blockLens[i];
304
+ mem32[SIMD_FLAGS_BASE + i] = flagsArr[i];
305
+ }
306
+ // Zero unused slots
307
+ for (let i = count; i < 4; i++) {
308
+ mem32[SIMD_COUNTER_LOW_BASE + i] = 0;
309
+ mem32[SIMD_COUNTER_HIGH_BASE + i] = 0;
310
+ mem32[SIMD_BLOCK_LEN_BASE + i] = 0;
311
+ mem32[SIMD_FLAGS_BASE + i] = 0;
312
+ }
313
+ }
314
+ /**
315
+ * Read output CVs from SIMD memory (untranspose).
316
+ */
317
+ function readSimdOutputCvs(mem32, outputCvs, // Flat array: 4 × 8 words
318
+ count) {
319
+ // Unrolled for 4 chunks (common case)
320
+ if (count === 4) {
321
+ for (let w = 0; w < 8; w++) {
322
+ const base = SIMD_OUT_BASE + w * 4;
323
+ outputCvs[w] = mem32[base];
324
+ outputCvs[8 + w] = mem32[base + 1];
325
+ outputCvs[16 + w] = mem32[base + 2];
326
+ outputCvs[24 + w] = mem32[base + 3];
327
+ }
328
+ }
329
+ else {
330
+ for (let w = 0; w < 8; w++) {
331
+ const base = SIMD_OUT_BASE + w * 4;
332
+ for (let c = 0; c < count; c++) {
333
+ outputCvs[c * 8 + w] = mem32[base + c];
334
+ }
335
+ }
336
+ }
337
+ }
338
+ function getBlockWords() {
339
+ if (!blockWords) {
340
+ blockWords = new Uint32Array(16);
341
+ }
342
+ return blockWords;
343
+ }
344
+ /**
345
+ * Hash a single chunk (up to 1024 bytes) with pre-created inputWords view.
346
+ * This is the optimized version that avoids creating Uint32Array views per chunk.
347
+ * (Fleek optimization Step 8)
348
+ */
349
+ function hashChunkWithWords(input, inputWords, // Pre-created view of entire input
350
+ inputOffset, inputLen, chunkCounter, flags, cv, cvOffset) {
351
+ // Use reusable temporary CV for intermediate blocks (single-threaded safe)
352
+ reusableTempCv.set(IV);
353
+ // Process full blocks
354
+ const fullBlocks = inputLen >>> 6; // inputLen / 64
355
+ const remainder = inputLen & 63; // inputLen % 64
356
+ // Calculate word offset for this chunk within the pre-created view
357
+ const chunkWordOffset = inputOffset >>> 2;
358
+ // Fast path for full chunks with aligned little-endian input
359
+ if (inputWords && remainder === 0 && inputLen === CHUNK_LEN) {
360
+ // All 16 blocks are full, use fast path exclusively
361
+ let wordOff = chunkWordOffset;
362
+ // Block 0 (CHUNK_START)
363
+ compress(reusableTempCv, 0, inputWords, wordOff, reusableTempCv, 0, false, chunkCounter, BLOCK_LEN, flags | CHUNK_START);
364
+ wordOff += 16;
365
+ // Blocks 1-14 (no special flags)
366
+ for (let i = 1; i < 15; i++) {
367
+ compress(reusableTempCv, 0, inputWords, wordOff, reusableTempCv, 0, false, chunkCounter, BLOCK_LEN, flags);
368
+ wordOff += 16;
369
+ }
370
+ // Block 15 (CHUNK_END)
371
+ compress(reusableTempCv, 0, inputWords, wordOff, reusableTempCv, 0, false, chunkCounter, BLOCK_LEN, flags | CHUNK_END);
372
+ cv.set(reusableTempCv, cvOffset);
373
+ return;
374
+ }
375
+ // Slower path for partial chunks or non-aligned input
376
+ const totalBlocks = fullBlocks + (remainder > 0 ? 1 : 0);
377
+ const block = getBlockWords();
378
+ for (let blockIdx = 0; blockIdx < totalBlocks; blockIdx++) {
379
+ const isFirst = blockIdx === 0;
380
+ const isLast = blockIdx === totalBlocks - 1;
381
+ const blockStart = blockIdx << 6;
382
+ const blockLen = isLast && remainder > 0 ? remainder : BLOCK_LEN;
383
+ // Determine flags for this block
384
+ let blockFlags = flags;
385
+ if (isFirst)
386
+ blockFlags |= CHUNK_START;
387
+ if (isLast)
388
+ blockFlags |= CHUNK_END;
389
+ // Load block words
390
+ if (isLast && remainder > 0) {
391
+ // Partial final block - need zero padding
392
+ readLittleEndianWordsPartial(input, inputOffset + blockStart, blockLen, block);
393
+ }
394
+ else if (inputWords && chunkWordOffset + (blockStart >>> 2) + 16 <= inputWords.length) {
395
+ // Fast path: use pre-created view directly
396
+ compress(reusableTempCv, 0, inputWords, chunkWordOffset + (blockStart >>> 2), reusableTempCv, 0, false, chunkCounter, blockLen, blockFlags);
397
+ continue;
398
+ }
399
+ else {
400
+ readLittleEndianWordsFull(input, inputOffset + blockStart, block);
401
+ }
402
+ compress(reusableTempCv, 0, block, 0, reusableTempCv, 0, false, chunkCounter, blockLen, blockFlags);
403
+ }
404
+ // Copy result to output
405
+ cv.set(reusableTempCv, cvOffset);
406
+ }
407
+ /**
408
+ * Hash input using pure JavaScript.
409
+ * Handles the full Merkle tree construction.
410
+ */
411
+ function hashPureJS(input, outputLen) {
412
+ const inputLen = input.length;
413
+ // Special case: empty input
414
+ if (inputLen === 0) {
415
+ const block = getBlockWords();
416
+ block.fill(0);
417
+ // Use reusable output buffer for common 32-byte case
418
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
419
+ compress(IV, 0, block, 0, out, 0, outputLen > 32, 0, 0, CHUNK_START | CHUNK_END | ROOT);
420
+ // Return result - use pre-created view for common 32-byte case
421
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
422
+ return reusableOut8View.slice();
423
+ }
424
+ const result = new Uint8Array(outputLen);
425
+ if (IS_LITTLE_ENDIAN) {
426
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
427
+ }
428
+ else {
429
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
430
+ }
431
+ return result;
432
+ }
433
+ // Calculate number of chunks
434
+ const numChunks = Math.ceil(inputLen / CHUNK_LEN);
435
+ // Single chunk optimization
436
+ if (numChunks === 1) {
437
+ // Use reusable output buffer for common 32-byte case
438
+ const cv = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
439
+ hashChunkRoot(input, 0, inputLen, 0, 0, cv, outputLen > 32);
440
+ // Return result - use pre-created view for common 32-byte case
441
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
442
+ return reusableOut8View.slice();
443
+ }
444
+ const result = new Uint8Array(outputLen);
445
+ if (IS_LITTLE_ENDIAN) {
446
+ result.set(new Uint8Array(cv.buffer, 0, outputLen));
447
+ }
448
+ else {
449
+ writeLittleEndianBytesPartial(cv, 0, result, 0, outputLen);
450
+ }
451
+ return result;
452
+ }
453
+ // Multiple chunks - need Merkle tree
454
+ // Use the global contiguous CV stack (no allocation)
455
+ const stack = HYPER_CV_STACK;
456
+ let stackLen = 0;
457
+ // Use reusable buffers (single-threaded safe)
458
+ const chunkCv = reusableChunkCv;
459
+ const parentBlock = reusablePureParentBlock;
460
+ const parentCv = reusablePureParentCv;
461
+ // Create Uint32Array view ONCE for entire input (Fleek optimization Step 8)
462
+ // This avoids creating views inside each chunk/block processing
463
+ let inputWords = null;
464
+ const canUseFastPath = IS_LITTLE_ENDIAN && input.byteOffset % 4 === 0;
465
+ if (canUseFastPath) {
466
+ inputWords = new Uint32Array(input.buffer, input.byteOffset, inputLen >>> 2);
467
+ }
468
+ // Determine how many full chunks we have
469
+ const fullChunks = inputLen >>> 10; // inputLen / 1024
470
+ const lastChunkLen = inputLen & 1023; // inputLen % 1024
471
+ // Process all full chunks with fast path (inlined for performance)
472
+ if (canUseFastPath && inputWords) {
473
+ for (let chunkIdx = 0; chunkIdx < fullChunks; chunkIdx++) {
474
+ // Inline chunk processing for full chunks
475
+ chunkCv.set(IV);
476
+ let wordOff = chunkIdx << 8; // chunkIdx * 256 (CHUNK_LEN/4)
477
+ // Block 0 (CHUNK_START)
478
+ compress(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, BLOCK_LEN, CHUNK_START);
479
+ wordOff += 16;
480
+ // Blocks 1-14 (no special flags)
481
+ for (let b = 1; b < 15; b++) {
482
+ compress(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, BLOCK_LEN, 0);
483
+ wordOff += 16;
484
+ }
485
+ // Block 15 (CHUNK_END)
486
+ compress(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, BLOCK_LEN, CHUNK_END);
487
+ // Merge completed subtrees (avoid subarray by using index math)
488
+ let totalChunks = chunkIdx + 1;
489
+ let cvSrcOff = 0;
490
+ let cvSrc = chunkCv;
491
+ // Check if this is the last chunk overall
492
+ const isLastChunk = chunkIdx === fullChunks - 1 && lastChunkLen === 0;
493
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
494
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
495
+ if (stackLen === 1 && isLastChunk) {
496
+ break;
497
+ }
498
+ stackLen--;
499
+ const stackOff = stackLen * 8;
500
+ // Copy left CV from stack to parentBlock[0..7] (unrolled)
501
+ copyCV8(stack, stackOff, parentBlock, 0);
502
+ // Copy current CV to parentBlock[8..15] (unrolled)
503
+ copyCV8(cvSrc, cvSrcOff, parentBlock, 8);
504
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
505
+ cvSrc = parentCv;
506
+ cvSrcOff = 0;
507
+ totalChunks >>>= 1;
508
+ }
509
+ // Push CV to stack (unrolled)
510
+ const stackOff = stackLen * 8;
511
+ copyCV8(cvSrc, cvSrcOff, stack, stackOff);
512
+ stackLen++;
513
+ }
514
+ // Process last partial chunk if any
515
+ if (lastChunkLen > 0) {
516
+ hashChunkWithWords(input, inputWords, fullChunks * CHUNK_LEN, lastChunkLen, fullChunks, 0, chunkCv, 0);
517
+ let totalChunks = fullChunks + 1;
518
+ let newCv = chunkCv;
519
+ let newCvOffset = 0;
520
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
521
+ // Skip final merge; this IS the last chunk, let finalization handle ROOT flag
522
+ if (stackLen === 1) {
523
+ break;
524
+ }
525
+ stackLen--;
526
+ const stackOff = stackLen * 8;
527
+ // Copy from stack to parentBlock[0..7] (unrolled)
528
+ copyCV8(stack, stackOff, parentBlock, 0);
529
+ // Copy from newCv to parentBlock[8..15] (unrolled)
530
+ copyCV8(newCv, newCvOffset, parentBlock, 8);
531
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
532
+ newCv = parentCv;
533
+ newCvOffset = 0;
534
+ totalChunks >>>= 1;
535
+ }
536
+ // Push CV to stack (unrolled)
537
+ const pushOff = stackLen * 8;
538
+ copyCV8(newCv, newCvOffset, stack, pushOff);
539
+ stackLen++;
540
+ }
541
+ }
542
+ else {
543
+ // Slow path for unaligned or big-endian
544
+ for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
545
+ const chunkStart = chunkIdx * CHUNK_LEN;
546
+ const chunkLen = Math.min(CHUNK_LEN, inputLen - chunkStart);
547
+ hashChunkWithWords(input, inputWords, chunkStart, chunkLen, chunkIdx, 0, chunkCv, 0);
548
+ // Merge completed subtrees
549
+ let totalChunks = chunkIdx + 1;
550
+ let newCv = chunkCv;
551
+ let newCvOffset = 0;
552
+ // Check if this is the last chunk
553
+ const isLastChunk = chunkIdx === numChunks - 1;
554
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
555
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
556
+ if (stackLen === 1 && isLastChunk) {
557
+ break;
558
+ }
559
+ stackLen--;
560
+ const stackOff = stackLen * 8;
561
+ // Copy from stack to parentBlock[0..7] (unrolled)
562
+ copyCV8(stack, stackOff, parentBlock, 0);
563
+ // Copy from newCv to parentBlock[8..15] (unrolled)
564
+ copyCV8(newCv, newCvOffset, parentBlock, 8);
565
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
566
+ newCv = parentCv;
567
+ newCvOffset = 0;
568
+ totalChunks >>>= 1;
569
+ }
570
+ // Push CV to stack (unrolled)
571
+ const pushOff = stackLen * 8;
572
+ copyCV8(newCv, newCvOffset, stack, pushOff);
573
+ stackLen++;
574
+ }
575
+ }
576
+ // Finalize: merge remaining stack entries
577
+ while (stackLen > 1) {
578
+ stackLen--;
579
+ const rightOff = stackLen * 8;
580
+ stackLen--;
581
+ const leftOff = stackLen * 8;
582
+ // Copy left CV to parentBlock[0..7] and right CV to parentBlock[8..15] (unrolled)
583
+ copyCV8(stack, leftOff, parentBlock, 0);
584
+ copyCV8(stack, rightOff, parentBlock, 8);
585
+ if (stackLen === 0) {
586
+ // This is the root - use reusable output buffer for common 32-byte case
587
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
588
+ compress(IV, 0, parentBlock, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, PARENT | ROOT);
589
+ // Return result - use pre-created view for common 32-byte case
590
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
591
+ return reusableOut8View.slice();
592
+ }
593
+ const result = new Uint8Array(outputLen);
594
+ if (IS_LITTLE_ENDIAN) {
595
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
596
+ }
597
+ else {
598
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
599
+ }
600
+ return result;
601
+ }
602
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
603
+ // Push to stack (unrolled)
604
+ copyCV8(parentCv, 0, stack, stackLen * 8);
605
+ stackLen++;
606
+ }
607
+ // Single entry in stack - this is the root
608
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
609
+ const lastBlock = getBlockWords();
610
+ lastBlock.fill(0);
611
+ // Copy first 8 words from stack (unrolled)
612
+ copyCV8(stack, 0, lastBlock, 0);
613
+ compress(IV, 0, lastBlock, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, ROOT);
614
+ // Return result - use pre-created view for common 32-byte case
615
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
616
+ return reusableOut8View.slice();
617
+ }
618
+ const result = new Uint8Array(outputLen);
619
+ if (IS_LITTLE_ENDIAN) {
620
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
621
+ }
622
+ else {
623
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
624
+ }
625
+ return result;
626
+ }
627
+ /**
628
+ * Hash a single chunk that is also the root (single chunk input).
629
+ */
630
+ function hashChunkRoot(input, inputOffset, inputLen, chunkCounter, flags, out, fullOutput) {
631
+ // Use reusable tempCv (single-threaded safe)
632
+ reusableTempCv.set(IV);
633
+ const block = getBlockWords();
634
+ // Process full blocks
635
+ const fullBlocks = inputLen >>> 6;
636
+ const remainder = inputLen & 63;
637
+ const totalBlocks = fullBlocks + (remainder > 0 ? 1 : 0) || 1; // At least 1 block
638
+ // Create a Uint32Array view if possible
639
+ let inputWords = null;
640
+ if (IS_LITTLE_ENDIAN && (input.byteOffset + inputOffset) % 4 === 0 && inputLen >= 4) {
641
+ inputWords = new Uint32Array(input.buffer, input.byteOffset + inputOffset, inputLen >>> 2);
642
+ }
643
+ for (let blockIdx = 0; blockIdx < totalBlocks; blockIdx++) {
644
+ const isFirst = blockIdx === 0;
645
+ const isLast = blockIdx === totalBlocks - 1;
646
+ const blockStart = blockIdx << 6;
647
+ const blockLen = isLast ? remainder || (inputLen > 0 ? BLOCK_LEN : 0) : BLOCK_LEN;
648
+ // Determine flags
649
+ let blockFlags = flags;
650
+ if (isFirst)
651
+ blockFlags |= CHUNK_START;
652
+ if (isLast)
653
+ blockFlags |= CHUNK_END | ROOT;
654
+ // Load block
655
+ if (isLast && remainder > 0) {
656
+ readLittleEndianWordsPartial(input, inputOffset + blockStart, blockLen, block);
657
+ }
658
+ else if (inputLen === 0) {
659
+ block.fill(0);
660
+ }
661
+ else if (inputWords && (blockStart >>> 2) + 16 <= inputWords.length) {
662
+ // Fast path
663
+ compress(reusableTempCv, 0, inputWords, blockStart >>> 2, isLast ? out : reusableTempCv, 0, isLast && fullOutput, chunkCounter, blockLen, blockFlags);
664
+ continue;
665
+ }
666
+ else {
667
+ readLittleEndianWordsFull(input, inputOffset + blockStart, block);
668
+ }
669
+ compress(reusableTempCv, 0, block, 0, isLast ? out : reusableTempCv, 0, isLast && fullOutput, chunkCounter, blockLen, blockFlags);
670
+ }
671
+ }
672
+ /**
673
+ * Hash using WASM SIMD - processes 4 chunks in parallel.
674
+ * Falls back to pure JS if SIMD fails.
675
+ */
676
+ function hashSimd(input, outputLen) {
677
+ const mem = getSimdMemory();
678
+ if (!mem) {
679
+ return hashPureJS(input, outputLen);
680
+ }
681
+ const { view32 } = mem;
682
+ const inputLen = input.length;
683
+ const numChunks = Math.ceil(inputLen / CHUNK_LEN);
684
+ // For small inputs, pure JS is faster (no transpose overhead)
685
+ if (numChunks < 4) {
686
+ return hashPureJS(input, outputLen);
687
+ }
688
+ // Try to use WASM arena buffers (zero JS heap allocation)
689
+ // Falls back to JS buffers if arena not available
690
+ const arena = getArenaBuffers();
691
+ const useWasmParent = arena !== null; // Use WASM parent compress when arena available
692
+ let stack;
693
+ let tempCvs;
694
+ let parentBlock;
695
+ let parentCv;
696
+ if (arena) {
697
+ // Use WASM-backed arena buffers
698
+ stack = arena.cvStack;
699
+ tempCvs = arena.tempCvs;
700
+ parentBlock = arena.parentBlock;
701
+ parentCv = arena.chunkCv;
702
+ }
703
+ else {
704
+ // Fallback to JS heap buffers - use global contiguous stack (no allocation)
705
+ stack = HYPER_CV_STACK;
706
+ tempCvs = reusableSimdCvs;
707
+ parentBlock = reusableSimdParentBlock;
708
+ parentCv = reusableSimdParentCv;
709
+ }
710
+ let stackLen = 0;
711
+ // Use TypedArrays instead of JS arrays for block parameters
712
+ const offsets = reusableOffsets;
713
+ const counters = reusableCounters;
714
+ const blockLens = reusableBlockLens;
715
+ const flagsArr = reusableFlags;
716
+ // Create Uint32Array view once for entire hash call (optimization: avoid allocation in hot loop)
717
+ const inputWords = IS_LITTLE_ENDIAN && input.byteOffset % 4 === 0
718
+ ? new Uint32Array(input.buffer, input.byteOffset, input.byteLength >>> 2)
719
+ : null;
720
+ // Calculate number of full chunks (1024 bytes each)
721
+ const numFullChunks = inputLen >>> 10; // inputLen / 1024
722
+ // Process chunks in groups of 4
723
+ let chunkIdx = 0;
724
+ while (chunkIdx < numChunks) {
725
+ const groupSize = Math.min(4, numChunks - chunkIdx);
726
+ // === BATCH FAST PATH: 4 full chunks ===
727
+ // Use compressChunks4x for groups of exactly 4 full chunks
728
+ // This reduces 16 WASM calls to 1 per group
729
+ const canUseBatchPath = groupSize === 4 && chunkIdx + 4 <= numFullChunks;
730
+ if (canUseBatchPath) {
731
+ // Set up chunk offsets for batch transpose
732
+ batchChunkOffsets[0] = chunkIdx * CHUNK_LEN;
733
+ batchChunkOffsets[1] = (chunkIdx + 1) * CHUNK_LEN;
734
+ batchChunkOffsets[2] = (chunkIdx + 2) * CHUNK_LEN;
735
+ batchChunkOffsets[3] = (chunkIdx + 3) * CHUNK_LEN;
736
+ // Transpose all 64 blocks (4 chunks × 16 blocks) at once
737
+ transposeBatchToSimd(input, batchChunkOffsets, view32, inputWords);
738
+ // Set up initial CVs (IV) in batch memory - transposed layout
739
+ for (let w = 0; w < 8; w++) {
740
+ const ivWord = IV[w];
741
+ const base = BATCH_CV_BASE + w * 4;
742
+ view32[base] = ivWord;
743
+ view32[base + 1] = ivWord;
744
+ view32[base + 2] = ivWord;
745
+ view32[base + 3] = ivWord;
746
+ }
747
+ // Set up counters in batch memory
748
+ view32[BATCH_COUNTER_LOW_BASE] = chunkIdx;
749
+ view32[BATCH_COUNTER_LOW_BASE + 1] = chunkIdx + 1;
750
+ view32[BATCH_COUNTER_LOW_BASE + 2] = chunkIdx + 2;
751
+ view32[BATCH_COUNTER_LOW_BASE + 3] = chunkIdx + 3;
752
+ // Set up base flags (0 - no keyed hashing)
753
+ view32[BATCH_FLAGS_BASE_OFFSET] = 0;
754
+ view32[BATCH_FLAGS_BASE_OFFSET + 1] = 0;
755
+ view32[BATCH_FLAGS_BASE_OFFSET + 2] = 0;
756
+ view32[BATCH_FLAGS_BASE_OFFSET + 3] = 0;
757
+ // Run batched compress (16 blocks × 4 chunks in one call!)
758
+ runCompressChunks4x();
759
+ // Read output CVs from batch output - untranspose to tempCvs
760
+ for (let w = 0; w < 8; w++) {
761
+ const base = BATCH_OUTPUT_BASE + w * 4;
762
+ tempCvs[w] = view32[base]; // chunk 0
763
+ tempCvs[8 + w] = view32[base + 1]; // chunk 1
764
+ tempCvs[16 + w] = view32[base + 2]; // chunk 2
765
+ tempCvs[24 + w] = view32[base + 3]; // chunk 3
766
+ }
767
+ }
768
+ else {
769
+ // === STANDARD PATH: block-by-block processing ===
770
+ // Used for partial chunks or groups < 4
771
+ // Initialize CVs for this group to IV (flat array: 4 × 8 words)
772
+ for (let g = 0; g < groupSize; g++) {
773
+ const base = g * 8;
774
+ tempCvs[base] = IV[0];
775
+ tempCvs[base + 1] = IV[1];
776
+ tempCvs[base + 2] = IV[2];
777
+ tempCvs[base + 3] = IV[3];
778
+ tempCvs[base + 4] = IV[4];
779
+ tempCvs[base + 5] = IV[5];
780
+ tempCvs[base + 6] = IV[6];
781
+ tempCvs[base + 7] = IV[7];
782
+ }
783
+ // Process all 16 blocks of each chunk in this group
784
+ for (let blockIdx = 0; blockIdx < 16; blockIdx++) {
785
+ // Calculate block offsets and parameters (reuse arrays)
786
+ for (let g = 0; g < groupSize; g++) {
787
+ const thisChunkIdx = chunkIdx + g;
788
+ const chunkStart = thisChunkIdx * CHUNK_LEN;
789
+ const chunkLen = Math.min(CHUNK_LEN, inputLen - chunkStart);
790
+ const thisBlockStart = chunkStart + blockIdx * BLOCK_LEN;
791
+ // Determine block length for this specific block
792
+ const blockStartInChunk = blockIdx * BLOCK_LEN;
793
+ let thisBlockLen = BLOCK_LEN;
794
+ if (blockStartInChunk >= chunkLen) {
795
+ thisBlockLen = 0;
796
+ }
797
+ else if (blockStartInChunk + BLOCK_LEN > chunkLen) {
798
+ thisBlockLen = chunkLen - blockStartInChunk;
799
+ }
800
+ offsets[g] = thisBlockStart;
801
+ counters[g] = thisChunkIdx;
802
+ // Determine flags
803
+ let flags = 0;
804
+ if (blockIdx === 0)
805
+ flags |= CHUNK_START;
806
+ const totalBlocksInChunk = Math.ceil(chunkLen / BLOCK_LEN) || 1;
807
+ if (blockIdx === totalBlocksInChunk - 1)
808
+ flags |= CHUNK_END;
809
+ blockLens[g] = thisBlockLen;
810
+ flagsArr[g] = flags;
811
+ }
812
+ // Check if any blocks need processing
813
+ if (blockLens[0] === 0 && blockLens[1] === 0 && blockLens[2] === 0 && blockLens[3] === 0)
814
+ continue;
815
+ // Transpose blocks into SIMD memory (pass pre-created view to avoid allocation)
816
+ transposeBlocksToSimd(input, offsets, blockLens, view32, groupSize, inputWords);
817
+ // Set up CVs in SIMD memory
818
+ setupSimdCvs(tempCvs, view32, groupSize);
819
+ // Set up parameters
820
+ setupSimdParams(view32, counters, blockLens, flagsArr, groupSize);
821
+ // Run SIMD compress
822
+ runCompress4x();
823
+ // Read output CVs back
824
+ readSimdOutputCvs(view32, simdChunkCvs, groupSize);
825
+ // Update tempCvs - copy from simdChunkCvs (both are flat 32-word arrays)
826
+ // simdChunkCvs layout matches tempCvs: [cv0_w0..cv0_w7, cv1_w0..cv1_w7, ...]
827
+ // IMPORTANT: Only update CVs for chunks that had data in this block!
828
+ // Skipping this check would corrupt CVs for partial chunks after their final block.
829
+ for (let g = 0; g < groupSize; g++) {
830
+ if (blockLens[g] === 0)
831
+ continue; // Don't update CV for chunks with no data in this block
832
+ const base = g * 8;
833
+ tempCvs[base] = simdChunkCvs[base];
834
+ tempCvs[base + 1] = simdChunkCvs[base + 1];
835
+ tempCvs[base + 2] = simdChunkCvs[base + 2];
836
+ tempCvs[base + 3] = simdChunkCvs[base + 3];
837
+ tempCvs[base + 4] = simdChunkCvs[base + 4];
838
+ tempCvs[base + 5] = simdChunkCvs[base + 5];
839
+ tempCvs[base + 6] = simdChunkCvs[base + 6];
840
+ tempCvs[base + 7] = simdChunkCvs[base + 7];
841
+ }
842
+ }
843
+ }
844
+ // Merge each chunk's CV into the Merkle tree
845
+ for (let g = 0; g < groupSize; g++) {
846
+ const thisChunkIdx = chunkIdx + g;
847
+ // Merge completed subtrees
848
+ let totalChunks = thisChunkIdx + 1;
849
+ // Track newCv source - either from tempCvs or parentCv
850
+ let newCvBase = g * 8; // Offset into tempCvs
851
+ let newCvSrc = tempCvs;
852
+ // Check if this is the last chunk
853
+ const isLastChunk = thisChunkIdx === numChunks - 1;
854
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
855
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
856
+ if (stackLen === 1 && isLastChunk) {
857
+ break;
858
+ }
859
+ // Pop left child
860
+ stackLen--;
861
+ const stackOff = stackLen * 8;
862
+ // Copy from stack to parentBlock[0..7] (unrolled)
863
+ copyCV8(stack, stackOff, parentBlock, 0);
864
+ // Copy from newCv source to parentBlock[8..15] (unrolled)
865
+ copyCV8(newCvSrc, newCvBase, parentBlock, 8);
866
+ if (useWasmParent) {
867
+ // WASM parent compress - data already in arena buffers
868
+ runCompressParent();
869
+ }
870
+ else {
871
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
872
+ }
873
+ newCvSrc = parentCv;
874
+ newCvBase = 0;
875
+ totalChunks >>>= 1;
876
+ }
877
+ // Push to stack (unrolled)
878
+ const pushOff = stackLen * 8;
879
+ copyCV8(newCvSrc, newCvBase, stack, pushOff);
880
+ stackLen++;
881
+ }
882
+ chunkIdx += groupSize;
883
+ }
884
+ // Finalize: merge remaining stack entries
885
+ while (stackLen > 1) {
886
+ stackLen--;
887
+ const rightOff = stackLen * 8;
888
+ stackLen--;
889
+ const leftOff = stackLen * 8;
890
+ // Copy left CV to parentBlock[0..7] and right CV to parentBlock[8..15] (unrolled)
891
+ copyCV8(stack, leftOff, parentBlock, 0);
892
+ copyCV8(stack, rightOff, parentBlock, 8);
893
+ if (stackLen === 0) {
894
+ // This is the root - use reusable output buffer
895
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
896
+ compress(IV, 0, parentBlock, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, PARENT | ROOT);
897
+ // Return result - use pre-created view for common 32-byte case
898
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
899
+ return reusableOut8View.slice();
900
+ }
901
+ const result = new Uint8Array(outputLen);
902
+ if (IS_LITTLE_ENDIAN) {
903
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
904
+ }
905
+ else {
906
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
907
+ }
908
+ return result;
909
+ }
910
+ if (useWasmParent) {
911
+ // WASM parent compress - data already in arena buffers
912
+ runCompressParent();
913
+ }
914
+ else {
915
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
916
+ }
917
+ // Push to stack (unrolled)
918
+ copyCV8(parentCv, 0, stack, stackLen * 8);
919
+ stackLen++;
920
+ }
921
+ // Single entry in stack - finalize as root
922
+ if (stackLen === 1) {
923
+ const block = getBlockWords();
924
+ block.fill(0);
925
+ // Copy first 8 words from stack (unrolled)
926
+ copyCV8(stack, 0, block, 0);
927
+ // Use reusable output buffer
928
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
929
+ compress(IV, 0, block, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, ROOT);
930
+ // Return result - use pre-created view for common 32-byte case
931
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
932
+ return reusableOut8View.slice();
933
+ }
934
+ const result = new Uint8Array(outputLen);
935
+ if (IS_LITTLE_ENDIAN) {
936
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
937
+ }
938
+ else {
939
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
940
+ }
941
+ return result;
942
+ }
943
+ // Should not reach here
944
+ return hashPureJS(input, outputLen);
945
+ }
946
+ /**
947
+ * Hash input data and return the result.
948
+ * Automatically uses WASM SIMD for large inputs when available.
949
+ *
950
+ * @param input - Data to hash
951
+ * @param outputLength - Number of bytes to output (default: 32)
952
+ * @returns The hash output
953
+ */
954
+ export function hash(input, outputLength = OUT_LEN) {
955
+ // For large inputs, use SIMD for ~1.5x performance improvement
956
+ if (input.length >= SIMD_THRESHOLD && ensureSimdSync()) {
957
+ return hashSimd(input, outputLength);
958
+ }
959
+ return hashPureJS(input, outputLength);
960
+ }
961
+ /**
962
+ * Pre-warm SIMD initialization (call early to avoid latency later).
963
+ */
964
+ export function warmupSimd() {
965
+ return ensureSimdSync();
966
+ }
967
+ /**
968
+ * Hash input data directly into a caller-provided output buffer.
969
+ * Zero-allocation for the common 32-byte case - ideal for performance-critical code.
970
+ *
971
+ * @param input - Data to hash
972
+ * @param output - Pre-allocated output buffer (must be at least outputLength bytes)
973
+ * @param outputLength - Number of bytes to output (default: 32, max: output.length)
974
+ */
975
+ export function hashInto(input, output, outputLength = OUT_LEN) {
976
+ // Validate output buffer
977
+ if (output.length < outputLength) {
978
+ throw new Error(`Output buffer too small: ${output.length} < ${outputLength}`);
979
+ }
980
+ // For large inputs, use SIMD for ~1.5x performance improvement
981
+ if (input.length >= SIMD_THRESHOLD && ensureSimdSync()) {
982
+ hashSimdInto(input, output, outputLength);
983
+ return;
984
+ }
985
+ hashPureJSInto(input, output, outputLength);
986
+ }
987
+ /**
988
+ * Internal: Hash using pure JS, writing directly to output buffer.
989
+ */
990
+ function hashPureJSInto(input, output, outputLen) {
991
+ const inputLen = input.length;
992
+ // Special case: empty input
993
+ if (inputLen === 0) {
994
+ const block = getBlockWords();
995
+ block.fill(0);
996
+ const out = outputLen <= 32 ? reusableOut8 : new Uint32Array(16);
997
+ compress(IV, 0, block, 0, out, 0, outputLen > 32, 0, 0, CHUNK_START | CHUNK_END | ROOT);
998
+ // Copy result to output
999
+ if (IS_LITTLE_ENDIAN) {
1000
+ output.set(new Uint8Array(out.buffer, out.byteOffset, outputLen));
1001
+ }
1002
+ else {
1003
+ writeLittleEndianBytesPartial(out, 0, output, 0, outputLen);
1004
+ }
1005
+ return;
1006
+ }
1007
+ // Calculate number of chunks
1008
+ const numChunks = Math.ceil(inputLen / CHUNK_LEN);
1009
+ // Single chunk optimization
1010
+ if (numChunks === 1) {
1011
+ const cv = outputLen <= 32 ? reusableOut8 : new Uint32Array(16);
1012
+ hashChunkRoot(input, 0, inputLen, 0, 0, cv, outputLen > 32);
1013
+ // Copy result to output
1014
+ if (IS_LITTLE_ENDIAN) {
1015
+ output.set(new Uint8Array(cv.buffer, cv.byteOffset, outputLen));
1016
+ }
1017
+ else {
1018
+ writeLittleEndianBytesPartial(cv, 0, output, 0, outputLen);
1019
+ }
1020
+ return;
1021
+ }
1022
+ // Multiple chunks - delegate to hashPureJS and copy result
1023
+ const result = hashPureJS(input, outputLen);
1024
+ output.set(result);
1025
+ }
1026
+ /**
1027
+ * Internal: Hash using SIMD, writing directly to output buffer.
1028
+ */
1029
+ function hashSimdInto(input, output, outputLen) {
1030
+ // Delegate to hashSimd and copy result (SIMD path already optimized)
1031
+ const result = hashSimd(input, outputLen);
1032
+ output.set(result);
1033
+ }
node_modules/@huggingface/blake3-jit/dist/esm/hasher.d.ts ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hasher - Incremental hashing with support for all modes
3
+ *
4
+ * Supports:
5
+ * - Regular hashing
6
+ * - Keyed hashing (MAC)
7
+ * - Key derivation (derive_key)
8
+ * - XOF (eXtendable Output Function) mode
9
+ */
10
+ /**
11
+ * Output state for XOF (eXtendable Output Function) mode.
12
+ * Allows reading arbitrary amounts of output.
13
+ */
14
+ export declare class XofReader {
15
+ private inputCv;
16
+ private blockWords;
17
+ private counter;
18
+ private blockLen;
19
+ private flags;
20
+ private outputBlock;
21
+ private outputBlockOffset;
22
+ constructor(inputCv: Uint32Array, blockWords: Uint32Array, counter: number, blockLen: number, flags: number);
23
+ /**
24
+ * Read the next `length` bytes of output.
25
+ */
26
+ read(length: number): Uint8Array;
27
+ }
28
+ /**
29
+ * Main BLAKE3 Hasher class.
30
+ *
31
+ * Usage:
32
+ * const hasher = new Hasher();
33
+ * hasher.update(data);
34
+ * const hash = hasher.finalize();
35
+ *
36
+ * Or with chaining:
37
+ * const hash = new Hasher().update(data).finalize();
38
+ */
39
+ export declare class Hasher {
40
+ private chunkState;
41
+ private keyWords;
42
+ private cvStack;
43
+ private cvStackLen;
44
+ private flags;
45
+ private parentBlock;
46
+ private parentCv;
47
+ private chunkCv;
48
+ private outWords;
49
+ private finalizeCv;
50
+ /**
51
+ * Create a new Hasher.
52
+ *
53
+ * @param keyWords - Initial key words (IV for regular hashing)
54
+ * @param flags - Domain separation flags
55
+ */
56
+ constructor(keyWords?: Uint32Array, flags?: number);
57
+ /**
58
+ * Reset the hasher to process a new message with the same key/flags.
59
+ * Reuses all internal buffers — zero allocations.
60
+ */
61
+ reset(): this;
62
+ /**
63
+ * Create a new keyed hasher (MAC).
64
+ *
65
+ * @param key - 32-byte key
66
+ */
67
+ static newKeyed(key: Uint8Array): Hasher;
68
+ /**
69
+ * Create a new key derivation hasher.
70
+ *
71
+ * @param context - Context string for domain separation
72
+ */
73
+ static newDeriveKey(context: string): Hasher;
74
+ /**
75
+ * Push a chaining value onto the stack.
76
+ */
77
+ private pushCv;
78
+ /**
79
+ * Pop a chaining value from the stack.
80
+ */
81
+ private popCv;
82
+ /**
83
+ * Add a chunk's chaining value and merge completed subtrees.
84
+ */
85
+ private addChunkCv;
86
+ /**
87
+ * Update the hasher with input data.
88
+ *
89
+ * @param input - Data to hash
90
+ * @returns this (for chaining)
91
+ */
92
+ update(input: Uint8Array): this;
93
+ /**
94
+ * Get the output parameters (for XOF mode or finalization).
95
+ */
96
+ private finalizeOutput;
97
+ /**
98
+ * Finalize the hash and return the result.
99
+ *
100
+ * @param outputLength - Number of bytes to output (default: 32)
101
+ * @returns The hash output
102
+ */
103
+ finalize(outputLength?: number): Uint8Array;
104
+ /**
105
+ * Finalize and return an XOF reader for arbitrary-length output.
106
+ */
107
+ finalizeXof(): XofReader;
108
+ }
node_modules/@huggingface/blake3-jit/dist/esm/hasher.js ADDED
@@ -0,0 +1,391 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hasher - Incremental hashing with support for all modes
3
+ *
4
+ * Supports:
5
+ * - Regular hashing
6
+ * - Keyed hashing (MAC)
7
+ * - Key derivation (derive_key)
8
+ * - XOF (eXtendable Output Function) mode
9
+ */
10
+ import { compress } from "./compress.js";
11
+ import { IV, CHUNK_START, CHUNK_END, PARENT, ROOT, KEYED_HASH, DERIVE_KEY_CONTEXT, DERIVE_KEY_MATERIAL, BLOCK_LEN, CHUNK_LEN, OUT_LEN, KEY_LEN, MAX_DEPTH, } from "./constants.js";
12
+ import { IS_LITTLE_ENDIAN, readLittleEndianWordsFull, writeLittleEndianBytesPartial, encodeUTF8, } from "./utils.js";
13
+ /**
14
+ * Output state for XOF (eXtendable Output Function) mode.
15
+ * Allows reading arbitrary amounts of output.
16
+ */
17
+ export class XofReader {
18
+ inputCv;
19
+ blockWords;
20
+ counter;
21
+ blockLen;
22
+ flags;
23
+ outputBlock;
24
+ outputBlockOffset;
25
+ constructor(inputCv, blockWords, counter, blockLen, flags) {
26
+ this.inputCv = inputCv;
27
+ this.blockWords = blockWords;
28
+ this.counter = counter;
29
+ this.blockLen = blockLen;
30
+ this.flags = flags | ROOT;
31
+ this.outputBlock = new Uint32Array(16);
32
+ this.outputBlockOffset = 64; // Forces generation on first read
33
+ }
34
+ /**
35
+ * Read the next `length` bytes of output.
36
+ */
37
+ read(length) {
38
+ const output = new Uint8Array(length);
39
+ let outputOffset = 0;
40
+ while (outputOffset < length) {
41
+ // Generate new output block if needed
42
+ if (this.outputBlockOffset >= 64) {
43
+ compress(this.inputCv, 0, this.blockWords, 0, this.outputBlock, 0, true, // full 64-byte output
44
+ this.counter++, this.blockLen, this.flags);
45
+ this.outputBlockOffset = 0;
46
+ }
47
+ // Copy bytes from output block
48
+ const available = 64 - this.outputBlockOffset;
49
+ const toCopy = Math.min(available, length - outputOffset);
50
+ // Optimized copy using writeLittleEndianBytesPartial
51
+ const wordOffset = this.outputBlockOffset >>> 2;
52
+ const byteWithinWord = this.outputBlockOffset & 3;
53
+ if (byteWithinWord === 0 && toCopy >= 4) {
54
+ // Aligned copy - can use word-at-a-time
55
+ const fullWords = toCopy >>> 2;
56
+ writeLittleEndianBytesPartial(this.outputBlock, wordOffset, output, outputOffset, fullWords << 2);
57
+ const bytesCopied = fullWords << 2;
58
+ outputOffset += bytesCopied;
59
+ this.outputBlockOffset += bytesCopied;
60
+ }
61
+ else {
62
+ // Byte-by-byte for unaligned access
63
+ for (let i = 0; i < toCopy; i++) {
64
+ const wordIdx = (this.outputBlockOffset + i) >>> 2;
65
+ const byteIdx = (this.outputBlockOffset + i) & 3;
66
+ output[outputOffset + i] = (this.outputBlock[wordIdx] >>> (byteIdx << 3)) & 0xff;
67
+ }
68
+ outputOffset += toCopy;
69
+ this.outputBlockOffset += toCopy;
70
+ }
71
+ }
72
+ return output;
73
+ }
74
+ }
75
+ /**
76
+ * Chunk state for processing input data.
77
+ * Each chunk is 1024 bytes and produces an 8-word chaining value.
78
+ */
79
+ class ChunkState {
80
+ chainingValue;
81
+ chunkCounter;
82
+ blockWords;
83
+ blockLen;
84
+ blocksCompressed;
85
+ flags;
86
+ constructor(keyWords, chunkCounter, flags) {
87
+ this.chainingValue = new Uint32Array(keyWords);
88
+ this.chunkCounter = chunkCounter;
89
+ this.blockWords = new Uint32Array(16);
90
+ this.blockLen = 0;
91
+ this.blocksCompressed = 0;
92
+ this.flags = flags;
93
+ }
94
+ resetTo(keyWords, chunkCounter, flags) {
95
+ this.chainingValue.set(keyWords);
96
+ this.chunkCounter = chunkCounter;
97
+ this.blockLen = 0;
98
+ this.blocksCompressed = 0;
99
+ this.flags = flags;
100
+ }
101
+ /**
102
+ * Get the flags for the current block.
103
+ */
104
+ startFlag() {
105
+ return this.blocksCompressed === 0 ? CHUNK_START : 0;
106
+ }
107
+ /**
108
+ * Update the chunk state with input data.
109
+ * Returns the number of bytes consumed.
110
+ */
111
+ update(input, inputOffset, inputLen) {
112
+ let consumed = 0;
113
+ while (inputLen > 0) {
114
+ // If we have a full block, compress it
115
+ if (this.blockLen === BLOCK_LEN) {
116
+ compress(this.chainingValue, 0, this.blockWords, 0, this.chainingValue, 0, false, this.chunkCounter, BLOCK_LEN, this.flags | this.startFlag());
117
+ this.blocksCompressed++;
118
+ this.blockLen = 0;
119
+ }
120
+ // Fill the block buffer
121
+ const want = BLOCK_LEN - this.blockLen;
122
+ const take = Math.min(want, inputLen);
123
+ if (this.blockLen === 0 && take === BLOCK_LEN) {
124
+ readLittleEndianWordsFull(input, inputOffset, this.blockWords);
125
+ }
126
+ else {
127
+ // Partial block - byte-by-byte into correct position
128
+ for (let i = 0; i < take; i++) {
129
+ const pos = this.blockLen + i;
130
+ const wordIdx = pos >>> 2;
131
+ const byteIdx = pos & 3;
132
+ if (byteIdx === 0) {
133
+ this.blockWords[wordIdx] = input[inputOffset + i];
134
+ }
135
+ else {
136
+ this.blockWords[wordIdx] |= input[inputOffset + i] << (byteIdx << 3);
137
+ }
138
+ }
139
+ }
140
+ this.blockLen += take;
141
+ inputOffset += take;
142
+ inputLen -= take;
143
+ consumed += take;
144
+ }
145
+ return consumed;
146
+ }
147
+ /**
148
+ * Finalize this chunk and return its output.
149
+ * Returns 8 words (chaining value) or 16 words (if root).
150
+ */
151
+ output() {
152
+ // Zero-pad unused words in blockWords to avoid stale data from previous blocks
153
+ // This is necessary when a partial block follows a full block within the same chunk
154
+ const usedWords = (this.blockLen + 3) >>> 2; // ceil(blockLen / 4)
155
+ for (let i = usedWords; i < 16; i++) {
156
+ this.blockWords[i] = 0;
157
+ }
158
+ return {
159
+ inputCv: this.chainingValue,
160
+ blockWords: this.blockWords,
161
+ blockLen: this.blockLen,
162
+ counter: this.chunkCounter,
163
+ flags: this.flags | this.startFlag() | CHUNK_END,
164
+ };
165
+ }
166
+ /**
167
+ * Get the number of bytes in this chunk.
168
+ */
169
+ len() {
170
+ return this.blocksCompressed * BLOCK_LEN + this.blockLen;
171
+ }
172
+ }
173
+ /**
174
+ * Main BLAKE3 Hasher class.
175
+ *
176
+ * Usage:
177
+ * const hasher = new Hasher();
178
+ * hasher.update(data);
179
+ * const hash = hasher.finalize();
180
+ *
181
+ * Or with chaining:
182
+ * const hash = new Hasher().update(data).finalize();
183
+ */
184
+ export class Hasher {
185
+ chunkState;
186
+ keyWords;
187
+ cvStack;
188
+ cvStackLen;
189
+ flags;
190
+ parentBlock;
191
+ parentCv;
192
+ chunkCv;
193
+ outWords;
194
+ finalizeCv;
195
+ /**
196
+ * Create a new Hasher.
197
+ *
198
+ * @param keyWords - Initial key words (IV for regular hashing)
199
+ * @param flags - Domain separation flags
200
+ */
201
+ constructor(keyWords, flags) {
202
+ this.keyWords = keyWords ? new Uint32Array(keyWords) : new Uint32Array(IV);
203
+ this.flags = flags ?? 0;
204
+ this.chunkState = new ChunkState(this.keyWords, 0, this.flags);
205
+ this.cvStack = new Uint32Array(MAX_DEPTH * 8);
206
+ this.cvStackLen = 0;
207
+ this.parentBlock = new Uint32Array(16);
208
+ this.parentCv = new Uint32Array(8);
209
+ this.chunkCv = new Uint32Array(8);
210
+ this.outWords = new Uint32Array(16);
211
+ this.finalizeCv = new Uint32Array(8);
212
+ }
213
+ /**
214
+ * Reset the hasher to process a new message with the same key/flags.
215
+ * Reuses all internal buffers — zero allocations.
216
+ */
217
+ reset() {
218
+ this.chunkState.resetTo(this.keyWords, 0, this.flags);
219
+ this.cvStackLen = 0;
220
+ return this;
221
+ }
222
+ /**
223
+ * Create a new keyed hasher (MAC).
224
+ *
225
+ * @param key - 32-byte key
226
+ */
227
+ static newKeyed(key) {
228
+ if (key.length !== KEY_LEN) {
229
+ throw new Error(`Key must be ${KEY_LEN} bytes, got ${key.length}`);
230
+ }
231
+ const keyWords = new Uint32Array(8);
232
+ if (IS_LITTLE_ENDIAN) {
233
+ const view = new Uint32Array(key.buffer, key.byteOffset, 8);
234
+ keyWords.set(view);
235
+ }
236
+ else {
237
+ for (let i = 0; i < 8; i++) {
238
+ const off = i * 4;
239
+ keyWords[i] = key[off] | (key[off + 1] << 8) | (key[off + 2] << 16) | (key[off + 3] << 24);
240
+ }
241
+ }
242
+ return new Hasher(keyWords, KEYED_HASH);
243
+ }
244
+ /**
245
+ * Create a new key derivation hasher.
246
+ *
247
+ * @param context - Context string for domain separation
248
+ */
249
+ static newDeriveKey(context) {
250
+ // First, hash the context string with DERIVE_KEY_CONTEXT flag
251
+ const contextBytes = encodeUTF8(context);
252
+ const contextHasher = new Hasher(new Uint32Array(IV), DERIVE_KEY_CONTEXT);
253
+ contextHasher.update(contextBytes);
254
+ // Get the context key
255
+ const contextKey = new Uint32Array(8);
256
+ const output = contextHasher.finalizeOutput();
257
+ compress(output.inputCv, 0, output.blockWords, 0, contextKey, 0, false, output.counter, output.blockLen, output.flags | ROOT);
258
+ // Return a hasher initialized with the context key
259
+ return new Hasher(contextKey, DERIVE_KEY_MATERIAL);
260
+ }
261
+ /**
262
+ * Push a chaining value onto the stack.
263
+ */
264
+ pushCv(cv, cvOffset) {
265
+ this.cvStack.set(cv.subarray(cvOffset, cvOffset + 8), this.cvStackLen * 8);
266
+ this.cvStackLen++;
267
+ }
268
+ /**
269
+ * Pop a chaining value from the stack.
270
+ */
271
+ popCv(out, outOffset) {
272
+ this.cvStackLen--;
273
+ out.set(this.cvStack.subarray(this.cvStackLen * 8, (this.cvStackLen + 1) * 8), outOffset);
274
+ }
275
+ /**
276
+ * Add a chunk's chaining value and merge completed subtrees.
277
+ */
278
+ addChunkCv(newCv, newCvOffset, totalChunks) {
279
+ const parentBlock = this.parentBlock;
280
+ const parentCv = this.parentCv;
281
+ while ((totalChunks & 1) === 0) {
282
+ // Pop left child, new CV is right child
283
+ this.popCv(parentBlock, 0);
284
+ parentBlock.set(newCv.subarray(newCvOffset, newCvOffset + 8), 8);
285
+ compress(this.keyWords, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, this.flags | PARENT);
286
+ newCv = parentCv;
287
+ newCvOffset = 0;
288
+ totalChunks >>>= 1;
289
+ }
290
+ this.pushCv(newCv, newCvOffset);
291
+ }
292
+ /**
293
+ * Update the hasher with input data.
294
+ *
295
+ * @param input - Data to hash
296
+ * @returns this (for chaining)
297
+ */
298
+ update(input) {
299
+ let inputOffset = 0;
300
+ let inputLen = input.length;
301
+ // Fill the current chunk
302
+ while (inputLen > 0) {
303
+ // If current chunk is full, finalize it and start a new one
304
+ if (this.chunkState.len() === CHUNK_LEN) {
305
+ const output = this.chunkState.output();
306
+ const chunkCv = this.chunkCv;
307
+ compress(output.inputCv, 0, output.blockWords, 0, chunkCv, 0, false, output.counter, output.blockLen, output.flags);
308
+ const totalChunks = this.chunkState.chunkCounter + 1;
309
+ this.addChunkCv(chunkCv, 0, totalChunks);
310
+ this.chunkState.resetTo(this.keyWords, totalChunks, this.flags);
311
+ }
312
+ // Fill the current chunk
313
+ const want = CHUNK_LEN - this.chunkState.len();
314
+ const take = Math.min(want, inputLen);
315
+ this.chunkState.update(input, inputOffset, take);
316
+ inputOffset += take;
317
+ inputLen -= take;
318
+ }
319
+ return this;
320
+ }
321
+ /**
322
+ * Get the output parameters (for XOF mode or finalization).
323
+ */
324
+ finalizeOutput() {
325
+ let output = this.chunkState.output();
326
+ let parentBlock = this.parentBlock;
327
+ let cv = this.finalizeCv;
328
+ // If there are chunks on the stack, merge them
329
+ if (this.cvStackLen > 0) {
330
+ // First compress the current chunk
331
+ compress(output.inputCv, 0, output.blockWords, 0, cv, 0, false, output.counter, output.blockLen, output.flags);
332
+ // Merge with parent nodes from stack
333
+ while (this.cvStackLen > 0) {
334
+ this.cvStackLen--;
335
+ parentBlock.set(this.cvStack.subarray(this.cvStackLen * 8, (this.cvStackLen + 1) * 8), 0);
336
+ parentBlock.set(cv, 8);
337
+ if (this.cvStackLen > 0) {
338
+ compress(this.keyWords, 0, parentBlock, 0, cv, 0, false, 0, BLOCK_LEN, this.flags | PARENT);
339
+ }
340
+ else {
341
+ // This is the root - return output params
342
+ return {
343
+ inputCv: this.keyWords,
344
+ blockWords: parentBlock,
345
+ blockLen: BLOCK_LEN,
346
+ counter: 0,
347
+ flags: this.flags | PARENT,
348
+ };
349
+ }
350
+ }
351
+ }
352
+ // Single chunk case
353
+ return output;
354
+ }
355
+ /**
356
+ * Finalize the hash and return the result.
357
+ *
358
+ * @param outputLength - Number of bytes to output (default: 32)
359
+ * @returns The hash output
360
+ */
361
+ finalize(outputLength = OUT_LEN) {
362
+ const output = this.finalizeOutput();
363
+ const result = new Uint8Array(outputLength);
364
+ if (outputLength <= 64) {
365
+ const outWords = this.outWords;
366
+ compress(output.inputCv, 0, output.blockWords, 0, outWords, 0, outputLength > 32, // full output if > 32 bytes
367
+ output.counter, output.blockLen, output.flags | ROOT);
368
+ if (IS_LITTLE_ENDIAN) {
369
+ const outBytes = new Uint8Array(outWords.buffer);
370
+ result.set(outBytes.subarray(0, outputLength));
371
+ }
372
+ else {
373
+ writeLittleEndianBytesPartial(outWords, 0, result, 0, outputLength);
374
+ }
375
+ }
376
+ else {
377
+ // Multiple blocks - use XOF
378
+ const xof = this.finalizeXof();
379
+ const full = xof.read(outputLength);
380
+ result.set(full);
381
+ }
382
+ return result;
383
+ }
384
+ /**
385
+ * Finalize and return an XOF reader for arbitrary-length output.
386
+ */
387
+ finalizeXof() {
388
+ const output = this.finalizeOutput();
389
+ return new XofReader(new Uint32Array(output.inputCv), new Uint32Array(output.blockWords), output.counter, output.blockLen, output.flags);
390
+ }
391
+ }
node_modules/@huggingface/blake3-jit/dist/esm/index.d.ts ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 - The fastest pure JavaScript implementation
3
+ *
4
+ * Features:
5
+ * - All 3 modes: hash, keyed (MAC), derive_key
6
+ * - XOF (eXtendable Output Function) support
7
+ * - Automatic WASM SIMD acceleration for large inputs
8
+ * - Zero dependencies
9
+ * - Tree-shakeable exports
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { hash, createKeyed, createDeriveKey } from 'blake3-jit';
14
+ *
15
+ * // Simple hashing
16
+ * const digest = hash(new Uint8Array([1, 2, 3]));
17
+ *
18
+ * // Keyed hashing (MAC)
19
+ * const mac = createKeyed(key).update(data).finalize();
20
+ *
21
+ * // Key derivation
22
+ * const derived = createDeriveKey("my context").update(material).finalize(64);
23
+ * ```
24
+ */
25
+ export { Hasher, XofReader } from "./hasher.js";
26
+ export { hash, hashInto, warmupSimd } from "./hash.js";
27
+ import { Hasher } from "./hasher.js";
28
+ /**
29
+ * Create a new keyed hasher (MAC).
30
+ *
31
+ * @param key - 32-byte key
32
+ * @returns A new Hasher configured for keyed hashing
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const key = new Uint8Array(32); // Your 32-byte key
37
+ * crypto.getRandomValues(key);
38
+ *
39
+ * const mac = createKeyed(key)
40
+ * .update(message)
41
+ * .finalize();
42
+ * ```
43
+ */
44
+ export declare function createKeyed(key: Uint8Array): Hasher;
45
+ /**
46
+ * Create a new key derivation hasher.
47
+ *
48
+ * @param context - Context string for domain separation
49
+ * @returns A new Hasher configured for key derivation
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const derivedKey = createDeriveKey("my-app encryption key v1")
54
+ * .update(inputKeyMaterial)
55
+ * .finalize(32);
56
+ * ```
57
+ */
58
+ export declare function createDeriveKey(context: string): Hasher;
59
+ /**
60
+ * Create a new regular hasher for incremental hashing.
61
+ *
62
+ * @returns A new Hasher
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const hasher = createHasher();
67
+ * hasher.update(chunk1);
68
+ * hasher.update(chunk2);
69
+ * const digest = hasher.finalize();
70
+ * ```
71
+ */
72
+ export declare function createHasher(): Hasher;
73
+ import { hash, hashInto } from "./hash.js";
74
+ declare const _default: {
75
+ hash: typeof hash;
76
+ hashInto: typeof hashInto;
77
+ Hasher: typeof Hasher;
78
+ createHasher: typeof createHasher;
79
+ createKeyed: typeof createKeyed;
80
+ createDeriveKey: typeof createDeriveKey;
81
+ };
82
+ export default _default;
node_modules/@huggingface/blake3-jit/dist/esm/index.js ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 - The fastest pure JavaScript implementation
3
+ *
4
+ * Features:
5
+ * - All 3 modes: hash, keyed (MAC), derive_key
6
+ * - XOF (eXtendable Output Function) support
7
+ * - Automatic WASM SIMD acceleration for large inputs
8
+ * - Zero dependencies
9
+ * - Tree-shakeable exports
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { hash, createKeyed, createDeriveKey } from 'blake3-jit';
14
+ *
15
+ * // Simple hashing
16
+ * const digest = hash(new Uint8Array([1, 2, 3]));
17
+ *
18
+ * // Keyed hashing (MAC)
19
+ * const mac = createKeyed(key).update(data).finalize();
20
+ *
21
+ * // Key derivation
22
+ * const derived = createDeriveKey("my context").update(material).finalize(64);
23
+ * ```
24
+ */
25
+ // Core exports
26
+ export { Hasher, XofReader } from "./hasher.js";
27
+ export { hash, hashInto, warmupSimd } from "./hash.js";
28
+ // Convenience imports
29
+ import { Hasher } from "./hasher.js";
30
+ /**
31
+ * Create a new keyed hasher (MAC).
32
+ *
33
+ * @param key - 32-byte key
34
+ * @returns A new Hasher configured for keyed hashing
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const key = new Uint8Array(32); // Your 32-byte key
39
+ * crypto.getRandomValues(key);
40
+ *
41
+ * const mac = createKeyed(key)
42
+ * .update(message)
43
+ * .finalize();
44
+ * ```
45
+ */
46
+ export function createKeyed(key) {
47
+ return Hasher.newKeyed(key);
48
+ }
49
+ /**
50
+ * Create a new key derivation hasher.
51
+ *
52
+ * @param context - Context string for domain separation
53
+ * @returns A new Hasher configured for key derivation
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const derivedKey = createDeriveKey("my-app encryption key v1")
58
+ * .update(inputKeyMaterial)
59
+ * .finalize(32);
60
+ * ```
61
+ */
62
+ export function createDeriveKey(context) {
63
+ return Hasher.newDeriveKey(context);
64
+ }
65
+ /**
66
+ * Create a new regular hasher for incremental hashing.
67
+ *
68
+ * @returns A new Hasher
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const hasher = createHasher();
73
+ * hasher.update(chunk1);
74
+ * hasher.update(chunk2);
75
+ * const digest = hasher.finalize();
76
+ * ```
77
+ */
78
+ export function createHasher() {
79
+ return new Hasher();
80
+ }
81
+ // Import for default export
82
+ import { hash, hashInto, warmupSimd } from "./hash.js";
83
+ // Pre-warm SIMD in browser environments (non-blocking)
84
+ // This avoids initialization latency on first large hash
85
+ if (typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined") {
86
+ queueMicrotask(() => {
87
+ warmupSimd();
88
+ });
89
+ }
90
+ // Default export for convenience
91
+ export default {
92
+ hash,
93
+ hashInto,
94
+ Hasher,
95
+ createHasher,
96
+ createKeyed,
97
+ createDeriveKey,
98
+ };
node_modules/@huggingface/blake3-jit/dist/esm/package.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "type": "module"
3
+ }
node_modules/@huggingface/blake3-jit/dist/esm/utils.d.ts ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Utility Functions
3
+ *
4
+ * Optimized for little-endian systems (most user-facing systems).
5
+ * BLAKE3 is little-endian friendly - on little-endian systems we can
6
+ * create Uint32Array views directly over input buffers.
7
+ */
8
+ /**
9
+ * Detect system endianness at module load time.
10
+ * On little-endian systems, the byte 0x01 will be at index 0.
11
+ */
12
+ export declare const IS_LITTLE_ENDIAN: boolean;
13
+ /**
14
+ * Read 16 little-endian 32-bit words from a byte array into a Uint32Array.
15
+ * This is only needed on big-endian systems.
16
+ *
17
+ * @param input - Source byte array
18
+ * @param offset - Starting byte offset in input
19
+ * @param words - Destination Uint32Array (must have at least 16 elements)
20
+ */
21
+ export declare function readLittleEndianWordsFull(input: Uint8Array, offset: number, words: Uint32Array): void;
22
+ /**
23
+ * Read N little-endian 32-bit words from a byte array.
24
+ * Handles partial reads (for final blocks).
25
+ *
26
+ * @param input - Source byte array
27
+ * @param offset - Starting byte offset
28
+ * @param words - Destination Uint32Array
29
+ * @param wordCount - Number of words to read
30
+ */
31
+ export declare function readLittleEndianWords(input: Uint8Array, offset: number, words: Uint32Array, wordCount: number): void;
32
+ /**
33
+ * Read a partial block with zero padding.
34
+ * Used for the final block when input length is not a multiple of 64.
35
+ *
36
+ * @param input - Source byte array
37
+ * @param offset - Starting byte offset
38
+ * @param length - Number of bytes to read (< 64)
39
+ * @param words - Destination Uint32Array (must have 16 elements)
40
+ */
41
+ export declare function readLittleEndianWordsPartial(input: Uint8Array, offset: number, length: number, words: Uint32Array): void;
42
+ /**
43
+ * Write 8 little-endian 32-bit words to a byte array.
44
+ *
45
+ * @param words - Source Uint32Array
46
+ * @param wordOffset - Starting word offset in source
47
+ * @param output - Destination byte array
48
+ * @param byteOffset - Starting byte offset in destination
49
+ */
50
+ export declare function writeLittleEndianWords(words: Uint32Array, wordOffset: number, output: Uint8Array, byteOffset: number): void;
51
+ /**
52
+ * Write N bytes from 32-bit words to output.
53
+ * Used for variable-length output (XOF mode).
54
+ *
55
+ * @param words - Source Uint32Array
56
+ * @param wordOffset - Starting word offset
57
+ * @param output - Destination byte array
58
+ * @param byteOffset - Starting byte offset in destination
59
+ * @param byteCount - Number of bytes to write
60
+ */
61
+ export declare function writeLittleEndianBytesPartial(words: Uint32Array, wordOffset: number, output: Uint8Array, byteOffset: number, byteCount: number): void;
62
+ /**
63
+ * Encode a UTF-8 string to Uint8Array.
64
+ * Used for derive_key context strings.
65
+ */
66
+ export declare function encodeUTF8(str: string): Uint8Array;
67
+ /**
68
+ * Count trailing zero bits in a 32-bit number using De Bruijn multiplication.
69
+ * This is O(1) and branchless for non-zero inputs.
70
+ *
71
+ * For Merkle tree merge: ctz32(chunkCounter) tells us how many merges to do.
72
+ */
73
+ export declare function ctz32(n: number): number;
74
+ /**
75
+ * Count trailing zero bits in a 64-bit number.
76
+ * Used to determine how many parent nodes to compute after adding a chunk.
77
+ *
78
+ * Note: JavaScript bitwise ops work on 32-bit signed integers,
79
+ * so we need to handle 64-bit numbers carefully.
80
+ */
81
+ export declare function countTrailingZeros(n: number): number;
82
+ /**
83
+ * Create a Uint32Array view of a Uint8Array.
84
+ * Only works correctly on little-endian systems when the offset is 4-byte aligned.
85
+ *
86
+ * @param arr - Source byte array
87
+ * @param byteOffset - Starting byte offset (must be 4-byte aligned)
88
+ * @param wordLength - Number of 32-bit words
89
+ */
90
+ export declare function uint32View(arr: Uint8Array, byteOffset: number, wordLength: number): Uint32Array;
node_modules/@huggingface/blake3-jit/dist/esm/utils.js ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Utility Functions
3
+ *
4
+ * Optimized for little-endian systems (most user-facing systems).
5
+ * BLAKE3 is little-endian friendly - on little-endian systems we can
6
+ * create Uint32Array views directly over input buffers.
7
+ */
8
+ /**
9
+ * Detect system endianness at module load time.
10
+ * On little-endian systems, the byte 0x01 will be at index 0.
11
+ */
12
+ export const IS_LITTLE_ENDIAN = new Uint8Array(new Uint32Array([0x01020304]).buffer)[0] === 0x04;
13
+ /**
14
+ * Read 16 little-endian 32-bit words from a byte array into a Uint32Array.
15
+ * This is only needed on big-endian systems.
16
+ *
17
+ * @param input - Source byte array
18
+ * @param offset - Starting byte offset in input
19
+ * @param words - Destination Uint32Array (must have at least 16 elements)
20
+ */
21
+ export function readLittleEndianWordsFull(input, offset, words) {
22
+ for (let i = 0; i < 16; ++i, offset += 4) {
23
+ words[i] =
24
+ input[offset] |
25
+ (input[offset + 1] << 8) |
26
+ (input[offset + 2] << 16) |
27
+ (input[offset + 3] << 24);
28
+ }
29
+ }
30
+ /**
31
+ * Read N little-endian 32-bit words from a byte array.
32
+ * Handles partial reads (for final blocks).
33
+ *
34
+ * @param input - Source byte array
35
+ * @param offset - Starting byte offset
36
+ * @param words - Destination Uint32Array
37
+ * @param wordCount - Number of words to read
38
+ */
39
+ export function readLittleEndianWords(input, offset, words, wordCount) {
40
+ for (let i = 0; i < wordCount; ++i, offset += 4) {
41
+ words[i] =
42
+ input[offset] |
43
+ (input[offset + 1] << 8) |
44
+ (input[offset + 2] << 16) |
45
+ (input[offset + 3] << 24);
46
+ }
47
+ }
48
+ /**
49
+ * Read a partial block with zero padding.
50
+ * Used for the final block when input length is not a multiple of 64.
51
+ *
52
+ * @param input - Source byte array
53
+ * @param offset - Starting byte offset
54
+ * @param length - Number of bytes to read (< 64)
55
+ * @param words - Destination Uint32Array (must have 16 elements)
56
+ */
57
+ export function readLittleEndianWordsPartial(input, offset, length, words) {
58
+ // Zero out all words first
59
+ words.fill(0);
60
+ // Read full words
61
+ const fullWords = length >>> 2;
62
+ let i = 0;
63
+ for (; i < fullWords; ++i, offset += 4) {
64
+ words[i] =
65
+ input[offset] |
66
+ (input[offset + 1] << 8) |
67
+ (input[offset + 2] << 16) |
68
+ (input[offset + 3] << 24);
69
+ }
70
+ // Handle remaining bytes (0-3)
71
+ const remaining = length & 3;
72
+ if (remaining > 0) {
73
+ let word = input[offset];
74
+ if (remaining > 1)
75
+ word |= input[offset + 1] << 8;
76
+ if (remaining > 2)
77
+ word |= input[offset + 2] << 16;
78
+ words[i] = word;
79
+ }
80
+ }
81
+ /**
82
+ * Write 8 little-endian 32-bit words to a byte array.
83
+ *
84
+ * @param words - Source Uint32Array
85
+ * @param wordOffset - Starting word offset in source
86
+ * @param output - Destination byte array
87
+ * @param byteOffset - Starting byte offset in destination
88
+ */
89
+ export function writeLittleEndianWords(words, wordOffset, output, byteOffset) {
90
+ for (let i = 0; i < 8; ++i, byteOffset += 4) {
91
+ const w = words[wordOffset + i];
92
+ output[byteOffset] = w & 0xff;
93
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
94
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
95
+ output[byteOffset + 3] = (w >>> 24) & 0xff;
96
+ }
97
+ }
98
+ /**
99
+ * Write N bytes from 32-bit words to output.
100
+ * Used for variable-length output (XOF mode).
101
+ *
102
+ * @param words - Source Uint32Array
103
+ * @param wordOffset - Starting word offset
104
+ * @param output - Destination byte array
105
+ * @param byteOffset - Starting byte offset in destination
106
+ * @param byteCount - Number of bytes to write
107
+ */
108
+ export function writeLittleEndianBytesPartial(words, wordOffset, output, byteOffset, byteCount) {
109
+ const fullWords = byteCount >>> 2;
110
+ let i = 0;
111
+ // Write full words
112
+ for (; i < fullWords; ++i, byteOffset += 4) {
113
+ const w = words[wordOffset + i];
114
+ output[byteOffset] = w & 0xff;
115
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
116
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
117
+ output[byteOffset + 3] = (w >>> 24) & 0xff;
118
+ }
119
+ // Write remaining bytes
120
+ const remaining = byteCount & 3;
121
+ if (remaining > 0) {
122
+ const w = words[wordOffset + i];
123
+ output[byteOffset] = w & 0xff;
124
+ if (remaining > 1)
125
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
126
+ if (remaining > 2)
127
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
128
+ }
129
+ }
130
+ /**
131
+ * Encode a UTF-8 string to Uint8Array.
132
+ * Used for derive_key context strings.
133
+ */
134
+ export function encodeUTF8(str) {
135
+ if (typeof TextEncoder !== "undefined") {
136
+ return new TextEncoder().encode(str);
137
+ }
138
+ // Fallback for older environments
139
+ const bytes = [];
140
+ for (let i = 0; i < str.length; i++) {
141
+ let c = str.charCodeAt(i);
142
+ if (c < 0x80) {
143
+ bytes.push(c);
144
+ }
145
+ else if (c < 0x800) {
146
+ bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
147
+ }
148
+ else if (c < 0xd800 || c >= 0xe000) {
149
+ bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
150
+ }
151
+ else {
152
+ // Surrogate pair
153
+ i++;
154
+ c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
155
+ bytes.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
156
+ }
157
+ }
158
+ return new Uint8Array(bytes);
159
+ }
160
+ /**
161
+ * De Bruijn lookup table for O(1) trailing zero count.
162
+ * The expression (n & -n) isolates the lowest set bit.
163
+ * Multiplying by the De Bruijn constant maps each power of 2 to a unique 5-bit index.
164
+ */
165
+ const CTZ32_TABLE = new Uint8Array([
166
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12,
167
+ 18, 6, 11, 5, 10, 9,
168
+ ]);
169
+ /**
170
+ * Count trailing zero bits in a 32-bit number using De Bruijn multiplication.
171
+ * This is O(1) and branchless for non-zero inputs.
172
+ *
173
+ * For Merkle tree merge: ctz32(chunkCounter) tells us how many merges to do.
174
+ */
175
+ export function ctz32(n) {
176
+ if (n === 0)
177
+ return 32;
178
+ // Use unsigned right shift to handle negative numbers correctly
179
+ return CTZ32_TABLE[(((n & -n) * 0x077cb531) >>> 27) & 31];
180
+ }
181
+ /**
182
+ * Count trailing zero bits in a 64-bit number.
183
+ * Used to determine how many parent nodes to compute after adding a chunk.
184
+ *
185
+ * Note: JavaScript bitwise ops work on 32-bit signed integers,
186
+ * so we need to handle 64-bit numbers carefully.
187
+ */
188
+ export function countTrailingZeros(n) {
189
+ if (n === 0)
190
+ return 64;
191
+ // For numbers that fit in 32 bits
192
+ const low = n | 0;
193
+ if (low !== 0) {
194
+ // Use Math.clz32 trick: ctz(x) = 31 - clz32(x & -x) for non-zero x
195
+ return 31 - Math.clz32(low & -low);
196
+ }
197
+ // High 32 bits
198
+ const high = (n / 0x100000000) | 0;
199
+ if (high !== 0) {
200
+ return 32 + (31 - Math.clz32(high & -high));
201
+ }
202
+ return 64;
203
+ }
204
+ /**
205
+ * Create a Uint32Array view of a Uint8Array.
206
+ * Only works correctly on little-endian systems when the offset is 4-byte aligned.
207
+ *
208
+ * @param arr - Source byte array
209
+ * @param byteOffset - Starting byte offset (must be 4-byte aligned)
210
+ * @param wordLength - Number of 32-bit words
211
+ */
212
+ export function uint32View(arr, byteOffset, wordLength) {
213
+ return new Uint32Array(arr.buffer, arr.byteOffset + byteOffset, wordLength);
214
+ }
node_modules/@huggingface/blake3-jit/dist/esm/wasm-simd.d.ts ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 WASM SIMD - Runtime bytecode generation
3
+ *
4
+ * Generates WebAssembly SIMD bytecode at runtime to process 4 compress
5
+ * operations in parallel using 128-bit SIMD vectors (i32x4).
6
+ *
7
+ * Key insight: One i32x4.add instruction performs 4 parallel additions,
8
+ * giving us 4x throughput for the same number of instructions.
9
+ *
10
+ * Memory layout (all values are transposed for SIMD access):
11
+ * 0-511: 4 x 16 message words (m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...)
12
+ * 512-639: 4 x 8 chaining values
13
+ * 640-767: 4 x 8 output values
14
+ * 768-783: 4 x counter low
15
+ * 784-799: 4 x counter high
16
+ * 800-815: 4 x block length
17
+ * 816-831: 4 x flags
18
+ */
19
+ /**
20
+ * Check if WASM SIMD is supported.
21
+ */
22
+ export declare function isSimdSupported(): boolean;
23
+ export declare function initSimdSync(): boolean;
24
+ /**
25
+ * Memory offsets for SIMD data layout
26
+ *
27
+ * WASM Arena Pattern: All working buffers live in WASM memory (64KB page)
28
+ * This eliminates JS heap allocations during hashing operations.
29
+ */
30
+ export declare const SIMD_MEMORY: {
31
+ readonly BLOCK_WORDS: 0;
32
+ readonly CHAINING_VALUES: 512;
33
+ readonly OUTPUT: 640;
34
+ readonly COUNTER_LOW: 768;
35
+ readonly COUNTER_HIGH: 784;
36
+ readonly BLOCK_LEN: 800;
37
+ readonly FLAGS: 816;
38
+ readonly BATCH_BLOCK_WORDS: 832;
39
+ readonly BATCH_CV: 4928;
40
+ readonly BATCH_COUNTER_LOW: 5056;
41
+ readonly BATCH_FLAGS_BASE: 5072;
42
+ readonly BATCH_OUTPUT: 5088;
43
+ readonly CV_STACK: 5216;
44
+ readonly PARENT_BLOCK: 7264;
45
+ readonly CHUNK_CV: 7328;
46
+ readonly TEMP_CVS: 7360;
47
+ };
48
+ /**
49
+ * Get the WASM memory views for writing input data.
50
+ */
51
+ export declare function getSimdMemory(): {
52
+ view: Uint8Array;
53
+ view32: Uint32Array;
54
+ } | null;
55
+ /**
56
+ * Get the arena buffers for Merkle tree operations.
57
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
58
+ */
59
+ export declare function getArenaBuffers(): {
60
+ cvStack: Uint32Array;
61
+ parentBlock: Uint32Array;
62
+ chunkCv: Uint32Array;
63
+ tempCvs: Uint32Array;
64
+ } | null;
65
+ /**
66
+ * Get the batch arena buffers for chunk-level batched operations.
67
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
68
+ */
69
+ export declare function getBatchArenaBuffers(): {
70
+ blockWords: Uint32Array;
71
+ cv: Uint32Array;
72
+ counterLow: Uint32Array;
73
+ flagsBase: Uint32Array;
74
+ output: Uint32Array;
75
+ } | null;
76
+ /**
77
+ * Run the compress4x function.
78
+ * Data must already be set up in WASM memory.
79
+ */
80
+ export declare function runCompress4x(): void;
81
+ /**
82
+ * Run the compressChunks4x function.
83
+ * Processes 4 full chunks (16 blocks each) in a single WASM call.
84
+ * Data must already be set up in batch arena buffers.
85
+ */
86
+ export declare function runCompressChunks4x(): void;
87
+ /**
88
+ * Run the compressParent function.
89
+ * Compresses a parent node: reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
90
+ * Data must already be set up in arena buffers (PARENT_BLOCK at offset 7264).
91
+ * Output is written to CHUNK_CV at offset 7328.
92
+ */
93
+ export declare function runCompressParent(): void;
94
+ /**
95
+ * Check if SIMD is initialized and ready.
96
+ */
97
+ export declare function isSimdReady(): boolean;
node_modules/@huggingface/blake3-jit/dist/esm/wasm-simd.js ADDED
@@ -0,0 +1,936 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 WASM SIMD - Runtime bytecode generation
3
+ *
4
+ * Generates WebAssembly SIMD bytecode at runtime to process 4 compress
5
+ * operations in parallel using 128-bit SIMD vectors (i32x4).
6
+ *
7
+ * Key insight: One i32x4.add instruction performs 4 parallel additions,
8
+ * giving us 4x throughput for the same number of instructions.
9
+ *
10
+ * Memory layout (all values are transposed for SIMD access):
11
+ * 0-511: 4 x 16 message words (m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...)
12
+ * 512-639: 4 x 8 chaining values
13
+ * 640-767: 4 x 8 output values
14
+ * 768-783: 4 x counter low
15
+ * 784-799: 4 x counter high
16
+ * 800-815: 4 x block length
17
+ * 816-831: 4 x flags
18
+ */
19
+ // LEB128 encoding with minimum 2 bytes
20
+ // This fixes a V8 quirk where single-byte values 64-127 cause issues
21
+ // when followed by certain SIMD instructions
22
+ function toLebU32Min2(n) {
23
+ // Always use at least 2 bytes
24
+ return [(n & 0x7f) | 0x80, (n >>> 7) & 0x7f];
25
+ }
26
+ // LEB128 encoding padded to exactly 5 bytes (for backpatching)
27
+ // Uses continuation bits for all but the last byte
28
+ function toLebU32Padded5(n) {
29
+ return [
30
+ (n & 0x7f) | 0x80,
31
+ ((n >>> 7) & 0x7f) | 0x80,
32
+ ((n >>> 14) & 0x7f) | 0x80,
33
+ ((n >>> 21) & 0x7f) | 0x80,
34
+ (n >>> 28) & 0x0f, // Last byte has no continuation bit
35
+ ];
36
+ }
37
+ // Signed LEB128 encoding for i32 constants (handles full 32-bit range)
38
+ // WASM i32.const uses signed LEB128 immediate
39
+ function toSignedLeb128_i32(n) {
40
+ const bytes = [];
41
+ // Treat as signed 32-bit integer
42
+ let value = n | 0;
43
+ let more = true;
44
+ while (more) {
45
+ let byte = value & 0x7f;
46
+ // Arithmetic right shift preserves sign
47
+ value >>= 7;
48
+ // Check if we're done:
49
+ // - If value is 0 and sign bit of byte is clear, we're done
50
+ // - If value is -1 and sign bit of byte is set, we're done
51
+ if ((value === 0 && (byte & 0x40) === 0) || (value === -1 && (byte & 0x40) !== 0)) {
52
+ more = false;
53
+ }
54
+ else {
55
+ byte |= 0x80;
56
+ }
57
+ bytes.push(byte);
58
+ }
59
+ return bytes;
60
+ }
61
+ // Precomputed message access order for all 7 rounds
62
+ const MSG_ACCESS_ORDER = [
63
+ // Round 1: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
64
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
65
+ // Round 2: 2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8
66
+ 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8,
67
+ // Round 3: 3,4,10,12,13,2,7,14,6,5,9,0,11,15,8,1
68
+ 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1,
69
+ // Round 4: 10,7,12,9,14,3,13,15,4,0,11,2,5,8,1,6
70
+ 10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6,
71
+ // Round 5: 12,13,9,11,15,10,14,8,7,2,5,3,0,1,6,4
72
+ 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4,
73
+ // Round 6: 9,14,11,5,8,12,15,1,13,3,0,10,2,6,4,7
74
+ 9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7,
75
+ // Round 7: 11,15,5,0,1,9,8,6,14,10,2,12,3,4,7,13
76
+ 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13,
77
+ ];
78
+ // BLAKE3 Constants (used in generated WASM code)
79
+ // CHUNK_START = 1, CHUNK_END = 2 are embedded directly in WASM bytecode
80
+ /**
81
+ * Generate the WASM module bytecode with compress4x, compressChunks4x, and compressParent functions.
82
+ */
83
+ function generateWasmBytes() {
84
+ const code = [];
85
+ // Helper to append bytes
86
+ function put(bytes) {
87
+ code.push(...bytes);
88
+ }
89
+ // WASM module header
90
+ put([0x00, 0x61, 0x73, 0x6d]); // Magic
91
+ put([0x01, 0x00, 0x00, 0x00]); // Version
92
+ // Section 1: Types
93
+ put([0x01]); // Section ID
94
+ put([0x04]); // Section size
95
+ put([0x01]); // 1 type
96
+ put([0x60, 0x00, 0x00]); // func () -> ()
97
+ // Section 2: Imports (memory from JS)
98
+ put([0x02]); // Section ID
99
+ put([0x0b]); // Section size
100
+ put([0x01]); // 1 import
101
+ put([0x02, 0x6a, 0x73]); // "js"
102
+ put([0x03, 0x6d, 0x65, 0x6d]); // "mem"
103
+ put([0x02, 0x00, 0x01]); // memory min=1, no max
104
+ // Section 3: Functions
105
+ put([0x03]); // Section ID
106
+ put([0x04]); // Section size (3 functions = 4 bytes)
107
+ put([0x03]); // 3 functions
108
+ put([0x00]); // Function 0: type index 0
109
+ put([0x00]); // Function 1: type index 0
110
+ put([0x00]); // Function 2: type index 0
111
+ // Section 7: Exports
112
+ // Size calculation: 1 (count) + (1+10+1+1) + (1+16+1+1) + (1+14+1+1) = 1 + 13 + 19 + 17 = 50 bytes
113
+ put([0x07]); // Section ID
114
+ put([0x32]); // Section size (50 bytes)
115
+ put([0x03]); // 3 exports
116
+ // "compress4x" -> func 0
117
+ put([0x0a]); // name length
118
+ put([0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x34, 0x78]); // "compress4x"
119
+ put([0x00, 0x00]); // func index 0
120
+ // "compressChunks4x" -> func 1
121
+ put([0x10]); // name length (16)
122
+ put([
123
+ 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x34, 0x78,
124
+ ]); // "compressChunks4x"
125
+ put([0x00, 0x01]); // func index 1
126
+ // "compressParent" -> func 2
127
+ put([0x0e]); // name length (14)
128
+ put([0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74]); // "compressParent"
129
+ put([0x00, 0x02]); // func index 2
130
+ // Section 10: Code
131
+ put([0x0a]); // Section ID
132
+ // Reserve 5 bytes for section size (LEB128 u32)
133
+ const sectionSizeOffset = code.length;
134
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
135
+ put([0x03]); // 3 functions
136
+ // === Function 0: compress4x ===
137
+ // Reserve 5 bytes for function size
138
+ const funcSizeOffset = code.length;
139
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
140
+ const funcBodyStart = code.length;
141
+ // Local declarations: 32 v128 locals
142
+ // Variables $0-$15: message words (m0-m15)
143
+ // Variables $16-$31: state words (s0-s15)
144
+ put([0x01]); // 1 local declaration
145
+ put([0x20, 0x7b]); // 32 x v128
146
+ // ===== Function body =====
147
+ // Load message words from memory (offset 0-255)
148
+ // Each v128 is 16 bytes, so m[i] is at offset i*16
149
+ // Note: we use toLebU32Min2 to avoid V8 quirk with single-byte values 64-127
150
+ for (let i = 0; i < 16; i++) {
151
+ put([0x41, ...toLebU32Min2(i * 16)]); // i32.const offset (2+ byte LEB128)
152
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
153
+ put([0x21, i]); // local.set $i
154
+ }
155
+ // Load chaining values (offset 512-639)
156
+ // cv[i] at offset 512 + i*16
157
+ for (let i = 0; i < 8; i++) {
158
+ put([0x41, ...toLebU32Min2(512 + i * 16)]); // i32.const offset
159
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
160
+ put([0x21, 16 + i]); // local.set $(16+i)
161
+ }
162
+ // Initialize state[8-15] from IV and parameters
163
+ // s8-s11 = IV[0-3]
164
+ const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a];
165
+ for (let i = 0; i < 4; i++) {
166
+ // Create v128 constant with all lanes set to IV[i]
167
+ const ivBytes = [];
168
+ for (let j = 0; j < 4; j++) {
169
+ ivBytes.push(IV[i] & 0xff);
170
+ ivBytes.push((IV[i] >>> 8) & 0xff);
171
+ ivBytes.push((IV[i] >>> 16) & 0xff);
172
+ ivBytes.push((IV[i] >>> 24) & 0xff);
173
+ }
174
+ put([0xfd, 0x0c, ...ivBytes]); // v128.const
175
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
176
+ }
177
+ // s12 = counter_low (offset 768)
178
+ put([0x41, ...toLebU32Min2(768)]); // i32.const
179
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
180
+ put([0x21, 28]); // local.set $28 -> s12
181
+ // s13 = counter_high (offset 784)
182
+ put([0x41, ...toLebU32Min2(784)]); // i32.const
183
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
184
+ put([0x21, 29]); // local.set $29 -> s13
185
+ // s14 = block_len (offset 800)
186
+ put([0x41, ...toLebU32Min2(800)]); // i32.const
187
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
188
+ put([0x21, 30]); // local.set $30 -> s14
189
+ // s15 = flags (offset 816)
190
+ put([0x41, ...toLebU32Min2(816)]); // i32.const
191
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
192
+ put([0x21, 31]); // local.set $31 -> s15
193
+ // ===== 7 rounds of mixing =====
194
+ let msgIdx = 0; // Index into MSG_ACCESS_ORDER
195
+ // Helper to generate G function (inlined)
196
+ // G(a, b, c, d) with two message words
197
+ function g(a, b, c, d) {
198
+ const mx = MSG_ACCESS_ORDER[msgIdx++];
199
+ const my = MSG_ACCESS_ORDER[msgIdx++];
200
+ // Variables: a,b,c,d are state indices (16-31), mx,my are message indices (0-15)
201
+ // First half of G
202
+ // s[a] = s[a] + s[b] + m[mx]
203
+ put([0x20, 16 + a]); // local.get s[a]
204
+ put([0x20, 16 + b]); // local.get s[b]
205
+ put([0xfd, 0xae, 0x01]); // i32x4.add
206
+ put([0x20, mx]); // local.get m[mx]
207
+ put([0xfd, 0xae, 0x01]); // i32x4.add
208
+ put([0x21, 16 + a]); // local.set s[a]
209
+ // s[d] = rotr(s[d] ^ s[a], 16) - using i8x16.shuffle (single instruction vs shift+or)
210
+ // ROTR16 pattern: [2,3,0,1, 6,7,4,5, 10,11,8,9, 14,15,12,13]
211
+ put([0x20, 16 + d]); // local.get s[d]
212
+ put([0x20, 16 + a]); // local.get s[a]
213
+ put([0xfd, 0x51]); // v128.xor
214
+ put([0x22, 16 + d]); // local.tee s[d]
215
+ put([0x20, 16 + d]); // local.get s[d] (second operand for shuffle)
216
+ put([0xfd, 0x0d, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13]); // i8x16.shuffle ROTR16
217
+ put([0x21, 16 + d]); // local.set s[d]
218
+ // s[c] = s[c] + s[d]
219
+ put([0x20, 16 + c]); // local.get s[c]
220
+ put([0x20, 16 + d]); // local.get s[d]
221
+ put([0xfd, 0xae, 0x01]); // i32x4.add
222
+ put([0x21, 16 + c]); // local.set s[c]
223
+ // s[b] = (s[b] ^ s[c]) >>> 12
224
+ put([0x20, 16 + b]); // local.get s[b]
225
+ put([0x20, 16 + c]); // local.get s[c]
226
+ put([0xfd, 0x51]); // v128.xor
227
+ put([0x22, 16 + b]); // local.tee s[b]
228
+ put([0x41, 0x0c]); // i32.const 12
229
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
230
+ put([0x20, 16 + b]); // local.get s[b]
231
+ put([0x41, 0x14]); // i32.const 20
232
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
233
+ put([0xfd, 0x50]); // v128.or
234
+ put([0x21, 16 + b]); // local.set s[b]
235
+ // Second half of G
236
+ // s[a] = s[a] + s[b] + m[my]
237
+ put([0x20, 16 + a]); // local.get s[a]
238
+ put([0x20, 16 + b]); // local.get s[b]
239
+ put([0xfd, 0xae, 0x01]); // i32x4.add
240
+ put([0x20, my]); // local.get m[my]
241
+ put([0xfd, 0xae, 0x01]); // i32x4.add
242
+ put([0x21, 16 + a]); // local.set s[a]
243
+ // s[d] = rotr(s[d] ^ s[a], 8) - using i8x16.shuffle (single instruction vs shift+or)
244
+ // ROTR8 pattern: [1,2,3,0, 5,6,7,4, 9,10,11,8, 13,14,15,12]
245
+ put([0x20, 16 + d]); // local.get s[d]
246
+ put([0x20, 16 + a]); // local.get s[a]
247
+ put([0xfd, 0x51]); // v128.xor
248
+ put([0x22, 16 + d]); // local.tee s[d]
249
+ put([0x20, 16 + d]); // local.get s[d] (second operand for shuffle)
250
+ put([0xfd, 0x0d, 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12]); // i8x16.shuffle ROTR8
251
+ put([0x21, 16 + d]); // local.set s[d]
252
+ // s[c] = s[c] + s[d]
253
+ put([0x20, 16 + c]); // local.get s[c]
254
+ put([0x20, 16 + d]); // local.get s[d]
255
+ put([0xfd, 0xae, 0x01]); // i32x4.add
256
+ put([0x21, 16 + c]); // local.set s[c]
257
+ // s[b] = (s[b] ^ s[c]) >>> 7
258
+ put([0x20, 16 + b]); // local.get s[b]
259
+ put([0x20, 16 + c]); // local.get s[c]
260
+ put([0xfd, 0x51]); // v128.xor
261
+ put([0x22, 16 + b]); // local.tee s[b]
262
+ put([0x41, 0x07]); // i32.const 7
263
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
264
+ put([0x20, 16 + b]); // local.get s[b]
265
+ put([0x41, 0x19]); // i32.const 25
266
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
267
+ put([0xfd, 0x50]); // v128.or
268
+ put([0x21, 16 + b]); // local.set s[b]
269
+ }
270
+ // Generate all 7 rounds
271
+ for (let round = 0; round < 7; round++) {
272
+ // Column mixing
273
+ g(0, 4, 8, 12);
274
+ g(1, 5, 9, 13);
275
+ g(2, 6, 10, 14);
276
+ g(3, 7, 11, 15);
277
+ // Diagonal mixing
278
+ g(0, 5, 10, 15);
279
+ g(1, 6, 11, 12);
280
+ g(2, 7, 8, 13);
281
+ g(3, 4, 9, 14);
282
+ }
283
+ // ===== Final XOR and store output =====
284
+ // out[i] = s[i] ^ s[i+8] for i in 0..7
285
+ // Store at offset 640-767
286
+ for (let i = 0; i < 8; i++) {
287
+ put([0x41, ...toLebU32Min2(640 + i * 16)]); // i32.const offset
288
+ put([0x20, 16 + i]); // local.get s[i]
289
+ put([0x20, 24 + i]); // local.get s[i+8]
290
+ put([0xfd, 0x51]); // v128.xor
291
+ put([0xfd, 0x0b, 0x02, 0x00]); // v128.store align=4
292
+ }
293
+ // End of function
294
+ put([0x0b]); // end
295
+ // Fill in function 0 size using padded LEB128
296
+ const funcBodySize = code.length - funcBodyStart;
297
+ const funcSizeBytes = toLebU32Padded5(funcBodySize);
298
+ for (let i = 0; i < 5; i++) {
299
+ code[funcSizeOffset + i] = funcSizeBytes[i];
300
+ }
301
+ // === Function 1: compressChunks4x ===
302
+ // Reserve 5 bytes for function size
303
+ const func1SizeOffset = code.length;
304
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
305
+ const func1BodyStart = code.length;
306
+ // Generate the compressChunks4x function body
307
+ const compressChunksBody = generateCompressChunks4xBody();
308
+ put(compressChunksBody);
309
+ // Fill in function 1 size using padded LEB128
310
+ const func1BodySize = code.length - func1BodyStart;
311
+ const func1SizeBytes = toLebU32Padded5(func1BodySize);
312
+ for (let i = 0; i < 5; i++) {
313
+ code[func1SizeOffset + i] = func1SizeBytes[i];
314
+ }
315
+ // === Function 2: compressParent ===
316
+ // Reserve 5 bytes for function size
317
+ const func2SizeOffset = code.length;
318
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
319
+ const func2BodyStart = code.length;
320
+ // Generate the compressParent function body
321
+ const compressParentBody = generateCompressParentBody();
322
+ put(compressParentBody);
323
+ // Fill in function 2 size using padded LEB128
324
+ const func2BodySize = code.length - func2BodyStart;
325
+ const func2SizeBytes = toLebU32Padded5(func2BodySize);
326
+ for (let i = 0; i < 5; i++) {
327
+ code[func2SizeOffset + i] = func2SizeBytes[i];
328
+ }
329
+ // Fill in section size using padded LEB128
330
+ const sectionSize = code.length - sectionSizeOffset - 5;
331
+ const sectionSizeBytes = toLebU32Padded5(sectionSize);
332
+ for (let i = 0; i < 5; i++) {
333
+ code[sectionSizeOffset + i] = sectionSizeBytes[i];
334
+ }
335
+ return new Uint8Array(code);
336
+ }
337
+ /**
338
+ * Generate compressChunks4x WASM function body.
339
+ * Processes all 16 blocks of 4 chunks in a single call.
340
+ */
341
+ function generateCompressChunks4xBody() {
342
+ const code = [];
343
+ function put(bytes) {
344
+ code.push(...bytes);
345
+ }
346
+ // Local declarations: 32 v128 locals + 1 i32 for position
347
+ // Locals $0-$15: message words (reloaded each iteration)
348
+ // Locals $16-$31: state words (s0-s15)
349
+ // Local $32: position counter (i32)
350
+ put([0x02]); // 2 local declarations
351
+ put([0x20, 0x7b]); // 32 x v128
352
+ put([0x01, 0x7f]); // 1 x i32
353
+ const BATCH_BLOCK_WORDS = SIMD_MEMORY.BATCH_BLOCK_WORDS;
354
+ const BATCH_CV = SIMD_MEMORY.BATCH_CV;
355
+ const BATCH_COUNTER_LOW = SIMD_MEMORY.BATCH_COUNTER_LOW;
356
+ const BATCH_FLAGS_BASE = SIMD_MEMORY.BATCH_FLAGS_BASE;
357
+ const BATCH_OUTPUT = SIMD_MEMORY.BATCH_OUTPUT;
358
+ // IV constants (same as compress4x)
359
+ const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a];
360
+ // Load initial CVs from BATCH_CV into locals $16-$23
361
+ for (let i = 0; i < 8; i++) {
362
+ put([0x41, ...toLebU32Min2(BATCH_CV + i * 16)]); // i32.const offset
363
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
364
+ put([0x21, 16 + i]); // local.set $(16+i) -> s0-s7
365
+ }
366
+ // Initialize $32 (pos) = 0
367
+ put([0x41, 0x00]); // i32.const 0
368
+ put([0x21, 0x20]); // local.set $32
369
+ // block $done
370
+ put([0x02, 0x40]); // block void
371
+ // loop $continue
372
+ put([0x03, 0x40]); // loop void
373
+ // === Load message words for position $pos ===
374
+ // offset = BATCH_BLOCK_WORDS + pos * 256 + word * 16
375
+ for (let w = 0; w < 16; w++) {
376
+ put([0x20, 0x20]); // local.get $32 (pos)
377
+ put([0x41, ...toLebU32Min2(256)]); // i32.const 256
378
+ put([0x6c]); // i32.mul
379
+ put([0x41, ...toLebU32Min2(BATCH_BLOCK_WORDS + w * 16)]); // i32.const base + word*16
380
+ put([0x6a]); // i32.add
381
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
382
+ put([0x21, w]); // local.set $w
383
+ }
384
+ // === Initialize state[8-15] ===
385
+ // s8-s11 = IV[0-3]
386
+ for (let i = 0; i < 4; i++) {
387
+ const ivBytes = [];
388
+ for (let j = 0; j < 4; j++) {
389
+ ivBytes.push(IV[i] & 0xff);
390
+ ivBytes.push((IV[i] >>> 8) & 0xff);
391
+ ivBytes.push((IV[i] >>> 16) & 0xff);
392
+ ivBytes.push((IV[i] >>> 24) & 0xff);
393
+ }
394
+ put([0xfd, 0x0c, ...ivBytes]); // v128.const
395
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
396
+ }
397
+ // s12 = counter_low (from BATCH_COUNTER_LOW)
398
+ put([0x41, ...toLebU32Min2(BATCH_COUNTER_LOW)]); // i32.const
399
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
400
+ put([0x21, 28]); // local.set $28 -> s12
401
+ // s13 = 0 (counter high - assume fits in 32 bits)
402
+ put([0xfd, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // v128.const 0
403
+ put([0x21, 29]); // local.set $29 -> s13
404
+ // s14 = 64 (block_len = 64 for full blocks)
405
+ const blockLen64 = [];
406
+ for (let j = 0; j < 4; j++) {
407
+ blockLen64.push(64, 0, 0, 0); // 64 in little-endian
408
+ }
409
+ put([0xfd, 0x0c, ...blockLen64]); // v128.const [64,64,64,64]
410
+ put([0x21, 30]); // local.set $30 -> s14
411
+ // s15 = flags = base_flags | (pos == 0 ? 1 : 0) | (pos == 15 ? 2 : 0)
412
+ // First load base flags
413
+ put([0x41, ...toLebU32Min2(BATCH_FLAGS_BASE)]); // i32.const
414
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load base flags
415
+ // Compute position-dependent bits
416
+ // CHUNK_START (1) if pos == 0
417
+ put([0x20, 0x20]); // local.get $32 (pos)
418
+ put([0x45]); // i32.eqz -> 1 if pos==0, 0 otherwise
419
+ // CHUNK_END (2) if pos == 15
420
+ put([0x20, 0x20]); // local.get $32 (pos)
421
+ put([0x41, 0x0f]); // i32.const 15
422
+ put([0x46]); // i32.eq -> 1 if pos==15, 0 otherwise
423
+ put([0x41, 0x01]); // i32.const 1 (shift amount)
424
+ put([0x74]); // i32.shl -> 2 if pos==15, 0 otherwise
425
+ // OR the two bits together
426
+ put([0x72]); // i32.or -> combined position bits
427
+ // Splat to v128 and OR with base flags (stack: base_flags, bits)
428
+ put([0xfd, 0x11]); // i32x4.splat
429
+ put([0xfd, 0x50]); // v128.or
430
+ put([0x21, 31]); // local.set $31 -> s15
431
+ // === 7 rounds of mixing ===
432
+ let msgIdx = 0;
433
+ function g(a, b, c, d) {
434
+ const mx = MSG_ACCESS_ORDER[msgIdx++];
435
+ const my = MSG_ACCESS_ORDER[msgIdx++];
436
+ // First half of G: s[a] = s[a] + s[b] + m[mx]
437
+ put([0x20, 16 + a]); // local.get s[a]
438
+ put([0x20, 16 + b]); // local.get s[b]
439
+ put([0xfd, 0xae, 0x01]); // i32x4.add
440
+ put([0x20, mx]); // local.get m[mx]
441
+ put([0xfd, 0xae, 0x01]); // i32x4.add
442
+ put([0x21, 16 + a]); // local.set s[a]
443
+ // s[d] = rotr(s[d] ^ s[a], 16) - byte shuffle
444
+ put([0x20, 16 + d]); // local.get s[d]
445
+ put([0x20, 16 + a]); // local.get s[a]
446
+ put([0xfd, 0x51]); // v128.xor
447
+ put([0x22, 16 + d]); // local.tee s[d]
448
+ put([0x20, 16 + d]); // local.get s[d]
449
+ put([0xfd, 0x0d, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13]); // i8x16.shuffle ROTR16
450
+ put([0x21, 16 + d]); // local.set s[d]
451
+ // s[c] = s[c] + s[d]
452
+ put([0x20, 16 + c]); // local.get s[c]
453
+ put([0x20, 16 + d]); // local.get s[d]
454
+ put([0xfd, 0xae, 0x01]); // i32x4.add
455
+ put([0x21, 16 + c]); // local.set s[c]
456
+ // s[b] = rotr(s[b] ^ s[c], 12)
457
+ put([0x20, 16 + b]); // local.get s[b]
458
+ put([0x20, 16 + c]); // local.get s[c]
459
+ put([0xfd, 0x51]); // v128.xor
460
+ put([0x22, 16 + b]); // local.tee s[b]
461
+ put([0x41, 0x0c]); // i32.const 12
462
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
463
+ put([0x20, 16 + b]); // local.get s[b]
464
+ put([0x41, 0x14]); // i32.const 20
465
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
466
+ put([0xfd, 0x50]); // v128.or
467
+ put([0x21, 16 + b]); // local.set s[b]
468
+ // Second half: s[a] = s[a] + s[b] + m[my]
469
+ put([0x20, 16 + a]); // local.get s[a]
470
+ put([0x20, 16 + b]); // local.get s[b]
471
+ put([0xfd, 0xae, 0x01]); // i32x4.add
472
+ put([0x20, my]); // local.get m[my]
473
+ put([0xfd, 0xae, 0x01]); // i32x4.add
474
+ put([0x21, 16 + a]); // local.set s[a]
475
+ // s[d] = rotr(s[d] ^ s[a], 8) - byte shuffle
476
+ put([0x20, 16 + d]); // local.get s[d]
477
+ put([0x20, 16 + a]); // local.get s[a]
478
+ put([0xfd, 0x51]); // v128.xor
479
+ put([0x22, 16 + d]); // local.tee s[d]
480
+ put([0x20, 16 + d]); // local.get s[d]
481
+ put([0xfd, 0x0d, 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12]); // i8x16.shuffle ROTR8
482
+ put([0x21, 16 + d]); // local.set s[d]
483
+ // s[c] = s[c] + s[d]
484
+ put([0x20, 16 + c]); // local.get s[c]
485
+ put([0x20, 16 + d]); // local.get s[d]
486
+ put([0xfd, 0xae, 0x01]); // i32x4.add
487
+ put([0x21, 16 + c]); // local.set s[c]
488
+ // s[b] = rotr(s[b] ^ s[c], 7)
489
+ put([0x20, 16 + b]); // local.get s[b]
490
+ put([0x20, 16 + c]); // local.get s[c]
491
+ put([0xfd, 0x51]); // v128.xor
492
+ put([0x22, 16 + b]); // local.tee s[b]
493
+ put([0x41, 0x07]); // i32.const 7
494
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
495
+ put([0x20, 16 + b]); // local.get s[b]
496
+ put([0x41, 0x19]); // i32.const 25
497
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
498
+ put([0xfd, 0x50]); // v128.or
499
+ put([0x21, 16 + b]); // local.set s[b]
500
+ }
501
+ // Generate all 7 rounds
502
+ for (let round = 0; round < 7; round++) {
503
+ // Column mixing
504
+ g(0, 4, 8, 12);
505
+ g(1, 5, 9, 13);
506
+ g(2, 6, 10, 14);
507
+ g(3, 7, 11, 15);
508
+ // Diagonal mixing
509
+ g(0, 5, 10, 15);
510
+ g(1, 6, 11, 12);
511
+ g(2, 7, 8, 13);
512
+ g(3, 4, 9, 14);
513
+ }
514
+ // === Update CVs: cv[i] = s[i] ^ s[i+8] ===
515
+ // Store back to state locals $16-$23 (the CV positions)
516
+ for (let i = 0; i < 8; i++) {
517
+ put([0x20, 16 + i]); // local.get s[i]
518
+ put([0x20, 24 + i]); // local.get s[i+8]
519
+ put([0xfd, 0x51]); // v128.xor
520
+ put([0x21, 16 + i]); // local.set $(16+i) - update CV
521
+ }
522
+ // === Loop control: pos++, continue if pos < 16 ===
523
+ put([0x20, 0x20]); // local.get $32 (pos)
524
+ put([0x41, 0x01]); // i32.const 1
525
+ put([0x6a]); // i32.add
526
+ put([0x22, 0x20]); // local.tee $32 (pos)
527
+ put([0x41, 0x10]); // i32.const 16
528
+ put([0x49]); // i32.lt_u
529
+ put([0x0d, 0x00]); // br_if 0 (continue loop)
530
+ // end loop
531
+ put([0x0b]); // end
532
+ // end block
533
+ put([0x0b]); // end
534
+ // === Store final CVs to BATCH_OUTPUT ===
535
+ for (let i = 0; i < 8; i++) {
536
+ put([0x41, ...toLebU32Min2(BATCH_OUTPUT + i * 16)]); // i32.const offset
537
+ put([0x20, 16 + i]); // local.get $(16+i) - CV word
538
+ put([0xfd, 0x0b, 0x02, 0x00]); // v128.store align=4
539
+ }
540
+ // end function
541
+ put([0x0b]); // end
542
+ return code;
543
+ }
544
+ /**
545
+ * Generate compressParent WASM function body.
546
+ * Performs a single parent node compression using scalar i32 operations.
547
+ * Reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
548
+ * Uses IV, counter=0, blockLen=64, flags=PARENT(4).
549
+ */
550
+ function generateCompressParentBody() {
551
+ const code = [];
552
+ function put(bytes) {
553
+ code.push(...bytes);
554
+ }
555
+ // Local declarations: 32 i32 locals for state (s0-s15) and message (m0-m15)
556
+ put([0x01]); // 1 local declaration
557
+ put([0x20, 0x7f]); // 32 x i32
558
+ // Message word indices: 0-15, State indices: 16-31
559
+ // Locals $0-$15: message words (m0-m15)
560
+ // Locals $16-$31: state words (s0-s15)
561
+ const PARENT_BLOCK_OFFSET = SIMD_MEMORY.PARENT_BLOCK;
562
+ const CHUNK_CV_OFFSET = SIMD_MEMORY.CHUNK_CV;
563
+ // BLAKE3 IV
564
+ const IV = [
565
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
566
+ ];
567
+ // Load message words from PARENT_BLOCK (16 words at offset 7264)
568
+ for (let i = 0; i < 16; i++) {
569
+ put([0x41, ...toLebU32Min2(PARENT_BLOCK_OFFSET + i * 4)]); // i32.const offset
570
+ put([0x28, 0x02, 0x00]); // i32.load align=4 offset=0
571
+ put([0x21, i]); // local.set $i (m0-m15)
572
+ }
573
+ // Initialize state s0-s7 = IV[0-7]
574
+ for (let i = 0; i < 8; i++) {
575
+ put([0x41, ...toSignedLeb128_i32(IV[i])]); // i32.const IV[i]
576
+ put([0x21, 16 + i]); // local.set $(16+i) -> s0-s7
577
+ }
578
+ // Initialize state s8-s11 = IV[0-3]
579
+ for (let i = 0; i < 4; i++) {
580
+ put([0x41, ...toSignedLeb128_i32(IV[i])]); // i32.const IV[i]
581
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
582
+ }
583
+ // s12 = counter_low = 0
584
+ put([0x41, 0x00]); // i32.const 0
585
+ put([0x21, 28]); // local.set $28 -> s12
586
+ // s13 = counter_high = 0
587
+ put([0x41, 0x00]); // i32.const 0
588
+ put([0x21, 29]); // local.set $29 -> s13
589
+ // s14 = block_len = 64
590
+ // Note: 0x40 alone is -64 in signed LEB128 (bit 6 is sign bit)
591
+ // For 64, we need [0xC0, 0x00] to avoid sign extension
592
+ put([0x41, 0xc0, 0x00]); // i32.const 64
593
+ put([0x21, 30]); // local.set $30 -> s14
594
+ // s15 = flags = PARENT = 4
595
+ put([0x41, 0x04]); // i32.const 4
596
+ put([0x21, 31]); // local.set $31 -> s15
597
+ // Helper to generate scalar G function (inlined)
598
+ // G(a, b, c, d, mx, my) where a,b,c,d are state indices 0-15, mx,my are message indices 0-15
599
+ function g(a, b, c, d, mx, my) {
600
+ const sa = 16 + a, sb = 16 + b, sc = 16 + c, sd = 16 + d;
601
+ // s[a] = (s[a] + s[b] + m[mx]) >>> 0
602
+ put([0x20, sa]); // local.get s[a]
603
+ put([0x20, sb]); // local.get s[b]
604
+ put([0x6a]); // i32.add
605
+ put([0x20, mx]); // local.get m[mx]
606
+ put([0x6a]); // i32.add
607
+ put([0x21, sa]); // local.set s[a]
608
+ // s[d] = rotr(s[d] ^ s[a], 16)
609
+ put([0x20, sd]); // local.get s[d]
610
+ put([0x20, sa]); // local.get s[a]
611
+ put([0x73]); // i32.xor
612
+ put([0x41, 0x10]); // i32.const 16
613
+ put([0x78]); // i32.rotr
614
+ put([0x21, sd]); // local.set s[d]
615
+ // s[c] = (s[c] + s[d]) >>> 0
616
+ put([0x20, sc]); // local.get s[c]
617
+ put([0x20, sd]); // local.get s[d]
618
+ put([0x6a]); // i32.add
619
+ put([0x21, sc]); // local.set s[c]
620
+ // s[b] = rotr(s[b] ^ s[c], 12)
621
+ put([0x20, sb]); // local.get s[b]
622
+ put([0x20, sc]); // local.get s[c]
623
+ put([0x73]); // i32.xor
624
+ put([0x41, 0x0c]); // i32.const 12
625
+ put([0x78]); // i32.rotr
626
+ put([0x21, sb]); // local.set s[b]
627
+ // s[a] = (s[a] + s[b] + m[my]) >>> 0
628
+ put([0x20, sa]); // local.get s[a]
629
+ put([0x20, sb]); // local.get s[b]
630
+ put([0x6a]); // i32.add
631
+ put([0x20, my]); // local.get m[my]
632
+ put([0x6a]); // i32.add
633
+ put([0x21, sa]); // local.set s[a]
634
+ // s[d] = rotr(s[d] ^ s[a], 8)
635
+ put([0x20, sd]); // local.get s[d]
636
+ put([0x20, sa]); // local.get s[a]
637
+ put([0x73]); // i32.xor
638
+ put([0x41, 0x08]); // i32.const 8
639
+ put([0x78]); // i32.rotr
640
+ put([0x21, sd]); // local.set s[d]
641
+ // s[c] = (s[c] + s[d]) >>> 0
642
+ put([0x20, sc]); // local.get s[c]
643
+ put([0x20, sd]); // local.get s[d]
644
+ put([0x6a]); // i32.add
645
+ put([0x21, sc]); // local.set s[c]
646
+ // s[b] = rotr(s[b] ^ s[c], 7)
647
+ put([0x20, sb]); // local.get s[b]
648
+ put([0x20, sc]); // local.get s[c]
649
+ put([0x73]); // i32.xor
650
+ put([0x41, 0x07]); // i32.const 7
651
+ put([0x78]); // i32.rotr
652
+ put([0x21, sb]); // local.set s[b]
653
+ }
654
+ // 7 rounds of mixing with permuted message schedule
655
+ let msgIdx = 0;
656
+ for (let round = 0; round < 7; round++) {
657
+ // Column mixing
658
+ g(0, 4, 8, 12, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
659
+ msgIdx += 2;
660
+ g(1, 5, 9, 13, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
661
+ msgIdx += 2;
662
+ g(2, 6, 10, 14, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
663
+ msgIdx += 2;
664
+ g(3, 7, 11, 15, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
665
+ msgIdx += 2;
666
+ // Diagonal mixing
667
+ g(0, 5, 10, 15, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
668
+ msgIdx += 2;
669
+ g(1, 6, 11, 12, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
670
+ msgIdx += 2;
671
+ g(2, 7, 8, 13, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
672
+ msgIdx += 2;
673
+ g(3, 4, 9, 14, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
674
+ msgIdx += 2;
675
+ }
676
+ // Store output: out[i] = s[i] ^ s[i+8] for i in 0..7
677
+ for (let i = 0; i < 8; i++) {
678
+ put([0x41, ...toLebU32Min2(CHUNK_CV_OFFSET + i * 4)]); // i32.const offset
679
+ put([0x20, 16 + i]); // local.get s[i]
680
+ put([0x20, 24 + i]); // local.get s[i+8]
681
+ put([0x73]); // i32.xor
682
+ put([0x36, 0x02, 0x00]); // i32.store align=4 offset=0
683
+ }
684
+ // end function
685
+ put([0x0b]); // end
686
+ return code;
687
+ }
688
+ // Cached WASM instance
689
+ let wasmInstance = null;
690
+ let wasmMemory = null;
691
+ let wasmCompress4x = null;
692
+ let wasmCompressChunks4x = null;
693
+ let wasmCompressParent = null;
694
+ let wasmMemoryView = null;
695
+ let wasmMemoryView32 = null;
696
+ /**
697
+ * Check if WASM SIMD is supported.
698
+ */
699
+ export function isSimdSupported() {
700
+ try {
701
+ // Minimal WASM module with v128.const instruction to test SIMD support
702
+ const simdTest = new Uint8Array([
703
+ 0x00,
704
+ 0x61,
705
+ 0x73,
706
+ 0x6d, // magic: \0asm
707
+ 0x01,
708
+ 0x00,
709
+ 0x00,
710
+ 0x00, // version: 1
711
+ // Type section (id=1): () -> v128
712
+ 0x01, // section id = 1 (type)
713
+ 0x05, // section length = 5
714
+ 0x01, // 1 type
715
+ 0x60,
716
+ 0x00,
717
+ 0x01,
718
+ 0x7b, // func () -> v128
719
+ // Function section (id=3)
720
+ 0x03, // section id = 3 (function)
721
+ 0x02, // section length = 2
722
+ 0x01, // 1 function
723
+ 0x00, // type index 0
724
+ // Code section (id=10) with v128.const
725
+ 0x0a, // section id = 10 (code)
726
+ 0x16, // section length = 22
727
+ 0x01, // 1 function body
728
+ 0x14, // body length = 20
729
+ 0x00, // 0 locals
730
+ 0xfd,
731
+ 0x0c, // v128.const opcode
732
+ 0x00,
733
+ 0x00,
734
+ 0x00,
735
+ 0x00,
736
+ 0x00,
737
+ 0x00,
738
+ 0x00,
739
+ 0x00,
740
+ 0x00,
741
+ 0x00,
742
+ 0x00,
743
+ 0x00,
744
+ 0x00,
745
+ 0x00,
746
+ 0x00,
747
+ 0x00,
748
+ 0x0b, // end
749
+ ]);
750
+ return WebAssembly.validate(simdTest);
751
+ }
752
+ catch {
753
+ return false;
754
+ }
755
+ }
756
+ /**
757
+ * Set up arena views over WASM memory.
758
+ * Called after WASM memory is allocated.
759
+ */
760
+ function setupArenaViews() {
761
+ if (!wasmMemory)
762
+ return;
763
+ const buffer = wasmMemory.buffer;
764
+ // Create TypedArray views over WASM memory for arena buffers
765
+ // These views are backed by WASM memory, eliminating JS heap allocation
766
+ arenaCvStack = new Uint32Array(buffer, SIMD_MEMORY.CV_STACK, 64 * 8); // 64 levels × 8 words
767
+ arenaParentBlock = new Uint32Array(buffer, SIMD_MEMORY.PARENT_BLOCK, 16); // 16 words
768
+ arenaChunkCv = new Uint32Array(buffer, SIMD_MEMORY.CHUNK_CV, 8); // 8 words
769
+ arenaTempCvs = new Uint32Array(buffer, SIMD_MEMORY.TEMP_CVS, 32); // 4 × 8 words
770
+ // Batch mode views
771
+ // 16 positions × 16 v128 words = 16 × 64 u32 words = 1024 words per position? No...
772
+ // In u32 terms: 16 positions × 16 words × 4 lanes = 1024 u32 values total
773
+ arenaBatchBlockWords = new Uint32Array(buffer, SIMD_MEMORY.BATCH_BLOCK_WORDS, 16 * 16 * 4); // 16 pos × 16 words × 4 lanes
774
+ arenaBatchCv = new Uint32Array(buffer, SIMD_MEMORY.BATCH_CV, 32); // 4 × 8 words
775
+ arenaBatchCounterLow = new Uint32Array(buffer, SIMD_MEMORY.BATCH_COUNTER_LOW, 4); // 4 words
776
+ arenaBatchFlagsBase = new Uint32Array(buffer, SIMD_MEMORY.BATCH_FLAGS_BASE, 4); // 4 words
777
+ arenaBatchOutput = new Uint32Array(buffer, SIMD_MEMORY.BATCH_OUTPUT, 32); // 4 × 8 words
778
+ }
779
+ /**
780
+ * Initialize the WASM SIMD module synchronously.
781
+ * Call this once before using compress4x.
782
+ */
783
+ // Cache generated WASM bytes to avoid regenerating on each init
784
+ let cachedWasmBytes = null;
785
+ export function initSimdSync() {
786
+ if (wasmInstance)
787
+ return true;
788
+ if (!isSimdSupported()) {
789
+ return false;
790
+ }
791
+ try {
792
+ const wasmBytes = cachedWasmBytes || generateWasmBytes();
793
+ cachedWasmBytes = wasmBytes;
794
+ wasmMemory = new WebAssembly.Memory({ initial: 1 });
795
+ const importObject = {
796
+ js: { mem: wasmMemory },
797
+ };
798
+ const module = new WebAssembly.Module(wasmBytes.buffer);
799
+ wasmInstance = new WebAssembly.Instance(module, importObject);
800
+ wasmCompress4x = wasmInstance.exports.compress4x;
801
+ wasmCompressChunks4x = wasmInstance.exports.compressChunks4x;
802
+ wasmCompressParent = wasmInstance.exports.compressParent;
803
+ wasmMemoryView = new Uint8Array(wasmMemory.buffer);
804
+ wasmMemoryView32 = new Uint32Array(wasmMemory.buffer);
805
+ // Set up arena views for Merkle tree operations
806
+ setupArenaViews();
807
+ return true;
808
+ }
809
+ catch (e) {
810
+ console.warn("Failed to initialize WASM SIMD:", e);
811
+ return false;
812
+ }
813
+ }
814
+ /**
815
+ * Memory offsets for SIMD data layout
816
+ *
817
+ * WASM Arena Pattern: All working buffers live in WASM memory (64KB page)
818
+ * This eliminates JS heap allocations during hashing operations.
819
+ */
820
+ export const SIMD_MEMORY = {
821
+ // SIMD compress4x working area (used by WASM code) - single block
822
+ BLOCK_WORDS: 0, // 4 x 16 words = 512 bytes (transposed layout)
823
+ CHAINING_VALUES: 512, // 4 x 8 words = 128 bytes
824
+ OUTPUT: 640, // 4 x 8 words = 128 bytes
825
+ COUNTER_LOW: 768, // 4 words = 16 bytes
826
+ COUNTER_HIGH: 784, // 4 words = 16 bytes
827
+ BLOCK_LEN: 800, // 4 words = 16 bytes
828
+ FLAGS: 816, // 4 words = 16 bytes
829
+ // End of single-block SIMD working area: 832 bytes
830
+ // SIMD compressChunks4x working area - 16 blocks batched
831
+ // Each block position has 16 v128 values (one per message word) = 256 bytes
832
+ // 16 block positions = 16 × 256 = 4096 bytes
833
+ BATCH_BLOCK_WORDS: 832, // 16 positions × 256 bytes = 4096 bytes (transposed), ends at 4928
834
+ BATCH_CV: 4928, // 4 × 8 words × 4 bytes = 128 bytes (working CVs), ends at 5056
835
+ BATCH_COUNTER_LOW: 5056, // 4 words × 4 bytes = 16 bytes (per-chunk counters), ends at 5072
836
+ BATCH_FLAGS_BASE: 5072, // 4 words × 4 bytes = 16 bytes (base flags, no START/END), ends at 5088
837
+ BATCH_OUTPUT: 5088, // 4 × 8 words × 4 bytes = 128 bytes (final output), ends at 5216
838
+ // End of batch working area: 5216 bytes
839
+ // WASM Arena: JS working buffers (accessed via TypedArray views)
840
+ CV_STACK: 5216, // 64 levels × 8 words × 4 bytes = 2048 bytes, ends at 7264
841
+ PARENT_BLOCK: 7264, // 16 words × 4 bytes = 64 bytes, ends at 7328
842
+ CHUNK_CV: 7328, // 8 words × 4 bytes = 32 bytes, ends at 7360
843
+ TEMP_CVS: 7360, // 4 × 8 words × 4 bytes = 128 bytes, ends at 7488
844
+ // Total arena usage: ~7488 bytes (fits comfortably in 64KB page)
845
+ };
846
+ // Arena views - created once when SIMD initializes
847
+ let arenaCvStack = null;
848
+ let arenaParentBlock = null;
849
+ let arenaChunkCv = null;
850
+ let arenaTempCvs = null;
851
+ // Batch mode arena views
852
+ let arenaBatchBlockWords = null;
853
+ let arenaBatchCv = null;
854
+ let arenaBatchCounterLow = null;
855
+ let arenaBatchFlagsBase = null;
856
+ let arenaBatchOutput = null;
857
+ /**
858
+ * Get the WASM memory views for writing input data.
859
+ */
860
+ export function getSimdMemory() {
861
+ if (!wasmMemoryView || !wasmMemoryView32)
862
+ return null;
863
+ return { view: wasmMemoryView, view32: wasmMemoryView32 };
864
+ }
865
+ /**
866
+ * Get the arena buffers for Merkle tree operations.
867
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
868
+ */
869
+ export function getArenaBuffers() {
870
+ if (!arenaCvStack || !arenaParentBlock || !arenaChunkCv || !arenaTempCvs)
871
+ return null;
872
+ return {
873
+ cvStack: arenaCvStack,
874
+ parentBlock: arenaParentBlock,
875
+ chunkCv: arenaChunkCv,
876
+ tempCvs: arenaTempCvs,
877
+ };
878
+ }
879
+ /**
880
+ * Get the batch arena buffers for chunk-level batched operations.
881
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
882
+ */
883
+ export function getBatchArenaBuffers() {
884
+ if (!arenaBatchBlockWords ||
885
+ !arenaBatchCv ||
886
+ !arenaBatchCounterLow ||
887
+ !arenaBatchFlagsBase ||
888
+ !arenaBatchOutput)
889
+ return null;
890
+ return {
891
+ blockWords: arenaBatchBlockWords,
892
+ cv: arenaBatchCv,
893
+ counterLow: arenaBatchCounterLow,
894
+ flagsBase: arenaBatchFlagsBase,
895
+ output: arenaBatchOutput,
896
+ };
897
+ }
898
+ /**
899
+ * Run the compress4x function.
900
+ * Data must already be set up in WASM memory.
901
+ */
902
+ export function runCompress4x() {
903
+ if (!wasmCompress4x) {
904
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
905
+ }
906
+ wasmCompress4x();
907
+ }
908
+ /**
909
+ * Run the compressChunks4x function.
910
+ * Processes 4 full chunks (16 blocks each) in a single WASM call.
911
+ * Data must already be set up in batch arena buffers.
912
+ */
913
+ export function runCompressChunks4x() {
914
+ if (!wasmCompressChunks4x) {
915
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
916
+ }
917
+ wasmCompressChunks4x();
918
+ }
919
+ /**
920
+ * Run the compressParent function.
921
+ * Compresses a parent node: reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
922
+ * Data must already be set up in arena buffers (PARENT_BLOCK at offset 7264).
923
+ * Output is written to CHUNK_CV at offset 7328.
924
+ */
925
+ export function runCompressParent() {
926
+ if (!wasmCompressParent) {
927
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
928
+ }
929
+ wasmCompressParent();
930
+ }
931
+ /**
932
+ * Check if SIMD is initialized and ready.
933
+ */
934
+ export function isSimdReady() {
935
+ return wasmCompress4x !== null;
936
+ }
node_modules/@huggingface/blake3-jit/package.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "@huggingface/blake3-jit",
3
+ "version": "0.0.2",
4
+ "description": "Temporary fork of blake3-jit with Hasher.reset() and pre-allocated buffers. Will be deprecated once upstream blake3-jit exposes reset().",
5
+ "keywords": [
6
+ "blake3",
7
+ "hash",
8
+ "fast",
9
+ "wasm"
10
+ ],
11
+ "license": "MIT",
12
+ "author": "Hugging Face",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "src",
19
+ "LICENSE",
20
+ "README.md"
21
+ ],
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "scripts": {
25
+ "prepare": "tshy"
26
+ },
27
+ "tshy": {
28
+ "exports": {
29
+ ".": "./src/index.ts",
30
+ "./package.json": "./package.json"
31
+ }
32
+ },
33
+ "exports": {
34
+ ".": {
35
+ "import": {
36
+ "types": "./dist/esm/index.d.ts",
37
+ "default": "./dist/esm/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/commonjs/index.d.ts",
41
+ "default": "./dist/commonjs/index.js"
42
+ }
43
+ },
44
+ "./package.json": "./package.json"
45
+ },
46
+ "main": "./dist/commonjs/index.js",
47
+ "types": "./dist/commonjs/index.d.ts",
48
+ "module": "./dist/esm/index.js"
49
+ }
node_modules/@huggingface/blake3-jit/src/compress.ts ADDED
@@ -0,0 +1,954 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Compression Function - Highly Optimized
3
+ *
4
+ * Optimization techniques applied (from Fleek Network case study):
5
+ * 1. Use 16 SMI variables for state instead of TypedArray
6
+ * 2. Use 16 SMI variables for message words
7
+ * 3. Fully inlined G function (no function call overhead)
8
+ * 4. Use `| 0` for integer coercion (forces V8 to use 32-bit ALU)
9
+ * 5. Hardcoded permutation swaps using only 2 temporary variables
10
+ * 6. Offset-based access pattern (avoid creating new views)
11
+ *
12
+ * The compression function takes:
13
+ * - cv: 8-word chaining value
14
+ * - block: 16-word message block (64 bytes)
15
+ * - counter: 64-bit block counter
16
+ * - blockLen: number of input bytes in this block
17
+ * - flags: domain separation flags
18
+ *
19
+ * And outputs 8 or 16 words depending on whether this is a root node.
20
+ */
21
+
22
+ /**
23
+ * Compress a single block.
24
+ *
25
+ * This is the hot path - every optimization matters here.
26
+ *
27
+ * @param cv - Chaining value array
28
+ * @param cvOff - Offset into cv
29
+ * @param block - Message block words
30
+ * @param blockOff - Offset into block
31
+ * @param out - Output array (8 or 16 words)
32
+ * @param outOff - Offset into out
33
+ * @param full - If true, output all 16 words (for XOF); if false, output 8 words
34
+ * @param counter - 64-bit block counter
35
+ * @param blockLen - Number of bytes in this block (0-64)
36
+ * @param flags - Domain separation flags
37
+ */
38
+ export function compress(
39
+ cv: Uint32Array,
40
+ cvOff: number,
41
+ block: Uint32Array,
42
+ blockOff: number,
43
+ out: Uint32Array,
44
+ outOff: number,
45
+ full: boolean,
46
+ counter: number,
47
+ blockLen: number,
48
+ flags: number,
49
+ ): void {
50
+ // Load message words into SMI variables for maximum performance
51
+ // V8 optimizes SMI arithmetic directly with the ALU
52
+ let m0 = block[blockOff] | 0;
53
+ let m1 = block[blockOff + 1] | 0;
54
+ let m2 = block[blockOff + 2] | 0;
55
+ let m3 = block[blockOff + 3] | 0;
56
+ let m4 = block[blockOff + 4] | 0;
57
+ let m5 = block[blockOff + 5] | 0;
58
+ let m6 = block[blockOff + 6] | 0;
59
+ let m7 = block[blockOff + 7] | 0;
60
+ let m8 = block[blockOff + 8] | 0;
61
+ let m9 = block[blockOff + 9] | 0;
62
+ let m10 = block[blockOff + 10] | 0;
63
+ let m11 = block[blockOff + 11] | 0;
64
+ let m12 = block[blockOff + 12] | 0;
65
+ let m13 = block[blockOff + 13] | 0;
66
+ let m14 = block[blockOff + 14] | 0;
67
+ let m15 = block[blockOff + 15] | 0;
68
+
69
+ // Initialize state: first 8 words from chaining value
70
+ let s0 = cv[cvOff] | 0;
71
+ let s1 = cv[cvOff + 1] | 0;
72
+ let s2 = cv[cvOff + 2] | 0;
73
+ let s3 = cv[cvOff + 3] | 0;
74
+ let s4 = cv[cvOff + 4] | 0;
75
+ let s5 = cv[cvOff + 5] | 0;
76
+ let s6 = cv[cvOff + 6] | 0;
77
+ let s7 = cv[cvOff + 7] | 0;
78
+
79
+ // Words 8-11: IV constants
80
+ let s8 = 0x6a09e667;
81
+ let s9 = 0xbb67ae85;
82
+ let s10 = 0x3c6ef372;
83
+ let s11 = 0xa54ff53a;
84
+
85
+ // Words 12-15: counter, blockLen, flags
86
+ // Note: counter is 64-bit, split into low and high 32-bit words
87
+ let s12 = counter | 0;
88
+ let s13 = (counter / 0x100000000) | 0;
89
+ let s14 = blockLen | 0;
90
+ let s15 = flags | 0;
91
+
92
+ // ============================================================
93
+ // 7 rounds of mixing
94
+ // Each round consists of 4 column G functions and 4 diagonal G functions
95
+ // followed by a message word permutation (except for round 7)
96
+ // ============================================================
97
+
98
+ // ROUND 1 (message schedule: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
99
+ // Column G functions
100
+ // G(0, 4, 8, 12) with m0, m1
101
+ s0 = (((s0 + s4) | 0) + m0) | 0;
102
+ s12 ^= s0;
103
+ s12 = (s12 >>> 16) | (s12 << 16);
104
+ s8 = (s8 + s12) | 0;
105
+ s4 ^= s8;
106
+ s4 = (s4 >>> 12) | (s4 << 20);
107
+ s0 = (((s0 + s4) | 0) + m1) | 0;
108
+ s12 ^= s0;
109
+ s12 = (s12 >>> 8) | (s12 << 24);
110
+ s8 = (s8 + s12) | 0;
111
+ s4 ^= s8;
112
+ s4 = (s4 >>> 7) | (s4 << 25);
113
+ // G(1, 5, 9, 13) with m2, m3
114
+ s1 = (((s1 + s5) | 0) + m2) | 0;
115
+ s13 ^= s1;
116
+ s13 = (s13 >>> 16) | (s13 << 16);
117
+ s9 = (s9 + s13) | 0;
118
+ s5 ^= s9;
119
+ s5 = (s5 >>> 12) | (s5 << 20);
120
+ s1 = (((s1 + s5) | 0) + m3) | 0;
121
+ s13 ^= s1;
122
+ s13 = (s13 >>> 8) | (s13 << 24);
123
+ s9 = (s9 + s13) | 0;
124
+ s5 ^= s9;
125
+ s5 = (s5 >>> 7) | (s5 << 25);
126
+ // G(2, 6, 10, 14) with m4, m5
127
+ s2 = (((s2 + s6) | 0) + m4) | 0;
128
+ s14 ^= s2;
129
+ s14 = (s14 >>> 16) | (s14 << 16);
130
+ s10 = (s10 + s14) | 0;
131
+ s6 ^= s10;
132
+ s6 = (s6 >>> 12) | (s6 << 20);
133
+ s2 = (((s2 + s6) | 0) + m5) | 0;
134
+ s14 ^= s2;
135
+ s14 = (s14 >>> 8) | (s14 << 24);
136
+ s10 = (s10 + s14) | 0;
137
+ s6 ^= s10;
138
+ s6 = (s6 >>> 7) | (s6 << 25);
139
+ // G(3, 7, 11, 15) with m6, m7
140
+ s3 = (((s3 + s7) | 0) + m6) | 0;
141
+ s15 ^= s3;
142
+ s15 = (s15 >>> 16) | (s15 << 16);
143
+ s11 = (s11 + s15) | 0;
144
+ s7 ^= s11;
145
+ s7 = (s7 >>> 12) | (s7 << 20);
146
+ s3 = (((s3 + s7) | 0) + m7) | 0;
147
+ s15 ^= s3;
148
+ s15 = (s15 >>> 8) | (s15 << 24);
149
+ s11 = (s11 + s15) | 0;
150
+ s7 ^= s11;
151
+ s7 = (s7 >>> 7) | (s7 << 25);
152
+ // Diagonal G functions
153
+ // G(0, 5, 10, 15) with m8, m9
154
+ s0 = (((s0 + s5) | 0) + m8) | 0;
155
+ s15 ^= s0;
156
+ s15 = (s15 >>> 16) | (s15 << 16);
157
+ s10 = (s10 + s15) | 0;
158
+ s5 ^= s10;
159
+ s5 = (s5 >>> 12) | (s5 << 20);
160
+ s0 = (((s0 + s5) | 0) + m9) | 0;
161
+ s15 ^= s0;
162
+ s15 = (s15 >>> 8) | (s15 << 24);
163
+ s10 = (s10 + s15) | 0;
164
+ s5 ^= s10;
165
+ s5 = (s5 >>> 7) | (s5 << 25);
166
+ // G(1, 6, 11, 12) with m10, m11
167
+ s1 = (((s1 + s6) | 0) + m10) | 0;
168
+ s12 ^= s1;
169
+ s12 = (s12 >>> 16) | (s12 << 16);
170
+ s11 = (s11 + s12) | 0;
171
+ s6 ^= s11;
172
+ s6 = (s6 >>> 12) | (s6 << 20);
173
+ s1 = (((s1 + s6) | 0) + m11) | 0;
174
+ s12 ^= s1;
175
+ s12 = (s12 >>> 8) | (s12 << 24);
176
+ s11 = (s11 + s12) | 0;
177
+ s6 ^= s11;
178
+ s6 = (s6 >>> 7) | (s6 << 25);
179
+ // G(2, 7, 8, 13) with m12, m13
180
+ s2 = (((s2 + s7) | 0) + m12) | 0;
181
+ s13 ^= s2;
182
+ s13 = (s13 >>> 16) | (s13 << 16);
183
+ s8 = (s8 + s13) | 0;
184
+ s7 ^= s8;
185
+ s7 = (s7 >>> 12) | (s7 << 20);
186
+ s2 = (((s2 + s7) | 0) + m13) | 0;
187
+ s13 ^= s2;
188
+ s13 = (s13 >>> 8) | (s13 << 24);
189
+ s8 = (s8 + s13) | 0;
190
+ s7 ^= s8;
191
+ s7 = (s7 >>> 7) | (s7 << 25);
192
+ // G(3, 4, 9, 14) with m14, m15
193
+ s3 = (((s3 + s4) | 0) + m14) | 0;
194
+ s14 ^= s3;
195
+ s14 = (s14 >>> 16) | (s14 << 16);
196
+ s9 = (s9 + s14) | 0;
197
+ s4 ^= s9;
198
+ s4 = (s4 >>> 12) | (s4 << 20);
199
+ s3 = (((s3 + s4) | 0) + m15) | 0;
200
+ s14 ^= s3;
201
+ s14 = (s14 >>> 8) | (s14 << 24);
202
+ s9 = (s9 + s14) | 0;
203
+ s4 ^= s9;
204
+ s4 = (s4 >>> 7) | (s4 << 25);
205
+
206
+ // Permute message words for round 2
207
+ // Permutation: [2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8]
208
+ // Using 2 temps for the two cycles in the permutation
209
+ {
210
+ const t0 = m0,
211
+ t1 = m1;
212
+ m0 = m2;
213
+ m2 = m3;
214
+ m3 = m10;
215
+ m10 = m12;
216
+ m12 = m9;
217
+ m9 = m11;
218
+ m11 = m5;
219
+ m5 = t0;
220
+ m1 = m6;
221
+ m6 = m4;
222
+ m4 = m7;
223
+ m7 = m13;
224
+ m13 = m14;
225
+ m14 = m15;
226
+ m15 = m8;
227
+ m8 = t1;
228
+ }
229
+
230
+ // ROUND 2 (message schedule: 2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8)
231
+ s0 = (((s0 + s4) | 0) + m0) | 0;
232
+ s12 ^= s0;
233
+ s12 = (s12 >>> 16) | (s12 << 16);
234
+ s8 = (s8 + s12) | 0;
235
+ s4 ^= s8;
236
+ s4 = (s4 >>> 12) | (s4 << 20);
237
+ s0 = (((s0 + s4) | 0) + m1) | 0;
238
+ s12 ^= s0;
239
+ s12 = (s12 >>> 8) | (s12 << 24);
240
+ s8 = (s8 + s12) | 0;
241
+ s4 ^= s8;
242
+ s4 = (s4 >>> 7) | (s4 << 25);
243
+ s1 = (((s1 + s5) | 0) + m2) | 0;
244
+ s13 ^= s1;
245
+ s13 = (s13 >>> 16) | (s13 << 16);
246
+ s9 = (s9 + s13) | 0;
247
+ s5 ^= s9;
248
+ s5 = (s5 >>> 12) | (s5 << 20);
249
+ s1 = (((s1 + s5) | 0) + m3) | 0;
250
+ s13 ^= s1;
251
+ s13 = (s13 >>> 8) | (s13 << 24);
252
+ s9 = (s9 + s13) | 0;
253
+ s5 ^= s9;
254
+ s5 = (s5 >>> 7) | (s5 << 25);
255
+ s2 = (((s2 + s6) | 0) + m4) | 0;
256
+ s14 ^= s2;
257
+ s14 = (s14 >>> 16) | (s14 << 16);
258
+ s10 = (s10 + s14) | 0;
259
+ s6 ^= s10;
260
+ s6 = (s6 >>> 12) | (s6 << 20);
261
+ s2 = (((s2 + s6) | 0) + m5) | 0;
262
+ s14 ^= s2;
263
+ s14 = (s14 >>> 8) | (s14 << 24);
264
+ s10 = (s10 + s14) | 0;
265
+ s6 ^= s10;
266
+ s6 = (s6 >>> 7) | (s6 << 25);
267
+ s3 = (((s3 + s7) | 0) + m6) | 0;
268
+ s15 ^= s3;
269
+ s15 = (s15 >>> 16) | (s15 << 16);
270
+ s11 = (s11 + s15) | 0;
271
+ s7 ^= s11;
272
+ s7 = (s7 >>> 12) | (s7 << 20);
273
+ s3 = (((s3 + s7) | 0) + m7) | 0;
274
+ s15 ^= s3;
275
+ s15 = (s15 >>> 8) | (s15 << 24);
276
+ s11 = (s11 + s15) | 0;
277
+ s7 ^= s11;
278
+ s7 = (s7 >>> 7) | (s7 << 25);
279
+ s0 = (((s0 + s5) | 0) + m8) | 0;
280
+ s15 ^= s0;
281
+ s15 = (s15 >>> 16) | (s15 << 16);
282
+ s10 = (s10 + s15) | 0;
283
+ s5 ^= s10;
284
+ s5 = (s5 >>> 12) | (s5 << 20);
285
+ s0 = (((s0 + s5) | 0) + m9) | 0;
286
+ s15 ^= s0;
287
+ s15 = (s15 >>> 8) | (s15 << 24);
288
+ s10 = (s10 + s15) | 0;
289
+ s5 ^= s10;
290
+ s5 = (s5 >>> 7) | (s5 << 25);
291
+ s1 = (((s1 + s6) | 0) + m10) | 0;
292
+ s12 ^= s1;
293
+ s12 = (s12 >>> 16) | (s12 << 16);
294
+ s11 = (s11 + s12) | 0;
295
+ s6 ^= s11;
296
+ s6 = (s6 >>> 12) | (s6 << 20);
297
+ s1 = (((s1 + s6) | 0) + m11) | 0;
298
+ s12 ^= s1;
299
+ s12 = (s12 >>> 8) | (s12 << 24);
300
+ s11 = (s11 + s12) | 0;
301
+ s6 ^= s11;
302
+ s6 = (s6 >>> 7) | (s6 << 25);
303
+ s2 = (((s2 + s7) | 0) + m12) | 0;
304
+ s13 ^= s2;
305
+ s13 = (s13 >>> 16) | (s13 << 16);
306
+ s8 = (s8 + s13) | 0;
307
+ s7 ^= s8;
308
+ s7 = (s7 >>> 12) | (s7 << 20);
309
+ s2 = (((s2 + s7) | 0) + m13) | 0;
310
+ s13 ^= s2;
311
+ s13 = (s13 >>> 8) | (s13 << 24);
312
+ s8 = (s8 + s13) | 0;
313
+ s7 ^= s8;
314
+ s7 = (s7 >>> 7) | (s7 << 25);
315
+ s3 = (((s3 + s4) | 0) + m14) | 0;
316
+ s14 ^= s3;
317
+ s14 = (s14 >>> 16) | (s14 << 16);
318
+ s9 = (s9 + s14) | 0;
319
+ s4 ^= s9;
320
+ s4 = (s4 >>> 12) | (s4 << 20);
321
+ s3 = (((s3 + s4) | 0) + m15) | 0;
322
+ s14 ^= s3;
323
+ s14 = (s14 >>> 8) | (s14 << 24);
324
+ s9 = (s9 + s14) | 0;
325
+ s4 ^= s9;
326
+ s4 = (s4 >>> 7) | (s4 << 25);
327
+
328
+ // Permute for round 3
329
+ {
330
+ const t0 = m0,
331
+ t1 = m1;
332
+ m0 = m2;
333
+ m2 = m3;
334
+ m3 = m10;
335
+ m10 = m12;
336
+ m12 = m9;
337
+ m9 = m11;
338
+ m11 = m5;
339
+ m5 = t0;
340
+ m1 = m6;
341
+ m6 = m4;
342
+ m4 = m7;
343
+ m7 = m13;
344
+ m13 = m14;
345
+ m14 = m15;
346
+ m15 = m8;
347
+ m8 = t1;
348
+ }
349
+
350
+ // ROUND 3 (message schedule: 3,4,10,12,13,2,7,14,6,5,9,0,11,15,8,1)
351
+ s0 = (((s0 + s4) | 0) + m0) | 0;
352
+ s12 ^= s0;
353
+ s12 = (s12 >>> 16) | (s12 << 16);
354
+ s8 = (s8 + s12) | 0;
355
+ s4 ^= s8;
356
+ s4 = (s4 >>> 12) | (s4 << 20);
357
+ s0 = (((s0 + s4) | 0) + m1) | 0;
358
+ s12 ^= s0;
359
+ s12 = (s12 >>> 8) | (s12 << 24);
360
+ s8 = (s8 + s12) | 0;
361
+ s4 ^= s8;
362
+ s4 = (s4 >>> 7) | (s4 << 25);
363
+ s1 = (((s1 + s5) | 0) + m2) | 0;
364
+ s13 ^= s1;
365
+ s13 = (s13 >>> 16) | (s13 << 16);
366
+ s9 = (s9 + s13) | 0;
367
+ s5 ^= s9;
368
+ s5 = (s5 >>> 12) | (s5 << 20);
369
+ s1 = (((s1 + s5) | 0) + m3) | 0;
370
+ s13 ^= s1;
371
+ s13 = (s13 >>> 8) | (s13 << 24);
372
+ s9 = (s9 + s13) | 0;
373
+ s5 ^= s9;
374
+ s5 = (s5 >>> 7) | (s5 << 25);
375
+ s2 = (((s2 + s6) | 0) + m4) | 0;
376
+ s14 ^= s2;
377
+ s14 = (s14 >>> 16) | (s14 << 16);
378
+ s10 = (s10 + s14) | 0;
379
+ s6 ^= s10;
380
+ s6 = (s6 >>> 12) | (s6 << 20);
381
+ s2 = (((s2 + s6) | 0) + m5) | 0;
382
+ s14 ^= s2;
383
+ s14 = (s14 >>> 8) | (s14 << 24);
384
+ s10 = (s10 + s14) | 0;
385
+ s6 ^= s10;
386
+ s6 = (s6 >>> 7) | (s6 << 25);
387
+ s3 = (((s3 + s7) | 0) + m6) | 0;
388
+ s15 ^= s3;
389
+ s15 = (s15 >>> 16) | (s15 << 16);
390
+ s11 = (s11 + s15) | 0;
391
+ s7 ^= s11;
392
+ s7 = (s7 >>> 12) | (s7 << 20);
393
+ s3 = (((s3 + s7) | 0) + m7) | 0;
394
+ s15 ^= s3;
395
+ s15 = (s15 >>> 8) | (s15 << 24);
396
+ s11 = (s11 + s15) | 0;
397
+ s7 ^= s11;
398
+ s7 = (s7 >>> 7) | (s7 << 25);
399
+ s0 = (((s0 + s5) | 0) + m8) | 0;
400
+ s15 ^= s0;
401
+ s15 = (s15 >>> 16) | (s15 << 16);
402
+ s10 = (s10 + s15) | 0;
403
+ s5 ^= s10;
404
+ s5 = (s5 >>> 12) | (s5 << 20);
405
+ s0 = (((s0 + s5) | 0) + m9) | 0;
406
+ s15 ^= s0;
407
+ s15 = (s15 >>> 8) | (s15 << 24);
408
+ s10 = (s10 + s15) | 0;
409
+ s5 ^= s10;
410
+ s5 = (s5 >>> 7) | (s5 << 25);
411
+ s1 = (((s1 + s6) | 0) + m10) | 0;
412
+ s12 ^= s1;
413
+ s12 = (s12 >>> 16) | (s12 << 16);
414
+ s11 = (s11 + s12) | 0;
415
+ s6 ^= s11;
416
+ s6 = (s6 >>> 12) | (s6 << 20);
417
+ s1 = (((s1 + s6) | 0) + m11) | 0;
418
+ s12 ^= s1;
419
+ s12 = (s12 >>> 8) | (s12 << 24);
420
+ s11 = (s11 + s12) | 0;
421
+ s6 ^= s11;
422
+ s6 = (s6 >>> 7) | (s6 << 25);
423
+ s2 = (((s2 + s7) | 0) + m12) | 0;
424
+ s13 ^= s2;
425
+ s13 = (s13 >>> 16) | (s13 << 16);
426
+ s8 = (s8 + s13) | 0;
427
+ s7 ^= s8;
428
+ s7 = (s7 >>> 12) | (s7 << 20);
429
+ s2 = (((s2 + s7) | 0) + m13) | 0;
430
+ s13 ^= s2;
431
+ s13 = (s13 >>> 8) | (s13 << 24);
432
+ s8 = (s8 + s13) | 0;
433
+ s7 ^= s8;
434
+ s7 = (s7 >>> 7) | (s7 << 25);
435
+ s3 = (((s3 + s4) | 0) + m14) | 0;
436
+ s14 ^= s3;
437
+ s14 = (s14 >>> 16) | (s14 << 16);
438
+ s9 = (s9 + s14) | 0;
439
+ s4 ^= s9;
440
+ s4 = (s4 >>> 12) | (s4 << 20);
441
+ s3 = (((s3 + s4) | 0) + m15) | 0;
442
+ s14 ^= s3;
443
+ s14 = (s14 >>> 8) | (s14 << 24);
444
+ s9 = (s9 + s14) | 0;
445
+ s4 ^= s9;
446
+ s4 = (s4 >>> 7) | (s4 << 25);
447
+
448
+ // Permute for round 4
449
+ {
450
+ const t0 = m0,
451
+ t1 = m1;
452
+ m0 = m2;
453
+ m2 = m3;
454
+ m3 = m10;
455
+ m10 = m12;
456
+ m12 = m9;
457
+ m9 = m11;
458
+ m11 = m5;
459
+ m5 = t0;
460
+ m1 = m6;
461
+ m6 = m4;
462
+ m4 = m7;
463
+ m7 = m13;
464
+ m13 = m14;
465
+ m14 = m15;
466
+ m15 = m8;
467
+ m8 = t1;
468
+ }
469
+
470
+ // ROUND 4 (message schedule: 10,7,12,9,14,3,13,15,4,0,11,2,5,8,1,6)
471
+ s0 = (((s0 + s4) | 0) + m0) | 0;
472
+ s12 ^= s0;
473
+ s12 = (s12 >>> 16) | (s12 << 16);
474
+ s8 = (s8 + s12) | 0;
475
+ s4 ^= s8;
476
+ s4 = (s4 >>> 12) | (s4 << 20);
477
+ s0 = (((s0 + s4) | 0) + m1) | 0;
478
+ s12 ^= s0;
479
+ s12 = (s12 >>> 8) | (s12 << 24);
480
+ s8 = (s8 + s12) | 0;
481
+ s4 ^= s8;
482
+ s4 = (s4 >>> 7) | (s4 << 25);
483
+ s1 = (((s1 + s5) | 0) + m2) | 0;
484
+ s13 ^= s1;
485
+ s13 = (s13 >>> 16) | (s13 << 16);
486
+ s9 = (s9 + s13) | 0;
487
+ s5 ^= s9;
488
+ s5 = (s5 >>> 12) | (s5 << 20);
489
+ s1 = (((s1 + s5) | 0) + m3) | 0;
490
+ s13 ^= s1;
491
+ s13 = (s13 >>> 8) | (s13 << 24);
492
+ s9 = (s9 + s13) | 0;
493
+ s5 ^= s9;
494
+ s5 = (s5 >>> 7) | (s5 << 25);
495
+ s2 = (((s2 + s6) | 0) + m4) | 0;
496
+ s14 ^= s2;
497
+ s14 = (s14 >>> 16) | (s14 << 16);
498
+ s10 = (s10 + s14) | 0;
499
+ s6 ^= s10;
500
+ s6 = (s6 >>> 12) | (s6 << 20);
501
+ s2 = (((s2 + s6) | 0) + m5) | 0;
502
+ s14 ^= s2;
503
+ s14 = (s14 >>> 8) | (s14 << 24);
504
+ s10 = (s10 + s14) | 0;
505
+ s6 ^= s10;
506
+ s6 = (s6 >>> 7) | (s6 << 25);
507
+ s3 = (((s3 + s7) | 0) + m6) | 0;
508
+ s15 ^= s3;
509
+ s15 = (s15 >>> 16) | (s15 << 16);
510
+ s11 = (s11 + s15) | 0;
511
+ s7 ^= s11;
512
+ s7 = (s7 >>> 12) | (s7 << 20);
513
+ s3 = (((s3 + s7) | 0) + m7) | 0;
514
+ s15 ^= s3;
515
+ s15 = (s15 >>> 8) | (s15 << 24);
516
+ s11 = (s11 + s15) | 0;
517
+ s7 ^= s11;
518
+ s7 = (s7 >>> 7) | (s7 << 25);
519
+ s0 = (((s0 + s5) | 0) + m8) | 0;
520
+ s15 ^= s0;
521
+ s15 = (s15 >>> 16) | (s15 << 16);
522
+ s10 = (s10 + s15) | 0;
523
+ s5 ^= s10;
524
+ s5 = (s5 >>> 12) | (s5 << 20);
525
+ s0 = (((s0 + s5) | 0) + m9) | 0;
526
+ s15 ^= s0;
527
+ s15 = (s15 >>> 8) | (s15 << 24);
528
+ s10 = (s10 + s15) | 0;
529
+ s5 ^= s10;
530
+ s5 = (s5 >>> 7) | (s5 << 25);
531
+ s1 = (((s1 + s6) | 0) + m10) | 0;
532
+ s12 ^= s1;
533
+ s12 = (s12 >>> 16) | (s12 << 16);
534
+ s11 = (s11 + s12) | 0;
535
+ s6 ^= s11;
536
+ s6 = (s6 >>> 12) | (s6 << 20);
537
+ s1 = (((s1 + s6) | 0) + m11) | 0;
538
+ s12 ^= s1;
539
+ s12 = (s12 >>> 8) | (s12 << 24);
540
+ s11 = (s11 + s12) | 0;
541
+ s6 ^= s11;
542
+ s6 = (s6 >>> 7) | (s6 << 25);
543
+ s2 = (((s2 + s7) | 0) + m12) | 0;
544
+ s13 ^= s2;
545
+ s13 = (s13 >>> 16) | (s13 << 16);
546
+ s8 = (s8 + s13) | 0;
547
+ s7 ^= s8;
548
+ s7 = (s7 >>> 12) | (s7 << 20);
549
+ s2 = (((s2 + s7) | 0) + m13) | 0;
550
+ s13 ^= s2;
551
+ s13 = (s13 >>> 8) | (s13 << 24);
552
+ s8 = (s8 + s13) | 0;
553
+ s7 ^= s8;
554
+ s7 = (s7 >>> 7) | (s7 << 25);
555
+ s3 = (((s3 + s4) | 0) + m14) | 0;
556
+ s14 ^= s3;
557
+ s14 = (s14 >>> 16) | (s14 << 16);
558
+ s9 = (s9 + s14) | 0;
559
+ s4 ^= s9;
560
+ s4 = (s4 >>> 12) | (s4 << 20);
561
+ s3 = (((s3 + s4) | 0) + m15) | 0;
562
+ s14 ^= s3;
563
+ s14 = (s14 >>> 8) | (s14 << 24);
564
+ s9 = (s9 + s14) | 0;
565
+ s4 ^= s9;
566
+ s4 = (s4 >>> 7) | (s4 << 25);
567
+
568
+ // Permute for round 5
569
+ {
570
+ const t0 = m0,
571
+ t1 = m1;
572
+ m0 = m2;
573
+ m2 = m3;
574
+ m3 = m10;
575
+ m10 = m12;
576
+ m12 = m9;
577
+ m9 = m11;
578
+ m11 = m5;
579
+ m5 = t0;
580
+ m1 = m6;
581
+ m6 = m4;
582
+ m4 = m7;
583
+ m7 = m13;
584
+ m13 = m14;
585
+ m14 = m15;
586
+ m15 = m8;
587
+ m8 = t1;
588
+ }
589
+
590
+ // ROUND 5 (message schedule: 12,13,9,11,15,10,14,8,7,2,5,3,0,1,6,4)
591
+ s0 = (((s0 + s4) | 0) + m0) | 0;
592
+ s12 ^= s0;
593
+ s12 = (s12 >>> 16) | (s12 << 16);
594
+ s8 = (s8 + s12) | 0;
595
+ s4 ^= s8;
596
+ s4 = (s4 >>> 12) | (s4 << 20);
597
+ s0 = (((s0 + s4) | 0) + m1) | 0;
598
+ s12 ^= s0;
599
+ s12 = (s12 >>> 8) | (s12 << 24);
600
+ s8 = (s8 + s12) | 0;
601
+ s4 ^= s8;
602
+ s4 = (s4 >>> 7) | (s4 << 25);
603
+ s1 = (((s1 + s5) | 0) + m2) | 0;
604
+ s13 ^= s1;
605
+ s13 = (s13 >>> 16) | (s13 << 16);
606
+ s9 = (s9 + s13) | 0;
607
+ s5 ^= s9;
608
+ s5 = (s5 >>> 12) | (s5 << 20);
609
+ s1 = (((s1 + s5) | 0) + m3) | 0;
610
+ s13 ^= s1;
611
+ s13 = (s13 >>> 8) | (s13 << 24);
612
+ s9 = (s9 + s13) | 0;
613
+ s5 ^= s9;
614
+ s5 = (s5 >>> 7) | (s5 << 25);
615
+ s2 = (((s2 + s6) | 0) + m4) | 0;
616
+ s14 ^= s2;
617
+ s14 = (s14 >>> 16) | (s14 << 16);
618
+ s10 = (s10 + s14) | 0;
619
+ s6 ^= s10;
620
+ s6 = (s6 >>> 12) | (s6 << 20);
621
+ s2 = (((s2 + s6) | 0) + m5) | 0;
622
+ s14 ^= s2;
623
+ s14 = (s14 >>> 8) | (s14 << 24);
624
+ s10 = (s10 + s14) | 0;
625
+ s6 ^= s10;
626
+ s6 = (s6 >>> 7) | (s6 << 25);
627
+ s3 = (((s3 + s7) | 0) + m6) | 0;
628
+ s15 ^= s3;
629
+ s15 = (s15 >>> 16) | (s15 << 16);
630
+ s11 = (s11 + s15) | 0;
631
+ s7 ^= s11;
632
+ s7 = (s7 >>> 12) | (s7 << 20);
633
+ s3 = (((s3 + s7) | 0) + m7) | 0;
634
+ s15 ^= s3;
635
+ s15 = (s15 >>> 8) | (s15 << 24);
636
+ s11 = (s11 + s15) | 0;
637
+ s7 ^= s11;
638
+ s7 = (s7 >>> 7) | (s7 << 25);
639
+ s0 = (((s0 + s5) | 0) + m8) | 0;
640
+ s15 ^= s0;
641
+ s15 = (s15 >>> 16) | (s15 << 16);
642
+ s10 = (s10 + s15) | 0;
643
+ s5 ^= s10;
644
+ s5 = (s5 >>> 12) | (s5 << 20);
645
+ s0 = (((s0 + s5) | 0) + m9) | 0;
646
+ s15 ^= s0;
647
+ s15 = (s15 >>> 8) | (s15 << 24);
648
+ s10 = (s10 + s15) | 0;
649
+ s5 ^= s10;
650
+ s5 = (s5 >>> 7) | (s5 << 25);
651
+ s1 = (((s1 + s6) | 0) + m10) | 0;
652
+ s12 ^= s1;
653
+ s12 = (s12 >>> 16) | (s12 << 16);
654
+ s11 = (s11 + s12) | 0;
655
+ s6 ^= s11;
656
+ s6 = (s6 >>> 12) | (s6 << 20);
657
+ s1 = (((s1 + s6) | 0) + m11) | 0;
658
+ s12 ^= s1;
659
+ s12 = (s12 >>> 8) | (s12 << 24);
660
+ s11 = (s11 + s12) | 0;
661
+ s6 ^= s11;
662
+ s6 = (s6 >>> 7) | (s6 << 25);
663
+ s2 = (((s2 + s7) | 0) + m12) | 0;
664
+ s13 ^= s2;
665
+ s13 = (s13 >>> 16) | (s13 << 16);
666
+ s8 = (s8 + s13) | 0;
667
+ s7 ^= s8;
668
+ s7 = (s7 >>> 12) | (s7 << 20);
669
+ s2 = (((s2 + s7) | 0) + m13) | 0;
670
+ s13 ^= s2;
671
+ s13 = (s13 >>> 8) | (s13 << 24);
672
+ s8 = (s8 + s13) | 0;
673
+ s7 ^= s8;
674
+ s7 = (s7 >>> 7) | (s7 << 25);
675
+ s3 = (((s3 + s4) | 0) + m14) | 0;
676
+ s14 ^= s3;
677
+ s14 = (s14 >>> 16) | (s14 << 16);
678
+ s9 = (s9 + s14) | 0;
679
+ s4 ^= s9;
680
+ s4 = (s4 >>> 12) | (s4 << 20);
681
+ s3 = (((s3 + s4) | 0) + m15) | 0;
682
+ s14 ^= s3;
683
+ s14 = (s14 >>> 8) | (s14 << 24);
684
+ s9 = (s9 + s14) | 0;
685
+ s4 ^= s9;
686
+ s4 = (s4 >>> 7) | (s4 << 25);
687
+
688
+ // Permute for round 6
689
+ {
690
+ const t0 = m0,
691
+ t1 = m1;
692
+ m0 = m2;
693
+ m2 = m3;
694
+ m3 = m10;
695
+ m10 = m12;
696
+ m12 = m9;
697
+ m9 = m11;
698
+ m11 = m5;
699
+ m5 = t0;
700
+ m1 = m6;
701
+ m6 = m4;
702
+ m4 = m7;
703
+ m7 = m13;
704
+ m13 = m14;
705
+ m14 = m15;
706
+ m15 = m8;
707
+ m8 = t1;
708
+ }
709
+
710
+ // ROUND 6 (message schedule: 9,14,11,5,8,12,15,1,13,3,0,10,2,6,4,7)
711
+ s0 = (((s0 + s4) | 0) + m0) | 0;
712
+ s12 ^= s0;
713
+ s12 = (s12 >>> 16) | (s12 << 16);
714
+ s8 = (s8 + s12) | 0;
715
+ s4 ^= s8;
716
+ s4 = (s4 >>> 12) | (s4 << 20);
717
+ s0 = (((s0 + s4) | 0) + m1) | 0;
718
+ s12 ^= s0;
719
+ s12 = (s12 >>> 8) | (s12 << 24);
720
+ s8 = (s8 + s12) | 0;
721
+ s4 ^= s8;
722
+ s4 = (s4 >>> 7) | (s4 << 25);
723
+ s1 = (((s1 + s5) | 0) + m2) | 0;
724
+ s13 ^= s1;
725
+ s13 = (s13 >>> 16) | (s13 << 16);
726
+ s9 = (s9 + s13) | 0;
727
+ s5 ^= s9;
728
+ s5 = (s5 >>> 12) | (s5 << 20);
729
+ s1 = (((s1 + s5) | 0) + m3) | 0;
730
+ s13 ^= s1;
731
+ s13 = (s13 >>> 8) | (s13 << 24);
732
+ s9 = (s9 + s13) | 0;
733
+ s5 ^= s9;
734
+ s5 = (s5 >>> 7) | (s5 << 25);
735
+ s2 = (((s2 + s6) | 0) + m4) | 0;
736
+ s14 ^= s2;
737
+ s14 = (s14 >>> 16) | (s14 << 16);
738
+ s10 = (s10 + s14) | 0;
739
+ s6 ^= s10;
740
+ s6 = (s6 >>> 12) | (s6 << 20);
741
+ s2 = (((s2 + s6) | 0) + m5) | 0;
742
+ s14 ^= s2;
743
+ s14 = (s14 >>> 8) | (s14 << 24);
744
+ s10 = (s10 + s14) | 0;
745
+ s6 ^= s10;
746
+ s6 = (s6 >>> 7) | (s6 << 25);
747
+ s3 = (((s3 + s7) | 0) + m6) | 0;
748
+ s15 ^= s3;
749
+ s15 = (s15 >>> 16) | (s15 << 16);
750
+ s11 = (s11 + s15) | 0;
751
+ s7 ^= s11;
752
+ s7 = (s7 >>> 12) | (s7 << 20);
753
+ s3 = (((s3 + s7) | 0) + m7) | 0;
754
+ s15 ^= s3;
755
+ s15 = (s15 >>> 8) | (s15 << 24);
756
+ s11 = (s11 + s15) | 0;
757
+ s7 ^= s11;
758
+ s7 = (s7 >>> 7) | (s7 << 25);
759
+ s0 = (((s0 + s5) | 0) + m8) | 0;
760
+ s15 ^= s0;
761
+ s15 = (s15 >>> 16) | (s15 << 16);
762
+ s10 = (s10 + s15) | 0;
763
+ s5 ^= s10;
764
+ s5 = (s5 >>> 12) | (s5 << 20);
765
+ s0 = (((s0 + s5) | 0) + m9) | 0;
766
+ s15 ^= s0;
767
+ s15 = (s15 >>> 8) | (s15 << 24);
768
+ s10 = (s10 + s15) | 0;
769
+ s5 ^= s10;
770
+ s5 = (s5 >>> 7) | (s5 << 25);
771
+ s1 = (((s1 + s6) | 0) + m10) | 0;
772
+ s12 ^= s1;
773
+ s12 = (s12 >>> 16) | (s12 << 16);
774
+ s11 = (s11 + s12) | 0;
775
+ s6 ^= s11;
776
+ s6 = (s6 >>> 12) | (s6 << 20);
777
+ s1 = (((s1 + s6) | 0) + m11) | 0;
778
+ s12 ^= s1;
779
+ s12 = (s12 >>> 8) | (s12 << 24);
780
+ s11 = (s11 + s12) | 0;
781
+ s6 ^= s11;
782
+ s6 = (s6 >>> 7) | (s6 << 25);
783
+ s2 = (((s2 + s7) | 0) + m12) | 0;
784
+ s13 ^= s2;
785
+ s13 = (s13 >>> 16) | (s13 << 16);
786
+ s8 = (s8 + s13) | 0;
787
+ s7 ^= s8;
788
+ s7 = (s7 >>> 12) | (s7 << 20);
789
+ s2 = (((s2 + s7) | 0) + m13) | 0;
790
+ s13 ^= s2;
791
+ s13 = (s13 >>> 8) | (s13 << 24);
792
+ s8 = (s8 + s13) | 0;
793
+ s7 ^= s8;
794
+ s7 = (s7 >>> 7) | (s7 << 25);
795
+ s3 = (((s3 + s4) | 0) + m14) | 0;
796
+ s14 ^= s3;
797
+ s14 = (s14 >>> 16) | (s14 << 16);
798
+ s9 = (s9 + s14) | 0;
799
+ s4 ^= s9;
800
+ s4 = (s4 >>> 12) | (s4 << 20);
801
+ s3 = (((s3 + s4) | 0) + m15) | 0;
802
+ s14 ^= s3;
803
+ s14 = (s14 >>> 8) | (s14 << 24);
804
+ s9 = (s9 + s14) | 0;
805
+ s4 ^= s9;
806
+ s4 = (s4 >>> 7) | (s4 << 25);
807
+
808
+ // Permute for round 7
809
+ {
810
+ const t0 = m0,
811
+ t1 = m1;
812
+ m0 = m2;
813
+ m2 = m3;
814
+ m3 = m10;
815
+ m10 = m12;
816
+ m12 = m9;
817
+ m9 = m11;
818
+ m11 = m5;
819
+ m5 = t0;
820
+ m1 = m6;
821
+ m6 = m4;
822
+ m4 = m7;
823
+ m7 = m13;
824
+ m13 = m14;
825
+ m14 = m15;
826
+ m15 = m8;
827
+ m8 = t1;
828
+ }
829
+
830
+ // ROUND 7 (message schedule: 11,15,5,0,1,9,8,6,14,10,2,12,3,4,7,13)
831
+ s0 = (((s0 + s4) | 0) + m0) | 0;
832
+ s12 ^= s0;
833
+ s12 = (s12 >>> 16) | (s12 << 16);
834
+ s8 = (s8 + s12) | 0;
835
+ s4 ^= s8;
836
+ s4 = (s4 >>> 12) | (s4 << 20);
837
+ s0 = (((s0 + s4) | 0) + m1) | 0;
838
+ s12 ^= s0;
839
+ s12 = (s12 >>> 8) | (s12 << 24);
840
+ s8 = (s8 + s12) | 0;
841
+ s4 ^= s8;
842
+ s4 = (s4 >>> 7) | (s4 << 25);
843
+ s1 = (((s1 + s5) | 0) + m2) | 0;
844
+ s13 ^= s1;
845
+ s13 = (s13 >>> 16) | (s13 << 16);
846
+ s9 = (s9 + s13) | 0;
847
+ s5 ^= s9;
848
+ s5 = (s5 >>> 12) | (s5 << 20);
849
+ s1 = (((s1 + s5) | 0) + m3) | 0;
850
+ s13 ^= s1;
851
+ s13 = (s13 >>> 8) | (s13 << 24);
852
+ s9 = (s9 + s13) | 0;
853
+ s5 ^= s9;
854
+ s5 = (s5 >>> 7) | (s5 << 25);
855
+ s2 = (((s2 + s6) | 0) + m4) | 0;
856
+ s14 ^= s2;
857
+ s14 = (s14 >>> 16) | (s14 << 16);
858
+ s10 = (s10 + s14) | 0;
859
+ s6 ^= s10;
860
+ s6 = (s6 >>> 12) | (s6 << 20);
861
+ s2 = (((s2 + s6) | 0) + m5) | 0;
862
+ s14 ^= s2;
863
+ s14 = (s14 >>> 8) | (s14 << 24);
864
+ s10 = (s10 + s14) | 0;
865
+ s6 ^= s10;
866
+ s6 = (s6 >>> 7) | (s6 << 25);
867
+ s3 = (((s3 + s7) | 0) + m6) | 0;
868
+ s15 ^= s3;
869
+ s15 = (s15 >>> 16) | (s15 << 16);
870
+ s11 = (s11 + s15) | 0;
871
+ s7 ^= s11;
872
+ s7 = (s7 >>> 12) | (s7 << 20);
873
+ s3 = (((s3 + s7) | 0) + m7) | 0;
874
+ s15 ^= s3;
875
+ s15 = (s15 >>> 8) | (s15 << 24);
876
+ s11 = (s11 + s15) | 0;
877
+ s7 ^= s11;
878
+ s7 = (s7 >>> 7) | (s7 << 25);
879
+ s0 = (((s0 + s5) | 0) + m8) | 0;
880
+ s15 ^= s0;
881
+ s15 = (s15 >>> 16) | (s15 << 16);
882
+ s10 = (s10 + s15) | 0;
883
+ s5 ^= s10;
884
+ s5 = (s5 >>> 12) | (s5 << 20);
885
+ s0 = (((s0 + s5) | 0) + m9) | 0;
886
+ s15 ^= s0;
887
+ s15 = (s15 >>> 8) | (s15 << 24);
888
+ s10 = (s10 + s15) | 0;
889
+ s5 ^= s10;
890
+ s5 = (s5 >>> 7) | (s5 << 25);
891
+ s1 = (((s1 + s6) | 0) + m10) | 0;
892
+ s12 ^= s1;
893
+ s12 = (s12 >>> 16) | (s12 << 16);
894
+ s11 = (s11 + s12) | 0;
895
+ s6 ^= s11;
896
+ s6 = (s6 >>> 12) | (s6 << 20);
897
+ s1 = (((s1 + s6) | 0) + m11) | 0;
898
+ s12 ^= s1;
899
+ s12 = (s12 >>> 8) | (s12 << 24);
900
+ s11 = (s11 + s12) | 0;
901
+ s6 ^= s11;
902
+ s6 = (s6 >>> 7) | (s6 << 25);
903
+ s2 = (((s2 + s7) | 0) + m12) | 0;
904
+ s13 ^= s2;
905
+ s13 = (s13 >>> 16) | (s13 << 16);
906
+ s8 = (s8 + s13) | 0;
907
+ s7 ^= s8;
908
+ s7 = (s7 >>> 12) | (s7 << 20);
909
+ s2 = (((s2 + s7) | 0) + m13) | 0;
910
+ s13 ^= s2;
911
+ s13 = (s13 >>> 8) | (s13 << 24);
912
+ s8 = (s8 + s13) | 0;
913
+ s7 ^= s8;
914
+ s7 = (s7 >>> 7) | (s7 << 25);
915
+ s3 = (((s3 + s4) | 0) + m14) | 0;
916
+ s14 ^= s3;
917
+ s14 = (s14 >>> 16) | (s14 << 16);
918
+ s9 = (s9 + s14) | 0;
919
+ s4 ^= s9;
920
+ s4 = (s4 >>> 12) | (s4 << 20);
921
+ s3 = (((s3 + s4) | 0) + m15) | 0;
922
+ s14 ^= s3;
923
+ s14 = (s14 >>> 8) | (s14 << 24);
924
+ s9 = (s9 + s14) | 0;
925
+ s4 ^= s9;
926
+ s4 = (s4 >>> 7) | (s4 << 25);
927
+
928
+ // ============================================================
929
+ // Final XOR and output
930
+ // ============================================================
931
+
932
+ // If full output needed (XOF mode), write words 8-15 first
933
+ // (written first in case out === cv)
934
+ if (full) {
935
+ out[outOff + 8] = s8 ^ cv[cvOff];
936
+ out[outOff + 9] = s9 ^ cv[cvOff + 1];
937
+ out[outOff + 10] = s10 ^ cv[cvOff + 2];
938
+ out[outOff + 11] = s11 ^ cv[cvOff + 3];
939
+ out[outOff + 12] = s12 ^ cv[cvOff + 4];
940
+ out[outOff + 13] = s13 ^ cv[cvOff + 5];
941
+ out[outOff + 14] = s14 ^ cv[cvOff + 6];
942
+ out[outOff + 15] = s15 ^ cv[cvOff + 7];
943
+ }
944
+
945
+ // Standard output: XOR state[0..7] with state[8..15]
946
+ out[outOff] = s0 ^ s8;
947
+ out[outOff + 1] = s1 ^ s9;
948
+ out[outOff + 2] = s2 ^ s10;
949
+ out[outOff + 3] = s3 ^ s11;
950
+ out[outOff + 4] = s4 ^ s12;
951
+ out[outOff + 5] = s5 ^ s13;
952
+ out[outOff + 6] = s6 ^ s14;
953
+ out[outOff + 7] = s7 ^ s15;
954
+ }
node_modules/@huggingface/blake3-jit/src/constants.ts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Constants
3
+ *
4
+ * IV values are the same as SHA-256: first 32 bits of the fractional parts
5
+ * of the square roots of the first 8 primes (2..19)
6
+ */
7
+
8
+ // Initialization Vector (same as SHA-256)
9
+ export const IV = new Uint32Array([
10
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
11
+ ]);
12
+
13
+ // Domain separation flags
14
+ export const CHUNK_START = 1;
15
+ export const CHUNK_END = 1 << 1;
16
+ export const PARENT = 1 << 2;
17
+ export const ROOT = 1 << 3;
18
+ export const KEYED_HASH = 1 << 4;
19
+ export const DERIVE_KEY_CONTEXT = 1 << 5;
20
+ export const DERIVE_KEY_MATERIAL = 1 << 6;
21
+
22
+ // Size constants
23
+ export const OUT_LEN = 32;
24
+ export const KEY_LEN = 32;
25
+ export const BLOCK_LEN = 64;
26
+ export const CHUNK_LEN = 1024;
27
+
28
+ // Maximum depth of the CV stack (supports up to 2^54 bytes input)
29
+ export const MAX_DEPTH = 54;
30
+
31
+ /**
32
+ * Precomputed message word permutations for all 7 rounds.
33
+ *
34
+ * The base permutation is: [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8]
35
+ * Each subsequent permutation is the previous one with this permutation applied.
36
+ *
37
+ * These are the indices into the message block for each round.
38
+ * By precomputing these, we avoid runtime permutation overhead.
39
+ */
40
+ export const MSG_SCHEDULE: ReadonlyArray<ReadonlyArray<number>> = [
41
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
42
+ [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8],
43
+ [3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1],
44
+ [10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6],
45
+ [12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4],
46
+ [9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7],
47
+ [11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13],
48
+ ];
49
+
50
+ /**
51
+ * Flattened permutation table for compress function optimization.
52
+ * This enables direct indexed access: PERMUTATIONS[round * 16 + index]
53
+ */
54
+ export const PERMUTATIONS = new Uint8Array([
55
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9,
56
+ 14, 15, 8, 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1, 10, 7, 12, 9, 14, 3, 13, 15, 4,
57
+ 0, 11, 2, 5, 8, 1, 6, 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4, 9, 14, 11, 5, 8, 12,
58
+ 15, 1, 13, 3, 0, 10, 2, 6, 4, 7, 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13,
59
+ ]);
node_modules/@huggingface/blake3-jit/src/hash.ts ADDED
@@ -0,0 +1,1318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hash Function - Simple one-shot API
3
+ *
4
+ * This provides a simple hash() function optimized for different input sizes.
5
+ * For small inputs, uses pure JS. For large inputs, uses WASM SIMD.
6
+ */
7
+
8
+ import { compress } from "./compress.js";
9
+ import {
10
+ IV,
11
+ CHUNK_START,
12
+ CHUNK_END,
13
+ PARENT,
14
+ ROOT,
15
+ BLOCK_LEN,
16
+ CHUNK_LEN,
17
+ OUT_LEN,
18
+ } from "./constants.js";
19
+ import {
20
+ IS_LITTLE_ENDIAN,
21
+ readLittleEndianWordsFull,
22
+ readLittleEndianWordsPartial,
23
+ writeLittleEndianBytesPartial,
24
+ } from "./utils.js";
25
+ import {
26
+ initSimdSync,
27
+ getSimdMemory,
28
+ getArenaBuffers,
29
+ runCompress4x,
30
+ runCompressChunks4x,
31
+ runCompressParent,
32
+ SIMD_MEMORY,
33
+ } from "./wasm-simd.js";
34
+
35
+ // Pre-allocated buffers for reuse (single-threaded optimization)
36
+ let blockWords: Uint32Array | null = null;
37
+
38
+ // ===== Contiguous Hyper CV Stack (Optimization #6) =====
39
+ // Maximum tree depth for practical inputs (2^64 chunks = essentially unlimited)
40
+ // Fixed allocation at module load - no runtime allocation
41
+ const CV_STACK_DEPTH = 64;
42
+ const HYPER_CV_STACK = new Uint32Array(CV_STACK_DEPTH * 8); // 64 CVs × 8 words = 512 words
43
+
44
+ // Pre-computed offsets for the first few stack levels (hot path optimization)
45
+ // Note: These can be used for further optimization if needed
46
+ // const CV_STACK_OFF_0 = 0;
47
+ // const CV_STACK_OFF_1 = 8;
48
+ // const CV_STACK_OFF_2 = 16;
49
+ // const CV_STACK_OFF_3 = 24;
50
+
51
+ // ===== Pre-allocated CV Pool with Views (avoids subarray() in hot paths) =====
52
+ const CV_POOL_SIZE = 64;
53
+ const CV_POOL = new Uint32Array(CV_POOL_SIZE * 8); // 64 CVs × 8 words = 512 words
54
+ const CV_VIEWS: Uint32Array[] = [];
55
+ for (let i = 0; i < CV_POOL_SIZE; i++) {
56
+ CV_VIEWS.push(CV_POOL.subarray(i * 8, i * 8 + 8));
57
+ }
58
+
59
+ // SIMD initialization state
60
+ let simdAvailable = false;
61
+
62
+ // Threshold for switching to SIMD (must be > 1 chunk to benefit from parallelism)
63
+ const SIMD_THRESHOLD = 4 * CHUNK_LEN; // 4KB - need at least 4 chunks for SIMD benefit
64
+
65
+ /**
66
+ * Initialize SIMD synchronously (lazy).
67
+ */
68
+ function ensureSimdSync(): boolean {
69
+ if (simdAvailable) return true;
70
+ simdAvailable = initSimdSync();
71
+ return simdAvailable;
72
+ }
73
+
74
+ // Reusable buffer for SIMD chunk CVs (4 chunks × 8 words)
75
+ const simdChunkCvs = new Uint32Array(32);
76
+
77
+ // ===== Module-level reusable buffers (single-threaded safe) =====
78
+ // These eliminate heap allocations in hot paths
79
+
80
+ // For hashChunkWithWords() and hashChunkRoot()
81
+ const reusableTempCv = new Uint32Array(8);
82
+
83
+ // For hashPureJS()
84
+ const reusableChunkCv = new Uint32Array(8);
85
+ const reusablePureParentBlock = new Uint32Array(16);
86
+ const reusablePureParentCv = new Uint32Array(8);
87
+
88
+ // For hashSimd() - use flat array for 4 chunk CVs (access via subarray)
89
+ const reusableSimdCvs = new Uint32Array(32); // 4 × 8 words flat
90
+
91
+ // For hashSimd() parent compression
92
+ const reusableSimdParentBlock = new Uint32Array(16);
93
+ const reusableSimdParentCv = new Uint32Array(8);
94
+
95
+ // For hashSimd() parameters - TypedArrays instead of JS arrays
96
+ const reusableOffsets = new Uint32Array(4);
97
+ const reusableCounters = new Uint32Array(4);
98
+ const reusableBlockLens = new Uint32Array(4);
99
+ const reusableFlags = new Uint32Array(4);
100
+
101
+ // Reusable output buffer for common 32-byte hash (eliminates allocations)
102
+ const reusableOut8 = new Uint32Array(8); // Standard 32-byte output
103
+ // Pre-created view to avoid allocation in hot path (Task 1 optimization)
104
+ const reusableOut8View = new Uint8Array(reusableOut8.buffer, 0, 32);
105
+
106
+ // ===== Unrolled CV Copy Helper (Task 7 optimization) =====
107
+ // V8 will inline this - avoids loop overhead in hot paths
108
+ function copyCV8(src: Uint32Array, srcOff: number, dst: Uint32Array, dstOff: number): void {
109
+ dst[dstOff] = src[srcOff];
110
+ dst[dstOff + 1] = src[srcOff + 1];
111
+ dst[dstOff + 2] = src[srcOff + 2];
112
+ dst[dstOff + 3] = src[srcOff + 3];
113
+ dst[dstOff + 4] = src[srcOff + 4];
114
+ dst[dstOff + 5] = src[srcOff + 5];
115
+ dst[dstOff + 6] = src[srcOff + 6];
116
+ dst[dstOff + 7] = src[srcOff + 7];
117
+ }
118
+
119
+ /**
120
+ * Transpose 4 blocks (64 bytes each) into SIMD memory layout.
121
+ * The SIMD compress4x expects: [m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...]
122
+ * where m{i}_{j} is message word i from block j.
123
+ *
124
+ * OPTIMIZED: Processes all 4 blocks together for each word position,
125
+ * writing 4 consecutive u32s at once for better cache locality.
126
+ *
127
+ * @param inputWords - Pre-created Uint32Array view of input (null if unaligned/non-LE).
128
+ * Created once per hash call to avoid allocation in hot loop.
129
+ */
130
+ function transposeBlocksToSimd(
131
+ input: Uint8Array,
132
+ offsets: Uint32Array, // Starting offsets for each of 4 blocks
133
+ blockLens: Uint32Array, // Length of each block (0-64 bytes)
134
+ mem32: Uint32Array,
135
+ blockCount: number, // 1-4 blocks
136
+ inputWords: Uint32Array | null, // Pre-created view passed from caller
137
+ ): void {
138
+ // Fast path: all blocks are full 64-byte blocks with aligned LE input
139
+ const allFull =
140
+ blockCount === 4 &&
141
+ blockLens[0] === 64 &&
142
+ blockLens[1] === 64 &&
143
+ blockLens[2] === 64 &&
144
+ blockLens[3] === 64;
145
+
146
+ if (
147
+ allFull &&
148
+ inputWords &&
149
+ offsets[0] % 4 === 0 &&
150
+ offsets[1] % 4 === 0 &&
151
+ offsets[2] % 4 === 0 &&
152
+ offsets[3] % 4 === 0
153
+ ) {
154
+ // Ultra-fast path: process all 4 blocks together, write 4 consecutive u32s per word
155
+ const wordOff0 = offsets[0] >>> 2;
156
+ const wordOff1 = offsets[1] >>> 2;
157
+ const wordOff2 = offsets[2] >>> 2;
158
+ const wordOff3 = offsets[3] >>> 2;
159
+
160
+ for (let w = 0; w < 16; w++) {
161
+ const dstBase = w * 4;
162
+ mem32[dstBase] = inputWords[wordOff0 + w];
163
+ mem32[dstBase + 1] = inputWords[wordOff1 + w];
164
+ mem32[dstBase + 2] = inputWords[wordOff2 + w];
165
+ mem32[dstBase + 3] = inputWords[wordOff3 + w];
166
+ }
167
+ return;
168
+ }
169
+
170
+ // Standard path: process each block independently (handles partial blocks)
171
+ for (let b = 0; b < blockCount; b++) {
172
+ const len = blockLens[b];
173
+ const off = offsets[b];
174
+
175
+ if (len === 64) {
176
+ // Full block
177
+ if (inputWords && off % 4 === 0) {
178
+ // Direct Uint32Array access for aligned LE blocks
179
+ const wordOff = off >>> 2;
180
+ for (let w = 0; w < 16; w++) {
181
+ mem32[w * 4 + b] = inputWords[wordOff + w];
182
+ }
183
+ } else {
184
+ // Byte-by-byte reconstruction
185
+ for (let w = 0; w < 16; w++) {
186
+ const srcOff = off + w * 4;
187
+ mem32[w * 4 + b] =
188
+ input[srcOff] |
189
+ (input[srcOff + 1] << 8) |
190
+ (input[srcOff + 2] << 16) |
191
+ (input[srcOff + 3] << 24);
192
+ }
193
+ }
194
+ } else if (len === 0) {
195
+ // Zero block
196
+ for (let w = 0; w < 16; w++) {
197
+ mem32[w * 4 + b] = 0;
198
+ }
199
+ } else {
200
+ // Partial block - handle word by word
201
+ for (let w = 0; w < 16; w++) {
202
+ const wordOff = w * 4;
203
+ if (wordOff >= len) {
204
+ mem32[w * 4 + b] = 0;
205
+ } else if (wordOff + 4 <= len) {
206
+ const srcOff = off + wordOff;
207
+ mem32[w * 4 + b] =
208
+ input[srcOff] |
209
+ (input[srcOff + 1] << 8) |
210
+ (input[srcOff + 2] << 16) |
211
+ (input[srcOff + 3] << 24);
212
+ } else {
213
+ // Partial word at end of block
214
+ let word = 0;
215
+ for (let i = 0; i < len - wordOff; i++) {
216
+ word |= input[off + wordOff + i] << (i * 8);
217
+ }
218
+ mem32[w * 4 + b] = word;
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ // Zero unused block slots
225
+ for (let b = blockCount; b < 4; b++) {
226
+ for (let w = 0; w < 16; w++) {
227
+ mem32[w * 4 + b] = 0;
228
+ }
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Transpose 4 full chunks (4 × 16 blocks = 64 blocks) into batch SIMD memory.
234
+ * This is used for the batched compressChunks4x function that processes
235
+ * all 16 blocks in a single WASM call.
236
+ *
237
+ * Memory layout: BATCH_BLOCK_WORDS has 16 positions, each with 16 v128 values.
238
+ * Position p, word w: mem32[(p * 64) + (w * 4) + lane]
239
+ *
240
+ * OPTIMIZED: Processes all 4 chunks together for each (pos, word) pair,
241
+ * writing 4 consecutive u32s at once for better cache locality.
242
+ *
243
+ * @param input - Input data (must have at least 4 full chunks = 4096 bytes)
244
+ * @param chunkOffsets - Starting offsets for each of 4 chunks
245
+ * @param mem32 - WASM memory view
246
+ * @param inputWords - Pre-created Uint32Array view (null if unaligned)
247
+ */
248
+ function transposeBatchToSimd(
249
+ input: Uint8Array,
250
+ chunkOffsets: Uint32Array,
251
+ mem32: Uint32Array,
252
+ inputWords: Uint32Array | null,
253
+ ): void {
254
+ const BATCH_BASE = SIMD_MEMORY.BATCH_BLOCK_WORDS / 4;
255
+
256
+ // Get base word offsets for each chunk (pre-computed for fast path)
257
+ const chunk0WordBase = chunkOffsets[0] >>> 2;
258
+ const chunk1WordBase = chunkOffsets[1] >>> 2;
259
+ const chunk2WordBase = chunkOffsets[2] >>> 2;
260
+ const chunk3WordBase = chunkOffsets[3] >>> 2;
261
+
262
+ // Fast path: all chunks aligned and LE - process 4 consecutive u32s at once
263
+ if (inputWords && chunkOffsets[0] % 4 === 0) {
264
+ for (let pos = 0; pos < 16; pos++) {
265
+ const posBase = BATCH_BASE + pos * 64; // 16 words × 4 lanes = 64
266
+ const blockWordOff = pos * 16; // 16 words per block (64 bytes / 4)
267
+
268
+ // Process all 16 words, writing 4 chunks at a time (cache-friendly: 16 bytes per write group)
269
+ for (let w = 0; w < 16; w++) {
270
+ const dstBase = posBase + w * 4;
271
+ // Read word w from all 4 chunks at positions that become consecutive in output
272
+ mem32[dstBase] = inputWords[chunk0WordBase + blockWordOff + w];
273
+ mem32[dstBase + 1] = inputWords[chunk1WordBase + blockWordOff + w];
274
+ mem32[dstBase + 2] = inputWords[chunk2WordBase + blockWordOff + w];
275
+ mem32[dstBase + 3] = inputWords[chunk3WordBase + blockWordOff + w];
276
+ }
277
+ }
278
+ } else {
279
+ // Slow path: byte-by-byte reconstruction, still cache-friendly write pattern
280
+ for (let pos = 0; pos < 16; pos++) {
281
+ const posBase = BATCH_BASE + pos * 64;
282
+ const blockByteOff = pos * 64; // 64 bytes per block
283
+
284
+ for (let w = 0; w < 16; w++) {
285
+ const dstBase = posBase + w * 4;
286
+ const wordByteOff = w * 4;
287
+
288
+ // Chunk 0
289
+ const off0 = chunkOffsets[0] + blockByteOff + wordByteOff;
290
+ mem32[dstBase] =
291
+ input[off0] | (input[off0 + 1] << 8) | (input[off0 + 2] << 16) | (input[off0 + 3] << 24);
292
+
293
+ // Chunk 1
294
+ const off1 = chunkOffsets[1] + blockByteOff + wordByteOff;
295
+ mem32[dstBase + 1] =
296
+ input[off1] | (input[off1 + 1] << 8) | (input[off1 + 2] << 16) | (input[off1 + 3] << 24);
297
+
298
+ // Chunk 2
299
+ const off2 = chunkOffsets[2] + blockByteOff + wordByteOff;
300
+ mem32[dstBase + 2] =
301
+ input[off2] | (input[off2 + 1] << 8) | (input[off2 + 2] << 16) | (input[off2 + 3] << 24);
302
+
303
+ // Chunk 3
304
+ const off3 = chunkOffsets[3] + blockByteOff + wordByteOff;
305
+ mem32[dstBase + 3] =
306
+ input[off3] | (input[off3 + 1] << 8) | (input[off3 + 2] << 16) | (input[off3 + 3] << 24);
307
+ }
308
+ }
309
+ }
310
+ }
311
+
312
+ // Pre-computed memory offsets for SIMD operations (single-block mode)
313
+ const SIMD_CV_BASE = SIMD_MEMORY.CHAINING_VALUES / 4;
314
+ const SIMD_OUT_BASE = SIMD_MEMORY.OUTPUT / 4;
315
+ const SIMD_COUNTER_LOW_BASE = SIMD_MEMORY.COUNTER_LOW / 4;
316
+ const SIMD_COUNTER_HIGH_BASE = SIMD_MEMORY.COUNTER_HIGH / 4;
317
+ const SIMD_BLOCK_LEN_BASE = SIMD_MEMORY.BLOCK_LEN / 4;
318
+
319
+ // Pre-computed memory offsets for batch SIMD operations (16-block mode)
320
+ const BATCH_CV_BASE = SIMD_MEMORY.BATCH_CV / 4;
321
+ const BATCH_COUNTER_LOW_BASE = SIMD_MEMORY.BATCH_COUNTER_LOW / 4;
322
+ const BATCH_FLAGS_BASE_OFFSET = SIMD_MEMORY.BATCH_FLAGS_BASE / 4;
323
+ const BATCH_OUTPUT_BASE = SIMD_MEMORY.BATCH_OUTPUT / 4;
324
+
325
+ // Reusable arrays for batch processing
326
+ const batchChunkOffsets = new Uint32Array(4);
327
+ const SIMD_FLAGS_BASE = SIMD_MEMORY.FLAGS / 4;
328
+
329
+ /**
330
+ * Set up chaining values in SIMD memory (transposed layout).
331
+ * Optimized: unrolled loops for common case of 4 chunks.
332
+ * cvs is flat: [cv0_word0..cv0_word7, cv1_word0..cv1_word7, ...]
333
+ */
334
+ function setupSimdCvs(
335
+ cvs: Uint32Array, // Flat array: 4 × 8 words
336
+ mem32: Uint32Array,
337
+ count: number,
338
+ ): void {
339
+ // Unrolled for 4 chunks (common case)
340
+ if (count === 4) {
341
+ for (let w = 0; w < 8; w++) {
342
+ const base = SIMD_CV_BASE + w * 4;
343
+ mem32[base] = cvs[w]; // cv0[w]
344
+ mem32[base + 1] = cvs[8 + w]; // cv1[w]
345
+ mem32[base + 2] = cvs[16 + w]; // cv2[w]
346
+ mem32[base + 3] = cvs[24 + w]; // cv3[w]
347
+ }
348
+ } else {
349
+ for (let w = 0; w < 8; w++) {
350
+ const base = SIMD_CV_BASE + w * 4;
351
+ for (let c = 0; c < count; c++) {
352
+ mem32[base + c] = cvs[c * 8 + w];
353
+ }
354
+ for (let c = count; c < 4; c++) {
355
+ mem32[base + c] = 0;
356
+ }
357
+ }
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Set up SIMD parameters (counters, flags, block lengths).
363
+ */
364
+ function setupSimdParams(
365
+ mem32: Uint32Array,
366
+ counters: Uint32Array,
367
+ blockLens: Uint32Array,
368
+ flagsArr: Uint32Array,
369
+ count: number,
370
+ ): void {
371
+ // Most chunk counters fit in 32 bits, so counter high is usually 0
372
+ for (let i = 0; i < count; i++) {
373
+ mem32[SIMD_COUNTER_LOW_BASE + i] = counters[i];
374
+ mem32[SIMD_COUNTER_HIGH_BASE + i] = 0; // Assume counters fit in 32 bits
375
+ mem32[SIMD_BLOCK_LEN_BASE + i] = blockLens[i];
376
+ mem32[SIMD_FLAGS_BASE + i] = flagsArr[i];
377
+ }
378
+ // Zero unused slots
379
+ for (let i = count; i < 4; i++) {
380
+ mem32[SIMD_COUNTER_LOW_BASE + i] = 0;
381
+ mem32[SIMD_COUNTER_HIGH_BASE + i] = 0;
382
+ mem32[SIMD_BLOCK_LEN_BASE + i] = 0;
383
+ mem32[SIMD_FLAGS_BASE + i] = 0;
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Read output CVs from SIMD memory (untranspose).
389
+ */
390
+ function readSimdOutputCvs(
391
+ mem32: Uint32Array,
392
+ outputCvs: Uint32Array, // Flat array: 4 × 8 words
393
+ count: number,
394
+ ): void {
395
+ // Unrolled for 4 chunks (common case)
396
+ if (count === 4) {
397
+ for (let w = 0; w < 8; w++) {
398
+ const base = SIMD_OUT_BASE + w * 4;
399
+ outputCvs[w] = mem32[base];
400
+ outputCvs[8 + w] = mem32[base + 1];
401
+ outputCvs[16 + w] = mem32[base + 2];
402
+ outputCvs[24 + w] = mem32[base + 3];
403
+ }
404
+ } else {
405
+ for (let w = 0; w < 8; w++) {
406
+ const base = SIMD_OUT_BASE + w * 4;
407
+ for (let c = 0; c < count; c++) {
408
+ outputCvs[c * 8 + w] = mem32[base + c];
409
+ }
410
+ }
411
+ }
412
+ }
413
+
414
+ function getBlockWords(): Uint32Array {
415
+ if (!blockWords) {
416
+ blockWords = new Uint32Array(16);
417
+ }
418
+ return blockWords;
419
+ }
420
+
421
+ /**
422
+ * Hash a single chunk (up to 1024 bytes) with pre-created inputWords view.
423
+ * This is the optimized version that avoids creating Uint32Array views per chunk.
424
+ * (Fleek optimization Step 8)
425
+ */
426
+ function hashChunkWithWords(
427
+ input: Uint8Array,
428
+ inputWords: Uint32Array | null, // Pre-created view of entire input
429
+ inputOffset: number,
430
+ inputLen: number,
431
+ chunkCounter: number,
432
+ flags: number,
433
+ cv: Uint32Array,
434
+ cvOffset: number,
435
+ ): void {
436
+ // Use reusable temporary CV for intermediate blocks (single-threaded safe)
437
+ reusableTempCv.set(IV);
438
+
439
+ // Process full blocks
440
+ const fullBlocks = inputLen >>> 6; // inputLen / 64
441
+ const remainder = inputLen & 63; // inputLen % 64
442
+
443
+ // Calculate word offset for this chunk within the pre-created view
444
+ const chunkWordOffset = inputOffset >>> 2;
445
+
446
+ // Fast path for full chunks with aligned little-endian input
447
+ if (inputWords && remainder === 0 && inputLen === CHUNK_LEN) {
448
+ // All 16 blocks are full, use fast path exclusively
449
+ let wordOff = chunkWordOffset;
450
+ // Block 0 (CHUNK_START)
451
+ compress(
452
+ reusableTempCv,
453
+ 0,
454
+ inputWords,
455
+ wordOff,
456
+ reusableTempCv,
457
+ 0,
458
+ false,
459
+ chunkCounter,
460
+ BLOCK_LEN,
461
+ flags | CHUNK_START,
462
+ );
463
+ wordOff += 16;
464
+ // Blocks 1-14 (no special flags)
465
+ for (let i = 1; i < 15; i++) {
466
+ compress(
467
+ reusableTempCv,
468
+ 0,
469
+ inputWords,
470
+ wordOff,
471
+ reusableTempCv,
472
+ 0,
473
+ false,
474
+ chunkCounter,
475
+ BLOCK_LEN,
476
+ flags,
477
+ );
478
+ wordOff += 16;
479
+ }
480
+ // Block 15 (CHUNK_END)
481
+ compress(
482
+ reusableTempCv,
483
+ 0,
484
+ inputWords,
485
+ wordOff,
486
+ reusableTempCv,
487
+ 0,
488
+ false,
489
+ chunkCounter,
490
+ BLOCK_LEN,
491
+ flags | CHUNK_END,
492
+ );
493
+
494
+ cv.set(reusableTempCv, cvOffset);
495
+ return;
496
+ }
497
+
498
+ // Slower path for partial chunks or non-aligned input
499
+ const totalBlocks = fullBlocks + (remainder > 0 ? 1 : 0);
500
+ const block = getBlockWords();
501
+
502
+ for (let blockIdx = 0; blockIdx < totalBlocks; blockIdx++) {
503
+ const isFirst = blockIdx === 0;
504
+ const isLast = blockIdx === totalBlocks - 1;
505
+ const blockStart = blockIdx << 6;
506
+ const blockLen = isLast && remainder > 0 ? remainder : BLOCK_LEN;
507
+
508
+ // Determine flags for this block
509
+ let blockFlags = flags;
510
+ if (isFirst) blockFlags |= CHUNK_START;
511
+ if (isLast) blockFlags |= CHUNK_END;
512
+
513
+ // Load block words
514
+ if (isLast && remainder > 0) {
515
+ // Partial final block - need zero padding
516
+ readLittleEndianWordsPartial(input, inputOffset + blockStart, blockLen, block);
517
+ } else if (inputWords && chunkWordOffset + (blockStart >>> 2) + 16 <= inputWords.length) {
518
+ // Fast path: use pre-created view directly
519
+ compress(
520
+ reusableTempCv,
521
+ 0,
522
+ inputWords,
523
+ chunkWordOffset + (blockStart >>> 2),
524
+ reusableTempCv,
525
+ 0,
526
+ false,
527
+ chunkCounter,
528
+ blockLen,
529
+ blockFlags,
530
+ );
531
+ continue;
532
+ } else {
533
+ readLittleEndianWordsFull(input, inputOffset + blockStart, block);
534
+ }
535
+
536
+ compress(
537
+ reusableTempCv,
538
+ 0,
539
+ block,
540
+ 0,
541
+ reusableTempCv,
542
+ 0,
543
+ false,
544
+ chunkCounter,
545
+ blockLen,
546
+ blockFlags,
547
+ );
548
+ }
549
+
550
+ // Copy result to output
551
+ cv.set(reusableTempCv, cvOffset);
552
+ }
553
+
554
+ /**
555
+ * Hash input using pure JavaScript.
556
+ * Handles the full Merkle tree construction.
557
+ */
558
+ function hashPureJS(input: Uint8Array, outputLen: number): Uint8Array {
559
+ const inputLen = input.length;
560
+
561
+ // Special case: empty input
562
+ if (inputLen === 0) {
563
+ const block = getBlockWords();
564
+ block.fill(0);
565
+ // Use reusable output buffer for common 32-byte case
566
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
567
+
568
+ compress(IV, 0, block, 0, out, 0, outputLen > 32, 0, 0, CHUNK_START | CHUNK_END | ROOT);
569
+
570
+ // Return result - use pre-created view for common 32-byte case
571
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
572
+ return reusableOut8View.slice();
573
+ }
574
+ const result = new Uint8Array(outputLen);
575
+ if (IS_LITTLE_ENDIAN) {
576
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
577
+ } else {
578
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
579
+ }
580
+ return result;
581
+ }
582
+
583
+ // Calculate number of chunks
584
+ const numChunks = Math.ceil(inputLen / CHUNK_LEN);
585
+
586
+ // Single chunk optimization
587
+ if (numChunks === 1) {
588
+ // Use reusable output buffer for common 32-byte case
589
+ const cv = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
590
+ hashChunkRoot(input, 0, inputLen, 0, 0, cv, outputLen > 32);
591
+
592
+ // Return result - use pre-created view for common 32-byte case
593
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
594
+ return reusableOut8View.slice();
595
+ }
596
+ const result = new Uint8Array(outputLen);
597
+ if (IS_LITTLE_ENDIAN) {
598
+ result.set(new Uint8Array(cv.buffer, 0, outputLen));
599
+ } else {
600
+ writeLittleEndianBytesPartial(cv, 0, result, 0, outputLen);
601
+ }
602
+ return result;
603
+ }
604
+
605
+ // Multiple chunks - need Merkle tree
606
+ // Use the global contiguous CV stack (no allocation)
607
+ const stack = HYPER_CV_STACK;
608
+ let stackLen = 0;
609
+
610
+ // Use reusable buffers (single-threaded safe)
611
+ const chunkCv = reusableChunkCv;
612
+ const parentBlock = reusablePureParentBlock;
613
+ const parentCv = reusablePureParentCv;
614
+
615
+ // Create Uint32Array view ONCE for entire input (Fleek optimization Step 8)
616
+ // This avoids creating views inside each chunk/block processing
617
+ let inputWords: Uint32Array | null = null;
618
+ const canUseFastPath = IS_LITTLE_ENDIAN && input.byteOffset % 4 === 0;
619
+ if (canUseFastPath) {
620
+ inputWords = new Uint32Array(input.buffer, input.byteOffset, inputLen >>> 2);
621
+ }
622
+
623
+ // Determine how many full chunks we have
624
+ const fullChunks = inputLen >>> 10; // inputLen / 1024
625
+ const lastChunkLen = inputLen & 1023; // inputLen % 1024
626
+
627
+ // Process all full chunks with fast path (inlined for performance)
628
+ if (canUseFastPath && inputWords) {
629
+ for (let chunkIdx = 0; chunkIdx < fullChunks; chunkIdx++) {
630
+ // Inline chunk processing for full chunks
631
+ chunkCv.set(IV);
632
+ let wordOff = chunkIdx << 8; // chunkIdx * 256 (CHUNK_LEN/4)
633
+
634
+ // Block 0 (CHUNK_START)
635
+ compress(
636
+ chunkCv,
637
+ 0,
638
+ inputWords,
639
+ wordOff,
640
+ chunkCv,
641
+ 0,
642
+ false,
643
+ chunkIdx,
644
+ BLOCK_LEN,
645
+ CHUNK_START,
646
+ );
647
+ wordOff += 16;
648
+ // Blocks 1-14 (no special flags)
649
+ for (let b = 1; b < 15; b++) {
650
+ compress(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, BLOCK_LEN, 0);
651
+ wordOff += 16;
652
+ }
653
+ // Block 15 (CHUNK_END)
654
+ compress(chunkCv, 0, inputWords, wordOff, chunkCv, 0, false, chunkIdx, BLOCK_LEN, CHUNK_END);
655
+
656
+ // Merge completed subtrees (avoid subarray by using index math)
657
+ let totalChunks = chunkIdx + 1;
658
+ let cvSrcOff = 0;
659
+ let cvSrc = chunkCv;
660
+
661
+ // Check if this is the last chunk overall
662
+ const isLastChunk = chunkIdx === fullChunks - 1 && lastChunkLen === 0;
663
+
664
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
665
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
666
+ if (stackLen === 1 && isLastChunk) {
667
+ break;
668
+ }
669
+ stackLen--;
670
+ const stackOff = stackLen * 8;
671
+ // Copy left CV from stack to parentBlock[0..7] (unrolled)
672
+ copyCV8(stack, stackOff, parentBlock, 0);
673
+ // Copy current CV to parentBlock[8..15] (unrolled)
674
+ copyCV8(cvSrc, cvSrcOff, parentBlock, 8);
675
+
676
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
677
+ cvSrc = parentCv;
678
+ cvSrcOff = 0;
679
+ totalChunks >>>= 1;
680
+ }
681
+
682
+ // Push CV to stack (unrolled)
683
+ const stackOff = stackLen * 8;
684
+ copyCV8(cvSrc, cvSrcOff, stack, stackOff);
685
+ stackLen++;
686
+ }
687
+
688
+ // Process last partial chunk if any
689
+ if (lastChunkLen > 0) {
690
+ hashChunkWithWords(
691
+ input,
692
+ inputWords,
693
+ fullChunks * CHUNK_LEN,
694
+ lastChunkLen,
695
+ fullChunks,
696
+ 0,
697
+ chunkCv,
698
+ 0,
699
+ );
700
+
701
+ let totalChunks = fullChunks + 1;
702
+ let newCv = chunkCv;
703
+ let newCvOffset = 0;
704
+
705
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
706
+ // Skip final merge; this IS the last chunk, let finalization handle ROOT flag
707
+ if (stackLen === 1) {
708
+ break;
709
+ }
710
+ stackLen--;
711
+ const stackOff = stackLen * 8;
712
+ // Copy from stack to parentBlock[0..7] (unrolled)
713
+ copyCV8(stack, stackOff, parentBlock, 0);
714
+ // Copy from newCv to parentBlock[8..15] (unrolled)
715
+ copyCV8(newCv, newCvOffset, parentBlock, 8);
716
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
717
+ newCv = parentCv;
718
+ newCvOffset = 0;
719
+ totalChunks >>>= 1;
720
+ }
721
+
722
+ // Push CV to stack (unrolled)
723
+ const pushOff = stackLen * 8;
724
+ copyCV8(newCv, newCvOffset, stack, pushOff);
725
+ stackLen++;
726
+ }
727
+ } else {
728
+ // Slow path for unaligned or big-endian
729
+ for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
730
+ const chunkStart = chunkIdx * CHUNK_LEN;
731
+ const chunkLen = Math.min(CHUNK_LEN, inputLen - chunkStart);
732
+
733
+ hashChunkWithWords(input, inputWords, chunkStart, chunkLen, chunkIdx, 0, chunkCv, 0);
734
+
735
+ // Merge completed subtrees
736
+ let totalChunks = chunkIdx + 1;
737
+ let newCv = chunkCv;
738
+ let newCvOffset = 0;
739
+
740
+ // Check if this is the last chunk
741
+ const isLastChunk = chunkIdx === numChunks - 1;
742
+
743
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
744
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
745
+ if (stackLen === 1 && isLastChunk) {
746
+ break;
747
+ }
748
+ stackLen--;
749
+ const stackOff = stackLen * 8;
750
+ // Copy from stack to parentBlock[0..7] (unrolled)
751
+ copyCV8(stack, stackOff, parentBlock, 0);
752
+ // Copy from newCv to parentBlock[8..15] (unrolled)
753
+ copyCV8(newCv, newCvOffset, parentBlock, 8);
754
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
755
+ newCv = parentCv;
756
+ newCvOffset = 0;
757
+ totalChunks >>>= 1;
758
+ }
759
+
760
+ // Push CV to stack (unrolled)
761
+ const pushOff = stackLen * 8;
762
+ copyCV8(newCv, newCvOffset, stack, pushOff);
763
+ stackLen++;
764
+ }
765
+ }
766
+
767
+ // Finalize: merge remaining stack entries
768
+ while (stackLen > 1) {
769
+ stackLen--;
770
+ const rightOff = stackLen * 8;
771
+ stackLen--;
772
+ const leftOff = stackLen * 8;
773
+ // Copy left CV to parentBlock[0..7] and right CV to parentBlock[8..15] (unrolled)
774
+ copyCV8(stack, leftOff, parentBlock, 0);
775
+ copyCV8(stack, rightOff, parentBlock, 8);
776
+
777
+ if (stackLen === 0) {
778
+ // This is the root - use reusable output buffer for common 32-byte case
779
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
780
+ compress(IV, 0, parentBlock, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, PARENT | ROOT);
781
+
782
+ // Return result - use pre-created view for common 32-byte case
783
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
784
+ return reusableOut8View.slice();
785
+ }
786
+ const result = new Uint8Array(outputLen);
787
+ if (IS_LITTLE_ENDIAN) {
788
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
789
+ } else {
790
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
791
+ }
792
+ return result;
793
+ }
794
+
795
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
796
+
797
+ // Push to stack (unrolled)
798
+ copyCV8(parentCv, 0, stack, stackLen * 8);
799
+ stackLen++;
800
+ }
801
+
802
+ // Single entry in stack - this is the root
803
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
804
+ const lastBlock = getBlockWords();
805
+ lastBlock.fill(0);
806
+ // Copy first 8 words from stack (unrolled)
807
+ copyCV8(stack, 0, lastBlock, 0);
808
+
809
+ compress(IV, 0, lastBlock, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, ROOT);
810
+
811
+ // Return result - use pre-created view for common 32-byte case
812
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
813
+ return reusableOut8View.slice();
814
+ }
815
+ const result = new Uint8Array(outputLen);
816
+ if (IS_LITTLE_ENDIAN) {
817
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
818
+ } else {
819
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
820
+ }
821
+ return result;
822
+ }
823
+
824
+ /**
825
+ * Hash a single chunk that is also the root (single chunk input).
826
+ */
827
+ function hashChunkRoot(
828
+ input: Uint8Array,
829
+ inputOffset: number,
830
+ inputLen: number,
831
+ chunkCounter: number,
832
+ flags: number,
833
+ out: Uint32Array,
834
+ fullOutput: boolean,
835
+ ): void {
836
+ // Use reusable tempCv (single-threaded safe)
837
+ reusableTempCv.set(IV);
838
+
839
+ const block = getBlockWords();
840
+
841
+ // Process full blocks
842
+ const fullBlocks = inputLen >>> 6;
843
+ const remainder = inputLen & 63;
844
+ const totalBlocks = fullBlocks + (remainder > 0 ? 1 : 0) || 1; // At least 1 block
845
+
846
+ // Create a Uint32Array view if possible
847
+ let inputWords: Uint32Array | null = null;
848
+ if (IS_LITTLE_ENDIAN && (input.byteOffset + inputOffset) % 4 === 0 && inputLen >= 4) {
849
+ inputWords = new Uint32Array(input.buffer, input.byteOffset + inputOffset, inputLen >>> 2);
850
+ }
851
+
852
+ for (let blockIdx = 0; blockIdx < totalBlocks; blockIdx++) {
853
+ const isFirst = blockIdx === 0;
854
+ const isLast = blockIdx === totalBlocks - 1;
855
+ const blockStart = blockIdx << 6;
856
+ const blockLen = isLast ? remainder || (inputLen > 0 ? BLOCK_LEN : 0) : BLOCK_LEN;
857
+
858
+ // Determine flags
859
+ let blockFlags = flags;
860
+ if (isFirst) blockFlags |= CHUNK_START;
861
+ if (isLast) blockFlags |= CHUNK_END | ROOT;
862
+
863
+ // Load block
864
+ if (isLast && remainder > 0) {
865
+ readLittleEndianWordsPartial(input, inputOffset + blockStart, blockLen, block);
866
+ } else if (inputLen === 0) {
867
+ block.fill(0);
868
+ } else if (inputWords && (blockStart >>> 2) + 16 <= inputWords.length) {
869
+ // Fast path
870
+ compress(
871
+ reusableTempCv,
872
+ 0,
873
+ inputWords,
874
+ blockStart >>> 2,
875
+ isLast ? out : reusableTempCv,
876
+ 0,
877
+ isLast && fullOutput,
878
+ chunkCounter,
879
+ blockLen,
880
+ blockFlags,
881
+ );
882
+ continue;
883
+ } else {
884
+ readLittleEndianWordsFull(input, inputOffset + blockStart, block);
885
+ }
886
+
887
+ compress(
888
+ reusableTempCv,
889
+ 0,
890
+ block,
891
+ 0,
892
+ isLast ? out : reusableTempCv,
893
+ 0,
894
+ isLast && fullOutput,
895
+ chunkCounter,
896
+ blockLen,
897
+ blockFlags,
898
+ );
899
+ }
900
+ }
901
+
902
+ /**
903
+ * Hash using WASM SIMD - processes 4 chunks in parallel.
904
+ * Falls back to pure JS if SIMD fails.
905
+ */
906
+ function hashSimd(input: Uint8Array, outputLen: number): Uint8Array {
907
+ const mem = getSimdMemory();
908
+ if (!mem) {
909
+ return hashPureJS(input, outputLen);
910
+ }
911
+
912
+ const { view32 } = mem;
913
+ const inputLen = input.length;
914
+ const numChunks = Math.ceil(inputLen / CHUNK_LEN);
915
+
916
+ // For small inputs, pure JS is faster (no transpose overhead)
917
+ if (numChunks < 4) {
918
+ return hashPureJS(input, outputLen);
919
+ }
920
+
921
+ // Try to use WASM arena buffers (zero JS heap allocation)
922
+ // Falls back to JS buffers if arena not available
923
+ const arena = getArenaBuffers();
924
+ const useWasmParent = arena !== null; // Use WASM parent compress when arena available
925
+ let stack: Uint32Array;
926
+ let tempCvs: Uint32Array;
927
+ let parentBlock: Uint32Array;
928
+ let parentCv: Uint32Array;
929
+
930
+ if (arena) {
931
+ // Use WASM-backed arena buffers
932
+ stack = arena.cvStack;
933
+ tempCvs = arena.tempCvs;
934
+ parentBlock = arena.parentBlock;
935
+ parentCv = arena.chunkCv;
936
+ } else {
937
+ // Fallback to JS heap buffers - use global contiguous stack (no allocation)
938
+ stack = HYPER_CV_STACK;
939
+ tempCvs = reusableSimdCvs;
940
+ parentBlock = reusableSimdParentBlock;
941
+ parentCv = reusableSimdParentCv;
942
+ }
943
+
944
+ let stackLen = 0;
945
+
946
+ // Use TypedArrays instead of JS arrays for block parameters
947
+ const offsets = reusableOffsets;
948
+ const counters = reusableCounters;
949
+ const blockLens = reusableBlockLens;
950
+ const flagsArr = reusableFlags;
951
+
952
+ // Create Uint32Array view once for entire hash call (optimization: avoid allocation in hot loop)
953
+ const inputWords =
954
+ IS_LITTLE_ENDIAN && input.byteOffset % 4 === 0
955
+ ? new Uint32Array(input.buffer, input.byteOffset, input.byteLength >>> 2)
956
+ : null;
957
+
958
+ // Calculate number of full chunks (1024 bytes each)
959
+ const numFullChunks = inputLen >>> 10; // inputLen / 1024
960
+
961
+ // Process chunks in groups of 4
962
+ let chunkIdx = 0;
963
+ while (chunkIdx < numChunks) {
964
+ const groupSize = Math.min(4, numChunks - chunkIdx);
965
+
966
+ // === BATCH FAST PATH: 4 full chunks ===
967
+ // Use compressChunks4x for groups of exactly 4 full chunks
968
+ // This reduces 16 WASM calls to 1 per group
969
+ const canUseBatchPath = groupSize === 4 && chunkIdx + 4 <= numFullChunks;
970
+
971
+ if (canUseBatchPath) {
972
+ // Set up chunk offsets for batch transpose
973
+ batchChunkOffsets[0] = chunkIdx * CHUNK_LEN;
974
+ batchChunkOffsets[1] = (chunkIdx + 1) * CHUNK_LEN;
975
+ batchChunkOffsets[2] = (chunkIdx + 2) * CHUNK_LEN;
976
+ batchChunkOffsets[3] = (chunkIdx + 3) * CHUNK_LEN;
977
+
978
+ // Transpose all 64 blocks (4 chunks × 16 blocks) at once
979
+ transposeBatchToSimd(input, batchChunkOffsets, view32, inputWords);
980
+
981
+ // Set up initial CVs (IV) in batch memory - transposed layout
982
+ for (let w = 0; w < 8; w++) {
983
+ const ivWord = IV[w];
984
+ const base = BATCH_CV_BASE + w * 4;
985
+ view32[base] = ivWord;
986
+ view32[base + 1] = ivWord;
987
+ view32[base + 2] = ivWord;
988
+ view32[base + 3] = ivWord;
989
+ }
990
+
991
+ // Set up counters in batch memory
992
+ view32[BATCH_COUNTER_LOW_BASE] = chunkIdx;
993
+ view32[BATCH_COUNTER_LOW_BASE + 1] = chunkIdx + 1;
994
+ view32[BATCH_COUNTER_LOW_BASE + 2] = chunkIdx + 2;
995
+ view32[BATCH_COUNTER_LOW_BASE + 3] = chunkIdx + 3;
996
+
997
+ // Set up base flags (0 - no keyed hashing)
998
+ view32[BATCH_FLAGS_BASE_OFFSET] = 0;
999
+ view32[BATCH_FLAGS_BASE_OFFSET + 1] = 0;
1000
+ view32[BATCH_FLAGS_BASE_OFFSET + 2] = 0;
1001
+ view32[BATCH_FLAGS_BASE_OFFSET + 3] = 0;
1002
+
1003
+ // Run batched compress (16 blocks × 4 chunks in one call!)
1004
+ runCompressChunks4x();
1005
+
1006
+ // Read output CVs from batch output - untranspose to tempCvs
1007
+ for (let w = 0; w < 8; w++) {
1008
+ const base = BATCH_OUTPUT_BASE + w * 4;
1009
+ tempCvs[w] = view32[base]; // chunk 0
1010
+ tempCvs[8 + w] = view32[base + 1]; // chunk 1
1011
+ tempCvs[16 + w] = view32[base + 2]; // chunk 2
1012
+ tempCvs[24 + w] = view32[base + 3]; // chunk 3
1013
+ }
1014
+ } else {
1015
+ // === STANDARD PATH: block-by-block processing ===
1016
+ // Used for partial chunks or groups < 4
1017
+
1018
+ // Initialize CVs for this group to IV (flat array: 4 × 8 words)
1019
+ for (let g = 0; g < groupSize; g++) {
1020
+ const base = g * 8;
1021
+ tempCvs[base] = IV[0];
1022
+ tempCvs[base + 1] = IV[1];
1023
+ tempCvs[base + 2] = IV[2];
1024
+ tempCvs[base + 3] = IV[3];
1025
+ tempCvs[base + 4] = IV[4];
1026
+ tempCvs[base + 5] = IV[5];
1027
+ tempCvs[base + 6] = IV[6];
1028
+ tempCvs[base + 7] = IV[7];
1029
+ }
1030
+
1031
+ // Process all 16 blocks of each chunk in this group
1032
+ for (let blockIdx = 0; blockIdx < 16; blockIdx++) {
1033
+ // Calculate block offsets and parameters (reuse arrays)
1034
+
1035
+ for (let g = 0; g < groupSize; g++) {
1036
+ const thisChunkIdx = chunkIdx + g;
1037
+ const chunkStart = thisChunkIdx * CHUNK_LEN;
1038
+ const chunkLen = Math.min(CHUNK_LEN, inputLen - chunkStart);
1039
+ const thisBlockStart = chunkStart + blockIdx * BLOCK_LEN;
1040
+
1041
+ // Determine block length for this specific block
1042
+ const blockStartInChunk = blockIdx * BLOCK_LEN;
1043
+ let thisBlockLen = BLOCK_LEN;
1044
+ if (blockStartInChunk >= chunkLen) {
1045
+ thisBlockLen = 0;
1046
+ } else if (blockStartInChunk + BLOCK_LEN > chunkLen) {
1047
+ thisBlockLen = chunkLen - blockStartInChunk;
1048
+ }
1049
+
1050
+ offsets[g] = thisBlockStart;
1051
+ counters[g] = thisChunkIdx;
1052
+
1053
+ // Determine flags
1054
+ let flags = 0;
1055
+ if (blockIdx === 0) flags |= CHUNK_START;
1056
+ const totalBlocksInChunk = Math.ceil(chunkLen / BLOCK_LEN) || 1;
1057
+ if (blockIdx === totalBlocksInChunk - 1) flags |= CHUNK_END;
1058
+
1059
+ blockLens[g] = thisBlockLen;
1060
+ flagsArr[g] = flags;
1061
+ }
1062
+
1063
+ // Check if any blocks need processing
1064
+ if (blockLens[0] === 0 && blockLens[1] === 0 && blockLens[2] === 0 && blockLens[3] === 0)
1065
+ continue;
1066
+
1067
+ // Transpose blocks into SIMD memory (pass pre-created view to avoid allocation)
1068
+ transposeBlocksToSimd(input, offsets, blockLens, view32, groupSize, inputWords);
1069
+
1070
+ // Set up CVs in SIMD memory
1071
+ setupSimdCvs(tempCvs, view32, groupSize);
1072
+
1073
+ // Set up parameters
1074
+ setupSimdParams(view32, counters, blockLens, flagsArr, groupSize);
1075
+
1076
+ // Run SIMD compress
1077
+ runCompress4x();
1078
+
1079
+ // Read output CVs back
1080
+ readSimdOutputCvs(view32, simdChunkCvs, groupSize);
1081
+
1082
+ // Update tempCvs - copy from simdChunkCvs (both are flat 32-word arrays)
1083
+ // simdChunkCvs layout matches tempCvs: [cv0_w0..cv0_w7, cv1_w0..cv1_w7, ...]
1084
+ // IMPORTANT: Only update CVs for chunks that had data in this block!
1085
+ // Skipping this check would corrupt CVs for partial chunks after their final block.
1086
+ for (let g = 0; g < groupSize; g++) {
1087
+ if (blockLens[g] === 0) continue; // Don't update CV for chunks with no data in this block
1088
+ const base = g * 8;
1089
+ tempCvs[base] = simdChunkCvs[base];
1090
+ tempCvs[base + 1] = simdChunkCvs[base + 1];
1091
+ tempCvs[base + 2] = simdChunkCvs[base + 2];
1092
+ tempCvs[base + 3] = simdChunkCvs[base + 3];
1093
+ tempCvs[base + 4] = simdChunkCvs[base + 4];
1094
+ tempCvs[base + 5] = simdChunkCvs[base + 5];
1095
+ tempCvs[base + 6] = simdChunkCvs[base + 6];
1096
+ tempCvs[base + 7] = simdChunkCvs[base + 7];
1097
+ }
1098
+ }
1099
+ }
1100
+
1101
+ // Merge each chunk's CV into the Merkle tree
1102
+ for (let g = 0; g < groupSize; g++) {
1103
+ const thisChunkIdx = chunkIdx + g;
1104
+
1105
+ // Merge completed subtrees
1106
+ let totalChunks = thisChunkIdx + 1;
1107
+ // Track newCv source - either from tempCvs or parentCv
1108
+ let newCvBase = g * 8; // Offset into tempCvs
1109
+ let newCvSrc = tempCvs;
1110
+
1111
+ // Check if this is the last chunk
1112
+ const isLastChunk = thisChunkIdx === numChunks - 1;
1113
+
1114
+ while ((totalChunks & 1) === 0 && stackLen > 0) {
1115
+ // Skip final merge if it would produce the root; let finalization handle it with ROOT flag
1116
+ if (stackLen === 1 && isLastChunk) {
1117
+ break;
1118
+ }
1119
+ // Pop left child
1120
+ stackLen--;
1121
+ const stackOff = stackLen * 8;
1122
+ // Copy from stack to parentBlock[0..7] (unrolled)
1123
+ copyCV8(stack, stackOff, parentBlock, 0);
1124
+ // Copy from newCv source to parentBlock[8..15] (unrolled)
1125
+ copyCV8(newCvSrc, newCvBase, parentBlock, 8);
1126
+
1127
+ if (useWasmParent) {
1128
+ // WASM parent compress - data already in arena buffers
1129
+ runCompressParent();
1130
+ } else {
1131
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
1132
+ }
1133
+
1134
+ newCvSrc = parentCv;
1135
+ newCvBase = 0;
1136
+ totalChunks >>>= 1;
1137
+ }
1138
+
1139
+ // Push to stack (unrolled)
1140
+ const pushOff = stackLen * 8;
1141
+ copyCV8(newCvSrc, newCvBase, stack, pushOff);
1142
+ stackLen++;
1143
+ }
1144
+
1145
+ chunkIdx += groupSize;
1146
+ }
1147
+
1148
+ // Finalize: merge remaining stack entries
1149
+ while (stackLen > 1) {
1150
+ stackLen--;
1151
+ const rightOff = stackLen * 8;
1152
+ stackLen--;
1153
+ const leftOff = stackLen * 8;
1154
+ // Copy left CV to parentBlock[0..7] and right CV to parentBlock[8..15] (unrolled)
1155
+ copyCV8(stack, leftOff, parentBlock, 0);
1156
+ copyCV8(stack, rightOff, parentBlock, 8);
1157
+
1158
+ if (stackLen === 0) {
1159
+ // This is the root - use reusable output buffer
1160
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
1161
+ compress(IV, 0, parentBlock, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, PARENT | ROOT);
1162
+
1163
+ // Return result - use pre-created view for common 32-byte case
1164
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
1165
+ return reusableOut8View.slice();
1166
+ }
1167
+ const result = new Uint8Array(outputLen);
1168
+ if (IS_LITTLE_ENDIAN) {
1169
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
1170
+ } else {
1171
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
1172
+ }
1173
+ return result;
1174
+ }
1175
+
1176
+ if (useWasmParent) {
1177
+ // WASM parent compress - data already in arena buffers
1178
+ runCompressParent();
1179
+ } else {
1180
+ compress(IV, 0, parentBlock, 0, parentCv, 0, false, 0, BLOCK_LEN, PARENT);
1181
+ }
1182
+
1183
+ // Push to stack (unrolled)
1184
+ copyCV8(parentCv, 0, stack, stackLen * 8);
1185
+ stackLen++;
1186
+ }
1187
+
1188
+ // Single entry in stack - finalize as root
1189
+ if (stackLen === 1) {
1190
+ const block = getBlockWords();
1191
+ block.fill(0);
1192
+ // Copy first 8 words from stack (unrolled)
1193
+ copyCV8(stack, 0, block, 0);
1194
+
1195
+ // Use reusable output buffer
1196
+ const out = outputLen === 32 ? reusableOut8 : new Uint32Array(outputLen > 32 ? 16 : 8);
1197
+ compress(IV, 0, block, 0, out, 0, outputLen > 32, 0, BLOCK_LEN, ROOT);
1198
+
1199
+ // Return result - use pre-created view for common 32-byte case
1200
+ if (outputLen === 32 && IS_LITTLE_ENDIAN) {
1201
+ return reusableOut8View.slice();
1202
+ }
1203
+ const result = new Uint8Array(outputLen);
1204
+ if (IS_LITTLE_ENDIAN) {
1205
+ result.set(new Uint8Array(out.buffer, 0, outputLen));
1206
+ } else {
1207
+ writeLittleEndianBytesPartial(out, 0, result, 0, outputLen);
1208
+ }
1209
+ return result;
1210
+ }
1211
+
1212
+ // Should not reach here
1213
+ return hashPureJS(input, outputLen);
1214
+ }
1215
+
1216
+ /**
1217
+ * Hash input data and return the result.
1218
+ * Automatically uses WASM SIMD for large inputs when available.
1219
+ *
1220
+ * @param input - Data to hash
1221
+ * @param outputLength - Number of bytes to output (default: 32)
1222
+ * @returns The hash output
1223
+ */
1224
+ export function hash(input: Uint8Array, outputLength: number = OUT_LEN): Uint8Array {
1225
+ // For large inputs, use SIMD for ~1.5x performance improvement
1226
+ if (input.length >= SIMD_THRESHOLD && ensureSimdSync()) {
1227
+ return hashSimd(input, outputLength);
1228
+ }
1229
+ return hashPureJS(input, outputLength);
1230
+ }
1231
+
1232
+ /**
1233
+ * Pre-warm SIMD initialization (call early to avoid latency later).
1234
+ */
1235
+ export function warmupSimd(): boolean {
1236
+ return ensureSimdSync();
1237
+ }
1238
+
1239
+ /**
1240
+ * Hash input data directly into a caller-provided output buffer.
1241
+ * Zero-allocation for the common 32-byte case - ideal for performance-critical code.
1242
+ *
1243
+ * @param input - Data to hash
1244
+ * @param output - Pre-allocated output buffer (must be at least outputLength bytes)
1245
+ * @param outputLength - Number of bytes to output (default: 32, max: output.length)
1246
+ */
1247
+ export function hashInto(
1248
+ input: Uint8Array,
1249
+ output: Uint8Array,
1250
+ outputLength: number = OUT_LEN,
1251
+ ): void {
1252
+ // Validate output buffer
1253
+ if (output.length < outputLength) {
1254
+ throw new Error(`Output buffer too small: ${output.length} < ${outputLength}`);
1255
+ }
1256
+
1257
+ // For large inputs, use SIMD for ~1.5x performance improvement
1258
+ if (input.length >= SIMD_THRESHOLD && ensureSimdSync()) {
1259
+ hashSimdInto(input, output, outputLength);
1260
+ return;
1261
+ }
1262
+
1263
+ hashPureJSInto(input, output, outputLength);
1264
+ }
1265
+
1266
+ /**
1267
+ * Internal: Hash using pure JS, writing directly to output buffer.
1268
+ */
1269
+ function hashPureJSInto(input: Uint8Array, output: Uint8Array, outputLen: number): void {
1270
+ const inputLen = input.length;
1271
+
1272
+ // Special case: empty input
1273
+ if (inputLen === 0) {
1274
+ const block = getBlockWords();
1275
+ block.fill(0);
1276
+ const out = outputLen <= 32 ? reusableOut8 : new Uint32Array(16);
1277
+
1278
+ compress(IV, 0, block, 0, out, 0, outputLen > 32, 0, 0, CHUNK_START | CHUNK_END | ROOT);
1279
+
1280
+ // Copy result to output
1281
+ if (IS_LITTLE_ENDIAN) {
1282
+ output.set(new Uint8Array(out.buffer, out.byteOffset, outputLen));
1283
+ } else {
1284
+ writeLittleEndianBytesPartial(out, 0, output, 0, outputLen);
1285
+ }
1286
+ return;
1287
+ }
1288
+
1289
+ // Calculate number of chunks
1290
+ const numChunks = Math.ceil(inputLen / CHUNK_LEN);
1291
+
1292
+ // Single chunk optimization
1293
+ if (numChunks === 1) {
1294
+ const cv = outputLen <= 32 ? reusableOut8 : new Uint32Array(16);
1295
+ hashChunkRoot(input, 0, inputLen, 0, 0, cv, outputLen > 32);
1296
+
1297
+ // Copy result to output
1298
+ if (IS_LITTLE_ENDIAN) {
1299
+ output.set(new Uint8Array(cv.buffer, cv.byteOffset, outputLen));
1300
+ } else {
1301
+ writeLittleEndianBytesPartial(cv, 0, output, 0, outputLen);
1302
+ }
1303
+ return;
1304
+ }
1305
+
1306
+ // Multiple chunks - delegate to hashPureJS and copy result
1307
+ const result = hashPureJS(input, outputLen);
1308
+ output.set(result);
1309
+ }
1310
+
1311
+ /**
1312
+ * Internal: Hash using SIMD, writing directly to output buffer.
1313
+ */
1314
+ function hashSimdInto(input: Uint8Array, output: Uint8Array, outputLen: number): void {
1315
+ // Delegate to hashSimd and copy result (SIMD path already optimized)
1316
+ const result = hashSimd(input, outputLen);
1317
+ output.set(result);
1318
+ }
node_modules/@huggingface/blake3-jit/src/hasher.ts ADDED
@@ -0,0 +1,576 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Hasher - Incremental hashing with support for all modes
3
+ *
4
+ * Supports:
5
+ * - Regular hashing
6
+ * - Keyed hashing (MAC)
7
+ * - Key derivation (derive_key)
8
+ * - XOF (eXtendable Output Function) mode
9
+ */
10
+
11
+ import { compress } from "./compress.js";
12
+ import {
13
+ IV,
14
+ CHUNK_START,
15
+ CHUNK_END,
16
+ PARENT,
17
+ ROOT,
18
+ KEYED_HASH,
19
+ DERIVE_KEY_CONTEXT,
20
+ DERIVE_KEY_MATERIAL,
21
+ BLOCK_LEN,
22
+ CHUNK_LEN,
23
+ OUT_LEN,
24
+ KEY_LEN,
25
+ MAX_DEPTH,
26
+ } from "./constants.js";
27
+ import {
28
+ IS_LITTLE_ENDIAN,
29
+ readLittleEndianWordsFull,
30
+ writeLittleEndianBytesPartial,
31
+ encodeUTF8,
32
+ } from "./utils.js";
33
+
34
+ /**
35
+ * Output state for XOF (eXtendable Output Function) mode.
36
+ * Allows reading arbitrary amounts of output.
37
+ */
38
+ export class XofReader {
39
+ private inputCv: Uint32Array;
40
+ private blockWords: Uint32Array;
41
+ private counter: number;
42
+ private blockLen: number;
43
+ private flags: number;
44
+ private outputBlock: Uint32Array;
45
+ private outputBlockOffset: number;
46
+
47
+ constructor(
48
+ inputCv: Uint32Array,
49
+ blockWords: Uint32Array,
50
+ counter: number,
51
+ blockLen: number,
52
+ flags: number,
53
+ ) {
54
+ this.inputCv = inputCv;
55
+ this.blockWords = blockWords;
56
+ this.counter = counter;
57
+ this.blockLen = blockLen;
58
+ this.flags = flags | ROOT;
59
+ this.outputBlock = new Uint32Array(16);
60
+ this.outputBlockOffset = 64; // Forces generation on first read
61
+ }
62
+
63
+ /**
64
+ * Read the next `length` bytes of output.
65
+ */
66
+ read(length: number): Uint8Array {
67
+ const output = new Uint8Array(length);
68
+ let outputOffset = 0;
69
+
70
+ while (outputOffset < length) {
71
+ // Generate new output block if needed
72
+ if (this.outputBlockOffset >= 64) {
73
+ compress(
74
+ this.inputCv,
75
+ 0,
76
+ this.blockWords,
77
+ 0,
78
+ this.outputBlock,
79
+ 0,
80
+ true, // full 64-byte output
81
+ this.counter++,
82
+ this.blockLen,
83
+ this.flags,
84
+ );
85
+ this.outputBlockOffset = 0;
86
+ }
87
+
88
+ // Copy bytes from output block
89
+ const available = 64 - this.outputBlockOffset;
90
+ const toCopy = Math.min(available, length - outputOffset);
91
+
92
+ // Optimized copy using writeLittleEndianBytesPartial
93
+ const wordOffset = this.outputBlockOffset >>> 2;
94
+ const byteWithinWord = this.outputBlockOffset & 3;
95
+
96
+ if (byteWithinWord === 0 && toCopy >= 4) {
97
+ // Aligned copy - can use word-at-a-time
98
+ const fullWords = toCopy >>> 2;
99
+ writeLittleEndianBytesPartial(
100
+ this.outputBlock,
101
+ wordOffset,
102
+ output,
103
+ outputOffset,
104
+ fullWords << 2,
105
+ );
106
+ const bytesCopied = fullWords << 2;
107
+ outputOffset += bytesCopied;
108
+ this.outputBlockOffset += bytesCopied;
109
+ } else {
110
+ // Byte-by-byte for unaligned access
111
+ for (let i = 0; i < toCopy; i++) {
112
+ const wordIdx = (this.outputBlockOffset + i) >>> 2;
113
+ const byteIdx = (this.outputBlockOffset + i) & 3;
114
+ output[outputOffset + i] = (this.outputBlock[wordIdx] >>> (byteIdx << 3)) & 0xff;
115
+ }
116
+ outputOffset += toCopy;
117
+ this.outputBlockOffset += toCopy;
118
+ }
119
+ }
120
+
121
+ return output;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Chunk state for processing input data.
127
+ * Each chunk is 1024 bytes and produces an 8-word chaining value.
128
+ */
129
+ class ChunkState {
130
+ chainingValue: Uint32Array;
131
+ chunkCounter: number;
132
+ blockWords: Uint32Array;
133
+ blockLen: number;
134
+ blocksCompressed: number;
135
+ flags: number;
136
+
137
+ constructor(keyWords: Uint32Array, chunkCounter: number, flags: number) {
138
+ this.chainingValue = new Uint32Array(keyWords);
139
+ this.chunkCounter = chunkCounter;
140
+ this.blockWords = new Uint32Array(16);
141
+ this.blockLen = 0;
142
+ this.blocksCompressed = 0;
143
+ this.flags = flags;
144
+ }
145
+
146
+ resetTo(keyWords: Uint32Array, chunkCounter: number, flags: number): void {
147
+ this.chainingValue.set(keyWords);
148
+ this.chunkCounter = chunkCounter;
149
+ this.blockLen = 0;
150
+ this.blocksCompressed = 0;
151
+ this.flags = flags;
152
+ }
153
+
154
+ /**
155
+ * Get the flags for the current block.
156
+ */
157
+ private startFlag(): number {
158
+ return this.blocksCompressed === 0 ? CHUNK_START : 0;
159
+ }
160
+
161
+ /**
162
+ * Update the chunk state with input data.
163
+ * Returns the number of bytes consumed.
164
+ */
165
+ update(input: Uint8Array, inputOffset: number, inputLen: number): number {
166
+ let consumed = 0;
167
+
168
+ while (inputLen > 0) {
169
+ // If we have a full block, compress it
170
+ if (this.blockLen === BLOCK_LEN) {
171
+ compress(
172
+ this.chainingValue,
173
+ 0,
174
+ this.blockWords,
175
+ 0,
176
+ this.chainingValue,
177
+ 0,
178
+ false,
179
+ this.chunkCounter,
180
+ BLOCK_LEN,
181
+ this.flags | this.startFlag(),
182
+ );
183
+ this.blocksCompressed++;
184
+ this.blockLen = 0;
185
+ }
186
+
187
+ // Fill the block buffer
188
+ const want = BLOCK_LEN - this.blockLen;
189
+ const take = Math.min(want, inputLen);
190
+
191
+ if (this.blockLen === 0 && take === BLOCK_LEN) {
192
+ readLittleEndianWordsFull(input, inputOffset, this.blockWords);
193
+ } else {
194
+ // Partial block - byte-by-byte into correct position
195
+ for (let i = 0; i < take; i++) {
196
+ const pos = this.blockLen + i;
197
+ const wordIdx = pos >>> 2;
198
+ const byteIdx = pos & 3;
199
+
200
+ if (byteIdx === 0) {
201
+ this.blockWords[wordIdx] = input[inputOffset + i];
202
+ } else {
203
+ this.blockWords[wordIdx] |= input[inputOffset + i] << (byteIdx << 3);
204
+ }
205
+ }
206
+ }
207
+
208
+ this.blockLen += take;
209
+ inputOffset += take;
210
+ inputLen -= take;
211
+ consumed += take;
212
+ }
213
+
214
+ return consumed;
215
+ }
216
+
217
+ /**
218
+ * Finalize this chunk and return its output.
219
+ * Returns 8 words (chaining value) or 16 words (if root).
220
+ */
221
+ output(): {
222
+ inputCv: Uint32Array;
223
+ blockWords: Uint32Array;
224
+ blockLen: number;
225
+ counter: number;
226
+ flags: number;
227
+ } {
228
+ // Zero-pad unused words in blockWords to avoid stale data from previous blocks
229
+ // This is necessary when a partial block follows a full block within the same chunk
230
+ const usedWords = (this.blockLen + 3) >>> 2; // ceil(blockLen / 4)
231
+ for (let i = usedWords; i < 16; i++) {
232
+ this.blockWords[i] = 0;
233
+ }
234
+
235
+ return {
236
+ inputCv: this.chainingValue,
237
+ blockWords: this.blockWords,
238
+ blockLen: this.blockLen,
239
+ counter: this.chunkCounter,
240
+ flags: this.flags | this.startFlag() | CHUNK_END,
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Get the number of bytes in this chunk.
246
+ */
247
+ len(): number {
248
+ return this.blocksCompressed * BLOCK_LEN + this.blockLen;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Main BLAKE3 Hasher class.
254
+ *
255
+ * Usage:
256
+ * const hasher = new Hasher();
257
+ * hasher.update(data);
258
+ * const hash = hasher.finalize();
259
+ *
260
+ * Or with chaining:
261
+ * const hash = new Hasher().update(data).finalize();
262
+ */
263
+ export class Hasher {
264
+ private chunkState: ChunkState;
265
+ private keyWords: Uint32Array;
266
+ private cvStack: Uint32Array;
267
+ private cvStackLen: number;
268
+ private flags: number;
269
+ private parentBlock: Uint32Array;
270
+ private parentCv: Uint32Array;
271
+ private chunkCv: Uint32Array;
272
+ private outWords: Uint32Array;
273
+ private finalizeCv: Uint32Array;
274
+
275
+ /**
276
+ * Create a new Hasher.
277
+ *
278
+ * @param keyWords - Initial key words (IV for regular hashing)
279
+ * @param flags - Domain separation flags
280
+ */
281
+ constructor(keyWords?: Uint32Array, flags?: number) {
282
+ this.keyWords = keyWords ? new Uint32Array(keyWords) : new Uint32Array(IV);
283
+ this.flags = flags ?? 0;
284
+ this.chunkState = new ChunkState(this.keyWords, 0, this.flags);
285
+ this.cvStack = new Uint32Array(MAX_DEPTH * 8);
286
+ this.cvStackLen = 0;
287
+ this.parentBlock = new Uint32Array(16);
288
+ this.parentCv = new Uint32Array(8);
289
+ this.chunkCv = new Uint32Array(8);
290
+ this.outWords = new Uint32Array(16);
291
+ this.finalizeCv = new Uint32Array(8);
292
+ }
293
+
294
+ /**
295
+ * Reset the hasher to process a new message with the same key/flags.
296
+ * Reuses all internal buffers — zero allocations.
297
+ */
298
+ reset(): this {
299
+ this.chunkState.resetTo(this.keyWords, 0, this.flags);
300
+ this.cvStackLen = 0;
301
+ return this;
302
+ }
303
+
304
+ /**
305
+ * Create a new keyed hasher (MAC).
306
+ *
307
+ * @param key - 32-byte key
308
+ */
309
+ static newKeyed(key: Uint8Array): Hasher {
310
+ if (key.length !== KEY_LEN) {
311
+ throw new Error(`Key must be ${KEY_LEN} bytes, got ${key.length}`);
312
+ }
313
+
314
+ const keyWords = new Uint32Array(8);
315
+ if (IS_LITTLE_ENDIAN) {
316
+ const view = new Uint32Array(key.buffer, key.byteOffset, 8);
317
+ keyWords.set(view);
318
+ } else {
319
+ for (let i = 0; i < 8; i++) {
320
+ const off = i * 4;
321
+ keyWords[i] = key[off] | (key[off + 1] << 8) | (key[off + 2] << 16) | (key[off + 3] << 24);
322
+ }
323
+ }
324
+
325
+ return new Hasher(keyWords, KEYED_HASH);
326
+ }
327
+
328
+ /**
329
+ * Create a new key derivation hasher.
330
+ *
331
+ * @param context - Context string for domain separation
332
+ */
333
+ static newDeriveKey(context: string): Hasher {
334
+ // First, hash the context string with DERIVE_KEY_CONTEXT flag
335
+ const contextBytes = encodeUTF8(context);
336
+ const contextHasher = new Hasher(new Uint32Array(IV), DERIVE_KEY_CONTEXT);
337
+ contextHasher.update(contextBytes);
338
+
339
+ // Get the context key
340
+ const contextKey = new Uint32Array(8);
341
+ const output = contextHasher.finalizeOutput();
342
+ compress(
343
+ output.inputCv,
344
+ 0,
345
+ output.blockWords,
346
+ 0,
347
+ contextKey,
348
+ 0,
349
+ false,
350
+ output.counter,
351
+ output.blockLen,
352
+ output.flags | ROOT,
353
+ );
354
+
355
+ // Return a hasher initialized with the context key
356
+ return new Hasher(contextKey, DERIVE_KEY_MATERIAL);
357
+ }
358
+
359
+ /**
360
+ * Push a chaining value onto the stack.
361
+ */
362
+ private pushCv(cv: Uint32Array, cvOffset: number): void {
363
+ this.cvStack.set(cv.subarray(cvOffset, cvOffset + 8), this.cvStackLen * 8);
364
+ this.cvStackLen++;
365
+ }
366
+
367
+ /**
368
+ * Pop a chaining value from the stack.
369
+ */
370
+ private popCv(out: Uint32Array, outOffset: number): void {
371
+ this.cvStackLen--;
372
+ out.set(this.cvStack.subarray(this.cvStackLen * 8, (this.cvStackLen + 1) * 8), outOffset);
373
+ }
374
+
375
+ /**
376
+ * Add a chunk's chaining value and merge completed subtrees.
377
+ */
378
+ private addChunkCv(newCv: Uint32Array, newCvOffset: number, totalChunks: number): void {
379
+ const parentBlock = this.parentBlock;
380
+ const parentCv = this.parentCv;
381
+
382
+ while ((totalChunks & 1) === 0) {
383
+ // Pop left child, new CV is right child
384
+ this.popCv(parentBlock, 0);
385
+ parentBlock.set(newCv.subarray(newCvOffset, newCvOffset + 8), 8);
386
+
387
+ compress(
388
+ this.keyWords,
389
+ 0,
390
+ parentBlock,
391
+ 0,
392
+ parentCv,
393
+ 0,
394
+ false,
395
+ 0,
396
+ BLOCK_LEN,
397
+ this.flags | PARENT,
398
+ );
399
+
400
+ newCv = parentCv;
401
+ newCvOffset = 0;
402
+ totalChunks >>>= 1;
403
+ }
404
+
405
+ this.pushCv(newCv, newCvOffset);
406
+ }
407
+
408
+ /**
409
+ * Update the hasher with input data.
410
+ *
411
+ * @param input - Data to hash
412
+ * @returns this (for chaining)
413
+ */
414
+ update(input: Uint8Array): this {
415
+ let inputOffset = 0;
416
+ let inputLen = input.length;
417
+
418
+ // Fill the current chunk
419
+ while (inputLen > 0) {
420
+ // If current chunk is full, finalize it and start a new one
421
+ if (this.chunkState.len() === CHUNK_LEN) {
422
+ const output = this.chunkState.output();
423
+ const chunkCv = this.chunkCv;
424
+
425
+ compress(
426
+ output.inputCv,
427
+ 0,
428
+ output.blockWords,
429
+ 0,
430
+ chunkCv,
431
+ 0,
432
+ false,
433
+ output.counter,
434
+ output.blockLen,
435
+ output.flags,
436
+ );
437
+
438
+ const totalChunks = this.chunkState.chunkCounter + 1;
439
+ this.addChunkCv(chunkCv, 0, totalChunks);
440
+
441
+ this.chunkState.resetTo(this.keyWords, totalChunks, this.flags);
442
+ }
443
+
444
+ // Fill the current chunk
445
+ const want = CHUNK_LEN - this.chunkState.len();
446
+ const take = Math.min(want, inputLen);
447
+
448
+ this.chunkState.update(input, inputOffset, take);
449
+ inputOffset += take;
450
+ inputLen -= take;
451
+ }
452
+
453
+ return this;
454
+ }
455
+
456
+ /**
457
+ * Get the output parameters (for XOF mode or finalization).
458
+ */
459
+ private finalizeOutput(): {
460
+ inputCv: Uint32Array;
461
+ blockWords: Uint32Array;
462
+ blockLen: number;
463
+ counter: number;
464
+ flags: number;
465
+ } {
466
+ let output = this.chunkState.output();
467
+ let parentBlock = this.parentBlock;
468
+ let cv = this.finalizeCv;
469
+
470
+ // If there are chunks on the stack, merge them
471
+ if (this.cvStackLen > 0) {
472
+ // First compress the current chunk
473
+ compress(
474
+ output.inputCv,
475
+ 0,
476
+ output.blockWords,
477
+ 0,
478
+ cv,
479
+ 0,
480
+ false,
481
+ output.counter,
482
+ output.blockLen,
483
+ output.flags,
484
+ );
485
+
486
+ // Merge with parent nodes from stack
487
+ while (this.cvStackLen > 0) {
488
+ this.cvStackLen--;
489
+ parentBlock.set(this.cvStack.subarray(this.cvStackLen * 8, (this.cvStackLen + 1) * 8), 0);
490
+ parentBlock.set(cv, 8);
491
+
492
+ if (this.cvStackLen > 0) {
493
+ compress(
494
+ this.keyWords,
495
+ 0,
496
+ parentBlock,
497
+ 0,
498
+ cv,
499
+ 0,
500
+ false,
501
+ 0,
502
+ BLOCK_LEN,
503
+ this.flags | PARENT,
504
+ );
505
+ } else {
506
+ // This is the root - return output params
507
+ return {
508
+ inputCv: this.keyWords,
509
+ blockWords: parentBlock,
510
+ blockLen: BLOCK_LEN,
511
+ counter: 0,
512
+ flags: this.flags | PARENT,
513
+ };
514
+ }
515
+ }
516
+ }
517
+
518
+ // Single chunk case
519
+ return output;
520
+ }
521
+
522
+ /**
523
+ * Finalize the hash and return the result.
524
+ *
525
+ * @param outputLength - Number of bytes to output (default: 32)
526
+ * @returns The hash output
527
+ */
528
+ finalize(outputLength: number = OUT_LEN): Uint8Array {
529
+ const output = this.finalizeOutput();
530
+ const result = new Uint8Array(outputLength);
531
+
532
+ if (outputLength <= 64) {
533
+ const outWords = this.outWords;
534
+ compress(
535
+ output.inputCv,
536
+ 0,
537
+ output.blockWords,
538
+ 0,
539
+ outWords,
540
+ 0,
541
+ outputLength > 32, // full output if > 32 bytes
542
+ output.counter,
543
+ output.blockLen,
544
+ output.flags | ROOT,
545
+ );
546
+
547
+ if (IS_LITTLE_ENDIAN) {
548
+ const outBytes = new Uint8Array(outWords.buffer);
549
+ result.set(outBytes.subarray(0, outputLength));
550
+ } else {
551
+ writeLittleEndianBytesPartial(outWords, 0, result, 0, outputLength);
552
+ }
553
+ } else {
554
+ // Multiple blocks - use XOF
555
+ const xof = this.finalizeXof();
556
+ const full = xof.read(outputLength);
557
+ result.set(full);
558
+ }
559
+
560
+ return result;
561
+ }
562
+
563
+ /**
564
+ * Finalize and return an XOF reader for arbitrary-length output.
565
+ */
566
+ finalizeXof(): XofReader {
567
+ const output = this.finalizeOutput();
568
+ return new XofReader(
569
+ new Uint32Array(output.inputCv),
570
+ new Uint32Array(output.blockWords),
571
+ output.counter,
572
+ output.blockLen,
573
+ output.flags,
574
+ );
575
+ }
576
+ }
node_modules/@huggingface/blake3-jit/src/index.ts ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 - The fastest pure JavaScript implementation
3
+ *
4
+ * Features:
5
+ * - All 3 modes: hash, keyed (MAC), derive_key
6
+ * - XOF (eXtendable Output Function) support
7
+ * - Automatic WASM SIMD acceleration for large inputs
8
+ * - Zero dependencies
9
+ * - Tree-shakeable exports
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { hash, createKeyed, createDeriveKey } from 'blake3-jit';
14
+ *
15
+ * // Simple hashing
16
+ * const digest = hash(new Uint8Array([1, 2, 3]));
17
+ *
18
+ * // Keyed hashing (MAC)
19
+ * const mac = createKeyed(key).update(data).finalize();
20
+ *
21
+ * // Key derivation
22
+ * const derived = createDeriveKey("my context").update(material).finalize(64);
23
+ * ```
24
+ */
25
+
26
+ // Core exports
27
+ export { Hasher, XofReader } from "./hasher.js";
28
+ export { hash, hashInto, warmupSimd } from "./hash.js";
29
+
30
+ // Convenience imports
31
+ import { Hasher } from "./hasher.js";
32
+
33
+ /**
34
+ * Create a new keyed hasher (MAC).
35
+ *
36
+ * @param key - 32-byte key
37
+ * @returns A new Hasher configured for keyed hashing
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const key = new Uint8Array(32); // Your 32-byte key
42
+ * crypto.getRandomValues(key);
43
+ *
44
+ * const mac = createKeyed(key)
45
+ * .update(message)
46
+ * .finalize();
47
+ * ```
48
+ */
49
+ export function createKeyed(key: Uint8Array): Hasher {
50
+ return Hasher.newKeyed(key);
51
+ }
52
+
53
+ /**
54
+ * Create a new key derivation hasher.
55
+ *
56
+ * @param context - Context string for domain separation
57
+ * @returns A new Hasher configured for key derivation
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const derivedKey = createDeriveKey("my-app encryption key v1")
62
+ * .update(inputKeyMaterial)
63
+ * .finalize(32);
64
+ * ```
65
+ */
66
+ export function createDeriveKey(context: string): Hasher {
67
+ return Hasher.newDeriveKey(context);
68
+ }
69
+
70
+ /**
71
+ * Create a new regular hasher for incremental hashing.
72
+ *
73
+ * @returns A new Hasher
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const hasher = createHasher();
78
+ * hasher.update(chunk1);
79
+ * hasher.update(chunk2);
80
+ * const digest = hasher.finalize();
81
+ * ```
82
+ */
83
+ export function createHasher(): Hasher {
84
+ return new Hasher();
85
+ }
86
+
87
+ // Import for default export
88
+ import { hash, hashInto, warmupSimd } from "./hash.js";
89
+
90
+ // Pre-warm SIMD in browser environments (non-blocking)
91
+ // This avoids initialization latency on first large hash
92
+ if (typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined") {
93
+ queueMicrotask(() => {
94
+ warmupSimd();
95
+ });
96
+ }
97
+
98
+ // Default export for convenience
99
+ export default {
100
+ hash,
101
+ hashInto,
102
+ Hasher,
103
+ createHasher,
104
+ createKeyed,
105
+ createDeriveKey,
106
+ };
node_modules/@huggingface/blake3-jit/src/utils.ts ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 Utility Functions
3
+ *
4
+ * Optimized for little-endian systems (most user-facing systems).
5
+ * BLAKE3 is little-endian friendly - on little-endian systems we can
6
+ * create Uint32Array views directly over input buffers.
7
+ */
8
+
9
+ /**
10
+ * Detect system endianness at module load time.
11
+ * On little-endian systems, the byte 0x01 will be at index 0.
12
+ */
13
+ export const IS_LITTLE_ENDIAN = new Uint8Array(new Uint32Array([0x01020304]).buffer)[0] === 0x04;
14
+
15
+ /**
16
+ * Read 16 little-endian 32-bit words from a byte array into a Uint32Array.
17
+ * This is only needed on big-endian systems.
18
+ *
19
+ * @param input - Source byte array
20
+ * @param offset - Starting byte offset in input
21
+ * @param words - Destination Uint32Array (must have at least 16 elements)
22
+ */
23
+ export function readLittleEndianWordsFull(
24
+ input: Uint8Array,
25
+ offset: number,
26
+ words: Uint32Array,
27
+ ): void {
28
+ for (let i = 0; i < 16; ++i, offset += 4) {
29
+ words[i] =
30
+ input[offset] |
31
+ (input[offset + 1] << 8) |
32
+ (input[offset + 2] << 16) |
33
+ (input[offset + 3] << 24);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Read N little-endian 32-bit words from a byte array.
39
+ * Handles partial reads (for final blocks).
40
+ *
41
+ * @param input - Source byte array
42
+ * @param offset - Starting byte offset
43
+ * @param words - Destination Uint32Array
44
+ * @param wordCount - Number of words to read
45
+ */
46
+ export function readLittleEndianWords(
47
+ input: Uint8Array,
48
+ offset: number,
49
+ words: Uint32Array,
50
+ wordCount: number,
51
+ ): void {
52
+ for (let i = 0; i < wordCount; ++i, offset += 4) {
53
+ words[i] =
54
+ input[offset] |
55
+ (input[offset + 1] << 8) |
56
+ (input[offset + 2] << 16) |
57
+ (input[offset + 3] << 24);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Read a partial block with zero padding.
63
+ * Used for the final block when input length is not a multiple of 64.
64
+ *
65
+ * @param input - Source byte array
66
+ * @param offset - Starting byte offset
67
+ * @param length - Number of bytes to read (< 64)
68
+ * @param words - Destination Uint32Array (must have 16 elements)
69
+ */
70
+ export function readLittleEndianWordsPartial(
71
+ input: Uint8Array,
72
+ offset: number,
73
+ length: number,
74
+ words: Uint32Array,
75
+ ): void {
76
+ // Zero out all words first
77
+ words.fill(0);
78
+
79
+ // Read full words
80
+ const fullWords = length >>> 2;
81
+ let i = 0;
82
+ for (; i < fullWords; ++i, offset += 4) {
83
+ words[i] =
84
+ input[offset] |
85
+ (input[offset + 1] << 8) |
86
+ (input[offset + 2] << 16) |
87
+ (input[offset + 3] << 24);
88
+ }
89
+
90
+ // Handle remaining bytes (0-3)
91
+ const remaining = length & 3;
92
+ if (remaining > 0) {
93
+ let word = input[offset];
94
+ if (remaining > 1) word |= input[offset + 1] << 8;
95
+ if (remaining > 2) word |= input[offset + 2] << 16;
96
+ words[i] = word;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Write 8 little-endian 32-bit words to a byte array.
102
+ *
103
+ * @param words - Source Uint32Array
104
+ * @param wordOffset - Starting word offset in source
105
+ * @param output - Destination byte array
106
+ * @param byteOffset - Starting byte offset in destination
107
+ */
108
+ export function writeLittleEndianWords(
109
+ words: Uint32Array,
110
+ wordOffset: number,
111
+ output: Uint8Array,
112
+ byteOffset: number,
113
+ ): void {
114
+ for (let i = 0; i < 8; ++i, byteOffset += 4) {
115
+ const w = words[wordOffset + i];
116
+ output[byteOffset] = w & 0xff;
117
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
118
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
119
+ output[byteOffset + 3] = (w >>> 24) & 0xff;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Write N bytes from 32-bit words to output.
125
+ * Used for variable-length output (XOF mode).
126
+ *
127
+ * @param words - Source Uint32Array
128
+ * @param wordOffset - Starting word offset
129
+ * @param output - Destination byte array
130
+ * @param byteOffset - Starting byte offset in destination
131
+ * @param byteCount - Number of bytes to write
132
+ */
133
+ export function writeLittleEndianBytesPartial(
134
+ words: Uint32Array,
135
+ wordOffset: number,
136
+ output: Uint8Array,
137
+ byteOffset: number,
138
+ byteCount: number,
139
+ ): void {
140
+ const fullWords = byteCount >>> 2;
141
+ let i = 0;
142
+
143
+ // Write full words
144
+ for (; i < fullWords; ++i, byteOffset += 4) {
145
+ const w = words[wordOffset + i];
146
+ output[byteOffset] = w & 0xff;
147
+ output[byteOffset + 1] = (w >>> 8) & 0xff;
148
+ output[byteOffset + 2] = (w >>> 16) & 0xff;
149
+ output[byteOffset + 3] = (w >>> 24) & 0xff;
150
+ }
151
+
152
+ // Write remaining bytes
153
+ const remaining = byteCount & 3;
154
+ if (remaining > 0) {
155
+ const w = words[wordOffset + i];
156
+ output[byteOffset] = w & 0xff;
157
+ if (remaining > 1) output[byteOffset + 1] = (w >>> 8) & 0xff;
158
+ if (remaining > 2) output[byteOffset + 2] = (w >>> 16) & 0xff;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Encode a UTF-8 string to Uint8Array.
164
+ * Used for derive_key context strings.
165
+ */
166
+ export function encodeUTF8(str: string): Uint8Array {
167
+ if (typeof TextEncoder !== "undefined") {
168
+ return new TextEncoder().encode(str);
169
+ }
170
+ // Fallback for older environments
171
+ const bytes: number[] = [];
172
+ for (let i = 0; i < str.length; i++) {
173
+ let c = str.charCodeAt(i);
174
+ if (c < 0x80) {
175
+ bytes.push(c);
176
+ } else if (c < 0x800) {
177
+ bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
178
+ } else if (c < 0xd800 || c >= 0xe000) {
179
+ bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
180
+ } else {
181
+ // Surrogate pair
182
+ i++;
183
+ c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
184
+ bytes.push(
185
+ 0xf0 | (c >> 18),
186
+ 0x80 | ((c >> 12) & 0x3f),
187
+ 0x80 | ((c >> 6) & 0x3f),
188
+ 0x80 | (c & 0x3f),
189
+ );
190
+ }
191
+ }
192
+ return new Uint8Array(bytes);
193
+ }
194
+
195
+ /**
196
+ * De Bruijn lookup table for O(1) trailing zero count.
197
+ * The expression (n & -n) isolates the lowest set bit.
198
+ * Multiplying by the De Bruijn constant maps each power of 2 to a unique 5-bit index.
199
+ */
200
+ const CTZ32_TABLE = new Uint8Array([
201
+ 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12,
202
+ 18, 6, 11, 5, 10, 9,
203
+ ]);
204
+
205
+ /**
206
+ * Count trailing zero bits in a 32-bit number using De Bruijn multiplication.
207
+ * This is O(1) and branchless for non-zero inputs.
208
+ *
209
+ * For Merkle tree merge: ctz32(chunkCounter) tells us how many merges to do.
210
+ */
211
+ export function ctz32(n: number): number {
212
+ if (n === 0) return 32;
213
+ // Use unsigned right shift to handle negative numbers correctly
214
+ return CTZ32_TABLE[(((n & -n) * 0x077cb531) >>> 27) & 31];
215
+ }
216
+
217
+ /**
218
+ * Count trailing zero bits in a 64-bit number.
219
+ * Used to determine how many parent nodes to compute after adding a chunk.
220
+ *
221
+ * Note: JavaScript bitwise ops work on 32-bit signed integers,
222
+ * so we need to handle 64-bit numbers carefully.
223
+ */
224
+ export function countTrailingZeros(n: number): number {
225
+ if (n === 0) return 64;
226
+
227
+ // For numbers that fit in 32 bits
228
+ const low = n | 0;
229
+ if (low !== 0) {
230
+ // Use Math.clz32 trick: ctz(x) = 31 - clz32(x & -x) for non-zero x
231
+ return 31 - Math.clz32(low & -low);
232
+ }
233
+
234
+ // High 32 bits
235
+ const high = (n / 0x100000000) | 0;
236
+ if (high !== 0) {
237
+ return 32 + (31 - Math.clz32(high & -high));
238
+ }
239
+
240
+ return 64;
241
+ }
242
+
243
+ /**
244
+ * Create a Uint32Array view of a Uint8Array.
245
+ * Only works correctly on little-endian systems when the offset is 4-byte aligned.
246
+ *
247
+ * @param arr - Source byte array
248
+ * @param byteOffset - Starting byte offset (must be 4-byte aligned)
249
+ * @param wordLength - Number of 32-bit words
250
+ */
251
+ export function uint32View(arr: Uint8Array, byteOffset: number, wordLength: number): Uint32Array {
252
+ return new Uint32Array(arr.buffer, arr.byteOffset + byteOffset, wordLength);
253
+ }
node_modules/@huggingface/blake3-jit/src/wasm-simd.ts ADDED
@@ -0,0 +1,1087 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BLAKE3 WASM SIMD - Runtime bytecode generation
3
+ *
4
+ * Generates WebAssembly SIMD bytecode at runtime to process 4 compress
5
+ * operations in parallel using 128-bit SIMD vectors (i32x4).
6
+ *
7
+ * Key insight: One i32x4.add instruction performs 4 parallel additions,
8
+ * giving us 4x throughput for the same number of instructions.
9
+ *
10
+ * Memory layout (all values are transposed for SIMD access):
11
+ * 0-511: 4 x 16 message words (m0_0,m0_1,m0_2,m0_3, m1_0,m1_1,m1_2,m1_3, ...)
12
+ * 512-639: 4 x 8 chaining values
13
+ * 640-767: 4 x 8 output values
14
+ * 768-783: 4 x counter low
15
+ * 784-799: 4 x counter high
16
+ * 800-815: 4 x block length
17
+ * 816-831: 4 x flags
18
+ */
19
+
20
+ // LEB128 encoding with minimum 2 bytes
21
+ // This fixes a V8 quirk where single-byte values 64-127 cause issues
22
+ // when followed by certain SIMD instructions
23
+ function toLebU32Min2(n: number): number[] {
24
+ // Always use at least 2 bytes
25
+ return [(n & 0x7f) | 0x80, (n >>> 7) & 0x7f];
26
+ }
27
+
28
+ // LEB128 encoding padded to exactly 5 bytes (for backpatching)
29
+ // Uses continuation bits for all but the last byte
30
+ function toLebU32Padded5(n: number): number[] {
31
+ return [
32
+ (n & 0x7f) | 0x80,
33
+ ((n >>> 7) & 0x7f) | 0x80,
34
+ ((n >>> 14) & 0x7f) | 0x80,
35
+ ((n >>> 21) & 0x7f) | 0x80,
36
+ (n >>> 28) & 0x0f, // Last byte has no continuation bit
37
+ ];
38
+ }
39
+
40
+ // Signed LEB128 encoding for i32 constants (handles full 32-bit range)
41
+ // WASM i32.const uses signed LEB128 immediate
42
+ function toSignedLeb128_i32(n: number): number[] {
43
+ const bytes: number[] = [];
44
+ // Treat as signed 32-bit integer
45
+ let value = n | 0;
46
+ let more = true;
47
+ while (more) {
48
+ let byte = value & 0x7f;
49
+ // Arithmetic right shift preserves sign
50
+ value >>= 7;
51
+ // Check if we're done:
52
+ // - If value is 0 and sign bit of byte is clear, we're done
53
+ // - If value is -1 and sign bit of byte is set, we're done
54
+ if ((value === 0 && (byte & 0x40) === 0) || (value === -1 && (byte & 0x40) !== 0)) {
55
+ more = false;
56
+ } else {
57
+ byte |= 0x80;
58
+ }
59
+ bytes.push(byte);
60
+ }
61
+ return bytes;
62
+ }
63
+
64
+ // Precomputed message access order for all 7 rounds
65
+ const MSG_ACCESS_ORDER = [
66
+ // Round 1: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
67
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
68
+ // Round 2: 2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8
69
+ 2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8,
70
+ // Round 3: 3,4,10,12,13,2,7,14,6,5,9,0,11,15,8,1
71
+ 3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1,
72
+ // Round 4: 10,7,12,9,14,3,13,15,4,0,11,2,5,8,1,6
73
+ 10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6,
74
+ // Round 5: 12,13,9,11,15,10,14,8,7,2,5,3,0,1,6,4
75
+ 12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4,
76
+ // Round 6: 9,14,11,5,8,12,15,1,13,3,0,10,2,6,4,7
77
+ 9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7,
78
+ // Round 7: 11,15,5,0,1,9,8,6,14,10,2,12,3,4,7,13
79
+ 11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13,
80
+ ];
81
+
82
+ // BLAKE3 Constants (used in generated WASM code)
83
+ // CHUNK_START = 1, CHUNK_END = 2 are embedded directly in WASM bytecode
84
+
85
+ /**
86
+ * Generate the WASM module bytecode with compress4x, compressChunks4x, and compressParent functions.
87
+ */
88
+ function generateWasmBytes(): Uint8Array {
89
+ const code: number[] = [];
90
+
91
+ // Helper to append bytes
92
+ function put(bytes: number[]): void {
93
+ code.push(...bytes);
94
+ }
95
+
96
+ // WASM module header
97
+ put([0x00, 0x61, 0x73, 0x6d]); // Magic
98
+ put([0x01, 0x00, 0x00, 0x00]); // Version
99
+
100
+ // Section 1: Types
101
+ put([0x01]); // Section ID
102
+ put([0x04]); // Section size
103
+ put([0x01]); // 1 type
104
+ put([0x60, 0x00, 0x00]); // func () -> ()
105
+
106
+ // Section 2: Imports (memory from JS)
107
+ put([0x02]); // Section ID
108
+ put([0x0b]); // Section size
109
+ put([0x01]); // 1 import
110
+ put([0x02, 0x6a, 0x73]); // "js"
111
+ put([0x03, 0x6d, 0x65, 0x6d]); // "mem"
112
+ put([0x02, 0x00, 0x01]); // memory min=1, no max
113
+
114
+ // Section 3: Functions
115
+ put([0x03]); // Section ID
116
+ put([0x04]); // Section size (3 functions = 4 bytes)
117
+ put([0x03]); // 3 functions
118
+ put([0x00]); // Function 0: type index 0
119
+ put([0x00]); // Function 1: type index 0
120
+ put([0x00]); // Function 2: type index 0
121
+
122
+ // Section 7: Exports
123
+ // Size calculation: 1 (count) + (1+10+1+1) + (1+16+1+1) + (1+14+1+1) = 1 + 13 + 19 + 17 = 50 bytes
124
+ put([0x07]); // Section ID
125
+ put([0x32]); // Section size (50 bytes)
126
+ put([0x03]); // 3 exports
127
+ // "compress4x" -> func 0
128
+ put([0x0a]); // name length
129
+ put([0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x34, 0x78]); // "compress4x"
130
+ put([0x00, 0x00]); // func index 0
131
+ // "compressChunks4x" -> func 1
132
+ put([0x10]); // name length (16)
133
+ put([
134
+ 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x34, 0x78,
135
+ ]); // "compressChunks4x"
136
+ put([0x00, 0x01]); // func index 1
137
+ // "compressParent" -> func 2
138
+ put([0x0e]); // name length (14)
139
+ put([0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74]); // "compressParent"
140
+ put([0x00, 0x02]); // func index 2
141
+
142
+ // Section 10: Code
143
+ put([0x0a]); // Section ID
144
+ // Reserve 5 bytes for section size (LEB128 u32)
145
+ const sectionSizeOffset = code.length;
146
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
147
+
148
+ put([0x03]); // 3 functions
149
+
150
+ // === Function 0: compress4x ===
151
+ // Reserve 5 bytes for function size
152
+ const funcSizeOffset = code.length;
153
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
154
+
155
+ const funcBodyStart = code.length;
156
+
157
+ // Local declarations: 32 v128 locals
158
+ // Variables $0-$15: message words (m0-m15)
159
+ // Variables $16-$31: state words (s0-s15)
160
+ put([0x01]); // 1 local declaration
161
+ put([0x20, 0x7b]); // 32 x v128
162
+
163
+ // ===== Function body =====
164
+
165
+ // Load message words from memory (offset 0-255)
166
+ // Each v128 is 16 bytes, so m[i] is at offset i*16
167
+ // Note: we use toLebU32Min2 to avoid V8 quirk with single-byte values 64-127
168
+ for (let i = 0; i < 16; i++) {
169
+ put([0x41, ...toLebU32Min2(i * 16)]); // i32.const offset (2+ byte LEB128)
170
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
171
+ put([0x21, i]); // local.set $i
172
+ }
173
+
174
+ // Load chaining values (offset 512-639)
175
+ // cv[i] at offset 512 + i*16
176
+ for (let i = 0; i < 8; i++) {
177
+ put([0x41, ...toLebU32Min2(512 + i * 16)]); // i32.const offset
178
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
179
+ put([0x21, 16 + i]); // local.set $(16+i)
180
+ }
181
+
182
+ // Initialize state[8-15] from IV and parameters
183
+ // s8-s11 = IV[0-3]
184
+ const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a];
185
+ for (let i = 0; i < 4; i++) {
186
+ // Create v128 constant with all lanes set to IV[i]
187
+ const ivBytes = [];
188
+ for (let j = 0; j < 4; j++) {
189
+ ivBytes.push(IV[i] & 0xff);
190
+ ivBytes.push((IV[i] >>> 8) & 0xff);
191
+ ivBytes.push((IV[i] >>> 16) & 0xff);
192
+ ivBytes.push((IV[i] >>> 24) & 0xff);
193
+ }
194
+ put([0xfd, 0x0c, ...ivBytes]); // v128.const
195
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
196
+ }
197
+
198
+ // s12 = counter_low (offset 768)
199
+ put([0x41, ...toLebU32Min2(768)]); // i32.const
200
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
201
+ put([0x21, 28]); // local.set $28 -> s12
202
+
203
+ // s13 = counter_high (offset 784)
204
+ put([0x41, ...toLebU32Min2(784)]); // i32.const
205
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
206
+ put([0x21, 29]); // local.set $29 -> s13
207
+
208
+ // s14 = block_len (offset 800)
209
+ put([0x41, ...toLebU32Min2(800)]); // i32.const
210
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
211
+ put([0x21, 30]); // local.set $30 -> s14
212
+
213
+ // s15 = flags (offset 816)
214
+ put([0x41, ...toLebU32Min2(816)]); // i32.const
215
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
216
+ put([0x21, 31]); // local.set $31 -> s15
217
+
218
+ // ===== 7 rounds of mixing =====
219
+
220
+ let msgIdx = 0; // Index into MSG_ACCESS_ORDER
221
+
222
+ // Helper to generate G function (inlined)
223
+ // G(a, b, c, d) with two message words
224
+ function g(a: number, b: number, c: number, d: number): void {
225
+ const mx = MSG_ACCESS_ORDER[msgIdx++];
226
+ const my = MSG_ACCESS_ORDER[msgIdx++];
227
+
228
+ // Variables: a,b,c,d are state indices (16-31), mx,my are message indices (0-15)
229
+
230
+ // First half of G
231
+ // s[a] = s[a] + s[b] + m[mx]
232
+ put([0x20, 16 + a]); // local.get s[a]
233
+ put([0x20, 16 + b]); // local.get s[b]
234
+ put([0xfd, 0xae, 0x01]); // i32x4.add
235
+ put([0x20, mx]); // local.get m[mx]
236
+ put([0xfd, 0xae, 0x01]); // i32x4.add
237
+ put([0x21, 16 + a]); // local.set s[a]
238
+
239
+ // s[d] = rotr(s[d] ^ s[a], 16) - using i8x16.shuffle (single instruction vs shift+or)
240
+ // ROTR16 pattern: [2,3,0,1, 6,7,4,5, 10,11,8,9, 14,15,12,13]
241
+ put([0x20, 16 + d]); // local.get s[d]
242
+ put([0x20, 16 + a]); // local.get s[a]
243
+ put([0xfd, 0x51]); // v128.xor
244
+ put([0x22, 16 + d]); // local.tee s[d]
245
+ put([0x20, 16 + d]); // local.get s[d] (second operand for shuffle)
246
+ put([0xfd, 0x0d, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13]); // i8x16.shuffle ROTR16
247
+ put([0x21, 16 + d]); // local.set s[d]
248
+
249
+ // s[c] = s[c] + s[d]
250
+ put([0x20, 16 + c]); // local.get s[c]
251
+ put([0x20, 16 + d]); // local.get s[d]
252
+ put([0xfd, 0xae, 0x01]); // i32x4.add
253
+ put([0x21, 16 + c]); // local.set s[c]
254
+
255
+ // s[b] = (s[b] ^ s[c]) >>> 12
256
+ put([0x20, 16 + b]); // local.get s[b]
257
+ put([0x20, 16 + c]); // local.get s[c]
258
+ put([0xfd, 0x51]); // v128.xor
259
+ put([0x22, 16 + b]); // local.tee s[b]
260
+ put([0x41, 0x0c]); // i32.const 12
261
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
262
+ put([0x20, 16 + b]); // local.get s[b]
263
+ put([0x41, 0x14]); // i32.const 20
264
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
265
+ put([0xfd, 0x50]); // v128.or
266
+ put([0x21, 16 + b]); // local.set s[b]
267
+
268
+ // Second half of G
269
+ // s[a] = s[a] + s[b] + m[my]
270
+ put([0x20, 16 + a]); // local.get s[a]
271
+ put([0x20, 16 + b]); // local.get s[b]
272
+ put([0xfd, 0xae, 0x01]); // i32x4.add
273
+ put([0x20, my]); // local.get m[my]
274
+ put([0xfd, 0xae, 0x01]); // i32x4.add
275
+ put([0x21, 16 + a]); // local.set s[a]
276
+
277
+ // s[d] = rotr(s[d] ^ s[a], 8) - using i8x16.shuffle (single instruction vs shift+or)
278
+ // ROTR8 pattern: [1,2,3,0, 5,6,7,4, 9,10,11,8, 13,14,15,12]
279
+ put([0x20, 16 + d]); // local.get s[d]
280
+ put([0x20, 16 + a]); // local.get s[a]
281
+ put([0xfd, 0x51]); // v128.xor
282
+ put([0x22, 16 + d]); // local.tee s[d]
283
+ put([0x20, 16 + d]); // local.get s[d] (second operand for shuffle)
284
+ put([0xfd, 0x0d, 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12]); // i8x16.shuffle ROTR8
285
+ put([0x21, 16 + d]); // local.set s[d]
286
+
287
+ // s[c] = s[c] + s[d]
288
+ put([0x20, 16 + c]); // local.get s[c]
289
+ put([0x20, 16 + d]); // local.get s[d]
290
+ put([0xfd, 0xae, 0x01]); // i32x4.add
291
+ put([0x21, 16 + c]); // local.set s[c]
292
+
293
+ // s[b] = (s[b] ^ s[c]) >>> 7
294
+ put([0x20, 16 + b]); // local.get s[b]
295
+ put([0x20, 16 + c]); // local.get s[c]
296
+ put([0xfd, 0x51]); // v128.xor
297
+ put([0x22, 16 + b]); // local.tee s[b]
298
+ put([0x41, 0x07]); // i32.const 7
299
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
300
+ put([0x20, 16 + b]); // local.get s[b]
301
+ put([0x41, 0x19]); // i32.const 25
302
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
303
+ put([0xfd, 0x50]); // v128.or
304
+ put([0x21, 16 + b]); // local.set s[b]
305
+ }
306
+
307
+ // Generate all 7 rounds
308
+ for (let round = 0; round < 7; round++) {
309
+ // Column mixing
310
+ g(0, 4, 8, 12);
311
+ g(1, 5, 9, 13);
312
+ g(2, 6, 10, 14);
313
+ g(3, 7, 11, 15);
314
+
315
+ // Diagonal mixing
316
+ g(0, 5, 10, 15);
317
+ g(1, 6, 11, 12);
318
+ g(2, 7, 8, 13);
319
+ g(3, 4, 9, 14);
320
+ }
321
+
322
+ // ===== Final XOR and store output =====
323
+
324
+ // out[i] = s[i] ^ s[i+8] for i in 0..7
325
+ // Store at offset 640-767
326
+ for (let i = 0; i < 8; i++) {
327
+ put([0x41, ...toLebU32Min2(640 + i * 16)]); // i32.const offset
328
+ put([0x20, 16 + i]); // local.get s[i]
329
+ put([0x20, 24 + i]); // local.get s[i+8]
330
+ put([0xfd, 0x51]); // v128.xor
331
+ put([0xfd, 0x0b, 0x02, 0x00]); // v128.store align=4
332
+ }
333
+
334
+ // End of function
335
+ put([0x0b]); // end
336
+
337
+ // Fill in function 0 size using padded LEB128
338
+ const funcBodySize = code.length - funcBodyStart;
339
+ const funcSizeBytes = toLebU32Padded5(funcBodySize);
340
+ for (let i = 0; i < 5; i++) {
341
+ code[funcSizeOffset + i] = funcSizeBytes[i];
342
+ }
343
+
344
+ // === Function 1: compressChunks4x ===
345
+ // Reserve 5 bytes for function size
346
+ const func1SizeOffset = code.length;
347
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
348
+
349
+ const func1BodyStart = code.length;
350
+
351
+ // Generate the compressChunks4x function body
352
+ const compressChunksBody = generateCompressChunks4xBody();
353
+ put(compressChunksBody);
354
+
355
+ // Fill in function 1 size using padded LEB128
356
+ const func1BodySize = code.length - func1BodyStart;
357
+ const func1SizeBytes = toLebU32Padded5(func1BodySize);
358
+ for (let i = 0; i < 5; i++) {
359
+ code[func1SizeOffset + i] = func1SizeBytes[i];
360
+ }
361
+
362
+ // === Function 2: compressParent ===
363
+ // Reserve 5 bytes for function size
364
+ const func2SizeOffset = code.length;
365
+ put([0x00, 0x00, 0x00, 0x00, 0x00]);
366
+
367
+ const func2BodyStart = code.length;
368
+
369
+ // Generate the compressParent function body
370
+ const compressParentBody = generateCompressParentBody();
371
+ put(compressParentBody);
372
+
373
+ // Fill in function 2 size using padded LEB128
374
+ const func2BodySize = code.length - func2BodyStart;
375
+ const func2SizeBytes = toLebU32Padded5(func2BodySize);
376
+ for (let i = 0; i < 5; i++) {
377
+ code[func2SizeOffset + i] = func2SizeBytes[i];
378
+ }
379
+
380
+ // Fill in section size using padded LEB128
381
+ const sectionSize = code.length - sectionSizeOffset - 5;
382
+ const sectionSizeBytes = toLebU32Padded5(sectionSize);
383
+ for (let i = 0; i < 5; i++) {
384
+ code[sectionSizeOffset + i] = sectionSizeBytes[i];
385
+ }
386
+
387
+ return new Uint8Array(code);
388
+ }
389
+
390
+ /**
391
+ * Generate compressChunks4x WASM function body.
392
+ * Processes all 16 blocks of 4 chunks in a single call.
393
+ */
394
+ function generateCompressChunks4xBody(): number[] {
395
+ const code: number[] = [];
396
+
397
+ function put(bytes: number[]): void {
398
+ code.push(...bytes);
399
+ }
400
+
401
+ // Local declarations: 32 v128 locals + 1 i32 for position
402
+ // Locals $0-$15: message words (reloaded each iteration)
403
+ // Locals $16-$31: state words (s0-s15)
404
+ // Local $32: position counter (i32)
405
+ put([0x02]); // 2 local declarations
406
+ put([0x20, 0x7b]); // 32 x v128
407
+ put([0x01, 0x7f]); // 1 x i32
408
+
409
+ const BATCH_BLOCK_WORDS = SIMD_MEMORY.BATCH_BLOCK_WORDS;
410
+ const BATCH_CV = SIMD_MEMORY.BATCH_CV;
411
+ const BATCH_COUNTER_LOW = SIMD_MEMORY.BATCH_COUNTER_LOW;
412
+ const BATCH_FLAGS_BASE = SIMD_MEMORY.BATCH_FLAGS_BASE;
413
+ const BATCH_OUTPUT = SIMD_MEMORY.BATCH_OUTPUT;
414
+
415
+ // IV constants (same as compress4x)
416
+ const IV = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a];
417
+
418
+ // Load initial CVs from BATCH_CV into locals $16-$23
419
+ for (let i = 0; i < 8; i++) {
420
+ put([0x41, ...toLebU32Min2(BATCH_CV + i * 16)]); // i32.const offset
421
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
422
+ put([0x21, 16 + i]); // local.set $(16+i) -> s0-s7
423
+ }
424
+
425
+ // Initialize $32 (pos) = 0
426
+ put([0x41, 0x00]); // i32.const 0
427
+ put([0x21, 0x20]); // local.set $32
428
+
429
+ // block $done
430
+ put([0x02, 0x40]); // block void
431
+
432
+ // loop $continue
433
+ put([0x03, 0x40]); // loop void
434
+
435
+ // === Load message words for position $pos ===
436
+ // offset = BATCH_BLOCK_WORDS + pos * 256 + word * 16
437
+ for (let w = 0; w < 16; w++) {
438
+ put([0x20, 0x20]); // local.get $32 (pos)
439
+ put([0x41, ...toLebU32Min2(256)]); // i32.const 256
440
+ put([0x6c]); // i32.mul
441
+ put([0x41, ...toLebU32Min2(BATCH_BLOCK_WORDS + w * 16)]); // i32.const base + word*16
442
+ put([0x6a]); // i32.add
443
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load align=4 offset=0
444
+ put([0x21, w]); // local.set $w
445
+ }
446
+
447
+ // === Initialize state[8-15] ===
448
+ // s8-s11 = IV[0-3]
449
+ for (let i = 0; i < 4; i++) {
450
+ const ivBytes = [];
451
+ for (let j = 0; j < 4; j++) {
452
+ ivBytes.push(IV[i] & 0xff);
453
+ ivBytes.push((IV[i] >>> 8) & 0xff);
454
+ ivBytes.push((IV[i] >>> 16) & 0xff);
455
+ ivBytes.push((IV[i] >>> 24) & 0xff);
456
+ }
457
+ put([0xfd, 0x0c, ...ivBytes]); // v128.const
458
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
459
+ }
460
+
461
+ // s12 = counter_low (from BATCH_COUNTER_LOW)
462
+ put([0x41, ...toLebU32Min2(BATCH_COUNTER_LOW)]); // i32.const
463
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load
464
+ put([0x21, 28]); // local.set $28 -> s12
465
+
466
+ // s13 = 0 (counter high - assume fits in 32 bits)
467
+ put([0xfd, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // v128.const 0
468
+ put([0x21, 29]); // local.set $29 -> s13
469
+
470
+ // s14 = 64 (block_len = 64 for full blocks)
471
+ const blockLen64 = [];
472
+ for (let j = 0; j < 4; j++) {
473
+ blockLen64.push(64, 0, 0, 0); // 64 in little-endian
474
+ }
475
+ put([0xfd, 0x0c, ...blockLen64]); // v128.const [64,64,64,64]
476
+ put([0x21, 30]); // local.set $30 -> s14
477
+
478
+ // s15 = flags = base_flags | (pos == 0 ? 1 : 0) | (pos == 15 ? 2 : 0)
479
+ // First load base flags
480
+ put([0x41, ...toLebU32Min2(BATCH_FLAGS_BASE)]); // i32.const
481
+ put([0xfd, 0x00, 0x02, 0x00]); // v128.load base flags
482
+
483
+ // Compute position-dependent bits
484
+ // CHUNK_START (1) if pos == 0
485
+ put([0x20, 0x20]); // local.get $32 (pos)
486
+ put([0x45]); // i32.eqz -> 1 if pos==0, 0 otherwise
487
+ // CHUNK_END (2) if pos == 15
488
+ put([0x20, 0x20]); // local.get $32 (pos)
489
+ put([0x41, 0x0f]); // i32.const 15
490
+ put([0x46]); // i32.eq -> 1 if pos==15, 0 otherwise
491
+ put([0x41, 0x01]); // i32.const 1 (shift amount)
492
+ put([0x74]); // i32.shl -> 2 if pos==15, 0 otherwise
493
+ // OR the two bits together
494
+ put([0x72]); // i32.or -> combined position bits
495
+ // Splat to v128 and OR with base flags (stack: base_flags, bits)
496
+ put([0xfd, 0x11]); // i32x4.splat
497
+ put([0xfd, 0x50]); // v128.or
498
+ put([0x21, 31]); // local.set $31 -> s15
499
+
500
+ // === 7 rounds of mixing ===
501
+ let msgIdx = 0;
502
+
503
+ function g(a: number, b: number, c: number, d: number): void {
504
+ const mx = MSG_ACCESS_ORDER[msgIdx++];
505
+ const my = MSG_ACCESS_ORDER[msgIdx++];
506
+
507
+ // First half of G: s[a] = s[a] + s[b] + m[mx]
508
+ put([0x20, 16 + a]); // local.get s[a]
509
+ put([0x20, 16 + b]); // local.get s[b]
510
+ put([0xfd, 0xae, 0x01]); // i32x4.add
511
+ put([0x20, mx]); // local.get m[mx]
512
+ put([0xfd, 0xae, 0x01]); // i32x4.add
513
+ put([0x21, 16 + a]); // local.set s[a]
514
+
515
+ // s[d] = rotr(s[d] ^ s[a], 16) - byte shuffle
516
+ put([0x20, 16 + d]); // local.get s[d]
517
+ put([0x20, 16 + a]); // local.get s[a]
518
+ put([0xfd, 0x51]); // v128.xor
519
+ put([0x22, 16 + d]); // local.tee s[d]
520
+ put([0x20, 16 + d]); // local.get s[d]
521
+ put([0xfd, 0x0d, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13]); // i8x16.shuffle ROTR16
522
+ put([0x21, 16 + d]); // local.set s[d]
523
+
524
+ // s[c] = s[c] + s[d]
525
+ put([0x20, 16 + c]); // local.get s[c]
526
+ put([0x20, 16 + d]); // local.get s[d]
527
+ put([0xfd, 0xae, 0x01]); // i32x4.add
528
+ put([0x21, 16 + c]); // local.set s[c]
529
+
530
+ // s[b] = rotr(s[b] ^ s[c], 12)
531
+ put([0x20, 16 + b]); // local.get s[b]
532
+ put([0x20, 16 + c]); // local.get s[c]
533
+ put([0xfd, 0x51]); // v128.xor
534
+ put([0x22, 16 + b]); // local.tee s[b]
535
+ put([0x41, 0x0c]); // i32.const 12
536
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
537
+ put([0x20, 16 + b]); // local.get s[b]
538
+ put([0x41, 0x14]); // i32.const 20
539
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
540
+ put([0xfd, 0x50]); // v128.or
541
+ put([0x21, 16 + b]); // local.set s[b]
542
+
543
+ // Second half: s[a] = s[a] + s[b] + m[my]
544
+ put([0x20, 16 + a]); // local.get s[a]
545
+ put([0x20, 16 + b]); // local.get s[b]
546
+ put([0xfd, 0xae, 0x01]); // i32x4.add
547
+ put([0x20, my]); // local.get m[my]
548
+ put([0xfd, 0xae, 0x01]); // i32x4.add
549
+ put([0x21, 16 + a]); // local.set s[a]
550
+
551
+ // s[d] = rotr(s[d] ^ s[a], 8) - byte shuffle
552
+ put([0x20, 16 + d]); // local.get s[d]
553
+ put([0x20, 16 + a]); // local.get s[a]
554
+ put([0xfd, 0x51]); // v128.xor
555
+ put([0x22, 16 + d]); // local.tee s[d]
556
+ put([0x20, 16 + d]); // local.get s[d]
557
+ put([0xfd, 0x0d, 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12]); // i8x16.shuffle ROTR8
558
+ put([0x21, 16 + d]); // local.set s[d]
559
+
560
+ // s[c] = s[c] + s[d]
561
+ put([0x20, 16 + c]); // local.get s[c]
562
+ put([0x20, 16 + d]); // local.get s[d]
563
+ put([0xfd, 0xae, 0x01]); // i32x4.add
564
+ put([0x21, 16 + c]); // local.set s[c]
565
+
566
+ // s[b] = rotr(s[b] ^ s[c], 7)
567
+ put([0x20, 16 + b]); // local.get s[b]
568
+ put([0x20, 16 + c]); // local.get s[c]
569
+ put([0xfd, 0x51]); // v128.xor
570
+ put([0x22, 16 + b]); // local.tee s[b]
571
+ put([0x41, 0x07]); // i32.const 7
572
+ put([0xfd, 0xad, 0x01]); // i32x4.shr_u (opcode 173 = 0xAD)
573
+ put([0x20, 16 + b]); // local.get s[b]
574
+ put([0x41, 0x19]); // i32.const 25
575
+ put([0xfd, 0xab, 0x01]); // i32x4.shl
576
+ put([0xfd, 0x50]); // v128.or
577
+ put([0x21, 16 + b]); // local.set s[b]
578
+ }
579
+
580
+ // Generate all 7 rounds
581
+ for (let round = 0; round < 7; round++) {
582
+ // Column mixing
583
+ g(0, 4, 8, 12);
584
+ g(1, 5, 9, 13);
585
+ g(2, 6, 10, 14);
586
+ g(3, 7, 11, 15);
587
+ // Diagonal mixing
588
+ g(0, 5, 10, 15);
589
+ g(1, 6, 11, 12);
590
+ g(2, 7, 8, 13);
591
+ g(3, 4, 9, 14);
592
+ }
593
+
594
+ // === Update CVs: cv[i] = s[i] ^ s[i+8] ===
595
+ // Store back to state locals $16-$23 (the CV positions)
596
+ for (let i = 0; i < 8; i++) {
597
+ put([0x20, 16 + i]); // local.get s[i]
598
+ put([0x20, 24 + i]); // local.get s[i+8]
599
+ put([0xfd, 0x51]); // v128.xor
600
+ put([0x21, 16 + i]); // local.set $(16+i) - update CV
601
+ }
602
+
603
+ // === Loop control: pos++, continue if pos < 16 ===
604
+ put([0x20, 0x20]); // local.get $32 (pos)
605
+ put([0x41, 0x01]); // i32.const 1
606
+ put([0x6a]); // i32.add
607
+ put([0x22, 0x20]); // local.tee $32 (pos)
608
+ put([0x41, 0x10]); // i32.const 16
609
+ put([0x49]); // i32.lt_u
610
+ put([0x0d, 0x00]); // br_if 0 (continue loop)
611
+
612
+ // end loop
613
+ put([0x0b]); // end
614
+
615
+ // end block
616
+ put([0x0b]); // end
617
+
618
+ // === Store final CVs to BATCH_OUTPUT ===
619
+ for (let i = 0; i < 8; i++) {
620
+ put([0x41, ...toLebU32Min2(BATCH_OUTPUT + i * 16)]); // i32.const offset
621
+ put([0x20, 16 + i]); // local.get $(16+i) - CV word
622
+ put([0xfd, 0x0b, 0x02, 0x00]); // v128.store align=4
623
+ }
624
+
625
+ // end function
626
+ put([0x0b]); // end
627
+
628
+ return code;
629
+ }
630
+
631
+ /**
632
+ * Generate compressParent WASM function body.
633
+ * Performs a single parent node compression using scalar i32 operations.
634
+ * Reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
635
+ * Uses IV, counter=0, blockLen=64, flags=PARENT(4).
636
+ */
637
+ function generateCompressParentBody(): number[] {
638
+ const code: number[] = [];
639
+
640
+ function put(bytes: number[]): void {
641
+ code.push(...bytes);
642
+ }
643
+
644
+ // Local declarations: 32 i32 locals for state (s0-s15) and message (m0-m15)
645
+ put([0x01]); // 1 local declaration
646
+ put([0x20, 0x7f]); // 32 x i32
647
+
648
+ // Message word indices: 0-15, State indices: 16-31
649
+ // Locals $0-$15: message words (m0-m15)
650
+ // Locals $16-$31: state words (s0-s15)
651
+
652
+ const PARENT_BLOCK_OFFSET = SIMD_MEMORY.PARENT_BLOCK;
653
+ const CHUNK_CV_OFFSET = SIMD_MEMORY.CHUNK_CV;
654
+
655
+ // BLAKE3 IV
656
+ const IV = [
657
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
658
+ ];
659
+
660
+ // Load message words from PARENT_BLOCK (16 words at offset 7264)
661
+ for (let i = 0; i < 16; i++) {
662
+ put([0x41, ...toLebU32Min2(PARENT_BLOCK_OFFSET + i * 4)]); // i32.const offset
663
+ put([0x28, 0x02, 0x00]); // i32.load align=4 offset=0
664
+ put([0x21, i]); // local.set $i (m0-m15)
665
+ }
666
+
667
+ // Initialize state s0-s7 = IV[0-7]
668
+ for (let i = 0; i < 8; i++) {
669
+ put([0x41, ...toSignedLeb128_i32(IV[i])]); // i32.const IV[i]
670
+ put([0x21, 16 + i]); // local.set $(16+i) -> s0-s7
671
+ }
672
+
673
+ // Initialize state s8-s11 = IV[0-3]
674
+ for (let i = 0; i < 4; i++) {
675
+ put([0x41, ...toSignedLeb128_i32(IV[i])]); // i32.const IV[i]
676
+ put([0x21, 24 + i]); // local.set $(24+i) -> s8-s11
677
+ }
678
+
679
+ // s12 = counter_low = 0
680
+ put([0x41, 0x00]); // i32.const 0
681
+ put([0x21, 28]); // local.set $28 -> s12
682
+
683
+ // s13 = counter_high = 0
684
+ put([0x41, 0x00]); // i32.const 0
685
+ put([0x21, 29]); // local.set $29 -> s13
686
+
687
+ // s14 = block_len = 64
688
+ // Note: 0x40 alone is -64 in signed LEB128 (bit 6 is sign bit)
689
+ // For 64, we need [0xC0, 0x00] to avoid sign extension
690
+ put([0x41, 0xc0, 0x00]); // i32.const 64
691
+ put([0x21, 30]); // local.set $30 -> s14
692
+
693
+ // s15 = flags = PARENT = 4
694
+ put([0x41, 0x04]); // i32.const 4
695
+ put([0x21, 31]); // local.set $31 -> s15
696
+
697
+ // Helper to generate scalar G function (inlined)
698
+ // G(a, b, c, d, mx, my) where a,b,c,d are state indices 0-15, mx,my are message indices 0-15
699
+ function g(a: number, b: number, c: number, d: number, mx: number, my: number): void {
700
+ const sa = 16 + a,
701
+ sb = 16 + b,
702
+ sc = 16 + c,
703
+ sd = 16 + d;
704
+
705
+ // s[a] = (s[a] + s[b] + m[mx]) >>> 0
706
+ put([0x20, sa]); // local.get s[a]
707
+ put([0x20, sb]); // local.get s[b]
708
+ put([0x6a]); // i32.add
709
+ put([0x20, mx]); // local.get m[mx]
710
+ put([0x6a]); // i32.add
711
+ put([0x21, sa]); // local.set s[a]
712
+
713
+ // s[d] = rotr(s[d] ^ s[a], 16)
714
+ put([0x20, sd]); // local.get s[d]
715
+ put([0x20, sa]); // local.get s[a]
716
+ put([0x73]); // i32.xor
717
+ put([0x41, 0x10]); // i32.const 16
718
+ put([0x78]); // i32.rotr
719
+ put([0x21, sd]); // local.set s[d]
720
+
721
+ // s[c] = (s[c] + s[d]) >>> 0
722
+ put([0x20, sc]); // local.get s[c]
723
+ put([0x20, sd]); // local.get s[d]
724
+ put([0x6a]); // i32.add
725
+ put([0x21, sc]); // local.set s[c]
726
+
727
+ // s[b] = rotr(s[b] ^ s[c], 12)
728
+ put([0x20, sb]); // local.get s[b]
729
+ put([0x20, sc]); // local.get s[c]
730
+ put([0x73]); // i32.xor
731
+ put([0x41, 0x0c]); // i32.const 12
732
+ put([0x78]); // i32.rotr
733
+ put([0x21, sb]); // local.set s[b]
734
+
735
+ // s[a] = (s[a] + s[b] + m[my]) >>> 0
736
+ put([0x20, sa]); // local.get s[a]
737
+ put([0x20, sb]); // local.get s[b]
738
+ put([0x6a]); // i32.add
739
+ put([0x20, my]); // local.get m[my]
740
+ put([0x6a]); // i32.add
741
+ put([0x21, sa]); // local.set s[a]
742
+
743
+ // s[d] = rotr(s[d] ^ s[a], 8)
744
+ put([0x20, sd]); // local.get s[d]
745
+ put([0x20, sa]); // local.get s[a]
746
+ put([0x73]); // i32.xor
747
+ put([0x41, 0x08]); // i32.const 8
748
+ put([0x78]); // i32.rotr
749
+ put([0x21, sd]); // local.set s[d]
750
+
751
+ // s[c] = (s[c] + s[d]) >>> 0
752
+ put([0x20, sc]); // local.get s[c]
753
+ put([0x20, sd]); // local.get s[d]
754
+ put([0x6a]); // i32.add
755
+ put([0x21, sc]); // local.set s[c]
756
+
757
+ // s[b] = rotr(s[b] ^ s[c], 7)
758
+ put([0x20, sb]); // local.get s[b]
759
+ put([0x20, sc]); // local.get s[c]
760
+ put([0x73]); // i32.xor
761
+ put([0x41, 0x07]); // i32.const 7
762
+ put([0x78]); // i32.rotr
763
+ put([0x21, sb]); // local.set s[b]
764
+ }
765
+
766
+ // 7 rounds of mixing with permuted message schedule
767
+ let msgIdx = 0;
768
+ for (let round = 0; round < 7; round++) {
769
+ // Column mixing
770
+ g(0, 4, 8, 12, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
771
+ msgIdx += 2;
772
+ g(1, 5, 9, 13, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
773
+ msgIdx += 2;
774
+ g(2, 6, 10, 14, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
775
+ msgIdx += 2;
776
+ g(3, 7, 11, 15, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
777
+ msgIdx += 2;
778
+
779
+ // Diagonal mixing
780
+ g(0, 5, 10, 15, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
781
+ msgIdx += 2;
782
+ g(1, 6, 11, 12, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
783
+ msgIdx += 2;
784
+ g(2, 7, 8, 13, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
785
+ msgIdx += 2;
786
+ g(3, 4, 9, 14, MSG_ACCESS_ORDER[msgIdx], MSG_ACCESS_ORDER[msgIdx + 1]);
787
+ msgIdx += 2;
788
+ }
789
+
790
+ // Store output: out[i] = s[i] ^ s[i+8] for i in 0..7
791
+ for (let i = 0; i < 8; i++) {
792
+ put([0x41, ...toLebU32Min2(CHUNK_CV_OFFSET + i * 4)]); // i32.const offset
793
+ put([0x20, 16 + i]); // local.get s[i]
794
+ put([0x20, 24 + i]); // local.get s[i+8]
795
+ put([0x73]); // i32.xor
796
+ put([0x36, 0x02, 0x00]); // i32.store align=4 offset=0
797
+ }
798
+
799
+ // end function
800
+ put([0x0b]); // end
801
+
802
+ return code;
803
+ }
804
+
805
+ // Cached WASM instance
806
+ let wasmInstance: WebAssembly.Instance | null = null;
807
+ let wasmMemory: WebAssembly.Memory | null = null;
808
+ let wasmCompress4x: (() => void) | null = null;
809
+ let wasmCompressChunks4x: (() => void) | null = null;
810
+ let wasmCompressParent: (() => void) | null = null;
811
+ let wasmMemoryView: Uint8Array | null = null;
812
+ let wasmMemoryView32: Uint32Array | null = null;
813
+
814
+ /**
815
+ * Check if WASM SIMD is supported.
816
+ */
817
+ export function isSimdSupported(): boolean {
818
+ try {
819
+ // Minimal WASM module with v128.const instruction to test SIMD support
820
+ const simdTest = new Uint8Array([
821
+ 0x00,
822
+ 0x61,
823
+ 0x73,
824
+ 0x6d, // magic: \0asm
825
+ 0x01,
826
+ 0x00,
827
+ 0x00,
828
+ 0x00, // version: 1
829
+
830
+ // Type section (id=1): () -> v128
831
+ 0x01, // section id = 1 (type)
832
+ 0x05, // section length = 5
833
+ 0x01, // 1 type
834
+ 0x60,
835
+ 0x00,
836
+ 0x01,
837
+ 0x7b, // func () -> v128
838
+
839
+ // Function section (id=3)
840
+ 0x03, // section id = 3 (function)
841
+ 0x02, // section length = 2
842
+ 0x01, // 1 function
843
+ 0x00, // type index 0
844
+
845
+ // Code section (id=10) with v128.const
846
+ 0x0a, // section id = 10 (code)
847
+ 0x16, // section length = 22
848
+ 0x01, // 1 function body
849
+ 0x14, // body length = 20
850
+ 0x00, // 0 locals
851
+ 0xfd,
852
+ 0x0c, // v128.const opcode
853
+ 0x00,
854
+ 0x00,
855
+ 0x00,
856
+ 0x00,
857
+ 0x00,
858
+ 0x00,
859
+ 0x00,
860
+ 0x00,
861
+ 0x00,
862
+ 0x00,
863
+ 0x00,
864
+ 0x00,
865
+ 0x00,
866
+ 0x00,
867
+ 0x00,
868
+ 0x00,
869
+ 0x0b, // end
870
+ ]);
871
+ return WebAssembly.validate(simdTest);
872
+ } catch {
873
+ return false;
874
+ }
875
+ }
876
+
877
+ /**
878
+ * Set up arena views over WASM memory.
879
+ * Called after WASM memory is allocated.
880
+ */
881
+ function setupArenaViews(): void {
882
+ if (!wasmMemory) return;
883
+
884
+ const buffer = wasmMemory.buffer;
885
+ // Create TypedArray views over WASM memory for arena buffers
886
+ // These views are backed by WASM memory, eliminating JS heap allocation
887
+ arenaCvStack = new Uint32Array(buffer, SIMD_MEMORY.CV_STACK, 64 * 8); // 64 levels × 8 words
888
+ arenaParentBlock = new Uint32Array(buffer, SIMD_MEMORY.PARENT_BLOCK, 16); // 16 words
889
+ arenaChunkCv = new Uint32Array(buffer, SIMD_MEMORY.CHUNK_CV, 8); // 8 words
890
+ arenaTempCvs = new Uint32Array(buffer, SIMD_MEMORY.TEMP_CVS, 32); // 4 × 8 words
891
+
892
+ // Batch mode views
893
+ // 16 positions × 16 v128 words = 16 × 64 u32 words = 1024 words per position? No...
894
+ // In u32 terms: 16 positions × 16 words × 4 lanes = 1024 u32 values total
895
+ arenaBatchBlockWords = new Uint32Array(buffer, SIMD_MEMORY.BATCH_BLOCK_WORDS, 16 * 16 * 4); // 16 pos × 16 words × 4 lanes
896
+ arenaBatchCv = new Uint32Array(buffer, SIMD_MEMORY.BATCH_CV, 32); // 4 × 8 words
897
+ arenaBatchCounterLow = new Uint32Array(buffer, SIMD_MEMORY.BATCH_COUNTER_LOW, 4); // 4 words
898
+ arenaBatchFlagsBase = new Uint32Array(buffer, SIMD_MEMORY.BATCH_FLAGS_BASE, 4); // 4 words
899
+ arenaBatchOutput = new Uint32Array(buffer, SIMD_MEMORY.BATCH_OUTPUT, 32); // 4 × 8 words
900
+ }
901
+
902
+ /**
903
+ * Initialize the WASM SIMD module synchronously.
904
+ * Call this once before using compress4x.
905
+ */
906
+ // Cache generated WASM bytes to avoid regenerating on each init
907
+ let cachedWasmBytes: Uint8Array | null = null;
908
+
909
+ export function initSimdSync(): boolean {
910
+ if (wasmInstance) return true;
911
+
912
+ if (!isSimdSupported()) {
913
+ return false;
914
+ }
915
+
916
+ try {
917
+ const wasmBytes = cachedWasmBytes || generateWasmBytes();
918
+ cachedWasmBytes = wasmBytes;
919
+ wasmMemory = new WebAssembly.Memory({ initial: 1 });
920
+
921
+ const importObject = {
922
+ js: { mem: wasmMemory },
923
+ };
924
+
925
+ const module = new WebAssembly.Module(wasmBytes.buffer as ArrayBuffer);
926
+ wasmInstance = new WebAssembly.Instance(module, importObject);
927
+ wasmCompress4x = wasmInstance.exports.compress4x as () => void;
928
+ wasmCompressChunks4x = wasmInstance.exports.compressChunks4x as () => void;
929
+ wasmCompressParent = wasmInstance.exports.compressParent as () => void;
930
+ wasmMemoryView = new Uint8Array(wasmMemory.buffer);
931
+ wasmMemoryView32 = new Uint32Array(wasmMemory.buffer);
932
+
933
+ // Set up arena views for Merkle tree operations
934
+ setupArenaViews();
935
+
936
+ return true;
937
+ } catch (e) {
938
+ console.warn("Failed to initialize WASM SIMD:", e);
939
+ return false;
940
+ }
941
+ }
942
+
943
+ /**
944
+ * Memory offsets for SIMD data layout
945
+ *
946
+ * WASM Arena Pattern: All working buffers live in WASM memory (64KB page)
947
+ * This eliminates JS heap allocations during hashing operations.
948
+ */
949
+ export const SIMD_MEMORY = {
950
+ // SIMD compress4x working area (used by WASM code) - single block
951
+ BLOCK_WORDS: 0, // 4 x 16 words = 512 bytes (transposed layout)
952
+ CHAINING_VALUES: 512, // 4 x 8 words = 128 bytes
953
+ OUTPUT: 640, // 4 x 8 words = 128 bytes
954
+ COUNTER_LOW: 768, // 4 words = 16 bytes
955
+ COUNTER_HIGH: 784, // 4 words = 16 bytes
956
+ BLOCK_LEN: 800, // 4 words = 16 bytes
957
+ FLAGS: 816, // 4 words = 16 bytes
958
+ // End of single-block SIMD working area: 832 bytes
959
+
960
+ // SIMD compressChunks4x working area - 16 blocks batched
961
+ // Each block position has 16 v128 values (one per message word) = 256 bytes
962
+ // 16 block positions = 16 × 256 = 4096 bytes
963
+ BATCH_BLOCK_WORDS: 832, // 16 positions × 256 bytes = 4096 bytes (transposed), ends at 4928
964
+ BATCH_CV: 4928, // 4 × 8 words × 4 bytes = 128 bytes (working CVs), ends at 5056
965
+ BATCH_COUNTER_LOW: 5056, // 4 words × 4 bytes = 16 bytes (per-chunk counters), ends at 5072
966
+ BATCH_FLAGS_BASE: 5072, // 4 words × 4 bytes = 16 bytes (base flags, no START/END), ends at 5088
967
+ BATCH_OUTPUT: 5088, // 4 × 8 words × 4 bytes = 128 bytes (final output), ends at 5216
968
+ // End of batch working area: 5216 bytes
969
+
970
+ // WASM Arena: JS working buffers (accessed via TypedArray views)
971
+ CV_STACK: 5216, // 64 levels × 8 words × 4 bytes = 2048 bytes, ends at 7264
972
+ PARENT_BLOCK: 7264, // 16 words × 4 bytes = 64 bytes, ends at 7328
973
+ CHUNK_CV: 7328, // 8 words × 4 bytes = 32 bytes, ends at 7360
974
+ TEMP_CVS: 7360, // 4 × 8 words × 4 bytes = 128 bytes, ends at 7488
975
+ // Total arena usage: ~7488 bytes (fits comfortably in 64KB page)
976
+ } as const;
977
+
978
+ // Arena views - created once when SIMD initializes
979
+ let arenaCvStack: Uint32Array | null = null;
980
+ let arenaParentBlock: Uint32Array | null = null;
981
+ let arenaChunkCv: Uint32Array | null = null;
982
+ let arenaTempCvs: Uint32Array | null = null;
983
+
984
+ // Batch mode arena views
985
+ let arenaBatchBlockWords: Uint32Array | null = null;
986
+ let arenaBatchCv: Uint32Array | null = null;
987
+ let arenaBatchCounterLow: Uint32Array | null = null;
988
+ let arenaBatchFlagsBase: Uint32Array | null = null;
989
+ let arenaBatchOutput: Uint32Array | null = null;
990
+
991
+ /**
992
+ * Get the WASM memory views for writing input data.
993
+ */
994
+ export function getSimdMemory(): { view: Uint8Array; view32: Uint32Array } | null {
995
+ if (!wasmMemoryView || !wasmMemoryView32) return null;
996
+ return { view: wasmMemoryView, view32: wasmMemoryView32 };
997
+ }
998
+
999
+ /**
1000
+ * Get the arena buffers for Merkle tree operations.
1001
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
1002
+ */
1003
+ export function getArenaBuffers(): {
1004
+ cvStack: Uint32Array;
1005
+ parentBlock: Uint32Array;
1006
+ chunkCv: Uint32Array;
1007
+ tempCvs: Uint32Array;
1008
+ } | null {
1009
+ if (!arenaCvStack || !arenaParentBlock || !arenaChunkCv || !arenaTempCvs) return null;
1010
+ return {
1011
+ cvStack: arenaCvStack,
1012
+ parentBlock: arenaParentBlock,
1013
+ chunkCv: arenaChunkCv,
1014
+ tempCvs: arenaTempCvs,
1015
+ };
1016
+ }
1017
+
1018
+ /**
1019
+ * Get the batch arena buffers for chunk-level batched operations.
1020
+ * These TypedArray views are backed by WASM memory - zero JS heap allocation.
1021
+ */
1022
+ export function getBatchArenaBuffers(): {
1023
+ blockWords: Uint32Array;
1024
+ cv: Uint32Array;
1025
+ counterLow: Uint32Array;
1026
+ flagsBase: Uint32Array;
1027
+ output: Uint32Array;
1028
+ } | null {
1029
+ if (
1030
+ !arenaBatchBlockWords ||
1031
+ !arenaBatchCv ||
1032
+ !arenaBatchCounterLow ||
1033
+ !arenaBatchFlagsBase ||
1034
+ !arenaBatchOutput
1035
+ )
1036
+ return null;
1037
+ return {
1038
+ blockWords: arenaBatchBlockWords,
1039
+ cv: arenaBatchCv,
1040
+ counterLow: arenaBatchCounterLow,
1041
+ flagsBase: arenaBatchFlagsBase,
1042
+ output: arenaBatchOutput,
1043
+ };
1044
+ }
1045
+
1046
+ /**
1047
+ * Run the compress4x function.
1048
+ * Data must already be set up in WASM memory.
1049
+ */
1050
+ export function runCompress4x(): void {
1051
+ if (!wasmCompress4x) {
1052
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
1053
+ }
1054
+ wasmCompress4x();
1055
+ }
1056
+
1057
+ /**
1058
+ * Run the compressChunks4x function.
1059
+ * Processes 4 full chunks (16 blocks each) in a single WASM call.
1060
+ * Data must already be set up in batch arena buffers.
1061
+ */
1062
+ export function runCompressChunks4x(): void {
1063
+ if (!wasmCompressChunks4x) {
1064
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
1065
+ }
1066
+ wasmCompressChunks4x();
1067
+ }
1068
+
1069
+ /**
1070
+ * Run the compressParent function.
1071
+ * Compresses a parent node: reads 16 words from PARENT_BLOCK, writes 8 words to CHUNK_CV.
1072
+ * Data must already be set up in arena buffers (PARENT_BLOCK at offset 7264).
1073
+ * Output is written to CHUNK_CV at offset 7328.
1074
+ */
1075
+ export function runCompressParent(): void {
1076
+ if (!wasmCompressParent) {
1077
+ throw new Error("WASM SIMD not initialized. Call initSimdSync() first.");
1078
+ }
1079
+ wasmCompressParent();
1080
+ }
1081
+
1082
+ /**
1083
+ * Check if SIMD is initialized and ready.
1084
+ */
1085
+ export function isSimdReady(): boolean {
1086
+ return wasmCompress4x !== null;
1087
+ }
node_modules/@huggingface/hub/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Hugging Face
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
node_modules/@huggingface/hub/README.md ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🤗 Hugging Face Hub API
2
+
3
+ Official utilities to use the Hugging Face Hub API.
4
+
5
+ ## Install
6
+
7
+ ```console
8
+ pnpm add @huggingface/hub
9
+
10
+ npm add @huggingface/hub
11
+
12
+ yarn add @huggingface/hub
13
+ ```
14
+
15
+ ### Deno
16
+
17
+ ```ts
18
+ // esm.sh
19
+ import { uploadFiles, listModels } from "https://esm.sh/@huggingface/hub"
20
+ // or npm:
21
+ import { uploadFiles, listModels } from "npm:@huggingface/hub"
22
+ ```
23
+
24
+ Check out the [full documentation](https://huggingface.co/docs/huggingface.js/hub/README).
25
+
26
+ ## Usage
27
+
28
+ For some of the calls, you need to create an account and generate an [access token](https://huggingface.co/settings/tokens).
29
+
30
+ Learn how to find free models using the hub package in this [interactive tutorial](https://scrimba.com/scrim/c7BbVPcd?pl=pkVnrP7uP).
31
+
32
+ ```ts
33
+ import * as hub from "@huggingface/hub";
34
+ import type { RepoDesignation } from "@huggingface/hub";
35
+
36
+ const repo: RepoDesignation = { type: "model", name: "myname/some-model" };
37
+
38
+ const {name: username} = await hub.whoAmI({accessToken: "hf_..."});
39
+
40
+ for await (const model of hub.listModels({search: {owner: username}, accessToken: "hf_..."})) {
41
+ console.log("My model:", model);
42
+ }
43
+
44
+ const specificModel = await hub.modelInfo({name: "openai-community/gpt2"});
45
+ await hub.checkRepoAccess({repo, accessToken: "hf_..."});
46
+
47
+ await hub.createRepo({ repo, accessToken: "hf_...", license: "mit" });
48
+
49
+ await hub.uploadFiles({
50
+ repo,
51
+ accessToken: "hf_...",
52
+ files: [
53
+ // path + blob content
54
+ {
55
+ path: "file.txt",
56
+ content: new Blob(["Hello World"]),
57
+ },
58
+ // Local file URL
59
+ pathToFileURL("./pytorch-model.bin"),
60
+ // Local folder URL
61
+ pathToFileURL("./models"),
62
+ // Web URL
63
+ new URL("https://huggingface.co/xlm-roberta-base/resolve/main/tokenizer.json"),
64
+ // Path + Web URL
65
+ {
66
+ path: "myfile.bin",
67
+ content: new URL("https://huggingface.co/bert-base-uncased/resolve/main/pytorch_model.bin")
68
+ }
69
+ // Can also work with native File in browsers
70
+ ],
71
+ });
72
+
73
+ // or
74
+
75
+ for await (const progressEvent of await hub.uploadFilesWithProgress({
76
+ repo,
77
+ accessToken: "hf_...",
78
+ files: [
79
+ ...
80
+ ],
81
+ })) {
82
+ console.log(progressEvent);
83
+ }
84
+
85
+ // Edit a file by adding prefix & suffix
86
+ await commit({
87
+ repo,
88
+ accessToken: "hf_...",
89
+ operations: [{
90
+ type: "edit",
91
+ originalContent: originalFile,
92
+ edits: [{
93
+ start: 0,
94
+ end: 0,
95
+ content: new Blob(["prefix"])
96
+ }, {
97
+ start: originalFile.length,
98
+ end: originalFile.length,
99
+ content: new Blob(["suffix"])
100
+ }]
101
+ }]
102
+ })
103
+
104
+ await hub.deleteFile({repo, accessToken: "hf_...", path: "myfile.bin"});
105
+
106
+ await (await hub.downloadFile({ repo, path: "README.md" })).text();
107
+
108
+ for await (const fileInfo of hub.listFiles({repo})) {
109
+ console.log(fileInfo);
110
+ }
111
+
112
+ await hub.deleteRepo({ repo, accessToken: "hf_..." });
113
+ ```
114
+
115
+ ## CLI usage
116
+
117
+ You can use `@huggingface/hub` in CLI mode to upload files and folders to your repo.
118
+
119
+ ```console
120
+ npx @huggingface/hub upload coyotte508/test-model .
121
+ npx @huggingface/hub upload datasets/coyotte508/test-dataset .
122
+ # Same thing
123
+ npx @huggingface/hub upload --repo-type dataset coyotte508/test-dataset .
124
+ # Upload new data with 0 history in a separate branch
125
+ npx @huggingface/hub branch create coyotte508/test-model release --empty
126
+ npx @huggingface/hub upload coyotte508/test-model . --revision release
127
+
128
+ npx @huggingface/hub --help
129
+ npx @huggingface/hub upload --help
130
+ ```
131
+
132
+ You can also install globally with `npm install -g @huggingface/hub`. Then you can do:
133
+
134
+ ```console
135
+ hfjs upload coyotte508/test-model .
136
+
137
+ hfjs branch create --repo-type dataset coyotte508/test-dataset release --empty
138
+ hfjs upload --repo-type dataset coyotte508/test-dataset . --revision release
139
+
140
+ hfjs --help
141
+ hfjs upload --help
142
+
143
+ hfjs help jobs
144
+ ```
145
+
146
+ ## OAuth Login
147
+
148
+ It's possible to login using OAuth (["Sign in with HF"](https://huggingface.co/docs/hub/oauth)).
149
+
150
+ This will allow you get an access token to use some of the API, depending on the scopes set inside the Space or the OAuth App.
151
+
152
+ ```ts
153
+ import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub";
154
+
155
+ const oauthResult = await oauthHandleRedirectIfPresent();
156
+
157
+ if (!oauthResult) {
158
+ // If the user is not logged in, redirect to the login page
159
+ window.location.href = await oauthLoginUrl();
160
+ }
161
+
162
+ // You can use oauthResult.accessToken, oauthResult.accessTokenExpiresAt and oauthResult.userInfo
163
+ console.log(oauthResult);
164
+ ```
165
+
166
+ Checkout the demo: https://huggingface.co/spaces/huggingfacejs/client-side-oauth
167
+
168
+ ## Hugging face cache
169
+
170
+ The `@huggingface/hub` package provide basic capabilities to scan the cache directory. Learn more about [Manage huggingface_hub cache-system](https://huggingface.co/docs/huggingface_hub/en/guides/manage-cache).
171
+
172
+ ### `scanCacheDir`
173
+
174
+ You can get the list of cached repositories using the `scanCacheDir` function.
175
+
176
+ ```ts
177
+ import { scanCacheDir } from "@huggingface/hub";
178
+
179
+ const result = await scanCacheDir();
180
+
181
+ console.log(result);
182
+ ```
183
+ Note: this does not work in the browser
184
+
185
+ ### `downloadFileToCacheDir`
186
+
187
+ You can cache a file of a repository using the `downloadFileToCacheDir` function.
188
+
189
+ ```ts
190
+ import { downloadFileToCacheDir } from "@huggingface/hub";
191
+
192
+ const file = await downloadFileToCacheDir({
193
+ repo: 'foo/bar',
194
+ path: 'README.md'
195
+ });
196
+
197
+ console.log(file);
198
+ ```
199
+ Note: this does not work in the browser
200
+
201
+ ### `snapshotDownload`
202
+
203
+ You can download an entire repository at a given revision in the cache directory using the `snapshotDownload` function.
204
+
205
+ ```ts
206
+ import { snapshotDownload } from "@huggingface/hub";
207
+
208
+ const directory = await snapshotDownload({
209
+ repo: 'foo/bar',
210
+ });
211
+
212
+ console.log(directory);
213
+ ```
214
+ The code use internally the `downloadFileToCacheDir` function.
215
+
216
+ Note: this does not work in the browser
217
+
218
+ ## Performance considerations
219
+
220
+ When uploading large files, you may want to run the `commit` calls inside a worker, to offload the sha256 computations.
221
+
222
+ Remote resources and local files should be passed as `URL` whenever it's possible so they can be lazy loaded in chunks to reduce RAM usage. Passing a `File` inside the browser's context is fine, because it natively behaves as a `Blob`.
223
+
224
+ Under the hood, `@huggingface/hub` uses a lazy blob implementation to load the file.
225
+
226
+ ## Dependencies
227
+
228
+ - `@huggingface/tasks` : Typings only
node_modules/@huggingface/hub/dist/FileBlob-RUOT7DBI.mjs ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import "./chunk-FFYIGW52.mjs";
2
+
3
+ // src/utils/FileBlob.ts
4
+ import { createReadStream } from "fs";
5
+ import { open, stat } from "fs/promises";
6
+ import { Readable } from "stream";
7
+ import { fileURLToPath } from "url";
8
+ var FileBlob = class extends Blob {
9
+ /**
10
+ * Creates a new FileBlob on the provided file.
11
+ *
12
+ * @param path Path to the file to be lazy readed
13
+ */
14
+ static async create(path) {
15
+ path = path instanceof URL ? fileURLToPath(path) : path;
16
+ const { size } = await stat(path);
17
+ const fileBlob = new FileBlob(path, 0, size);
18
+ return fileBlob;
19
+ }
20
+ path;
21
+ start;
22
+ end;
23
+ constructor(path, start, end) {
24
+ super();
25
+ this.path = path;
26
+ this.start = start;
27
+ this.end = end;
28
+ }
29
+ /**
30
+ * Returns the size of the blob.
31
+ */
32
+ get size() {
33
+ return this.end - this.start;
34
+ }
35
+ /**
36
+ * Returns a new instance of FileBlob that is a slice of the current one.
37
+ *
38
+ * The slice is inclusive of the start and exclusive of the end.
39
+ *
40
+ * The slice method does not supports negative start/end.
41
+ *
42
+ * @param start beginning of the slice
43
+ * @param end end of the slice
44
+ */
45
+ slice(start = 0, end = this.size) {
46
+ if (start < 0 || end < 0) {
47
+ new TypeError("Unsupported negative start/end on FileBlob.slice");
48
+ }
49
+ const slice = new FileBlob(this.path, this.start + start, Math.min(this.start + end, this.end));
50
+ return slice;
51
+ }
52
+ /**
53
+ * Read the part of the file delimited by the FileBlob and returns it as an ArrayBuffer.
54
+ */
55
+ async arrayBuffer() {
56
+ const slice = await this.execute((file) => file.read(Buffer.alloc(this.size), 0, this.size, this.start));
57
+ return slice.buffer;
58
+ }
59
+ /**
60
+ * Read the part of the file delimited by the FileBlob and returns it as a string.
61
+ */
62
+ async text() {
63
+ const buffer = await this.arrayBuffer();
64
+ return buffer.toString("utf8");
65
+ }
66
+ /**
67
+ * Returns a stream around the part of the file delimited by the FileBlob.
68
+ */
69
+ stream() {
70
+ if (this.start === this.end) {
71
+ return new Blob([]).stream();
72
+ }
73
+ return Readable.toWeb(createReadStream(this.path, { start: this.start, end: this.end - 1 }));
74
+ }
75
+ /**
76
+ * We are opening and closing the file for each action to prevent file descriptor leaks.
77
+ *
78
+ * It is an intended choice of developer experience over performances.
79
+ */
80
+ async execute(action) {
81
+ const file = await open(this.path, "r");
82
+ try {
83
+ return await action(file);
84
+ } finally {
85
+ await file.close();
86
+ }
87
+ }
88
+ };
89
+ export {
90
+ FileBlob
91
+ };
node_modules/@huggingface/hub/dist/browser/FileBlob-7MRLQ6TG.mjs ADDED
File without changes
node_modules/@huggingface/hub/dist/browser/FileBlob-YC2EPDW4.js ADDED
@@ -0,0 +1 @@
 
 
1
+ "use strict";
node_modules/@huggingface/hub/dist/browser/index.js ADDED
The diff for this file is too large to render. See raw diff