@ddunigma/node 1.0.25 → 1.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/cjs/Custom64.d.ts +29 -0
- package/dist/cjs/Custom64.js +205 -0
- package/dist/cjs/Ddu1024.d.ts +26 -0
- package/dist/cjs/Ddu1024.js +262 -0
- package/dist/cjs/Ddu128.d.ts +26 -0
- package/dist/cjs/Ddu128.js +150 -0
- package/dist/cjs/Ddu512.d.ts +26 -0
- package/dist/cjs/Ddu512.js +198 -0
- package/dist/cjs/Ddu64.d.ts +29 -0
- package/dist/cjs/Ddu64.js +149 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +11 -329
- package/dist/mjs/Custom64.d.ts +29 -0
- package/dist/mjs/Custom64.js +203 -0
- package/dist/mjs/Ddu1024.d.ts +26 -0
- package/dist/mjs/Ddu1024.js +261 -0
- package/dist/mjs/Ddu128.d.ts +26 -0
- package/dist/mjs/Ddu128.js +149 -0
- package/dist/mjs/Ddu512.d.ts +26 -0
- package/dist/mjs/Ddu512.js +197 -0
- package/dist/mjs/Ddu64.d.ts +29 -0
- package/dist/mjs/Ddu64.js +147 -0
- package/dist/mjs/index.d.ts +5 -0
- package/dist/mjs/index.js +5 -330
- package/package.json +2 -3
- package/dist/cjs/test/test2.d.ts +0 -1
- package/dist/cjs/test/test2.js +0 -44
- package/dist/cjs/test/test3.d.ts +0 -1
- package/dist/cjs/test/test3.js +0 -20
- package/dist/cjs/test/tester2.d.ts +0 -1
- package/dist/cjs/test/tester2.js +0 -46
- package/dist/index.d.ts +0 -58
- package/dist/mjs/test/test2.d.ts +0 -1
- package/dist/mjs/test/test2.js +0 -42
- package/dist/mjs/test/test3.d.ts +0 -1
- package/dist/mjs/test/test3.js +0 -18
- package/dist/mjs/test/tester2.d.ts +0 -1
- package/dist/mjs/test/tester2.js +0 -44
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Ddu128 = void 0;
|
|
4
|
+
class Ddu128 {
|
|
5
|
+
constructor(dduChar, paddingChar) {
|
|
6
|
+
// 128개의 2글자 URL-safe 문자열 (앞글자: E,l,y 제외 / 뒷글자: s,i,a 제외)
|
|
7
|
+
this.dduCharDefault = [
|
|
8
|
+
'Z9', '25', 'Dk', '86', 'un', 'cb', 'V1', 'Bf',
|
|
9
|
+
'uc', '7A', 'FL', 'Wm', 'CL', 'XA', '3K', 'W3',
|
|
10
|
+
'0h', 'RV', 'TI', 'Ze', 'dC', 'CG', 'FN', 'PC',
|
|
11
|
+
'W8', '5s', '8L', 'If', 'tz', 'J-', 'WU', 'dk',
|
|
12
|
+
'tC', 'VS', 'Hi', 'BF', 'W-', 'co', 'pm', 'Hb',
|
|
13
|
+
'gp', 'TO', 'MY', 'Bs', 'Vj', '4x', 'Ye', 'dx',
|
|
14
|
+
'A1', 'tH', 'kv', 'us', '_Y', 'kU', '3m', 'a1',
|
|
15
|
+
'gw', 'rp', 'k6', 'g1', 'Yn', 'IL', 'Yb', 'rI',
|
|
16
|
+
'_6', 'dK', 'ZL', 'uJ', 'Z6', 'TK', 'xj', 'DI',
|
|
17
|
+
'XZ', 'tQ', 'zK', 'Be', 'wQ', 'a3', 'd5', 'M9',
|
|
18
|
+
'TS', 'W1', '_9', 'Ds', 'Bx', 'Om', '0v', 'DU',
|
|
19
|
+
'Hs', '7K', 'dN', 'Ff', 'kn', 'JL', 'p6', 'MA',
|
|
20
|
+
'TH', '3f', 'Fe', 'Te', 'rZ', 'rz', 'Jv', 'X3',
|
|
21
|
+
't0', 'R5', 'OU', 'Ws', 'Ch', 'zN', 'rU', 'Tx',
|
|
22
|
+
'VU', 'qq', 'WZ', 'F9', 'ph', 'uC', 'Fh', 'dG',
|
|
23
|
+
'4w', 'tw', 'Zn', 'M-', 'cK', '4o', 'wU', '7n'
|
|
24
|
+
];
|
|
25
|
+
this.paddingCharDefault = "El";
|
|
26
|
+
this.defaultEncoding = "utf-8";
|
|
27
|
+
this.binaryLookup = Array.from({ length: 256 }, (_, i) => i.toString(2).padStart(8, "0"));
|
|
28
|
+
this.dduBinaryLookup = new Map();
|
|
29
|
+
this.dduBinaryLookupKr = new Map();
|
|
30
|
+
this.paddingRegex = new Map();
|
|
31
|
+
// 기본값 설정
|
|
32
|
+
const sourceChar = dduChar || this.dduCharDefault;
|
|
33
|
+
const sourcePadding = paddingChar || this.paddingCharDefault;
|
|
34
|
+
// 유효성 검사: 최소 128개 이상의 문자 필요
|
|
35
|
+
if (sourceChar.length < 128) {
|
|
36
|
+
throw new Error(`Ddu128 requires at least 128 characters in the character set. Provided: ${sourceChar.length}`);
|
|
37
|
+
}
|
|
38
|
+
// 첫 번째 문자의 길이를 기준으로 설정
|
|
39
|
+
this.charLength = sourceChar[0].length;
|
|
40
|
+
// 모든 문자의 길이가 동일한지 검사
|
|
41
|
+
for (let i = 0; i < sourceChar.length; i++) {
|
|
42
|
+
if (sourceChar[i].length !== this.charLength) {
|
|
43
|
+
throw new Error(`All characters must have the same length. Expected: ${this.charLength}, but character at index ${i} ("${sourceChar[i]}") has length ${sourceChar[i].length}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 중복 검사
|
|
47
|
+
const uniqueChars = new Set(sourceChar);
|
|
48
|
+
if (uniqueChars.size !== sourceChar.length) {
|
|
49
|
+
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${sourceChar.length}`);
|
|
50
|
+
}
|
|
51
|
+
// 패딩 문자가 문자셋에 포함되어 있는지 검사
|
|
52
|
+
if (uniqueChars.has(sourcePadding)) {
|
|
53
|
+
throw new Error(`Padding character "${sourcePadding}" cannot be in the character set`);
|
|
54
|
+
}
|
|
55
|
+
// 앞에서부터 정확히 128개만 사용
|
|
56
|
+
this.dduChar = sourceChar.slice(0, 128);
|
|
57
|
+
this.paddingChar = sourcePadding;
|
|
58
|
+
// 룩업 테이블 생성
|
|
59
|
+
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
|
|
60
|
+
this.dduCharDefault.forEach((char, index) => this.dduBinaryLookupKr.set(char, index));
|
|
61
|
+
this.paddingRegex.set("default", new RegExp(this.escapeRegExp(this.paddingChar), "g"));
|
|
62
|
+
this.paddingRegex.set("KR", new RegExp(this.escapeRegExp(this.paddingCharDefault), "g"));
|
|
63
|
+
}
|
|
64
|
+
escapeRegExp(str) {
|
|
65
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
66
|
+
}
|
|
67
|
+
*splitString(s, length) {
|
|
68
|
+
for (let i = 0; i < s.length; i += length) {
|
|
69
|
+
yield s.slice(i, Math.min(i + length, s.length));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
getSelectedSets(dduSetSymbol) {
|
|
73
|
+
const isKR = dduSetSymbol === "KR";
|
|
74
|
+
const dduSet = isKR ? this.dduCharDefault : this.dduChar;
|
|
75
|
+
const padChar = isKR ? this.paddingCharDefault : this.paddingChar;
|
|
76
|
+
return {
|
|
77
|
+
dduSet,
|
|
78
|
+
padChar,
|
|
79
|
+
lookupTable: isKR ? this.dduBinaryLookupKr : this.dduBinaryLookup,
|
|
80
|
+
paddingRegExp: this.paddingRegex.get(isKR ? "KR" : "default"),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
bufferToDduBinary(input) {
|
|
84
|
+
const encodedBin = input.reduce((acc, byte) => acc + this.binaryLookup[byte], "");
|
|
85
|
+
if (encodedBin.length === 0) {
|
|
86
|
+
return { dduBinary: [], padding: 0 };
|
|
87
|
+
}
|
|
88
|
+
const bitLength = 7; // 128 = 2^7
|
|
89
|
+
const dduBinary = Array.from(this.splitString(encodedBin, bitLength));
|
|
90
|
+
const padding = bitLength - dduBinary[dduBinary.length - 1].length;
|
|
91
|
+
if (padding > 0) {
|
|
92
|
+
dduBinary[dduBinary.length - 1] += "0".repeat(padding);
|
|
93
|
+
}
|
|
94
|
+
return { dduBinary, padding };
|
|
95
|
+
}
|
|
96
|
+
dduBinaryToBuffer(decodedBin, paddingBits) {
|
|
97
|
+
if (paddingBits > 0) {
|
|
98
|
+
decodedBin = decodedBin.slice(0, -paddingBits);
|
|
99
|
+
}
|
|
100
|
+
const buffer = [];
|
|
101
|
+
for (let i = 0; i < decodedBin.length; i += 8) {
|
|
102
|
+
buffer.push(parseInt(decodedBin.slice(i, i + 8), 2));
|
|
103
|
+
}
|
|
104
|
+
return Buffer.from(buffer);
|
|
105
|
+
}
|
|
106
|
+
encode(input, options = {}) {
|
|
107
|
+
const { dduSetSymbol = "default", encoding = this.defaultEncoding, } = options;
|
|
108
|
+
const bufferInput = typeof input === "string" ? Buffer.from(input, encoding) : input;
|
|
109
|
+
const { dduSet, padChar } = this.getSelectedSets(dduSetSymbol);
|
|
110
|
+
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput);
|
|
111
|
+
let resultString = "";
|
|
112
|
+
// 각 7비트 청크를 3글자 문자열로 변환
|
|
113
|
+
for (const binaryChunk of dduBinary) {
|
|
114
|
+
const charInt = parseInt(binaryChunk, 2);
|
|
115
|
+
resultString += dduSet[charInt];
|
|
116
|
+
}
|
|
117
|
+
// 패딩 비트 정보를 padChar + 패딩비트수(0-6) 형태로 추가
|
|
118
|
+
// padding이 0이 아니면 padChar를 추가
|
|
119
|
+
if (padding > 0) {
|
|
120
|
+
resultString += padChar + padding;
|
|
121
|
+
}
|
|
122
|
+
return resultString;
|
|
123
|
+
}
|
|
124
|
+
decode(input, options = {}) {
|
|
125
|
+
const { dduSetSymbol = "default", encoding = this.defaultEncoding, } = options;
|
|
126
|
+
const { lookupTable, padChar } = this.getSelectedSets(dduSetSymbol);
|
|
127
|
+
// 패딩 정보 추출
|
|
128
|
+
let paddingBits = 0;
|
|
129
|
+
const padCharIndex = input.indexOf(padChar);
|
|
130
|
+
if (padCharIndex >= 0) {
|
|
131
|
+
// padChar 이후의 숫자가 패딩 비트 수
|
|
132
|
+
const paddingStr = input.substring(padCharIndex + padChar.length);
|
|
133
|
+
paddingBits = parseInt(paddingStr) || 0;
|
|
134
|
+
// 패딩 정보 제거
|
|
135
|
+
input = input.substring(0, padCharIndex);
|
|
136
|
+
}
|
|
137
|
+
let dduBinary = "";
|
|
138
|
+
// charLength 길이만큼 읽어서 디코딩
|
|
139
|
+
for (let i = 0; i < input.length; i += this.charLength) {
|
|
140
|
+
const charChunk = input.slice(i, i + this.charLength);
|
|
141
|
+
const charIndex = lookupTable.get(charChunk);
|
|
142
|
+
if (charIndex === undefined)
|
|
143
|
+
throw new Error(`Invalid character: ${charChunk}`);
|
|
144
|
+
dduBinary += charIndex.toString(2).padStart(7, "0");
|
|
145
|
+
}
|
|
146
|
+
const decoded = this.dduBinaryToBuffer(dduBinary, paddingBits);
|
|
147
|
+
return decoded.toString(encoding);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.Ddu128 = Ddu128;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class Ddu512 {
|
|
2
|
+
private readonly dduChar;
|
|
3
|
+
private readonly paddingChar;
|
|
4
|
+
private readonly charLength;
|
|
5
|
+
private readonly dduCharDefault;
|
|
6
|
+
private readonly paddingCharDefault;
|
|
7
|
+
private readonly defaultEncoding;
|
|
8
|
+
private readonly binaryLookup;
|
|
9
|
+
private readonly dduBinaryLookup;
|
|
10
|
+
private readonly dduBinaryLookupKr;
|
|
11
|
+
private readonly paddingRegex;
|
|
12
|
+
constructor(dduChar?: string[], paddingChar?: string);
|
|
13
|
+
private escapeRegExp;
|
|
14
|
+
private splitString;
|
|
15
|
+
private getSelectedSets;
|
|
16
|
+
private bufferToDduBinary;
|
|
17
|
+
private dduBinaryToBuffer;
|
|
18
|
+
encode(input: Buffer | string, options?: {
|
|
19
|
+
dduSetSymbol?: string;
|
|
20
|
+
encoding?: BufferEncoding;
|
|
21
|
+
}): string;
|
|
22
|
+
decode(input: string, options?: {
|
|
23
|
+
dduSetSymbol?: string;
|
|
24
|
+
encoding?: BufferEncoding;
|
|
25
|
+
}): string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Ddu512 = void 0;
|
|
4
|
+
class Ddu512 {
|
|
5
|
+
constructor(dduChar, paddingChar) {
|
|
6
|
+
// 512개의 2글자 URL-safe 문자열 (앞글자: E,l,y 제외 / 뒷글자: s,i,a 제외)
|
|
7
|
+
this.dduCharDefault = [
|
|
8
|
+
'ZL', 'IX', 'da', 'Mz', 'nn', '1z', '7v', 'XV',
|
|
9
|
+
'1L', 'j0', 'DB', '2z', 'OQ', 'FT', 'WQ', 'm2',
|
|
10
|
+
'Oc', 'jH', 'Ag', 'sb', '7Z', 'T9', '-B', 'ri',
|
|
11
|
+
'2x', 'o9', 'PP', '4v', 'dT', 'sk', 'cN', 'UL',
|
|
12
|
+
'iV', 'It', 'ib', 'Tf', 'j4', 'cK', 'Wf', 'wD',
|
|
13
|
+
'Au', 'tK', '16', '-h', '1g', '6b', 'ae', 'ZS',
|
|
14
|
+
'tk', 'mT', 'AU', 'wq', '5f', '18', '_o', 'GK',
|
|
15
|
+
'Ah', 'hQ', 'Cz', 'r6', 'MU', '-v', 'r4', 'mu',
|
|
16
|
+
'MC', 'U0', 'wu', 'O4', '6k', '3B', '_2', 'F4',
|
|
17
|
+
'OH', '-W', 'RX', 'U9', 'jQ', '2k', '-T', 'De',
|
|
18
|
+
'38', 'UJ', '6Q', 'Y4', 'Ie', '_N', 'Ue', 'uJ',
|
|
19
|
+
'iz', 'w6', 'pi', 'dU', '-K', 'D0', '5h', '1X',
|
|
20
|
+
'-b', 'mX', 'WL', 'iQ', 'jx', 'rU', 'Ft', 'jK',
|
|
21
|
+
'19', 'qV', 'mz', '-z', 'Fa', '29', 'dZ', 'hx',
|
|
22
|
+
'R3', 'GC', '7c', 'sX', 'Fh', 'C0', '5V', 'C8',
|
|
23
|
+
'of', 'RJ', 'Zk', 'cJ', '-x', 'tN', '3g', 'X0',
|
|
24
|
+
'3z', '-2', 'oJ', 'dH', 'qQ', 'r3', 'Mh', 'Dk',
|
|
25
|
+
'6g', 'Wv', 'F9', '39', '6B', 'qK', 'dQ', 'ok',
|
|
26
|
+
'pt', 'jB', 'YV', 'Gq', 'DV', 'jC', '7X', 'Y3',
|
|
27
|
+
'X9', 'Oh', '5g', 'RH', 'Ub', 'sC', 'ub', '_h',
|
|
28
|
+
'_k', 'Hb', 'IU', 'cg', 'd8', 'pf', '58', 'TV',
|
|
29
|
+
'dz', 'OW', 'ik', 'Ff', 'MD', 'Ze', 'rH', 'sL',
|
|
30
|
+
'4N', '1V', 'MN', '-N', 'T0', '_z', 'qS', 'OS',
|
|
31
|
+
'49', 'dt', '-9', 'XN', '3Q', 'jV', 'Hf', 'h8',
|
|
32
|
+
'Rv', 'Yo', 'rv', '_C', 'ag', 'j3', 'mc', 'Dg',
|
|
33
|
+
'DL', '4x', '2V', '5o', 'Rh', 'Fg', 'Dz', 'Aa',
|
|
34
|
+
'Ce', 'aS', 'tL', 'Ac', '-3', '7h', 'Oz', 'st',
|
|
35
|
+
'O3', 'TQ', 'RL', '2b', 'pq', 'we', '1x', 're',
|
|
36
|
+
'5i', 'cz', 'tb', 'Rb', 'sB', 'IS', 'mo', '5v',
|
|
37
|
+
'w3', 'dc', 'MX', 'rh', 'ON', '4J', 'jk', 'hL',
|
|
38
|
+
'1a', '1U', 'hf', 'uv', 'Ov', 'AX', 'YT', '5a',
|
|
39
|
+
'6v', 'Re', '1T', 'rc', 'XK', 'mq', '-f', 'IV',
|
|
40
|
+
'Ru', '_H', 'FV', 'jo', 'HL', 'jf', 'OX', 'sK',
|
|
41
|
+
'_g', '_a', '7f', 'Ib', '_x', 's4', 'Xk', 'MQ',
|
|
42
|
+
'pC', '1Q', 'sT', 'sQ', 'Io', '4z', 'Rt', 'ph',
|
|
43
|
+
'Gc', 'pV', 'dV', 'hz', 'mg', '4k', 'Ai', '6e',
|
|
44
|
+
'dJ', 'wN', 'wx', 'Rf', 'FJ', 'M2', '5c', 'MH',
|
|
45
|
+
'O8', 'q9', 'w4', '7k', '3S', '1f', 'aJ', '5D',
|
|
46
|
+
'iS', 'YW', 'if', 'Rc', 'Me', 'G0', 'RK', '-k',
|
|
47
|
+
'WB', 'ug', 'sN', '7i', '-H', 'dN', '_U', 'Fz',
|
|
48
|
+
'a8', 'cv', 'FS', 'Ik', 'IN', 'Fq', 'oV', '2J',
|
|
49
|
+
'IL', 'w9', '-X', 'wv', 'Yk', 'GD', '7H', 'jt',
|
|
50
|
+
'oL', 'AT', 'w2', '-e', 'mQ', '5N', 's9', 'dq',
|
|
51
|
+
'mS', '-C', 'hg', 'Ya', 'Ou', 'Mv', 'M0', 'FX',
|
|
52
|
+
'jW', 'rS', 'Wz', 'pk', '2g', 'i9', 'IT', '6K',
|
|
53
|
+
'5W', 'sa', 'mh', '3k', 'UN', 'mk', 'qe', '5L',
|
|
54
|
+
'Ga', 'pN', 'RU', 'ox', 'T8', 's6', 'rk', 'IJ',
|
|
55
|
+
'AW', '74', 'Oi', 'Wx', '7B', 'AD', 'G9', 'Tg',
|
|
56
|
+
'tf', '-c', '-u', '6S', 'rK', 'CB', 'YX', 'UV',
|
|
57
|
+
'Uz', 'jZ', 'i8', 'Mc', 'si', 'MV', 'm9', '53',
|
|
58
|
+
'YJ', 'I0', '_V', 'GT', 'd9', 'YD', 'sW', 'Cx',
|
|
59
|
+
'wk', '20', '28', '_K', 'di', 'c0', 'AN', 'j9',
|
|
60
|
+
'Ux', 'M6', 'uk', 'Fk', 'iJ', 'Ot', 'dB', '_0',
|
|
61
|
+
'ob', 'sh', '7W', 'Ih', 'He', 'Gx', 'Mf', '4Q',
|
|
62
|
+
'Ic', '7u', 'pS', 'A0', '5k', '40', 'jN', 'R8',
|
|
63
|
+
'cL', 'm4', 'uS', 'RN', 'FC', 'Yx', '7q', 'rL',
|
|
64
|
+
'wL', 'ix', 'FU', '3V', 'Ig', 'j2', 'Zv', 'Hz',
|
|
65
|
+
'GL', 't9', 'Tv', 'O9', 'jq', 'Dx', 'R2', 'uV',
|
|
66
|
+
'Zg', 'rC', 'Ii', '1J', 'I2', 'mK', '7x', 'wK',
|
|
67
|
+
'AQ', '-Z', 'qg', 'p9', 'dv', 'u8', 'jL', '5J',
|
|
68
|
+
'Ug', 'p4', 'Y9', '-i', 's3', 'Y6', 'RW', 'FB',
|
|
69
|
+
'1h', 'ZK', 'HS', '-4', 'wJ', 'XS', 'dg', '_f',
|
|
70
|
+
'78', 'aL', 'Og', 'jh', 'Mg', 'Zz', '1u', 'UB',
|
|
71
|
+
'pe', 'wZ', 'OZ', 'G3', 'IZ', 'aN', 'HK', 'sH'
|
|
72
|
+
];
|
|
73
|
+
this.paddingCharDefault = "lE";
|
|
74
|
+
this.defaultEncoding = "utf-8";
|
|
75
|
+
this.binaryLookup = Array.from({ length: 256 }, (_, i) => i.toString(2).padStart(8, "0"));
|
|
76
|
+
this.dduBinaryLookup = new Map();
|
|
77
|
+
this.dduBinaryLookupKr = new Map();
|
|
78
|
+
this.paddingRegex = new Map();
|
|
79
|
+
// 기본값 설정
|
|
80
|
+
const sourceChar = dduChar || this.dduCharDefault;
|
|
81
|
+
const sourcePadding = paddingChar || this.paddingCharDefault;
|
|
82
|
+
// 유효성 검사: 최소 512개 이상의 문자 필요
|
|
83
|
+
if (sourceChar.length < 512) {
|
|
84
|
+
throw new Error(`Ddu512 requires at least 512 characters in the character set. Provided: ${sourceChar.length}`);
|
|
85
|
+
}
|
|
86
|
+
// 첫 번째 문자의 길이를 기준으로 설정
|
|
87
|
+
this.charLength = sourceChar[0].length;
|
|
88
|
+
// 모든 문자의 길이가 동일한지 검사
|
|
89
|
+
for (let i = 0; i < sourceChar.length; i++) {
|
|
90
|
+
if (sourceChar[i].length !== this.charLength) {
|
|
91
|
+
throw new Error(`All characters must have the same length. Expected: ${this.charLength}, but character at index ${i} ("${sourceChar[i]}") has length ${sourceChar[i].length}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// 중복 검사
|
|
95
|
+
const uniqueChars = new Set(sourceChar);
|
|
96
|
+
if (uniqueChars.size !== sourceChar.length) {
|
|
97
|
+
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${sourceChar.length}`);
|
|
98
|
+
}
|
|
99
|
+
// 패딩 문자가 문자셋에 포함되어 있는지 검사
|
|
100
|
+
if (uniqueChars.has(sourcePadding)) {
|
|
101
|
+
throw new Error(`Padding character "${sourcePadding}" cannot be in the character set`);
|
|
102
|
+
}
|
|
103
|
+
// 앞에서부터 정확히 512개만 사용
|
|
104
|
+
this.dduChar = sourceChar.slice(0, 512);
|
|
105
|
+
this.paddingChar = sourcePadding;
|
|
106
|
+
// 룩업 테이블 생성
|
|
107
|
+
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
|
|
108
|
+
this.dduCharDefault.forEach((char, index) => this.dduBinaryLookupKr.set(char, index));
|
|
109
|
+
this.paddingRegex.set("default", new RegExp(this.escapeRegExp(this.paddingChar), "g"));
|
|
110
|
+
this.paddingRegex.set("KR", new RegExp(this.escapeRegExp(this.paddingCharDefault), "g"));
|
|
111
|
+
}
|
|
112
|
+
escapeRegExp(str) {
|
|
113
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
114
|
+
}
|
|
115
|
+
*splitString(s, length) {
|
|
116
|
+
for (let i = 0; i < s.length; i += length) {
|
|
117
|
+
yield s.slice(i, Math.min(i + length, s.length));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
getSelectedSets(dduSetSymbol) {
|
|
121
|
+
const isKR = dduSetSymbol === "KR";
|
|
122
|
+
const dduSet = isKR ? this.dduCharDefault : this.dduChar;
|
|
123
|
+
const padChar = isKR ? this.paddingCharDefault : this.paddingChar;
|
|
124
|
+
return {
|
|
125
|
+
dduSet,
|
|
126
|
+
padChar,
|
|
127
|
+
lookupTable: isKR ? this.dduBinaryLookupKr : this.dduBinaryLookup,
|
|
128
|
+
paddingRegExp: this.paddingRegex.get(isKR ? "KR" : "default"),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
bufferToDduBinary(input) {
|
|
132
|
+
const encodedBin = input.reduce((acc, byte) => acc + this.binaryLookup[byte], "");
|
|
133
|
+
if (encodedBin.length === 0) {
|
|
134
|
+
return { dduBinary: [], padding: 0 };
|
|
135
|
+
}
|
|
136
|
+
const bitLength = 9; // 512 = 2^9
|
|
137
|
+
const dduBinary = Array.from(this.splitString(encodedBin, bitLength));
|
|
138
|
+
const padding = bitLength - dduBinary[dduBinary.length - 1].length;
|
|
139
|
+
if (padding > 0) {
|
|
140
|
+
dduBinary[dduBinary.length - 1] += "0".repeat(padding);
|
|
141
|
+
}
|
|
142
|
+
return { dduBinary, padding };
|
|
143
|
+
}
|
|
144
|
+
dduBinaryToBuffer(decodedBin, paddingBits) {
|
|
145
|
+
if (paddingBits > 0) {
|
|
146
|
+
decodedBin = decodedBin.slice(0, -paddingBits);
|
|
147
|
+
}
|
|
148
|
+
const buffer = [];
|
|
149
|
+
for (let i = 0; i < decodedBin.length; i += 8) {
|
|
150
|
+
buffer.push(parseInt(decodedBin.slice(i, i + 8), 2));
|
|
151
|
+
}
|
|
152
|
+
return Buffer.from(buffer);
|
|
153
|
+
}
|
|
154
|
+
encode(input, options = {}) {
|
|
155
|
+
const { dduSetSymbol = "default", encoding = this.defaultEncoding, } = options;
|
|
156
|
+
const bufferInput = typeof input === "string" ? Buffer.from(input, encoding) : input;
|
|
157
|
+
const { dduSet, padChar } = this.getSelectedSets(dduSetSymbol);
|
|
158
|
+
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput);
|
|
159
|
+
let resultString = "";
|
|
160
|
+
// 각 9비트 청크를 charLength 글자 문자열로 변환
|
|
161
|
+
for (const binaryChunk of dduBinary) {
|
|
162
|
+
const charInt = parseInt(binaryChunk, 2);
|
|
163
|
+
resultString += dduSet[charInt];
|
|
164
|
+
}
|
|
165
|
+
// 패딩 비트 정보를 padChar + 패딩비트수(0-8) 형태로 추가
|
|
166
|
+
// padding이 0이 아니면 padChar를 추가
|
|
167
|
+
if (padding > 0) {
|
|
168
|
+
resultString += padChar + padding;
|
|
169
|
+
}
|
|
170
|
+
return resultString;
|
|
171
|
+
}
|
|
172
|
+
decode(input, options = {}) {
|
|
173
|
+
const { dduSetSymbol = "default", encoding = this.defaultEncoding, } = options;
|
|
174
|
+
const { lookupTable, padChar } = this.getSelectedSets(dduSetSymbol);
|
|
175
|
+
// 패딩 정보 추출
|
|
176
|
+
let paddingBits = 0;
|
|
177
|
+
const padCharIndex = input.indexOf(padChar);
|
|
178
|
+
if (padCharIndex >= 0) {
|
|
179
|
+
// padChar 이후의 숫자가 패딩 비트 수
|
|
180
|
+
const paddingStr = input.substring(padCharIndex + padChar.length);
|
|
181
|
+
paddingBits = parseInt(paddingStr) || 0;
|
|
182
|
+
// 패딩 정보 제거
|
|
183
|
+
input = input.substring(0, padCharIndex);
|
|
184
|
+
}
|
|
185
|
+
let dduBinary = "";
|
|
186
|
+
// charLength 길이만큼 읽어서 디코딩
|
|
187
|
+
for (let i = 0; i < input.length; i += this.charLength) {
|
|
188
|
+
const charChunk = input.slice(i, i + this.charLength);
|
|
189
|
+
const charIndex = lookupTable.get(charChunk);
|
|
190
|
+
if (charIndex === undefined)
|
|
191
|
+
throw new Error(`Invalid character: ${charChunk}`);
|
|
192
|
+
dduBinary += charIndex.toString(2).padStart(9, "0");
|
|
193
|
+
}
|
|
194
|
+
const decoded = this.dduBinaryToBuffer(dduBinary, paddingBits);
|
|
195
|
+
return decoded.toString(encoding);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
exports.Ddu512 = Ddu512;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare class Ddu64 {
|
|
2
|
+
private readonly dduChar;
|
|
3
|
+
private readonly paddingChar;
|
|
4
|
+
private readonly dduCharDefault;
|
|
5
|
+
private readonly paddingCharDefault;
|
|
6
|
+
private readonly defaultEncoding;
|
|
7
|
+
private readonly bitLengthMap;
|
|
8
|
+
private readonly binaryLookup;
|
|
9
|
+
private readonly dduBinaryLookup;
|
|
10
|
+
private readonly dduBinaryLookupKr;
|
|
11
|
+
private readonly paddingRegex;
|
|
12
|
+
constructor(dduChar?: string[] | string, paddingChar?: string);
|
|
13
|
+
private getBitLength;
|
|
14
|
+
private getLargestPowerOfTwo;
|
|
15
|
+
private splitString;
|
|
16
|
+
private getSelectedSets;
|
|
17
|
+
private bufferToDduBinary;
|
|
18
|
+
private dduBinaryToBuffer;
|
|
19
|
+
encode(input: Buffer | string, options?: {
|
|
20
|
+
dduSetSymbol?: string;
|
|
21
|
+
encoding?: BufferEncoding;
|
|
22
|
+
usePowerOfTwo?: boolean;
|
|
23
|
+
}): string;
|
|
24
|
+
decode(input: string, options?: {
|
|
25
|
+
dduSetSymbol?: string;
|
|
26
|
+
encoding?: BufferEncoding;
|
|
27
|
+
usePowerOfTwo?: boolean;
|
|
28
|
+
}): string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Ddu64 = void 0;
|
|
4
|
+
class Ddu64 {
|
|
5
|
+
constructor(dduChar, paddingChar) {
|
|
6
|
+
this.dduCharDefault = [
|
|
7
|
+
"뜌",
|
|
8
|
+
"땨",
|
|
9
|
+
"이",
|
|
10
|
+
"우",
|
|
11
|
+
"야",
|
|
12
|
+
"!",
|
|
13
|
+
"?",
|
|
14
|
+
".",
|
|
15
|
+
];
|
|
16
|
+
this.paddingCharDefault = "뭐";
|
|
17
|
+
this.defaultEncoding = "utf-8";
|
|
18
|
+
this.bitLengthMap = new Map();
|
|
19
|
+
this.binaryLookup = Array.from({ length: 256 }, (_, i) => i.toString(2).padStart(8, "0"));
|
|
20
|
+
this.dduBinaryLookup = new Map();
|
|
21
|
+
this.dduBinaryLookupKr = new Map();
|
|
22
|
+
this.paddingRegex = new Map();
|
|
23
|
+
if (typeof dduChar === "string") {
|
|
24
|
+
const removeDuplicatesString = [...new Set(dduChar.trim())].join("");
|
|
25
|
+
this.dduChar = removeDuplicatesString.trim().split("");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.dduChar = dduChar || this.dduCharDefault;
|
|
29
|
+
}
|
|
30
|
+
this.paddingChar = paddingChar || this.paddingCharDefault;
|
|
31
|
+
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
|
|
32
|
+
this.dduCharDefault.forEach((char, index) => this.dduBinaryLookupKr.set(char, index));
|
|
33
|
+
this.paddingRegex.set("default", new RegExp(this.paddingChar, "g"));
|
|
34
|
+
this.paddingRegex.set("KR", new RegExp(this.paddingCharDefault, "g"));
|
|
35
|
+
}
|
|
36
|
+
getBitLength(setLength) {
|
|
37
|
+
let cached = this.bitLengthMap.get(setLength);
|
|
38
|
+
if (cached === undefined) {
|
|
39
|
+
cached = Math.ceil(Math.log2(setLength));
|
|
40
|
+
this.bitLengthMap.set(setLength, cached);
|
|
41
|
+
}
|
|
42
|
+
return cached;
|
|
43
|
+
}
|
|
44
|
+
getLargestPowerOfTwo(n) {
|
|
45
|
+
return Math.pow(2, Math.floor(Math.log2(n)));
|
|
46
|
+
}
|
|
47
|
+
*splitString(s, length) {
|
|
48
|
+
for (let i = 0; i < s.length; i += length) {
|
|
49
|
+
yield s.slice(i, Math.min(i + length, s.length));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
getSelectedSets(dduSetSymbol, usePowerOfTwo) {
|
|
53
|
+
const isKR = dduSetSymbol === "KR";
|
|
54
|
+
const dduSet = isKR ? this.dduCharDefault : this.dduChar;
|
|
55
|
+
const padChar = isKR ? this.paddingCharDefault : this.paddingChar;
|
|
56
|
+
let dduLength = dduSet.length;
|
|
57
|
+
if (usePowerOfTwo) {
|
|
58
|
+
const powerOfTwoLength = this.getLargestPowerOfTwo(dduLength);
|
|
59
|
+
dduLength = powerOfTwoLength;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
dduSet: usePowerOfTwo ? dduSet.slice(0, dduLength) : dduSet,
|
|
63
|
+
padChar,
|
|
64
|
+
dduLength,
|
|
65
|
+
bitLength: this.getBitLength(dduLength),
|
|
66
|
+
lookupTable: isKR ? this.dduBinaryLookupKr : this.dduBinaryLookup,
|
|
67
|
+
paddingRegExp: this.paddingRegex.get(isKR ? "KR" : "default"),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
bufferToDduBinary(input, bitLength) {
|
|
71
|
+
const encodedBin = input.reduce((acc, byte) => acc + this.binaryLookup[byte], "");
|
|
72
|
+
if (encodedBin.length === 0) {
|
|
73
|
+
return { dduBinary: [], padding: 0 };
|
|
74
|
+
}
|
|
75
|
+
const dduBinary = Array.from(this.splitString(encodedBin, bitLength));
|
|
76
|
+
const padding = bitLength - dduBinary[dduBinary.length - 1].length;
|
|
77
|
+
if (padding > 0) {
|
|
78
|
+
dduBinary[dduBinary.length - 1] += "0".repeat(padding);
|
|
79
|
+
}
|
|
80
|
+
return { dduBinary, padding };
|
|
81
|
+
}
|
|
82
|
+
dduBinaryToBuffer(decodedBin, paddingBits) {
|
|
83
|
+
if (paddingBits > 0) {
|
|
84
|
+
decodedBin = decodedBin.slice(0, -paddingBits);
|
|
85
|
+
}
|
|
86
|
+
const buffer = [];
|
|
87
|
+
for (let i = 0; i < decodedBin.length; i += 8) {
|
|
88
|
+
buffer.push(parseInt(decodedBin.slice(i, i + 8), 2));
|
|
89
|
+
}
|
|
90
|
+
return Buffer.from(buffer);
|
|
91
|
+
}
|
|
92
|
+
encode(input, options = {}) {
|
|
93
|
+
const { dduSetSymbol = "default", encoding = this.defaultEncoding, usePowerOfTwo = true, } = options;
|
|
94
|
+
const bufferInput = typeof input === "string" ? Buffer.from(input, encoding) : input;
|
|
95
|
+
const { dduSet, padChar, dduLength, bitLength } = this.getSelectedSets(dduSetSymbol, usePowerOfTwo);
|
|
96
|
+
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, bitLength);
|
|
97
|
+
let resultString = "";
|
|
98
|
+
for (const binaryChunk of dduBinary) {
|
|
99
|
+
const charInt = parseInt(binaryChunk, 2);
|
|
100
|
+
if (usePowerOfTwo) {
|
|
101
|
+
resultString += dduSet[charInt];
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const quotient = Math.floor(charInt / dduLength);
|
|
105
|
+
const remainder = charInt % dduLength;
|
|
106
|
+
resultString += dduSet[quotient] + dduSet[remainder];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// 패딩 비트 수를 padChar + 숫자로 명시 (모든 모드)
|
|
110
|
+
if (padding > 0) {
|
|
111
|
+
resultString += padChar + padding;
|
|
112
|
+
}
|
|
113
|
+
return resultString;
|
|
114
|
+
}
|
|
115
|
+
decode(input, options = {}) {
|
|
116
|
+
const { dduSetSymbol = "default", encoding = this.defaultEncoding, usePowerOfTwo = true, } = options;
|
|
117
|
+
const { dduSet, dduLength, bitLength, lookupTable, padChar, paddingRegExp } = this.getSelectedSets(dduSetSymbol, usePowerOfTwo);
|
|
118
|
+
// 패딩 정보 추출: padChar 이후의 숫자가 패딩 비트 수 (모든 모드)
|
|
119
|
+
let paddingBits = 0;
|
|
120
|
+
const padCharIndex = input.indexOf(padChar);
|
|
121
|
+
if (padCharIndex >= 0) {
|
|
122
|
+
const paddingStr = input.substring(padCharIndex + padChar.length);
|
|
123
|
+
paddingBits = parseInt(paddingStr) || 0;
|
|
124
|
+
input = input.substring(0, padCharIndex);
|
|
125
|
+
}
|
|
126
|
+
let dduBinary = "";
|
|
127
|
+
for (let i = 0; i < input.length; i += usePowerOfTwo ? 1 : 2) {
|
|
128
|
+
const firstChar = input[i];
|
|
129
|
+
const secondChar = input[i + 1];
|
|
130
|
+
if (usePowerOfTwo) {
|
|
131
|
+
const charIndex = lookupTable.get(firstChar);
|
|
132
|
+
if (charIndex === undefined)
|
|
133
|
+
throw new Error(`Invalid character: ${firstChar}`);
|
|
134
|
+
dduBinary += charIndex.toString(2).padStart(bitLength, "0");
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const firstIndex = lookupTable.get(firstChar);
|
|
138
|
+
const secondIndex = lookupTable.get(secondChar);
|
|
139
|
+
if (firstIndex === undefined || secondIndex === undefined)
|
|
140
|
+
throw new Error(`Invalid character: ${firstChar} or ${secondChar}`);
|
|
141
|
+
const value = firstIndex * dduLength + secondIndex;
|
|
142
|
+
dduBinary += value.toString(2).padStart(bitLength, "0");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const decoded = this.dduBinaryToBuffer(dduBinary, paddingBits);
|
|
146
|
+
return decoded.toString(encoding);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
exports.Ddu64 = Ddu64;
|