@ddunigma/node 1.1.2 → 1.1.3
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/dist/cjs/encoders/Ddu64.d.ts +10 -1
- package/dist/cjs/encoders/Ddu64.js +193 -72
- package/dist/mjs/encoders/Ddu64.d.ts +10 -1
- package/dist/mjs/encoders/Ddu64.js +194 -69
- package/package.json +1 -1
|
@@ -9,7 +9,9 @@ export declare class Ddu64 extends BaseDdu {
|
|
|
9
9
|
protected readonly encoding: BufferEncoding;
|
|
10
10
|
protected readonly dduBinaryLookup: Map<string, number>;
|
|
11
11
|
private readonly isPredefinedCharSet;
|
|
12
|
-
private readonly
|
|
12
|
+
private readonly effectiveBitLength;
|
|
13
|
+
private readonly binaryChunkToIntFn;
|
|
14
|
+
private readonly indexToBinaryCache;
|
|
13
15
|
constructor(dduChar?: string[] | string, paddingChar?: string, dduOptions?: DduConstructorOptions);
|
|
14
16
|
/**
|
|
15
17
|
* charset 초기화 (커스텀 또는 미리 정의된 charset)
|
|
@@ -29,12 +31,19 @@ export declare class Ddu64 extends BaseDdu {
|
|
|
29
31
|
private processDefaultCharSet;
|
|
30
32
|
/**
|
|
31
33
|
* fallback charset 가져오기
|
|
34
|
+
* 우선순위: dduOptions.dduSetSymbol > dduDefaultConstructorOptions.dduSetSymbol > DduSetSymbol.ONECHARSET
|
|
32
35
|
*/
|
|
33
36
|
private getFallbackCharSet;
|
|
37
|
+
private normalizeCharSet;
|
|
38
|
+
private getBinaryFromIndex;
|
|
34
39
|
/**
|
|
35
40
|
* string 또는 배열을 문자 배열로 변환
|
|
36
41
|
*/
|
|
37
42
|
private convertToCharArray;
|
|
43
|
+
/**
|
|
44
|
+
* 숫자가 2의 제곱수인지 확인
|
|
45
|
+
*/
|
|
46
|
+
private isPowerOfTwo;
|
|
38
47
|
/**
|
|
39
48
|
* usePowerOfTwo 옵션 적용
|
|
40
49
|
*/
|
|
@@ -9,7 +9,6 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
9
9
|
var _a, _b, _c;
|
|
10
10
|
super();
|
|
11
11
|
this.dduBinaryLookup = new Map();
|
|
12
|
-
this.binaryToIntLookup = new Map();
|
|
13
12
|
// charset 초기화
|
|
14
13
|
let finalCharSet;
|
|
15
14
|
let finalPadding;
|
|
@@ -18,7 +17,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
18
17
|
let isPredefined = false;
|
|
19
18
|
try {
|
|
20
19
|
const result = this.initializeCharSet(dduChar, paddingChar, dduOptions);
|
|
21
|
-
finalCharSet = result.finalCharSet;
|
|
20
|
+
finalCharSet = [...result.finalCharSet];
|
|
22
21
|
finalPadding = result.finalPadding;
|
|
23
22
|
requiredLength = result.requiredLength;
|
|
24
23
|
bitLength = result.bitLength;
|
|
@@ -31,69 +30,70 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
31
30
|
}
|
|
32
31
|
// fallback 처리
|
|
33
32
|
const fallbackResult = this.getFallbackCharSet(dduOptions);
|
|
34
|
-
finalCharSet = fallbackResult.finalCharSet;
|
|
33
|
+
finalCharSet = [...fallbackResult.finalCharSet];
|
|
35
34
|
finalPadding = fallbackResult.finalPadding;
|
|
36
35
|
requiredLength = fallbackResult.requiredLength;
|
|
37
36
|
bitLength = fallbackResult.bitLength;
|
|
38
37
|
isPredefined = true; // fallback도 predefined charset 사용
|
|
39
38
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
// 중복 검사
|
|
57
|
-
const uniqueChars = new Set(finalCharSet);
|
|
58
|
-
if (uniqueChars.size !== finalCharSet.length) {
|
|
59
|
-
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${finalCharSet.length}`);
|
|
60
|
-
}
|
|
61
|
-
// 패딩 문자가 문자셋에 포함되어 있는지 검사
|
|
62
|
-
if (uniqueChars.has(finalPadding)) {
|
|
63
|
-
throw new Error(`Padding character "${finalPadding}" cannot be in the character set`);
|
|
64
|
-
}
|
|
65
|
-
// 2글자 이상의 문자셋인 경우 조합 중복 검사 (미리 정의된 charset은 건너뜀)
|
|
66
|
-
if (this.charLength >= 2 && !isPredefined) {
|
|
67
|
-
this.validateCombinationDuplicates(finalCharSet, finalPadding, requiredLength);
|
|
68
|
-
}
|
|
69
|
-
// 앞에서부터 정확히 requiredLength개만 사용
|
|
70
|
-
this.dduChar = finalCharSet.slice(0, requiredLength);
|
|
71
|
-
this.paddingChar = finalPadding;
|
|
72
|
-
this.bitLength = bitLength;
|
|
73
|
-
this.isPredefinedCharSet = isPredefined;
|
|
74
|
-
// requiredLength가 2의 제곱수인 경우 자동으로 usePowerOfTwo 활성화
|
|
75
|
-
const isPowerOfTwo = (requiredLength & (requiredLength - 1)) === 0 && requiredLength > 0;
|
|
76
|
-
this.usePowerOfTwo = isPowerOfTwo || ((_b = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) !== null && _b !== void 0 ? _b : true);
|
|
77
|
-
// encoding 설정
|
|
78
|
-
this.encoding = (_c = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.encoding) !== null && _c !== void 0 ? _c : this.defaultEncoding;
|
|
79
|
-
// 룩업 테이블 생성
|
|
80
|
-
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
|
|
81
|
-
// parseInt 최적화: 이진수 문자열 → 정수 룩업 테이블 생성
|
|
82
|
-
// 가능한 모든 비트 패턴을 미리 계산
|
|
83
|
-
// 성능 최적화: bitLength가 12 이하일 때만 룩업 테이블 사용 (4096 엔트리 이하)
|
|
84
|
-
// 그 이상은 parseInt가 더 효율적 (캐시 미스 감소)
|
|
39
|
+
const shouldThrowError = (_b = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.useBuildErrorReturn) !== null && _b !== void 0 ? _b : false;
|
|
40
|
+
const normalized = this.normalizeCharSet({
|
|
41
|
+
charSet: finalCharSet,
|
|
42
|
+
padding: finalPadding,
|
|
43
|
+
requiredLength,
|
|
44
|
+
bitLength,
|
|
45
|
+
isPredefined,
|
|
46
|
+
shouldThrowError,
|
|
47
|
+
dduOptions,
|
|
48
|
+
});
|
|
49
|
+
this.dduChar = normalized.charSet;
|
|
50
|
+
this.paddingChar = normalized.padding;
|
|
51
|
+
this.charLength = normalized.charLength;
|
|
52
|
+
this.bitLength = normalized.bitLength;
|
|
53
|
+
this.isPredefinedCharSet = normalized.isPredefined;
|
|
85
54
|
const dduLength = this.dduChar.length;
|
|
86
|
-
|
|
55
|
+
this.usePowerOfTwo = dduLength > 0 && (dduLength & (dduLength - 1)) === 0;
|
|
56
|
+
this.effectiveBitLength = this.usePowerOfTwo
|
|
87
57
|
? this.bitLength
|
|
88
58
|
: this.getBitLength(dduLength);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
59
|
+
const canUseBitwise = this.effectiveBitLength <= 26;
|
|
60
|
+
if (canUseBitwise) {
|
|
61
|
+
this.binaryChunkToIntFn = (chunk) => {
|
|
62
|
+
let value = 0;
|
|
63
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
64
|
+
value = (value << 1) | (chunk.charCodeAt(i) & 1);
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.binaryChunkToIntFn = (chunk) => {
|
|
71
|
+
let value = 0;
|
|
72
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
73
|
+
value = value * 2 + (chunk.charCodeAt(i) & 1);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
this.encoding = (_c = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.encoding) !== null && _c !== void 0 ? _c : this.defaultEncoding;
|
|
79
|
+
this.dduChar.forEach((char, index) => {
|
|
80
|
+
this.dduBinaryLookup.set(char, index);
|
|
81
|
+
});
|
|
82
|
+
if (this.charLength >= 2 && !this.isPredefinedCharSet) {
|
|
83
|
+
this.validateCombinationDuplicates(this.dduChar, this.paddingChar, this.dduChar.length);
|
|
84
|
+
}
|
|
85
|
+
if (this.effectiveBitLength <= 16) {
|
|
86
|
+
const cacheSize = 1 << this.effectiveBitLength;
|
|
87
|
+
this.indexToBinaryCache = new Array(cacheSize);
|
|
88
|
+
for (let i = 0; i < cacheSize; i++) {
|
|
89
|
+
this.indexToBinaryCache[i] = i
|
|
90
|
+
.toString(2)
|
|
91
|
+
.padStart(this.effectiveBitLength, "0");
|
|
94
92
|
}
|
|
95
93
|
}
|
|
96
|
-
|
|
94
|
+
else {
|
|
95
|
+
this.indexToBinaryCache = null;
|
|
96
|
+
}
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
99
|
* charset 초기화 (커스텀 또는 미리 정의된 charset)
|
|
@@ -116,7 +116,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
116
116
|
* 커스텀 charset 처리
|
|
117
117
|
*/
|
|
118
118
|
processCustomCharSet(dduChar, paddingChar, dduOptions) {
|
|
119
|
-
var _a
|
|
119
|
+
var _a;
|
|
120
120
|
if (!paddingChar) {
|
|
121
121
|
throw new Error("paddingChar is required when dduChar or dduOptions.dduChar is provided");
|
|
122
122
|
}
|
|
@@ -127,7 +127,10 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
127
127
|
throw new Error(`dduChar must be at least ${dduCharLength} characters long. Provided: ${dduCharArray.length}`);
|
|
128
128
|
}
|
|
129
129
|
// usePowerOfTwo 처리
|
|
130
|
-
|
|
130
|
+
// charArray의 길이가 2의 제곱수이거나 option에 usePowerOfTwo가 명시적으로 true일 때만 적용
|
|
131
|
+
const shouldUsePowerOfTwo = (dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === true ||
|
|
132
|
+
((dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === undefined && this.isPowerOfTwo(dduCharLength));
|
|
133
|
+
const result = this.applyPowerOfTwoOption(dduCharArray, paddingChar, dduCharLength, shouldUsePowerOfTwo);
|
|
131
134
|
return Object.assign(Object.assign({}, result), { isPredefined: false // 커스텀 charset은 검증 필요
|
|
132
135
|
});
|
|
133
136
|
}
|
|
@@ -135,14 +138,16 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
135
138
|
* 미리 정의된 charset 처리
|
|
136
139
|
*/
|
|
137
140
|
processPredefinedCharSet(symbol, dduOptions) {
|
|
138
|
-
var _a;
|
|
139
141
|
const fixedCharSet = (0, charSets_1.getCharSet)(symbol);
|
|
140
142
|
if (!fixedCharSet) {
|
|
141
143
|
throw new Error(`CharSet with symbol ${symbol} not found`);
|
|
142
144
|
}
|
|
143
145
|
const { charSet, paddingChar, maxRequiredLength, bitLength } = fixedCharSet;
|
|
144
146
|
// usePowerOfTwo 처리
|
|
145
|
-
|
|
147
|
+
// charArray의 길이가 2의 제곱수이거나 option에 usePowerOfTwo가 명시적으로 true일 때만 적용
|
|
148
|
+
const shouldUsePowerOfTwo = (dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === true ||
|
|
149
|
+
((dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === undefined && this.isPowerOfTwo(maxRequiredLength));
|
|
150
|
+
if (shouldUsePowerOfTwo) {
|
|
146
151
|
const result = this.applyPowerOfTwoOption(charSet, paddingChar, maxRequiredLength, true);
|
|
147
152
|
return Object.assign(Object.assign({}, result), { isPredefined: true // 미리 정의된 charset은 검증 패스
|
|
148
153
|
});
|
|
@@ -175,13 +180,25 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
175
180
|
}
|
|
176
181
|
/**
|
|
177
182
|
* fallback charset 가져오기
|
|
183
|
+
* 우선순위: dduOptions.dduSetSymbol > dduDefaultConstructorOptions.dduSetSymbol > DduSetSymbol.ONECHARSET
|
|
178
184
|
*/
|
|
179
185
|
getFallbackCharSet(dduOptions) {
|
|
180
186
|
var _a, _b;
|
|
181
|
-
|
|
187
|
+
// 우선순위에 따라 fallback symbol 결정
|
|
188
|
+
const fallbackSymbol = (_b = (_a = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduSetSymbol) !== null && _a !== void 0 ? _a : types_1.dduDefaultConstructorOptions.dduSetSymbol) !== null && _b !== void 0 ? _b : types_1.DduSetSymbol.ONECHARSET; // DDU 대신 ONECHARSET 사용
|
|
182
189
|
const fixedCharSet = (0, charSets_1.getCharSet)(fallbackSymbol);
|
|
183
190
|
if (!fixedCharSet) {
|
|
184
|
-
|
|
191
|
+
// 최후의 fallback: ONECHARSET
|
|
192
|
+
const lastResort = (0, charSets_1.getCharSet)(types_1.DduSetSymbol.ONECHARSET);
|
|
193
|
+
if (!lastResort) {
|
|
194
|
+
throw new Error(`Critical: No fallback CharSet available`);
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
finalCharSet: lastResort.charSet,
|
|
198
|
+
finalPadding: lastResort.paddingChar,
|
|
199
|
+
requiredLength: lastResort.maxRequiredLength,
|
|
200
|
+
bitLength: lastResort.bitLength,
|
|
201
|
+
};
|
|
185
202
|
}
|
|
186
203
|
return {
|
|
187
204
|
finalCharSet: fixedCharSet.charSet,
|
|
@@ -190,6 +207,110 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
190
207
|
bitLength: fixedCharSet.bitLength,
|
|
191
208
|
};
|
|
192
209
|
}
|
|
210
|
+
normalizeCharSet(params) {
|
|
211
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
212
|
+
let currentCharSet = [...params.charSet];
|
|
213
|
+
let currentPadding = params.padding;
|
|
214
|
+
let currentRequiredLength = params.requiredLength;
|
|
215
|
+
let currentBitLength = params.bitLength;
|
|
216
|
+
let currentIsPredefined = params.isPredefined;
|
|
217
|
+
const { shouldThrowError, dduOptions } = params;
|
|
218
|
+
let fallbackCache = null;
|
|
219
|
+
const getFallback = () => {
|
|
220
|
+
if (!fallbackCache) {
|
|
221
|
+
const fallback = this.getFallbackCharSet(dduOptions);
|
|
222
|
+
fallbackCache = {
|
|
223
|
+
finalCharSet: [...fallback.finalCharSet],
|
|
224
|
+
finalPadding: fallback.finalPadding,
|
|
225
|
+
requiredLength: fallback.requiredLength,
|
|
226
|
+
bitLength: fallback.bitLength,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return fallbackCache;
|
|
230
|
+
};
|
|
231
|
+
const applyFallback = () => {
|
|
232
|
+
const fallback = getFallback();
|
|
233
|
+
currentCharSet = [...fallback.finalCharSet];
|
|
234
|
+
currentPadding = fallback.finalPadding;
|
|
235
|
+
currentRequiredLength = fallback.requiredLength;
|
|
236
|
+
currentBitLength = fallback.bitLength;
|
|
237
|
+
currentIsPredefined = true;
|
|
238
|
+
};
|
|
239
|
+
const ensureMinLength = () => {
|
|
240
|
+
if (currentCharSet.length < currentRequiredLength) {
|
|
241
|
+
if (shouldThrowError) {
|
|
242
|
+
throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: ${currentCharSet.length}`);
|
|
243
|
+
}
|
|
244
|
+
applyFallback();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
if (!currentIsPredefined) {
|
|
248
|
+
const uniqueChars = new Set(currentCharSet);
|
|
249
|
+
if (uniqueChars.size !== currentCharSet.length) {
|
|
250
|
+
if (shouldThrowError) {
|
|
251
|
+
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${currentCharSet.length}`);
|
|
252
|
+
}
|
|
253
|
+
currentCharSet = Array.from(uniqueChars);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
ensureMinLength();
|
|
257
|
+
let charLength = (_b = (_a = currentCharSet[0]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
258
|
+
if (charLength === 0) {
|
|
259
|
+
if (shouldThrowError) {
|
|
260
|
+
throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: ${currentCharSet.length}`);
|
|
261
|
+
}
|
|
262
|
+
applyFallback();
|
|
263
|
+
charLength = (_d = (_c = currentCharSet[0]) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
|
|
264
|
+
}
|
|
265
|
+
const invalidIndex = currentCharSet.findIndex((char) => char.length !== charLength);
|
|
266
|
+
if (invalidIndex !== -1) {
|
|
267
|
+
if (shouldThrowError) {
|
|
268
|
+
throw new Error(`All characters must have the same length. Expected: ${charLength}, but character at index ${invalidIndex} ("${currentCharSet[invalidIndex]}") has length ${currentCharSet[invalidIndex].length}`);
|
|
269
|
+
}
|
|
270
|
+
currentCharSet = currentCharSet.filter((char) => char.length === charLength);
|
|
271
|
+
ensureMinLength();
|
|
272
|
+
charLength = (_f = (_e = currentCharSet[0]) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0;
|
|
273
|
+
}
|
|
274
|
+
if (!currentCharSet.length) {
|
|
275
|
+
if (shouldThrowError) {
|
|
276
|
+
throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: 0`);
|
|
277
|
+
}
|
|
278
|
+
applyFallback();
|
|
279
|
+
charLength = (_h = (_g = currentCharSet[0]) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : 0;
|
|
280
|
+
}
|
|
281
|
+
if (currentPadding.length !== charLength) {
|
|
282
|
+
if (shouldThrowError) {
|
|
283
|
+
throw new Error(`Padding character must have the same length as the characters. Expected: ${charLength}, but padding character has length ${currentPadding.length}`);
|
|
284
|
+
}
|
|
285
|
+
applyFallback();
|
|
286
|
+
charLength = (_k = (_j = currentCharSet[0]) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : 0;
|
|
287
|
+
}
|
|
288
|
+
const charSetLookup = new Set(currentCharSet);
|
|
289
|
+
if (charSetLookup.has(currentPadding)) {
|
|
290
|
+
if (shouldThrowError) {
|
|
291
|
+
throw new Error(`Padding character "${currentPadding}" cannot be in the character set`);
|
|
292
|
+
}
|
|
293
|
+
currentCharSet = currentCharSet.filter((char) => char !== currentPadding);
|
|
294
|
+
ensureMinLength();
|
|
295
|
+
charLength = (_m = (_l = currentCharSet[0]) === null || _l === void 0 ? void 0 : _l.length) !== null && _m !== void 0 ? _m : 0;
|
|
296
|
+
}
|
|
297
|
+
currentCharSet = currentCharSet.slice(0, currentRequiredLength);
|
|
298
|
+
return {
|
|
299
|
+
charSet: currentCharSet,
|
|
300
|
+
padding: currentPadding,
|
|
301
|
+
requiredLength: currentRequiredLength,
|
|
302
|
+
bitLength: currentBitLength,
|
|
303
|
+
isPredefined: currentIsPredefined,
|
|
304
|
+
charLength,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
getBinaryFromIndex(index) {
|
|
308
|
+
const cache = this.indexToBinaryCache;
|
|
309
|
+
if (cache && index < cache.length) {
|
|
310
|
+
return cache[index];
|
|
311
|
+
}
|
|
312
|
+
return index.toString(2).padStart(this.effectiveBitLength, "0");
|
|
313
|
+
}
|
|
193
314
|
/**
|
|
194
315
|
* string 또는 배열을 문자 배열로 변환
|
|
195
316
|
*/
|
|
@@ -199,6 +320,12 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
199
320
|
}
|
|
200
321
|
return input;
|
|
201
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* 숫자가 2의 제곱수인지 확인
|
|
325
|
+
*/
|
|
326
|
+
isPowerOfTwo(n) {
|
|
327
|
+
return n > 0 && (n & (n - 1)) === 0;
|
|
328
|
+
}
|
|
202
329
|
/**
|
|
203
330
|
* usePowerOfTwo 옵션 적용
|
|
204
331
|
*/
|
|
@@ -275,22 +402,18 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
275
402
|
}
|
|
276
403
|
}
|
|
277
404
|
encode(input, _options) {
|
|
278
|
-
var _a;
|
|
279
405
|
// options는 구버전 호환성을 위해 유지하지만 사용하지 않음
|
|
280
406
|
const bufferInput = typeof input === "string" ? Buffer.from(input, this.encoding) : input;
|
|
281
407
|
// 생성자에서 설정된 값 사용
|
|
282
408
|
const dduLength = this.dduChar.length;
|
|
283
|
-
const effectiveBitLength = this.
|
|
284
|
-
? this.bitLength
|
|
285
|
-
: this.getBitLength(dduLength);
|
|
409
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
286
410
|
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, effectiveBitLength);
|
|
287
411
|
// 문자열 연결 최적화: Array + join 사용
|
|
288
412
|
const resultParts = new Array(dduBinary.length);
|
|
289
413
|
// 각 비트 청크를 변환
|
|
290
414
|
for (let i = 0; i < dduBinary.length; i++) {
|
|
291
415
|
const binaryChunk = dduBinary[i];
|
|
292
|
-
|
|
293
|
-
const charInt = (_a = this.binaryToIntLookup.get(binaryChunk)) !== null && _a !== void 0 ? _a : parseInt(binaryChunk, 2);
|
|
416
|
+
const charInt = this.binaryChunkToIntFn(binaryChunk);
|
|
294
417
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
295
418
|
// 가변 길이 조합 인코딩
|
|
296
419
|
const quotient = Math.floor(charInt / dduLength);
|
|
@@ -322,9 +445,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
322
445
|
let dduBinary = "";
|
|
323
446
|
// 생성자에서 설정된 값 사용
|
|
324
447
|
const dduLength = this.dduChar.length;
|
|
325
|
-
const effectiveBitLength = this.
|
|
326
|
-
? this.bitLength
|
|
327
|
-
: this.getBitLength(dduLength);
|
|
448
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
328
449
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
329
450
|
// 가변 길이 조합 디코딩 (2개씩 읽음)
|
|
330
451
|
for (let i = 0; i < input.length; i += 2) {
|
|
@@ -335,7 +456,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
335
456
|
if (firstIndex === undefined || secondIndex === undefined)
|
|
336
457
|
throw new Error(`Invalid character: ${firstChar} or ${secondChar}`);
|
|
337
458
|
const value = firstIndex * dduLength + secondIndex;
|
|
338
|
-
dduBinary +=
|
|
459
|
+
dduBinary += this.getBinaryFromIndex(value);
|
|
339
460
|
}
|
|
340
461
|
}
|
|
341
462
|
else {
|
|
@@ -345,7 +466,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
345
466
|
const charIndex = this.dduBinaryLookup.get(charChunk);
|
|
346
467
|
if (charIndex === undefined)
|
|
347
468
|
throw new Error(`Invalid character: ${charChunk}`);
|
|
348
|
-
dduBinary +=
|
|
469
|
+
dduBinary += this.getBinaryFromIndex(charIndex);
|
|
349
470
|
}
|
|
350
471
|
}
|
|
351
472
|
const decoded = this.dduBinaryToBuffer(dduBinary, paddingBits);
|
|
@@ -9,7 +9,9 @@ export declare class Ddu64 extends BaseDdu {
|
|
|
9
9
|
protected readonly encoding: BufferEncoding;
|
|
10
10
|
protected readonly dduBinaryLookup: Map<string, number>;
|
|
11
11
|
private readonly isPredefinedCharSet;
|
|
12
|
-
private readonly
|
|
12
|
+
private readonly effectiveBitLength;
|
|
13
|
+
private readonly binaryChunkToIntFn;
|
|
14
|
+
private readonly indexToBinaryCache;
|
|
13
15
|
constructor(dduChar?: string[] | string, paddingChar?: string, dduOptions?: DduConstructorOptions);
|
|
14
16
|
/**
|
|
15
17
|
* charset 초기화 (커스텀 또는 미리 정의된 charset)
|
|
@@ -29,12 +31,19 @@ export declare class Ddu64 extends BaseDdu {
|
|
|
29
31
|
private processDefaultCharSet;
|
|
30
32
|
/**
|
|
31
33
|
* fallback charset 가져오기
|
|
34
|
+
* 우선순위: dduOptions.dduSetSymbol > dduDefaultConstructorOptions.dduSetSymbol > DduSetSymbol.ONECHARSET
|
|
32
35
|
*/
|
|
33
36
|
private getFallbackCharSet;
|
|
37
|
+
private normalizeCharSet;
|
|
38
|
+
private getBinaryFromIndex;
|
|
34
39
|
/**
|
|
35
40
|
* string 또는 배열을 문자 배열로 변환
|
|
36
41
|
*/
|
|
37
42
|
private convertToCharArray;
|
|
43
|
+
/**
|
|
44
|
+
* 숫자가 2의 제곱수인지 확인
|
|
45
|
+
*/
|
|
46
|
+
private isPowerOfTwo;
|
|
38
47
|
/**
|
|
39
48
|
* usePowerOfTwo 옵션 적용
|
|
40
49
|
*/
|
|
@@ -10,7 +10,9 @@ export class Ddu64 extends BaseDdu {
|
|
|
10
10
|
encoding;
|
|
11
11
|
dduBinaryLookup = new Map();
|
|
12
12
|
isPredefinedCharSet;
|
|
13
|
-
|
|
13
|
+
effectiveBitLength;
|
|
14
|
+
binaryChunkToIntFn;
|
|
15
|
+
indexToBinaryCache;
|
|
14
16
|
constructor(dduChar, paddingChar, dduOptions) {
|
|
15
17
|
super();
|
|
16
18
|
// charset 초기화
|
|
@@ -21,7 +23,7 @@ export class Ddu64 extends BaseDdu {
|
|
|
21
23
|
let isPredefined = false;
|
|
22
24
|
try {
|
|
23
25
|
const result = this.initializeCharSet(dduChar, paddingChar, dduOptions);
|
|
24
|
-
finalCharSet = result.finalCharSet;
|
|
26
|
+
finalCharSet = [...result.finalCharSet];
|
|
25
27
|
finalPadding = result.finalPadding;
|
|
26
28
|
requiredLength = result.requiredLength;
|
|
27
29
|
bitLength = result.bitLength;
|
|
@@ -34,69 +36,70 @@ export class Ddu64 extends BaseDdu {
|
|
|
34
36
|
}
|
|
35
37
|
// fallback 처리
|
|
36
38
|
const fallbackResult = this.getFallbackCharSet(dduOptions);
|
|
37
|
-
finalCharSet = fallbackResult.finalCharSet;
|
|
39
|
+
finalCharSet = [...fallbackResult.finalCharSet];
|
|
38
40
|
finalPadding = fallbackResult.finalPadding;
|
|
39
41
|
requiredLength = fallbackResult.requiredLength;
|
|
40
42
|
bitLength = fallbackResult.bitLength;
|
|
41
43
|
isPredefined = true; // fallback도 predefined charset 사용
|
|
42
44
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
// 중복 검사
|
|
60
|
-
const uniqueChars = new Set(finalCharSet);
|
|
61
|
-
if (uniqueChars.size !== finalCharSet.length) {
|
|
62
|
-
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${finalCharSet.length}`);
|
|
63
|
-
}
|
|
64
|
-
// 패딩 문자가 문자셋에 포함되어 있는지 검사
|
|
65
|
-
if (uniqueChars.has(finalPadding)) {
|
|
66
|
-
throw new Error(`Padding character "${finalPadding}" cannot be in the character set`);
|
|
67
|
-
}
|
|
68
|
-
// 2글자 이상의 문자셋인 경우 조합 중복 검사 (미리 정의된 charset은 건너뜀)
|
|
69
|
-
if (this.charLength >= 2 && !isPredefined) {
|
|
70
|
-
this.validateCombinationDuplicates(finalCharSet, finalPadding, requiredLength);
|
|
71
|
-
}
|
|
72
|
-
// 앞에서부터 정확히 requiredLength개만 사용
|
|
73
|
-
this.dduChar = finalCharSet.slice(0, requiredLength);
|
|
74
|
-
this.paddingChar = finalPadding;
|
|
75
|
-
this.bitLength = bitLength;
|
|
76
|
-
this.isPredefinedCharSet = isPredefined;
|
|
77
|
-
// requiredLength가 2의 제곱수인 경우 자동으로 usePowerOfTwo 활성화
|
|
78
|
-
const isPowerOfTwo = (requiredLength & (requiredLength - 1)) === 0 && requiredLength > 0;
|
|
79
|
-
this.usePowerOfTwo = isPowerOfTwo || (dduOptions?.usePowerOfTwo ?? true);
|
|
80
|
-
// encoding 설정
|
|
81
|
-
this.encoding = dduOptions?.encoding ?? this.defaultEncoding;
|
|
82
|
-
// 룩업 테이블 생성
|
|
83
|
-
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
|
|
84
|
-
// parseInt 최적화: 이진수 문자열 → 정수 룩업 테이블 생성
|
|
85
|
-
// 가능한 모든 비트 패턴을 미리 계산
|
|
86
|
-
// 성능 최적화: bitLength가 12 이하일 때만 룩업 테이블 사용 (4096 엔트리 이하)
|
|
87
|
-
// 그 이상은 parseInt가 더 효율적 (캐시 미스 감소)
|
|
45
|
+
const shouldThrowError = dduOptions?.useBuildErrorReturn ?? false;
|
|
46
|
+
const normalized = this.normalizeCharSet({
|
|
47
|
+
charSet: finalCharSet,
|
|
48
|
+
padding: finalPadding,
|
|
49
|
+
requiredLength,
|
|
50
|
+
bitLength,
|
|
51
|
+
isPredefined,
|
|
52
|
+
shouldThrowError,
|
|
53
|
+
dduOptions,
|
|
54
|
+
});
|
|
55
|
+
this.dduChar = normalized.charSet;
|
|
56
|
+
this.paddingChar = normalized.padding;
|
|
57
|
+
this.charLength = normalized.charLength;
|
|
58
|
+
this.bitLength = normalized.bitLength;
|
|
59
|
+
this.isPredefinedCharSet = normalized.isPredefined;
|
|
88
60
|
const dduLength = this.dduChar.length;
|
|
89
|
-
|
|
61
|
+
this.usePowerOfTwo = dduLength > 0 && (dduLength & (dduLength - 1)) === 0;
|
|
62
|
+
this.effectiveBitLength = this.usePowerOfTwo
|
|
90
63
|
? this.bitLength
|
|
91
64
|
: this.getBitLength(dduLength);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
65
|
+
const canUseBitwise = this.effectiveBitLength <= 26;
|
|
66
|
+
if (canUseBitwise) {
|
|
67
|
+
this.binaryChunkToIntFn = (chunk) => {
|
|
68
|
+
let value = 0;
|
|
69
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
70
|
+
value = (value << 1) | (chunk.charCodeAt(i) & 1);
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.binaryChunkToIntFn = (chunk) => {
|
|
77
|
+
let value = 0;
|
|
78
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
79
|
+
value = value * 2 + (chunk.charCodeAt(i) & 1);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
this.encoding = dduOptions?.encoding ?? this.defaultEncoding;
|
|
85
|
+
this.dduChar.forEach((char, index) => {
|
|
86
|
+
this.dduBinaryLookup.set(char, index);
|
|
87
|
+
});
|
|
88
|
+
if (this.charLength >= 2 && !this.isPredefinedCharSet) {
|
|
89
|
+
this.validateCombinationDuplicates(this.dduChar, this.paddingChar, this.dduChar.length);
|
|
90
|
+
}
|
|
91
|
+
if (this.effectiveBitLength <= 16) {
|
|
92
|
+
const cacheSize = 1 << this.effectiveBitLength;
|
|
93
|
+
this.indexToBinaryCache = new Array(cacheSize);
|
|
94
|
+
for (let i = 0; i < cacheSize; i++) {
|
|
95
|
+
this.indexToBinaryCache[i] = i
|
|
96
|
+
.toString(2)
|
|
97
|
+
.padStart(this.effectiveBitLength, "0");
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
|
-
|
|
100
|
+
else {
|
|
101
|
+
this.indexToBinaryCache = null;
|
|
102
|
+
}
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
102
105
|
* charset 초기화 (커스텀 또는 미리 정의된 charset)
|
|
@@ -129,7 +132,10 @@ export class Ddu64 extends BaseDdu {
|
|
|
129
132
|
throw new Error(`dduChar must be at least ${dduCharLength} characters long. Provided: ${dduCharArray.length}`);
|
|
130
133
|
}
|
|
131
134
|
// usePowerOfTwo 처리
|
|
132
|
-
|
|
135
|
+
// charArray의 길이가 2의 제곱수이거나 option에 usePowerOfTwo가 명시적으로 true일 때만 적용
|
|
136
|
+
const shouldUsePowerOfTwo = dduOptions?.usePowerOfTwo === true ||
|
|
137
|
+
(dduOptions?.usePowerOfTwo === undefined && this.isPowerOfTwo(dduCharLength));
|
|
138
|
+
const result = this.applyPowerOfTwoOption(dduCharArray, paddingChar, dduCharLength, shouldUsePowerOfTwo);
|
|
133
139
|
return {
|
|
134
140
|
...result,
|
|
135
141
|
isPredefined: false // 커스텀 charset은 검증 필요
|
|
@@ -145,7 +151,10 @@ export class Ddu64 extends BaseDdu {
|
|
|
145
151
|
}
|
|
146
152
|
const { charSet, paddingChar, maxRequiredLength, bitLength } = fixedCharSet;
|
|
147
153
|
// usePowerOfTwo 처리
|
|
148
|
-
|
|
154
|
+
// charArray의 길이가 2의 제곱수이거나 option에 usePowerOfTwo가 명시적으로 true일 때만 적용
|
|
155
|
+
const shouldUsePowerOfTwo = dduOptions?.usePowerOfTwo === true ||
|
|
156
|
+
(dduOptions?.usePowerOfTwo === undefined && this.isPowerOfTwo(maxRequiredLength));
|
|
157
|
+
if (shouldUsePowerOfTwo) {
|
|
149
158
|
const result = this.applyPowerOfTwoOption(charSet, paddingChar, maxRequiredLength, true);
|
|
150
159
|
return {
|
|
151
160
|
...result,
|
|
@@ -179,14 +188,26 @@ export class Ddu64 extends BaseDdu {
|
|
|
179
188
|
}
|
|
180
189
|
/**
|
|
181
190
|
* fallback charset 가져오기
|
|
191
|
+
* 우선순위: dduOptions.dduSetSymbol > dduDefaultConstructorOptions.dduSetSymbol > DduSetSymbol.ONECHARSET
|
|
182
192
|
*/
|
|
183
193
|
getFallbackCharSet(dduOptions) {
|
|
194
|
+
// 우선순위에 따라 fallback symbol 결정
|
|
184
195
|
const fallbackSymbol = dduOptions?.dduSetSymbol ??
|
|
185
196
|
dduDefaultConstructorOptions.dduSetSymbol ??
|
|
186
|
-
DduSetSymbol.DDU
|
|
197
|
+
DduSetSymbol.ONECHARSET; // DDU 대신 ONECHARSET 사용
|
|
187
198
|
const fixedCharSet = getCharSet(fallbackSymbol);
|
|
188
199
|
if (!fixedCharSet) {
|
|
189
|
-
|
|
200
|
+
// 최후의 fallback: ONECHARSET
|
|
201
|
+
const lastResort = getCharSet(DduSetSymbol.ONECHARSET);
|
|
202
|
+
if (!lastResort) {
|
|
203
|
+
throw new Error(`Critical: No fallback CharSet available`);
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
finalCharSet: lastResort.charSet,
|
|
207
|
+
finalPadding: lastResort.paddingChar,
|
|
208
|
+
requiredLength: lastResort.maxRequiredLength,
|
|
209
|
+
bitLength: lastResort.bitLength,
|
|
210
|
+
};
|
|
190
211
|
}
|
|
191
212
|
return {
|
|
192
213
|
finalCharSet: fixedCharSet.charSet,
|
|
@@ -195,6 +216,109 @@ export class Ddu64 extends BaseDdu {
|
|
|
195
216
|
bitLength: fixedCharSet.bitLength,
|
|
196
217
|
};
|
|
197
218
|
}
|
|
219
|
+
normalizeCharSet(params) {
|
|
220
|
+
let currentCharSet = [...params.charSet];
|
|
221
|
+
let currentPadding = params.padding;
|
|
222
|
+
let currentRequiredLength = params.requiredLength;
|
|
223
|
+
let currentBitLength = params.bitLength;
|
|
224
|
+
let currentIsPredefined = params.isPredefined;
|
|
225
|
+
const { shouldThrowError, dduOptions } = params;
|
|
226
|
+
let fallbackCache = null;
|
|
227
|
+
const getFallback = () => {
|
|
228
|
+
if (!fallbackCache) {
|
|
229
|
+
const fallback = this.getFallbackCharSet(dduOptions);
|
|
230
|
+
fallbackCache = {
|
|
231
|
+
finalCharSet: [...fallback.finalCharSet],
|
|
232
|
+
finalPadding: fallback.finalPadding,
|
|
233
|
+
requiredLength: fallback.requiredLength,
|
|
234
|
+
bitLength: fallback.bitLength,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return fallbackCache;
|
|
238
|
+
};
|
|
239
|
+
const applyFallback = () => {
|
|
240
|
+
const fallback = getFallback();
|
|
241
|
+
currentCharSet = [...fallback.finalCharSet];
|
|
242
|
+
currentPadding = fallback.finalPadding;
|
|
243
|
+
currentRequiredLength = fallback.requiredLength;
|
|
244
|
+
currentBitLength = fallback.bitLength;
|
|
245
|
+
currentIsPredefined = true;
|
|
246
|
+
};
|
|
247
|
+
const ensureMinLength = () => {
|
|
248
|
+
if (currentCharSet.length < currentRequiredLength) {
|
|
249
|
+
if (shouldThrowError) {
|
|
250
|
+
throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: ${currentCharSet.length}`);
|
|
251
|
+
}
|
|
252
|
+
applyFallback();
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
if (!currentIsPredefined) {
|
|
256
|
+
const uniqueChars = new Set(currentCharSet);
|
|
257
|
+
if (uniqueChars.size !== currentCharSet.length) {
|
|
258
|
+
if (shouldThrowError) {
|
|
259
|
+
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${currentCharSet.length}`);
|
|
260
|
+
}
|
|
261
|
+
currentCharSet = Array.from(uniqueChars);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
ensureMinLength();
|
|
265
|
+
let charLength = currentCharSet[0]?.length ?? 0;
|
|
266
|
+
if (charLength === 0) {
|
|
267
|
+
if (shouldThrowError) {
|
|
268
|
+
throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: ${currentCharSet.length}`);
|
|
269
|
+
}
|
|
270
|
+
applyFallback();
|
|
271
|
+
charLength = currentCharSet[0]?.length ?? 0;
|
|
272
|
+
}
|
|
273
|
+
const invalidIndex = currentCharSet.findIndex((char) => char.length !== charLength);
|
|
274
|
+
if (invalidIndex !== -1) {
|
|
275
|
+
if (shouldThrowError) {
|
|
276
|
+
throw new Error(`All characters must have the same length. Expected: ${charLength}, but character at index ${invalidIndex} ("${currentCharSet[invalidIndex]}") has length ${currentCharSet[invalidIndex].length}`);
|
|
277
|
+
}
|
|
278
|
+
currentCharSet = currentCharSet.filter((char) => char.length === charLength);
|
|
279
|
+
ensureMinLength();
|
|
280
|
+
charLength = currentCharSet[0]?.length ?? 0;
|
|
281
|
+
}
|
|
282
|
+
if (!currentCharSet.length) {
|
|
283
|
+
if (shouldThrowError) {
|
|
284
|
+
throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: 0`);
|
|
285
|
+
}
|
|
286
|
+
applyFallback();
|
|
287
|
+
charLength = currentCharSet[0]?.length ?? 0;
|
|
288
|
+
}
|
|
289
|
+
if (currentPadding.length !== charLength) {
|
|
290
|
+
if (shouldThrowError) {
|
|
291
|
+
throw new Error(`Padding character must have the same length as the characters. Expected: ${charLength}, but padding character has length ${currentPadding.length}`);
|
|
292
|
+
}
|
|
293
|
+
applyFallback();
|
|
294
|
+
charLength = currentCharSet[0]?.length ?? 0;
|
|
295
|
+
}
|
|
296
|
+
const charSetLookup = new Set(currentCharSet);
|
|
297
|
+
if (charSetLookup.has(currentPadding)) {
|
|
298
|
+
if (shouldThrowError) {
|
|
299
|
+
throw new Error(`Padding character "${currentPadding}" cannot be in the character set`);
|
|
300
|
+
}
|
|
301
|
+
currentCharSet = currentCharSet.filter((char) => char !== currentPadding);
|
|
302
|
+
ensureMinLength();
|
|
303
|
+
charLength = currentCharSet[0]?.length ?? 0;
|
|
304
|
+
}
|
|
305
|
+
currentCharSet = currentCharSet.slice(0, currentRequiredLength);
|
|
306
|
+
return {
|
|
307
|
+
charSet: currentCharSet,
|
|
308
|
+
padding: currentPadding,
|
|
309
|
+
requiredLength: currentRequiredLength,
|
|
310
|
+
bitLength: currentBitLength,
|
|
311
|
+
isPredefined: currentIsPredefined,
|
|
312
|
+
charLength,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
getBinaryFromIndex(index) {
|
|
316
|
+
const cache = this.indexToBinaryCache;
|
|
317
|
+
if (cache && index < cache.length) {
|
|
318
|
+
return cache[index];
|
|
319
|
+
}
|
|
320
|
+
return index.toString(2).padStart(this.effectiveBitLength, "0");
|
|
321
|
+
}
|
|
198
322
|
/**
|
|
199
323
|
* string 또는 배열을 문자 배열로 변환
|
|
200
324
|
*/
|
|
@@ -204,6 +328,12 @@ export class Ddu64 extends BaseDdu {
|
|
|
204
328
|
}
|
|
205
329
|
return input;
|
|
206
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* 숫자가 2의 제곱수인지 확인
|
|
333
|
+
*/
|
|
334
|
+
isPowerOfTwo(n) {
|
|
335
|
+
return n > 0 && (n & (n - 1)) === 0;
|
|
336
|
+
}
|
|
207
337
|
/**
|
|
208
338
|
* usePowerOfTwo 옵션 적용
|
|
209
339
|
*/
|
|
@@ -284,17 +414,14 @@ export class Ddu64 extends BaseDdu {
|
|
|
284
414
|
const bufferInput = typeof input === "string" ? Buffer.from(input, this.encoding) : input;
|
|
285
415
|
// 생성자에서 설정된 값 사용
|
|
286
416
|
const dduLength = this.dduChar.length;
|
|
287
|
-
const effectiveBitLength = this.
|
|
288
|
-
? this.bitLength
|
|
289
|
-
: this.getBitLength(dduLength);
|
|
417
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
290
418
|
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, effectiveBitLength);
|
|
291
419
|
// 문자열 연결 최적화: Array + join 사용
|
|
292
420
|
const resultParts = new Array(dduBinary.length);
|
|
293
421
|
// 각 비트 청크를 변환
|
|
294
422
|
for (let i = 0; i < dduBinary.length; i++) {
|
|
295
423
|
const binaryChunk = dduBinary[i];
|
|
296
|
-
|
|
297
|
-
const charInt = this.binaryToIntLookup.get(binaryChunk) ?? parseInt(binaryChunk, 2);
|
|
424
|
+
const charInt = this.binaryChunkToIntFn(binaryChunk);
|
|
298
425
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
299
426
|
// 가변 길이 조합 인코딩
|
|
300
427
|
const quotient = Math.floor(charInt / dduLength);
|
|
@@ -326,9 +453,7 @@ export class Ddu64 extends BaseDdu {
|
|
|
326
453
|
let dduBinary = "";
|
|
327
454
|
// 생성자에서 설정된 값 사용
|
|
328
455
|
const dduLength = this.dduChar.length;
|
|
329
|
-
const effectiveBitLength = this.
|
|
330
|
-
? this.bitLength
|
|
331
|
-
: this.getBitLength(dduLength);
|
|
456
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
332
457
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
333
458
|
// 가변 길이 조합 디코딩 (2개씩 읽음)
|
|
334
459
|
for (let i = 0; i < input.length; i += 2) {
|
|
@@ -339,7 +464,7 @@ export class Ddu64 extends BaseDdu {
|
|
|
339
464
|
if (firstIndex === undefined || secondIndex === undefined)
|
|
340
465
|
throw new Error(`Invalid character: ${firstChar} or ${secondChar}`);
|
|
341
466
|
const value = firstIndex * dduLength + secondIndex;
|
|
342
|
-
dduBinary +=
|
|
467
|
+
dduBinary += this.getBinaryFromIndex(value);
|
|
343
468
|
}
|
|
344
469
|
}
|
|
345
470
|
else {
|
|
@@ -349,7 +474,7 @@ export class Ddu64 extends BaseDdu {
|
|
|
349
474
|
const charIndex = this.dduBinaryLookup.get(charChunk);
|
|
350
475
|
if (charIndex === undefined)
|
|
351
476
|
throw new Error(`Invalid character: ${charChunk}`);
|
|
352
|
-
dduBinary +=
|
|
477
|
+
dduBinary += this.getBinaryFromIndex(charIndex);
|
|
353
478
|
}
|
|
354
479
|
}
|
|
355
480
|
const decoded = this.dduBinaryToBuffer(dduBinary, paddingBits);
|