@subhog/proquint
Version:
73 lines (71 loc) • 2.19 kB
JavaScript
import getRandomValues from "get-random-values";
/*
Generate proquint-based ID with the form `babab-babab-00000000000000000000`.
Each proquint gives 12 bits of entropy,
and the final base-32 encoded string gives additional 100 bits
for a total of 124 bits of entropy – more than 122 in a V4 UUID.
In the current implementation, the last bit is reserved for simple error detection.
This reduces the total entropy to 119 bits, but makes the ID slightly more robust
while still keeping the 32-character length.
*/
/*
Alphabets for various character classes in the ID.
*/
const A = "bdfghjklmnprstvz";
const B = "aiou";
const C = "0123456789abcdghijklmnopqrstuvwz";
/*
Sequence of different character classes.
*/
const T = [
A, B, A, B, A,
A, B, A, B, A,
C, C, C, C, C,
C, C, C, C, C,
C, C, C, C, C,
C, C, C, C, C,
];
/*
Generate a random proquint-based ID.
*/
export const makeId = () => {
// Generate random numbers
const r = new Uint8Array(30);
getRandomValues(r);
const nums = r.map((n, i) => n % T[i].length);
// Swap the last number with a checksum
nums[29] = nums.reduce((a, b) => a ^ b, nums[29]);
// Convert numbers to characters
const chars = Array.from(nums).map((n, i) => T[i][n]);
// Insert dashes
chars.splice(10, 0, "-");
chars.splice(5, 0, "-");
return chars.join("");
};
/*
Get the checksum of an ID.
Should return 0 if the ID is valid.
*/
export const checkId = (id) => {
const s = id.replace(/\-/g, "");
if (id.length !== 32 || s.length !== 30)
return -1;
const a = Array.from(s);
for (let i in a)
if (T[i].indexOf(a[i]) === -1)
return -1;
return a.reduce((sum, val, idx) => (sum ^ T[idx].indexOf(val)), 0);
};
const fiveToNumber = (five) => {
return (A.indexOf(five[0]) << 12 |
B.indexOf(five[1]) << 10 |
A.indexOf(five[2]) << 6 |
B.indexOf(five[3]) << 4 |
A.indexOf(five[4]) << 0);
};
/*
Convert a proquint-based ID to a random number between 0 and 1.
*/
export const toRand = (id) => {
return (fiveToNumber(id.substring(0, 5)) + fiveToNumber(id.substring(6, 11)) / 65536) / 65536;
};