46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
const SECRET = process.env.FEISTEL_SECRET || "change-me";
|
|
const ROUNDS = 8;
|
|
const HALF = 20;
|
|
const MASK = (1 << HALF) - 1;
|
|
const CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
|
|
function roundFn(right: number, round: number): number {
|
|
const h = new Bun.CryptoHasher("sha256").update(SECRET + ":" + round + ":" + right).digest();
|
|
return (h[0] << 12 | h[1] << 4 | h[2] >> 4) & MASK;
|
|
}
|
|
|
|
function toBase62(n: number): string {
|
|
let s = "";
|
|
for (let i = 0; i < 7; i++) { s = CHARS[n % 62] + s; n = Math.floor(n / 62); }
|
|
return s;
|
|
}
|
|
|
|
function fromBase62(s: string): number {
|
|
let n = 0;
|
|
for (const c of s) n = n * 62 + CHARS.indexOf(c);
|
|
return n;
|
|
}
|
|
|
|
export function encodeId(val: number): string {
|
|
let left = Math.floor(val / (MASK + 1)) & MASK;
|
|
let right = val & MASK;
|
|
for (let i = 0; i < ROUNDS; i++) {
|
|
const tmp = (left ^ roundFn(right, i)) & MASK;
|
|
left = right;
|
|
right = tmp;
|
|
}
|
|
return toBase62(left * (MASK + 1) + right);
|
|
}
|
|
|
|
export function decodeId(s: string): number {
|
|
const val = fromBase62(s);
|
|
let left = Math.floor(val / (MASK + 1)) & MASK;
|
|
let right = val & MASK;
|
|
for (let i = ROUNDS - 1; i >= 0; i--) {
|
|
const tmp = (right ^ roundFn(left, i)) & MASK;
|
|
right = left;
|
|
left = tmp;
|
|
}
|
|
return left * (MASK + 1) + right;
|
|
}
|