Spaces:
Sleeping
Sleeping
| /** | |
| * gearhash-jit — Fast GEAR rolling hash for content-defined chunking. | |
| * | |
| * Uses a tiny hand-written WASM module with native i64 arithmetic. | |
| * The hash state is kept as raw bytes in JS (avoiding BigInt in the hot path) | |
| * and written to WASM memory only for the `nextMatch` call. | |
| */ | |
| import { initWasm, wasmNextMatch, getView, HASH_OFFSET, MASK_OFFSET, INPUT_OFFSET, MAX_INPUT_SIZE, } from "./wasm.js"; | |
| export { GEAR_TABLE } from "./table.js"; | |
| export class Hasher { | |
| maskBytes; | |
| /** | |
| * The current 64-bit rolling hash state as 8 little-endian bytes. | |
| * Updated after every `nextMatch` call. Zeroed by `resetHash()`. | |
| */ | |
| hash; | |
| constructor(mask) { | |
| initWasm(); | |
| this.maskBytes = new Uint8Array(8); | |
| this.hash = new Uint8Array(8); | |
| new DataView(this.maskBytes.buffer).setBigUint64(0, mask, true); | |
| } | |
| /** | |
| * Scan `buf` for the next gear-hash match. The internal hash state | |
| * carries over between calls (for split-buffer scanning). | |
| * | |
| * @returns 1-based byte position of the match, or -1 if none found. | |
| */ | |
| nextMatch(buf) { | |
| const len = buf.length; | |
| if (len === 0) | |
| return -1; | |
| if (len > MAX_INPUT_SIZE) { | |
| throw new RangeError(`Input too large: ${len} > ${MAX_INPUT_SIZE}`); | |
| } | |
| const view = getView(); | |
| view.set(this.hash, HASH_OFFSET); | |
| view.set(this.maskBytes, MASK_OFFSET); | |
| view.set(buf, INPUT_OFFSET); | |
| const pos = wasmNextMatch(INPUT_OFFSET, len); | |
| this.hash.set(view.subarray(HASH_OFFSET, HASH_OFFSET + 8)); | |
| return pos; | |
| } | |
| /** Reset rolling hash to zero (call when starting a new chunk). */ | |
| resetHash() { | |
| this.hash.fill(0); | |
| } | |
| } | |