UNPKG

base45-web

Version:

Encode/decode base45 data - including non-utf8 compliant data

112 lines (83 loc) 4.04 kB
(function () { "use strict"; const baseSize = 45; const baseSizeSquared = 2025; const chunkSize = 2; const encodedChunkSize = 3; const smallEncodedChunkSize = 2; const byteSize = 256; const encoding = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", " ", "$", "%", "*", "+", "-", ".", "/", ":"]; var decoding; function encode(byteArrayArg) { if (byteArrayArg === null || byteArrayArg === undefined) throw new Error("byteArrayArg is null or undefined."); //TODO check is array-like? const wholeChunkCount = Math.trunc(byteArrayArg.length / chunkSize); const resultSize = wholeChunkCount * encodedChunkSize + (byteArrayArg.length % chunkSize === 1 ? smallEncodedChunkSize : 0); if (resultSize === 0) return ""; const result = new Array(resultSize); var resultIndex = 0; const wholeChunkLength = wholeChunkCount * chunkSize; for (let i = 0; i < wholeChunkLength;) { const value = byteArrayArg[i++] * byteSize + byteArrayArg[i++]; result[resultIndex++] = encoding[value % baseSize]; result[resultIndex++] = encoding[Math.trunc(value / baseSize) % baseSize]; result[resultIndex++] = encoding[Math.trunc(value / baseSizeSquared) % baseSize]; } if (byteArrayArg.length % chunkSize === 0) return result.join(""); result[result.length - 2] = encoding[byteArrayArg[byteArrayArg.length - 1] % baseSize]; result[result.length - 1] = byteArrayArg[byteArrayArg.length - 1] < baseSize ? encoding[0] : encoding[Math.trunc(byteArrayArg[byteArrayArg.length - 1] / baseSize) % baseSize]; return result.join(""); }; function decode(utf8StringArg) { if (utf8StringArg === null || utf8StringArg === undefined) throw new Error("utf8StringArg is null or undefined."); if (utf8StringArg.length === 0) return []; var remainderSize = utf8StringArg.length % encodedChunkSize; if (remainderSize === 1) throw new Error("utf8StringArg has incorrect length."); if (decoding === undefined) { decoding = {}; for (let i = 0; i < encoding.length; ++i) decoding[encoding[i]] = i; } const buffer = new Array(utf8StringArg.length); for (let i = 0; i < utf8StringArg.length; ++i) { const found = decoding[utf8StringArg[i]]; if (found === undefined) throw new Error("Invalid character at position ".concat(i).concat(".")); buffer[i] = found; } const wholeChunkCount = Math.trunc(buffer.length / encodedChunkSize); var result = new Array(wholeChunkCount * chunkSize + (remainderSize === chunkSize ? 1 : 0)); var resultIndex = 0; const wholeChunkLength = wholeChunkCount * encodedChunkSize; for (let i = 0; i < wholeChunkLength;) { const val = buffer[i++] + baseSize * buffer[i++] + baseSizeSquared * buffer[i++]; result[resultIndex++] = Math.trunc(val / byteSize); //result is always in the range 0-255 - % ByteSize omitted. result[resultIndex++] = val % byteSize; } if (remainderSize === 0) return result; result[result.length - 1] = buffer[buffer.length - 2] + baseSize * buffer[buffer.length - 1]; //result is always in the range 0-255 - % ByteSize omitted. return result; } function decodeToUtf8String(utf8StringArg) { var data = decode(utf8StringArg); var str = ""; var count = data.length; for (let i= 0; i < count; ++i) str += String.fromCharCode(data[i]); return str; } module.exports = { encode, decode, decodeToUtf8String }; })();