UNPKG

@ddunigma/node

Version:
332 lines (331 loc) 13 kB
"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;