UNPKG

inthash

Version:

Efficient integer hashing library using Knuth's multiplicative method for Javascript and Typescript, perfect for obfuscating sequential numbers.

96 lines (95 loc) 2.88 kB
import { randomPrime } from "./prime.js"; function modInv(a, b) { let t = 0n; let r = b; let nextT = 1n; let nextR = a; while (nextR > 0) { const q = ~~(r / nextR); const [lastT, lastR] = [t, r]; [t, r] = [nextT, nextR]; [nextT, nextR] = [lastT - q * nextT, lastR - q * nextR]; } return t < 0 ? t + b : t; } function generateXor(bits) { let result = 0n; for (let i = 0; i < bits; i++) { result = (result << 1n) | (Math.random() < 0.5 ? 1n : 0n); } return result; } export class Hasher { static generate(bits) { bits = bits ?? Number.MAX_SAFE_INTEGER.toString(2).length; // default to Number.MAX_SAFE_INTEGER if (bits < 2) { throw new Error("bits must be greater than 2"); } const modBase = 2n ** BigInt(bits); const prime = randomPrime(bits); return { bits, prime: prime.toString(), inverse: modInv(prime, modBase).toString(), xor: generateXor(bits).toString(), }; } constructor({ bits, prime, inverse, xor }) { Object.defineProperty(this, "_prime", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_inverse", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_xor", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_mask", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_max", { enumerable: true, configurable: true, writable: true, value: void 0 }); this._prime = BigInt(prime); this._inverse = BigInt(inverse); this._xor = BigInt(xor); this._mask = 2n ** BigInt(bits) - 1n; this._max = (1n << BigInt(bits)) - 1n; } encode(n) { if (typeof n === "string") { return this.encode(BigInt(n)).toString(); } if (typeof n === "number") { return Number(this.encode(BigInt(n))); } if (n > this._max) { console.warn(`input ${n} is greater than max ${this._max}`); } return n * this._prime & this._mask ^ this._xor; } decode(n) { if (typeof n === "string") { return this.decode(BigInt(n)).toString(); } if (typeof n === "number") { return Number(this.decode(BigInt(n))); } return (n ^ this._xor) * this._inverse & this._mask; } }