Spaces:
Sleeping
Sleeping
Use ken-q org namespace for project Spaces
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- AGENTS.md +1 -1
- README.md +1 -1
- node_modules/.bin/hfjs +1 -0
- node_modules/.package-lock.json +123 -0
- node_modules/@huggingface/blake3-jit/LICENSE +22 -0
- node_modules/@huggingface/blake3-jit/README.md +56 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/compress.d.ts +37 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/compress.js +919 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/constants.d.ts +34 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/constants.js +56 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/hash.d.ts +28 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/hash.js +1038 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/hasher.d.ts +108 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/hasher.js +396 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/index.d.ts +82 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/index.js +109 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/package.json +3 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/utils.d.ts +90 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/utils.js +226 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/wasm-simd.d.ts +97 -0
- node_modules/@huggingface/blake3-jit/dist/commonjs/wasm-simd.js +948 -0
- node_modules/@huggingface/blake3-jit/dist/esm/compress.d.ts +37 -0
- node_modules/@huggingface/blake3-jit/dist/esm/compress.js +916 -0
- node_modules/@huggingface/blake3-jit/dist/esm/constants.d.ts +34 -0
- node_modules/@huggingface/blake3-jit/dist/esm/constants.js +53 -0
- node_modules/@huggingface/blake3-jit/dist/esm/hash.d.ts +28 -0
- node_modules/@huggingface/blake3-jit/dist/esm/hash.js +1033 -0
- node_modules/@huggingface/blake3-jit/dist/esm/hasher.d.ts +108 -0
- node_modules/@huggingface/blake3-jit/dist/esm/hasher.js +391 -0
- node_modules/@huggingface/blake3-jit/dist/esm/index.d.ts +82 -0
- node_modules/@huggingface/blake3-jit/dist/esm/index.js +98 -0
- node_modules/@huggingface/blake3-jit/dist/esm/package.json +3 -0
- node_modules/@huggingface/blake3-jit/dist/esm/utils.d.ts +90 -0
- node_modules/@huggingface/blake3-jit/dist/esm/utils.js +214 -0
- node_modules/@huggingface/blake3-jit/dist/esm/wasm-simd.d.ts +97 -0
- node_modules/@huggingface/blake3-jit/dist/esm/wasm-simd.js +936 -0
- node_modules/@huggingface/blake3-jit/package.json +49 -0
- node_modules/@huggingface/blake3-jit/src/compress.ts +954 -0
- node_modules/@huggingface/blake3-jit/src/constants.ts +59 -0
- node_modules/@huggingface/blake3-jit/src/hash.ts +1318 -0
- node_modules/@huggingface/blake3-jit/src/hasher.ts +576 -0
- node_modules/@huggingface/blake3-jit/src/index.ts +106 -0
- node_modules/@huggingface/blake3-jit/src/utils.ts +253 -0
- node_modules/@huggingface/blake3-jit/src/wasm-simd.ts +1087 -0
- node_modules/@huggingface/hub/LICENSE +21 -0
- node_modules/@huggingface/hub/README.md +228 -0
- node_modules/@huggingface/hub/dist/FileBlob-RUOT7DBI.mjs +91 -0
- node_modules/@huggingface/hub/dist/browser/FileBlob-7MRLQ6TG.mjs +0 -0
- node_modules/@huggingface/hub/dist/browser/FileBlob-YC2EPDW4.js +1 -0
- 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 |
-
-
|
| 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://
|
|
|
|
| 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
|
|
|