Last active
December 2, 2020 14:25
-
-
Save teyfix/df897fc9c6e13958a93bc4b629b80988 to your computer and use it in GitHub Desktop.
base converting tool for javascript
This file contains hidden or 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
class BaseConvert { | |
static defaultDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; | |
static test() { | |
const expects = [ | |
{ | |
base: 64, | |
encode: [ | |
{input: 150, output: 'CW'}, | |
{input: 19238, output: 'Esm'}, | |
{input: 497179179, output: 'dolor'}, | |
], | |
decode: [ | |
{input: 'js', output: 2284}, | |
{input: 'abc', output: 108252}, | |
{input: 'pen', output: 169895}, | |
{input: 'Hello', output: 125458792}, | |
], | |
}, | |
]; | |
const test = (label, expected, result) => { | |
if (expected === result) { | |
return; | |
} | |
console.log('Test failed:', label); | |
console.log('Expected:', expected); | |
console.log('Result:', result); | |
console.log(); | |
}; | |
for (const expect of expects) { | |
const bc = new BaseConvert(expect.base); | |
for (const encode of expect.encode) { | |
test(`Base${expect.base} encoding`, encode.output, bc.encode(encode.input)); | |
} | |
for (const decode of expect.decode) { | |
test(`Base${expect.base} decoding`, decode.output, bc.decode(decode.input)); | |
} | |
} | |
} | |
static assertNum({value, label, min, max, integer}) { | |
label = label || 'Input'; | |
if ('number' !== typeof value) { | |
throw new TypeError(`${label} must be a number`); | |
} | |
if ('number' === typeof min && value < min) { | |
throw new RangeError(`${label} must be greater than or equal to ${min}`); | |
} | |
if ('number' === typeof max && value > max) { | |
throw new RangeError(`${label} must be less than or equal to ${max}`); | |
} | |
if (integer && !Number.isInteger(value)) { | |
throw new Error(`${label} must be an integer`); | |
} | |
} | |
static assertStr({value, label, minLength, duplicate, charset}) { | |
if ('string' !== typeof value) { | |
throw new TypeError(`${label} must be a string`); | |
} | |
if (value.length < minLength) { | |
throw new RangeError(`${label} must be longer than 2 characters`); | |
} | |
if (duplicate || charset) { | |
for (let i = 0; i < value.length; i++) { | |
const char = value[i]; | |
if (duplicate) { | |
if (value.indexOf(char) !== value.lastIndexOf(char)) { | |
throw new Error(`${label} contains duplicate characters`); | |
} | |
} | |
if (charset) { | |
if (charset.indexOf(char) === -1) { | |
throw new Error(`${label} contains characters out of current charset`); | |
} | |
} | |
} | |
} | |
} | |
digits = ''; | |
assertNum = BaseConvert.assertNum; | |
assertStr = BaseConvert.assertStr; | |
constructor(digits) { | |
if (null == digits) { | |
return; | |
} | |
const {assertNum, assertStr, defaultDigits} = BaseConvert; | |
if ('number' === typeof digits) { | |
assertNum({value: digits, label: 'Base', min: 2, max: BaseConvert.defaultDigits.length, integer: true}); | |
this.digits = defaultDigits.slice(0, digits); | |
} else { | |
assertStr({value: digits, label: 'Charset', duplicate: true}); | |
this.digits = digits; | |
} | |
} | |
encode(value) { | |
const {digits, assertNum} = this; | |
const base = digits.length; | |
let result = ''; | |
assertNum({value, min: 0, integer: true}); | |
do { | |
const digit = value % base; | |
result = digits.charAt(digit) + result; | |
value = Math.floor(value / base); | |
} while (value); | |
return result; | |
} | |
decode(value) { | |
const {digits, assertStr} = this; | |
const base = digits.length; | |
let result = 0; | |
assertStr({value, charset: digits}); | |
for (const char of value) { | |
result = (result * base) + digits.indexOf(char); | |
} | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment