1/**2 * Deterministic non-crypto 64-bit hash: two independent FNV-1a-style 32-bit3 * streams (different basis/prime) over a canonical byte feed, concatenated as4 * a 16-char hex string. Pure 32-bit integer math (Math.imul), so identical in5 * Node and browsers.6 */7export class Hasher {8 private a = 0x811c9dc5 | 0; // FNV-1a 32 offset basis9 private b = 0x6c62272e | 0; // high word of the FNV-64 basis, as a distinct seed1011 byte(v: number): void {12 const x = v & 0xff;13 this.a = Math.imul(this.a ^ x, 0x01000193); // FNV-1a 32 prime14 this.b = Math.imul(this.b ^ x, 0x85ebca6b); // murmur3 fmix constant (odd)15 }1617 /** Feed a 32-bit integer (little-endian bytes). */18 int(v: number): void {19 const x = v | 0;20 this.byte(x);21 this.byte(x >>> 8);22 this.byte(x >>> 16);23 this.byte(x >>> 24);24 }2526 /** Feed raw bytes. */27 bytes(arr: Uint8Array): void {28 for (let i = 0; i < arr.length; i++) this.byte(arr[i]!);29 }3031 digest(): string {32 return (33 (this.a >>> 0).toString(16).padStart(8, '0') +34 (this.b >>> 0).toString(16).padStart(8, '0')35 );36 }37}