Skip to content

Instantly share code, notes, and snippets.

@afk-mario
Created February 9, 2022 19:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afk-mario/37d69d983131dec81cfa0c1ae21c7552 to your computer and use it in GitHub Desktop.
Save afk-mario/37d69d983131dec81cfa0c1ae21c7552 to your computer and use it in GitHub Desktop.
// Proquints JS implementation without needing the DOM
// https://github.com/dsw/proquint
import crypto from "crypto";
export function genquint() {
const hex = `x${crypto.randomBytes(16).toString("hex")}`;
try {
const quint = hex2quint(hex);
return { hex, quint };
} catch (e) {
console.error(e);
}
}
/**
Converts quint list (dash separated) of arbitrary length to corresponding hex dump prefixed with x
Quint list must contain pairs of quints to align with 32 bit boundary
*/
export function quint2hex(inp) {
let ret = "x";
let remaining = inp;
while (remaining.length > 0) {
const current = remaining.substring(0, 11);
if (
!current.match(/^[bdfghjklmnprstvzaiou]{5}-[bdfghjklmnprstvzaiou]{5}$/)
) {
throw new Error(`Bad quint format for: ${inp}`);
}
if (remaining.length > 12) remaining = remaining.substring(12);
else remaining = remaining.substring(11);
let currentHex = quint2uint(current).toString(16);
while (currentHex.length < 8) currentHex = `0${currentHex}`;
ret += currentHex;
}
return ret;
}
/**
Converts hex dump (x prefixed) to a list of quints. Hex dump must contain leading zeros when necessary.
Hex dump length must by divisible by 8.
*/
export function hex2quint(hex) {
const sep = "-";
let ret = "";
let remaining = hex.substring(1); //skip x
while (remaining.length > 0) {
if (remaining.length < 8 && remaining.length % 8 != 0) {
const message = `Bad hex length: ${remaining.length}`;
throw new Error(message);
}
const current = remaining.substring(0, 8);
remaining = remaining.substring(8);
const currentQuint = uint2quint(parseInt(current, 16) >> 0);
ret += currentQuint;
if (sep) ret += sep;
}
if (ret.endsWith(sep)) ret = ret.substring(0, ret.length - sep.length);
return ret;
}
//following code is based on original java impl.
const uint2consonant = [
"b",
"d",
"f",
"g",
"h",
"j",
"k",
"l",
"m",
"n",
"p",
"r",
"s",
"t",
"v",
"z",
];
const uint2vowel = ["a", "i", "o", "u"];
const MASK_FIRST4 = 0xf0000000;
const MASK_FIRST2 = 0xc0000000;
function uint2quint(i) {
let j = "";
let quint = "";
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
quint += "-";
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
return quint;
}
export function quint2uint(quint) {
let res = 0;
let remaining = quint.length;
let i = 0;
while (remaining > 0) {
const c = quint[i++];
remaining--;
switch (c) {
/* consonants */
case "b":
res <<= 4;
res += 0;
break;
case "d":
res <<= 4;
res += 1;
break;
case "f":
res <<= 4;
res += 2;
break;
case "g":
res <<= 4;
res += 3;
break;
case "h":
res <<= 4;
res += 4;
break;
case "j":
res <<= 4;
res += 5;
break;
case "k":
res <<= 4;
res += 6;
break;
case "l":
res <<= 4;
res += 7;
break;
case "m":
res <<= 4;
res += 8;
break;
case "n":
res <<= 4;
res += 9;
break;
case "p":
res <<= 4;
res += 10;
break;
case "r":
res <<= 4;
res += 11;
break;
case "s":
res <<= 4;
res += 12;
break;
case "t":
res <<= 4;
res += 13;
break;
case "v":
res <<= 4;
res += 14;
break;
case "z":
res <<= 4;
res += 15;
break;
/* vowels */
case "a":
res <<= 2;
res += 0;
break;
case "i":
res <<= 2;
res += 1;
break;
case "o":
res <<= 2;
res += 2;
break;
case "u":
res <<= 2;
res += 3;
break;
/* separators */
default:
break;
}
}
return res >>> 0; //the unsigned right shift fixes signed int issue - http://stackoverflow.com/a/17106974/1777150
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment