ES6 Plato on Github
Report Home
Summary Display
basex.mjs
Maintainability
59.65
Lines of code
102
Difficulty
82.83
Estimated Errors
1.34
Function weight
By Complexity
By SLOC
// Inspired by bs58, base-x then @thi.ng/base-n module const B58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; const B64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; // Padding '=' export const b58 = basex(B58_ALPHABET); export const b16 = basex('0123456789abcdef'); export default basex; const _b64 = basex(B64_ALPHABET); export const b64 = { encode: source => { const bSource = (typeof source === 'string') ? (new TextEncoder()).encode(source) : new Uint8Array(source); const paddedSize = Math.ceil(bSource.length / 3) * 3; const sizedArray = new Uint8Array(paddedSize); sizedArray.set(bSource); const b64str = _b64.encode(sizedArray).split(''); const b64PaddedSize = paddedSize*4/3; while (b64str.length < b64PaddedSize) b64str.unshift(B64_ALPHABET[0]); for (let i = 0; i < paddedSize - bSource.length; i++) b64str[b64str.length - 1 - i] = '='; return b64str.join(''); }, decode: b64str => { const rawArray = _b64.decode(b64str.replace(/=/g, 'A')); const targetSize = Math.ceil(3 * b64str.length / 4); const postCut = (b64str.match(/=/g)||[]).length; const preCut = rawArray.length - targetSize; return rawArray.slice(preCut, rawArray.length-postCut); } }; export function basex(ALPHABET) { const config = { ALPHABET_MAP: {}, BASE: ALPHABET.length, LEADER: ALPHABET.charAt(0), ALPHABET }; // Pre-compute lookup table for (let z = 0; z < config.ALPHABET.length; z++) { const x = config.ALPHABET.charAt(z); if (config.ALPHABET_MAP[x] !== undefined) throw new TypeError(x + ' is ambiguous'); config.ALPHABET_MAP[x] = z; } return { encode: source => encode(source, config), decode: string => decode(string, config) }; } function encode(source, config) { if (source.length === 0) return ''; const digits = [0]; for (let carry of source) { for (let j = 0; j < digits.length; ++j) { carry += digits[j] << 8; digits[j] = carry % config.BASE; carry = (carry / config.BASE) | 0; // eslint-disable-line unicorn/prefer-math-trunc } while (carry > 0) { digits.push(carry % config.BASE); carry = (carry / config.BASE) | 0; // eslint-disable-line unicorn/prefer-math-trunc } } let string = ''; for (let k = 0; source[k] === 0 && k < source.length - 1; ++k) string += config.LEADER; // Deal with leading zeros for (let q = digits.length - 1; q >= 0; --q) string += config.ALPHABET[digits[q]]; // Convert digits to a string return string; } function decodeUnsafe(string, config) { if (typeof string !== 'string') throw new TypeError('Expected String'); if (string.length === 0) return new Uint8Array(0); const bytes = [0]; for (const chr of string) { const value = config.ALPHABET_MAP[chr]; if (value === undefined) return; let carry = value; for (let j = 0; j < bytes.length; ++j) { carry += bytes[j] * config.BASE; bytes[j] = carry & 0xff; carry >>= 8; } while (carry > 0) { bytes.push(carry & 0xff); carry >>= 8; } } for (let k = 0; string[k] === config.LEADER && k < string.length - 1; ++k) bytes.push(0); // Deal with leading zeros return new Uint8Array(bytes.reverse()); } function decode(string, config) { const buffer = decodeUnsafe(string, config); if (buffer) return buffer; throw new Error('Non-base' + config.BASE + ' character'); }