Created
February 9, 2022 19:40
-
-
Save afk-mario/37d69d983131dec81cfa0c1ae21c7552 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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