@ddunigma/node
Version:
332 lines (331 loc) • 13 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Custom64 = exports.Ddu64 = void 0;
class Ddu64 {
constructor(dduChar, paddingChar) {
this.dduCharKr = [
"뜌",
"땨",
"이",
"우",
"야",
"!",
"?",
".",
];
this.paddingCharKr = "뭐";
this.defaultEncoding = "utf-8";
this.bitLengthMap = new Map();
this.binaryLookup = Array.from({ length: 256 }, (_, i) => i.toString(2).padStart(8, "0"));
this.dduBinaryLookup = new Map();
this.dduBinaryLookupKr = new Map();
this.paddingRegex = new Map();
if (typeof dduChar === "string") {
const removeDuplicatesString = [...new Set(dduChar.trim())].join("");
this.dduChar = removeDuplicatesString.trim().split("");
}
else {
this.dduChar = dduChar || this.dduCharKr;
}
this.paddingChar = paddingChar || this.paddingCharKr;
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
this.dduCharKr.forEach((char, index) => this.dduBinaryLookupKr.set(char, index));
this.paddingRegex.set("default", new RegExp(this.paddingChar, "g"));
this.paddingRegex.set("KR", new RegExp(this.paddingCharKr, "g"));
}
getBitLength(setLength) {
let cached = this.bitLengthMap.get(setLength);
if (cached === undefined) {
cached = Math.ceil(Math.log2(setLength));
this.bitLengthMap.set(setLength, cached);
}
return cached;
}
getLargestPowerOfTwo(n) {
return Math.pow(2, Math.floor(Math.log2(n)));
}
*splitString(s, length) {
for (let i = 0; i < s.length; i += length) {
yield s.slice(i, Math.min(i + length, s.length));
}
}
getSelectedSets(dduSetSymbol, usePowerOfTwo) {
const isKR = dduSetSymbol === "KR";
const dduSet = isKR ? this.dduCharKr : this.dduChar;
const padChar = isKR ? this.paddingCharKr : this.paddingChar;
let dduLength = dduSet.length;
if (usePowerOfTwo) {
const powerOfTwoLength = this.getLargestPowerOfTwo(dduLength);
dduLength = powerOfTwoLength;
}
return {
dduSet: usePowerOfTwo ? dduSet.slice(0, dduLength) : dduSet,
padChar,
dduLength,
bitLength: this.getBitLength(dduLength),
lookupTable: isKR ? this.dduBinaryLookupKr : this.dduBinaryLookup,
paddingRegExp: this.paddingRegex.get(isKR ? "KR" : "default"),
};
}
bufferToDduBinary(input, bitLength) {
const encodedBin = input.reduce((acc, byte) => acc + this.binaryLookup[byte], "");
const dduBinary = Array.from(this.splitString(encodedBin, bitLength));
const padding = bitLength - dduBinary[dduBinary.length - 1].length;
if (padding > 0) {
dduBinary[dduBinary.length - 1] += "0".repeat(padding);
}
return { dduBinary, padding };
}
dduBinaryToBuffer(decodedBin, paddingBits) {
if (paddingBits > 0) {
decodedBin = decodedBin.slice(0, -paddingBits);
}
const buffer = [];
for (let i = 0; i < decodedBin.length; i += 8) {
buffer.push(parseInt(decodedBin.slice(i, i + 8), 2));
}
return Buffer.from(buffer);
}
encode(input, options = {}) {
const { dduSetSymbol = "default", encoding = this.defaultEncoding, usePowerOfTwo = true, } = options;
const bufferInput = typeof input === "string" ? Buffer.from(input, encoding) : input;
const { dduSet, padChar, dduLength, bitLength } = this.getSelectedSets(dduSetSymbol, usePowerOfTwo);
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, bitLength);
let resultString = "";
for (const binaryChunk of dduBinary) {
const charInt = parseInt(binaryChunk, 2);
if (usePowerOfTwo) {
resultString += dduSet[charInt];
}
else {
const quotient = Math.floor(charInt / dduLength);
const remainder = charInt % dduLength;
resultString += dduSet[quotient] + dduSet[remainder];
}
}
return padding > 0
? resultString + padChar.repeat(Math.floor(padding / 2))
: resultString;
}
decode(input, options = {}) {
const { dduSetSymbol = "default", encoding = this.defaultEncoding, usePowerOfTwo = true, } = options;
const { dduSet, dduLength, bitLength, lookupTable, paddingRegExp } = this.getSelectedSets(dduSetSymbol, usePowerOfTwo);
const paddingCount = (input.match(paddingRegExp) || []).length;
const paddingBits = paddingCount * 2;
input = input.replace(paddingRegExp, "");
let dduBinary = "";
for (let i = 0; i < input.length; i += usePowerOfTwo ? 1 : 2) {
const firstChar = input[i];
const secondChar = input[i + 1];
if (usePowerOfTwo) {
const charIndex = lookupTable.get(firstChar);
if (charIndex === undefined)
throw new Error(`Invalid character: ${firstChar}`);
dduBinary += charIndex.toString(2).padStart(bitLength, "0");
}
else {
const firstIndex = lookupTable.get(firstChar);
const secondIndex = lookupTable.get(secondChar);
if (firstIndex === undefined || secondIndex === undefined)
throw new Error(`Invalid character: ${firstChar} or ${secondChar}`);
const value = firstIndex * dduLength + secondIndex;
dduBinary += value.toString(2).padStart(bitLength, "0");
}
}
const decoded = this.dduBinaryToBuffer(dduBinary, usePowerOfTwo ? paddingBits : paddingCount * bitLength);
return decoded.toString(encoding);
}
}
exports.Ddu64 = Ddu64;
class Custom64 {
constructor(dduChar, paddingChar) {
this.dduCharKr = [
"A",
"s",
"q",
"r",
"0",
"z",
"3",
"t",
"y",
"1",
"5",
"2",
"4",
"E",
"B",
"C",
"Q",
"F",
"R",
"T",
"U",
"W",
"V",
"X",
"Y",
"Z",
"b",
"a",
"c",
"d",
"D",
"G",
"L",
"H",
"I",
"+",
"J",
"K",
"M",
"O",
"N",
"f",
"e",
"h",
"g",
"P",
"i",
"S",
"k",
"l",
"m",
"u",
"j",
"v",
"n",
"o",
"p",
"9",
"w",
"6",
"7",
"8",
"x",
"/",
];
this.paddingCharKr = "=";
this.defaultEncoding = "utf-8";
this.bitLengthMap = new Map();
this.binaryLookup = Array.from({ length: 256 }, (_, i) => i.toString(2).padStart(8, "0"));
this.dduBinaryLookup = new Map();
this.dduBinaryLookupKr = new Map();
this.paddingRegex = new Map();
if (typeof dduChar === "string") {
const removeDuplicatesString = [...new Set(dduChar.trim())].join("");
this.dduChar = removeDuplicatesString.trim().split("");
}
else {
this.dduChar = dduChar || this.dduCharKr;
}
this.paddingChar = paddingChar || this.paddingCharKr;
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
this.dduCharKr.forEach((char, index) => this.dduBinaryLookupKr.set(char, index));
this.paddingRegex.set("default", new RegExp(this.paddingChar, "g"));
this.paddingRegex.set("KR", new RegExp(this.paddingCharKr, "g"));
}
getBitLength(setLength) {
let cached = this.bitLengthMap.get(setLength);
if (cached === undefined) {
cached = Math.ceil(Math.log2(setLength));
this.bitLengthMap.set(setLength, cached);
}
return cached;
}
getLargestPowerOfTwo(n) {
return Math.pow(2, Math.floor(Math.log2(n)));
}
*splitString(s, length) {
for (let i = 0; i < s.length; i += length) {
yield s.slice(i, Math.min(i + length, s.length));
}
}
getSelectedSets(dduSetSymbol, usePowerOfTwo) {
const isKR = dduSetSymbol === "KR";
const dduSet = isKR ? this.dduCharKr : this.dduChar;
const padChar = isKR ? this.paddingCharKr : this.paddingChar;
let dduLength = dduSet.length;
if (usePowerOfTwo) {
const powerOfTwoLength = this.getLargestPowerOfTwo(dduLength);
dduLength = powerOfTwoLength;
}
return {
dduSet: usePowerOfTwo ? dduSet.slice(0, dduLength) : dduSet,
padChar,
dduLength,
bitLength: this.getBitLength(dduLength),
lookupTable: isKR ? this.dduBinaryLookupKr : this.dduBinaryLookup,
paddingRegExp: this.paddingRegex.get(isKR ? "KR" : "default"),
};
}
bufferToDduBinary(input, bitLength) {
const encodedBin = input.reduce((acc, byte) => acc + this.binaryLookup[byte], "");
const dduBinary = Array.from(this.splitString(encodedBin, bitLength));
const padding = bitLength - dduBinary[dduBinary.length - 1].length;
if (padding > 0) {
dduBinary[dduBinary.length - 1] += "0".repeat(padding);
}
return { dduBinary, padding };
}
dduBinaryToBuffer(decodedBin, paddingBits) {
if (paddingBits > 0) {
decodedBin = decodedBin.slice(0, -paddingBits);
}
const buffer = [];
for (let i = 0; i < decodedBin.length; i += 8) {
buffer.push(parseInt(decodedBin.slice(i, i + 8), 2));
}
return Buffer.from(buffer);
}
encode(input, options = {}) {
const { dduSetSymbol = "default", encoding = this.defaultEncoding, usePowerOfTwo = true, } = options;
const bufferInput = typeof input === "string" ? Buffer.from(input, encoding) : input;
const { dduSet, padChar, dduLength, bitLength } = this.getSelectedSets(dduSetSymbol, usePowerOfTwo);
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, bitLength);
let resultString = "";
for (const binaryChunk of dduBinary) {
const charInt = parseInt(binaryChunk, 2);
if (usePowerOfTwo) {
resultString += dduSet[charInt];
}
else {
const quotient = Math.floor(charInt / dduLength);
const remainder = charInt % dduLength;
resultString += dduSet[quotient] + dduSet[remainder];
}
}
return padding > 0
? resultString + padChar.repeat(Math.floor(padding / 2))
: resultString;
}
decode(input, options = {}) {
const { dduSetSymbol = "default", encoding = this.defaultEncoding, usePowerOfTwo = true, } = options;
const { dduSet, dduLength, bitLength, lookupTable, paddingRegExp } = this.getSelectedSets(dduSetSymbol, usePowerOfTwo);
const paddingCount = (input.match(paddingRegExp) || []).length;
const paddingBits = paddingCount * 2;
input = input.replace(paddingRegExp, "");
let dduBinary = "";
for (let i = 0; i < input.length; i += usePowerOfTwo ? 1 : 2) {
const firstChar = input[i];
const secondChar = input[i + 1];
if (usePowerOfTwo) {
const charIndex = lookupTable.get(firstChar);
if (charIndex === undefined)
throw new Error(`Invalid character: ${firstChar}`);
dduBinary += charIndex.toString(2).padStart(bitLength, "0");
}
else {
const firstIndex = lookupTable.get(firstChar);
const secondIndex = lookupTable.get(secondChar);
if (firstIndex === undefined || secondIndex === undefined)
throw new Error(`Invalid character: ${firstChar} or ${secondChar}`);
const value = firstIndex * dduLength + secondIndex;
dduBinary += value.toString(2).padStart(bitLength, "0");
}
}
const decoded = this.dduBinaryToBuffer(dduBinary, usePowerOfTwo ? paddingBits : paddingCount * bitLength);
return decoded.toString(encoding);
}
}
exports.Custom64 = Custom64;