@ddunigma/node 1.1.2 → 1.1.4
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 +62 -74
- package/dist/cjs/encoders/Ddu64.d.ts +5 -33
- package/dist/cjs/encoders/Ddu64.js +159 -245
- package/dist/mjs/encoders/Ddu64.d.ts +5 -33
- package/dist/mjs/encoders/Ddu64.js +157 -244
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,92 +1,80 @@
|
|
|
1
|
-
|
|
1
|
+
# DDU ENIGMA - Node.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
데이터를 특정 문자셋으로 인코딩하는 Node.js 라이브러리입니다.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 크레딧
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- 원본 Python 구현: [@i3ls](https://github.com/i3l3), [@gunu3371](https://github.com/gunu3371)
|
|
8
|
+
- 원본 저장소: [ddunigma](https://github.com/i3l3/ddunigma)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
- [@i3ls](https://github.com/i3l3)
|
|
11
|
-
- [@gunu3371](https://github.com/gunu3371)
|
|
12
|
-
- Original Repository: [ddunigma](https://github.com/i3l3/ddunigma)
|
|
10
|
+
## 설치
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
```bash
|
|
13
|
+
npm install @ddunigma/node
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 기본 사용법
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
### 간단한 문자열 인코딩/디코딩
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
18
21
|
import { Ddu64 } from "@ddunigma/node";
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
// 커스텀 charset으로 인코더 생성
|
|
24
|
+
const encoder = new Ddu64("우따야", "뭐");
|
|
25
|
+
|
|
26
|
+
const text = "Hello World!";
|
|
27
|
+
const encoded = encoder.encode(text);
|
|
28
|
+
console.log(encoded); // 우따야뭐우따... (인코딩된 문자열)
|
|
29
|
+
|
|
30
|
+
const decoded = encoder.decode(encoded);
|
|
31
|
+
console.log(decoded); // "Hello World!"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 미리 정의된 Charset 사용
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
```typescript
|
|
37
|
+
import { Ddu64, DduSetSymbol } from "@ddunigma/node";
|
|
23
38
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
console.log(decoded);
|
|
29
|
-
//뜌땨어 고수가 될거야!
|
|
39
|
+
// ONECHARSET (64개 문자)
|
|
40
|
+
const encoder1 = new Ddu64(undefined, undefined, {
|
|
41
|
+
dduSetSymbol: DduSetSymbol.ONECHARSET
|
|
42
|
+
});
|
|
30
43
|
|
|
31
|
-
//
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
// DDU (8개 문자)
|
|
45
|
+
const encoder2 = new Ddu64(undefined, undefined, {
|
|
46
|
+
dduSetSymbol: DduSetSymbol.DDU
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// TWOCHARSET (1024개 문자)
|
|
50
|
+
const encoder3 = new Ddu64(undefined, undefined, {
|
|
51
|
+
dduSetSymbol: DduSetSymbol.TWOCHARSET
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// THREECHARSET (32768개 문자)
|
|
55
|
+
const encoder4 = new Ddu64(undefined, undefined, {
|
|
56
|
+
dduSetSymbol: DduSetSymbol.THREECHARSET
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const text = "안녕하세요";
|
|
60
|
+
const encoded = encoder1.encode(text);
|
|
61
|
+
const decoded = encoder1.decode(encoded);
|
|
38
62
|
```
|
|
39
63
|
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
### 커스텀 Charset 사용
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
42
67
|
import { Ddu64 } from "@ddunigma/node";
|
|
68
|
+
|
|
43
69
|
const koreanChars = [
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
"뜪", "뜫", "뜬", "뜭", "뜮", "뜯", "뜰", "뜱", "뜲", "뜳", "뜴", "뜵", "뜶", "뜷", "뜸",
|
|
48
|
-
"뜹", "뜺", "뜻", "뜼", "뜽", "뜾", "뜿",
|
|
49
|
-
// 땨 계열
|
|
50
|
-
"땨", "땩", "땪", "땫", "땬", "땭", "땮", "땯", "땰", "땱", "땲", "땳", "땴", "땵", "땶",
|
|
51
|
-
"땷", "땸", "땹", "땺", "땻", "땼", "땽", "땾", "땿", "떀", "떁", "떂", "떃", "떄", "떅",
|
|
52
|
-
"떆", "떇", "떈", "떉", "떊", "떋", "떌", "떍", "떎", "떏", "떐", "떑", "떒", "떓", "떔",
|
|
53
|
-
"떕", "떖", "떗", "떘", "떙", "떚", "떛",
|
|
54
|
-
// 우 계열
|
|
55
|
-
"우", "욱", "욲", "욳", "운", "울", "욶", "욷", "움", "웁", "웂", "웃", "웄", "웅", "웆",
|
|
56
|
-
"웇", "워", "웍", "웎", "웏", "원", "월", "웒", "웓", "월", "웕", "웖", "웗", "웘", "웙",
|
|
57
|
-
"웚", "웛", "위", "윅", "윆", "윇", "윈", "윉", "윊", "윋", "윌", "윍", "윎", "윏", "윐",
|
|
58
|
-
"윑", "윒", "윓", "윔", "윕", "윖",
|
|
59
|
-
// 따 계열
|
|
60
|
-
"따", "딱", "딲", "딳", "딴", "딵", "딶", "딷", "딸", "딹", "딺", "딻", "딼", "딽", "딾",
|
|
61
|
-
"딿", "땀", "땁", "땂", "땃", "땄", "땅", "땆", "땇", "땈", "땉", "땊", "땋", "때", "땍",
|
|
62
|
-
"땎", "땏", "때", "땑", "땒", "땓", "땔", "땕", "땖", "땗", "땘", "땙", "땚", "땛", "땜",
|
|
63
|
-
"땝", "땞", "땟", "땠", "땡", "땢",
|
|
64
|
-
// 야 계열
|
|
65
|
-
"야", "약", "얂", "얃", "얄", "얅", "얆", "얇", "얈", "얉", "얊", "얋", "얌", "얍", "얎",
|
|
66
|
-
"얏", "양", "양", "얒", "얓", "얔", "얕", "얖", "얗", "얘", "얙", "얚", "얛", "얜", "얝",
|
|
67
|
-
"얞", "얟", "얠", "얡", "얢", "얣", "얤", "얥", "얦", "얧", "얨", "얩", "얪", "얫", "얬",
|
|
68
|
-
"얭", "얮", "얯", "얰", "얱"
|
|
70
|
+
"뜌", "뜍", "뜎", "뜏", "뜐", "뜑", "뜒", "뜓", "뜔", "뜕", "뜖", "뜗", "뜘", "뜙", "뜚", "뜛",
|
|
71
|
+
"뜜", "뜝", "뜞", "뜟", "뜠", "뜡", "뜢", "뜣", "뜤", "뜥", "뜦", "뜧", "뜨", "뜩", "뜪", "뜫",
|
|
72
|
+
// ... 256개 문자
|
|
69
73
|
];
|
|
70
74
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const answer = "안녕하세요";
|
|
74
|
-
const encoded = ddu64.encode(answer);
|
|
75
|
-
console.log(encoded);
|
|
76
|
-
//뜌얞뜌윑뜌위뜌얝뜌웙뜌윑뜌얟뜌윑뜌윔뜌얞뜌웘뜌땍뜌얞뜌윖뜌윐
|
|
77
|
-
const decoded = ddu64.decode(encoded);
|
|
78
|
-
console.log(decoded);
|
|
79
|
-
//안녕하세요
|
|
80
|
-
|
|
81
|
-
//Encoding Shorter
|
|
82
|
-
const encodedShorter = ddu64.encode(answer, { usePowerOfTwo: true });
|
|
83
|
-
console.log(encodedShorter);
|
|
84
|
-
//얞윑위얝웙윑얟윑윔얞웘땍얞윖윐
|
|
85
|
-
const decodedShorter = ddu64.decode(encodedShorter, { usePowerOfTwo: true });
|
|
86
|
-
console.log(decodedShorter);
|
|
87
|
-
//안녕하세요
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Add Ddu128, Ddu512, Ddu1024
|
|
91
|
-
It's UrlSafeEncoding
|
|
75
|
+
const encoder = new Ddu64(koreanChars, "뭐");
|
|
92
76
|
|
|
77
|
+
const text = "안녕하세요12";
|
|
78
|
+
const encoded = encoder.encode(text);
|
|
79
|
+
const decoded = encoder.decode(encoded);
|
|
80
|
+
```
|
|
@@ -9,40 +9,12 @@ 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
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
private initializeCharSet;
|
|
18
|
-
/**
|
|
19
|
-
* 커스텀 charset 처리
|
|
20
|
-
*/
|
|
21
|
-
private processCustomCharSet;
|
|
22
|
-
/**
|
|
23
|
-
* 미리 정의된 charset 처리
|
|
24
|
-
*/
|
|
25
|
-
private processPredefinedCharSet;
|
|
26
|
-
/**
|
|
27
|
-
* 기본 charset 처리
|
|
28
|
-
*/
|
|
29
|
-
private processDefaultCharSet;
|
|
30
|
-
/**
|
|
31
|
-
* fallback charset 가져오기
|
|
32
|
-
*/
|
|
33
|
-
private getFallbackCharSet;
|
|
34
|
-
/**
|
|
35
|
-
* string 또는 배열을 문자 배열로 변환
|
|
36
|
-
*/
|
|
37
|
-
private convertToCharArray;
|
|
38
|
-
/**
|
|
39
|
-
* usePowerOfTwo 옵션 적용
|
|
40
|
-
*/
|
|
41
|
-
private applyPowerOfTwoOption;
|
|
42
|
-
/**
|
|
43
|
-
* 2글자 이상의 문자셋에서 조합으로 인한 중복을 검증
|
|
44
|
-
* 예: ["AB", "CD"] + padding "AB" 또는 "A" + "B" = "AB" 같은 경우 감지
|
|
45
|
-
*/
|
|
16
|
+
private normalizeCharSet;
|
|
17
|
+
private getBinaryFromIndex;
|
|
46
18
|
private validateCombinationDuplicates;
|
|
47
19
|
encode(input: Buffer | string, _options?: DduOptions): string;
|
|
48
20
|
decode(input: string, _options?: DduOptions): string;
|
|
@@ -6,267 +6,187 @@ const types_1 = require("../types");
|
|
|
6
6
|
const charSets_1 = require("../charSets");
|
|
7
7
|
class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
8
8
|
constructor(dduChar, paddingChar, dduOptions) {
|
|
9
|
-
var _a, _b, _c;
|
|
9
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
10
10
|
super();
|
|
11
11
|
this.dduBinaryLookup = new Map();
|
|
12
|
-
|
|
12
|
+
const shouldThrowError = (_a = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.useBuildErrorReturn) !== null && _a !== void 0 ? _a : false;
|
|
13
|
+
const getCharSetFromSymbol = (symbol) => {
|
|
14
|
+
const cs = (0, charSets_1.getCharSet)(symbol);
|
|
15
|
+
if (!cs)
|
|
16
|
+
throw new Error(`CharSet with symbol ${symbol} not found`);
|
|
17
|
+
return cs;
|
|
18
|
+
};
|
|
13
19
|
// charset 초기화
|
|
14
|
-
let
|
|
15
|
-
let finalPadding;
|
|
16
|
-
let requiredLength;
|
|
17
|
-
let bitLength;
|
|
18
|
-
let isPredefined = false;
|
|
20
|
+
let charSet, padding, requiredLength, bitLength, isPredefined;
|
|
19
21
|
try {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
const finalDduChar = dduChar !== null && dduChar !== void 0 ? dduChar : dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduChar;
|
|
23
|
+
const finalPadding = paddingChar !== null && paddingChar !== void 0 ? paddingChar : dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.paddingChar;
|
|
24
|
+
if (finalDduChar) {
|
|
25
|
+
// 커스텀 charset
|
|
26
|
+
if (!finalPadding)
|
|
27
|
+
throw new Error("paddingChar is required when dduChar or dduOptions.dduChar is provided");
|
|
28
|
+
const arr = typeof finalDduChar === "string" ? [...new Set(finalDduChar.trim())] : finalDduChar;
|
|
29
|
+
const len = (_b = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.requiredLength) !== null && _b !== void 0 ? _b : arr.length;
|
|
30
|
+
if (arr.length < len)
|
|
31
|
+
throw new Error(`dduChar must be at least ${len} characters long. Provided: ${arr.length}`);
|
|
32
|
+
const usePow2 = (dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === true || ((dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === undefined && len > 0 && (len & (len - 1)) === 0);
|
|
33
|
+
if (usePow2) {
|
|
34
|
+
const exp = this.getLargestPowerOfTwoExponent(len);
|
|
35
|
+
const pow2Len = 1 << exp;
|
|
36
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, pow2Len), finalPadding, pow2Len, exp, false];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, len), finalPadding, len, this.getBitLength(len), false];
|
|
40
|
+
}
|
|
31
41
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw new Error(`All characters must have the same length. Expected: ${this.charLength}, but character at index ${i} ("${finalCharSet[i]}") has length ${finalCharSet[i].length}`);
|
|
42
|
+
else if (dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduSetSymbol) {
|
|
43
|
+
// 미리 정의된 charset
|
|
44
|
+
const cs = getCharSetFromSymbol(dduOptions.dduSetSymbol);
|
|
45
|
+
const usePow2 = (dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === true || ((dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo) === undefined && cs.maxRequiredLength > 0 && (cs.maxRequiredLength & (cs.maxRequiredLength - 1)) === 0);
|
|
46
|
+
if (usePow2) {
|
|
47
|
+
const exp = this.getLargestPowerOfTwoExponent(cs.maxRequiredLength);
|
|
48
|
+
const pow2Len = 1 << exp;
|
|
49
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet.slice(0, pow2Len), cs.paddingChar, pow2Len, exp, true];
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// 기본 charset
|
|
57
|
+
const cs = getCharSetFromSymbol((_c = types_1.dduDefaultConstructorOptions.dduSetSymbol) !== null && _c !== void 0 ? _c : types_1.DduSetSymbol.DDU);
|
|
58
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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);
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (shouldThrowError)
|
|
63
|
+
throw error;
|
|
64
|
+
// fallback
|
|
65
|
+
const fallbackSymbol = (_e = (_d = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduSetSymbol) !== null && _d !== void 0 ? _d : types_1.dduDefaultConstructorOptions.dduSetSymbol) !== null && _e !== void 0 ? _e : types_1.DduSetSymbol.ONECHARSET;
|
|
66
|
+
const cs = (_f = (0, charSets_1.getCharSet)(fallbackSymbol)) !== null && _f !== void 0 ? _f : (0, charSets_1.getCharSet)(types_1.DduSetSymbol.ONECHARSET);
|
|
67
|
+
if (!cs)
|
|
68
|
+
throw new Error(`Critical: No fallback CharSet available`);
|
|
69
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
|
|
68
70
|
}
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
this.
|
|
73
|
-
this.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.
|
|
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가 더 효율적 (캐시 미스 감소)
|
|
71
|
+
// 정규화 및 검증
|
|
72
|
+
const normalized = this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
|
|
73
|
+
this.dduChar = normalized.charSet;
|
|
74
|
+
this.paddingChar = normalized.padding;
|
|
75
|
+
this.charLength = normalized.charLength;
|
|
76
|
+
this.bitLength = normalized.bitLength;
|
|
77
|
+
this.isPredefinedCharSet = normalized.isPredefined;
|
|
78
|
+
this.encoding = (_g = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.encoding) !== null && _g !== void 0 ? _g : this.defaultEncoding;
|
|
85
79
|
const dduLength = this.dduChar.length;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
80
|
+
this.usePowerOfTwo = dduLength > 0 && (dduLength & (dduLength - 1)) === 0;
|
|
81
|
+
this.effectiveBitLength = this.usePowerOfTwo ? this.bitLength : this.getBitLength(dduLength);
|
|
82
|
+
this.binaryChunkToIntFn = this.effectiveBitLength <= 26
|
|
83
|
+
? (chunk) => chunk.split('').reduce((v, c) => (v << 1) | (c.charCodeAt(0) & 1), 0)
|
|
84
|
+
: (chunk) => chunk.split('').reduce((v, c) => v * 2 + (c.charCodeAt(0) & 1), 0);
|
|
85
|
+
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
|
|
86
|
+
if (this.charLength >= 2 && !this.isPredefinedCharSet) {
|
|
87
|
+
this.validateCombinationDuplicates(this.dduChar, this.paddingChar, dduLength);
|
|
95
88
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* charset 초기화 (커스텀 또는 미리 정의된 charset)
|
|
100
|
-
*/
|
|
101
|
-
initializeCharSet(dduChar, paddingChar, dduOptions) {
|
|
102
|
-
const finalDduChar = dduChar !== null && dduChar !== void 0 ? dduChar : dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduChar;
|
|
103
|
-
const finalPaddingChar = paddingChar !== null && paddingChar !== void 0 ? paddingChar : dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.paddingChar;
|
|
104
|
-
// 1. 커스텀 charset 사용
|
|
105
|
-
if (finalDduChar) {
|
|
106
|
-
return this.processCustomCharSet(finalDduChar, finalPaddingChar, dduOptions);
|
|
89
|
+
if (this.effectiveBitLength <= 16) {
|
|
90
|
+
const size = 1 << this.effectiveBitLength;
|
|
91
|
+
this.indexToBinaryCache = Array.from({ length: size }, (_, i) => i.toString(2).padStart(this.effectiveBitLength, "0"));
|
|
107
92
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return this.processPredefinedCharSet(dduOptions.dduSetSymbol, dduOptions);
|
|
93
|
+
else {
|
|
94
|
+
this.indexToBinaryCache = null;
|
|
111
95
|
}
|
|
112
|
-
// 3. 기본 charset 사용
|
|
113
|
-
return this.processDefaultCharSet();
|
|
114
96
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
97
|
+
normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions) {
|
|
98
|
+
var _a, _b, _c, _d, _e, _f;
|
|
99
|
+
const applyFallback = () => {
|
|
100
|
+
var _a, _b, _c;
|
|
101
|
+
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;
|
|
102
|
+
const cs = (_c = (0, charSets_1.getCharSet)(fallbackSymbol)) !== null && _c !== void 0 ? _c : (0, charSets_1.getCharSet)(types_1.DduSetSymbol.ONECHARSET);
|
|
103
|
+
if (!cs)
|
|
104
|
+
throw new Error(`Critical: No fallback CharSet available`);
|
|
105
|
+
return { charSet: cs.charSet, padding: cs.paddingChar, requiredLength: cs.maxRequiredLength, bitLength: cs.bitLength, isPredefined: true };
|
|
106
|
+
};
|
|
107
|
+
const validate = (condition, message) => {
|
|
108
|
+
if (condition) {
|
|
109
|
+
if (shouldThrowError)
|
|
110
|
+
throw new Error(message);
|
|
111
|
+
const fb = applyFallback();
|
|
112
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [fb.charSet, fb.padding, fb.requiredLength, fb.bitLength, fb.isPredefined];
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
};
|
|
117
|
+
// 중복 제거 (커스텀 charset만)
|
|
118
|
+
if (!isPredefined) {
|
|
119
|
+
const uniqueChars = new Set(charSet);
|
|
120
|
+
if (uniqueChars.size !== charSet.length) {
|
|
121
|
+
if (shouldThrowError)
|
|
122
|
+
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${charSet.length}`);
|
|
123
|
+
charSet = Array.from(uniqueChars);
|
|
124
|
+
requiredLength = charSet.length;
|
|
125
|
+
}
|
|
128
126
|
}
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* 미리 정의된 charset 처리
|
|
136
|
-
*/
|
|
137
|
-
processPredefinedCharSet(symbol, dduOptions) {
|
|
138
|
-
var _a;
|
|
139
|
-
const fixedCharSet = (0, charSets_1.getCharSet)(symbol);
|
|
140
|
-
if (!fixedCharSet) {
|
|
141
|
-
throw new Error(`CharSet with symbol ${symbol} not found`);
|
|
127
|
+
// 검증
|
|
128
|
+
if (validate(charSet.length < requiredLength, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`)) {
|
|
129
|
+
return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
|
|
142
130
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
131
|
+
let charLength = (_b = (_a = charSet[0]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
132
|
+
validate(charLength === 0, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`);
|
|
133
|
+
const invalidIndex = charSet.findIndex(char => char.length !== charLength);
|
|
134
|
+
if (invalidIndex !== -1) {
|
|
135
|
+
if (shouldThrowError)
|
|
136
|
+
throw new Error(`All characters must have the same length. Expected: ${charLength}, but index ${invalidIndex} has length ${charSet[invalidIndex].length}`);
|
|
137
|
+
charSet = charSet.filter(char => char.length === charLength);
|
|
138
|
+
validate(charSet.length < requiredLength, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`);
|
|
139
|
+
charLength = (_d = (_c = charSet[0]) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
|
|
149
140
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 기본 charset 처리
|
|
160
|
-
*/
|
|
161
|
-
processDefaultCharSet() {
|
|
162
|
-
var _a;
|
|
163
|
-
const defaultSymbol = (_a = types_1.dduDefaultConstructorOptions.dduSetSymbol) !== null && _a !== void 0 ? _a : types_1.DduSetSymbol.DDU;
|
|
164
|
-
const fixedCharSet = (0, charSets_1.getCharSet)(defaultSymbol);
|
|
165
|
-
if (!fixedCharSet) {
|
|
166
|
-
throw new Error(`Default CharSet with symbol ${defaultSymbol} not found`);
|
|
141
|
+
validate(!charSet.length, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: 0`);
|
|
142
|
+
validate(padding.length !== charLength, `Padding character must have length ${charLength}, got ${padding.length}`);
|
|
143
|
+
if (new Set(charSet).has(padding)) {
|
|
144
|
+
if (shouldThrowError)
|
|
145
|
+
throw new Error(`Padding character "${padding}" cannot be in the character set`);
|
|
146
|
+
charSet = charSet.filter(char => char !== padding);
|
|
147
|
+
validate(charSet.length < requiredLength, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`);
|
|
148
|
+
charLength = (_f = (_e = charSet[0]) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0;
|
|
167
149
|
}
|
|
168
|
-
return {
|
|
169
|
-
finalCharSet: fixedCharSet.charSet,
|
|
170
|
-
finalPadding: fixedCharSet.paddingChar,
|
|
171
|
-
requiredLength: fixedCharSet.maxRequiredLength,
|
|
172
|
-
bitLength: fixedCharSet.bitLength,
|
|
173
|
-
isPredefined: true // 기본 charset도 미리 정의된 것이므로 검증 패스
|
|
174
|
-
};
|
|
150
|
+
return { charSet: charSet.slice(0, requiredLength), padding, requiredLength, bitLength, isPredefined, charLength };
|
|
175
151
|
}
|
|
176
|
-
|
|
177
|
-
* fallback charset 가져오기
|
|
178
|
-
*/
|
|
179
|
-
getFallbackCharSet(dduOptions) {
|
|
152
|
+
getBinaryFromIndex(index) {
|
|
180
153
|
var _a, _b;
|
|
181
|
-
|
|
182
|
-
const fixedCharSet = (0, charSets_1.getCharSet)(fallbackSymbol);
|
|
183
|
-
if (!fixedCharSet) {
|
|
184
|
-
throw new Error(`Fallback CharSet with symbol ${fallbackSymbol} not found`);
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
finalCharSet: fixedCharSet.charSet,
|
|
188
|
-
finalPadding: fixedCharSet.paddingChar,
|
|
189
|
-
requiredLength: fixedCharSet.maxRequiredLength,
|
|
190
|
-
bitLength: fixedCharSet.bitLength,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* string 또는 배열을 문자 배열로 변환
|
|
195
|
-
*/
|
|
196
|
-
convertToCharArray(input) {
|
|
197
|
-
if (typeof input === "string") {
|
|
198
|
-
return [...new Set(input.trim())].map((c) => c);
|
|
199
|
-
}
|
|
200
|
-
return input;
|
|
154
|
+
return (_b = (_a = this.indexToBinaryCache) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : index.toString(2).padStart(this.effectiveBitLength, "0");
|
|
201
155
|
}
|
|
202
|
-
/**
|
|
203
|
-
* usePowerOfTwo 옵션 적용
|
|
204
|
-
*/
|
|
205
|
-
applyPowerOfTwoOption(charArray, padding, length, usePowerOfTwo) {
|
|
206
|
-
if (usePowerOfTwo) {
|
|
207
|
-
const powerOfTwoExponent = this.getLargestPowerOfTwoExponent(length);
|
|
208
|
-
const powerOfTwoLength = Math.pow(2, powerOfTwoExponent);
|
|
209
|
-
return {
|
|
210
|
-
finalCharSet: charArray.slice(0, powerOfTwoLength),
|
|
211
|
-
finalPadding: padding,
|
|
212
|
-
requiredLength: powerOfTwoLength,
|
|
213
|
-
bitLength: powerOfTwoExponent,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
return {
|
|
217
|
-
finalCharSet: charArray.slice(0, length),
|
|
218
|
-
finalPadding: padding,
|
|
219
|
-
requiredLength: length,
|
|
220
|
-
bitLength: this.getBitLength(length),
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* 2글자 이상의 문자셋에서 조합으로 인한 중복을 검증
|
|
225
|
-
* 예: ["AB", "CD"] + padding "AB" 또는 "A" + "B" = "AB" 같은 경우 감지
|
|
226
|
-
*/
|
|
227
156
|
validateCombinationDuplicates(charSet, paddingChar, requiredLength) {
|
|
228
157
|
const charLength = charSet[0].length;
|
|
229
|
-
const allStrings = new Set();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// 조합이 기존 문자와 중복되는지 확인
|
|
238
|
-
if (allStrings.has(combination)) {
|
|
239
|
-
throw new Error(`Combination conflict detected: "${charSet[i]}" + "${charSet[j]}" = "${combination}" already exists in the character set or padding`);
|
|
158
|
+
const allStrings = new Set([...charSet.slice(0, requiredLength), paddingChar]);
|
|
159
|
+
const limit = Math.min(charSet.length, requiredLength);
|
|
160
|
+
// 2개 조합 검사
|
|
161
|
+
for (let i = 0; i < limit; i++) {
|
|
162
|
+
for (let j = 0; j < limit; j++) {
|
|
163
|
+
const combo = charSet[i] + charSet[j];
|
|
164
|
+
if (allStrings.has(combo)) {
|
|
165
|
+
throw new Error(`Combination conflict: "${charSet[i]}" + "${charSet[j]}" = "${combo}" already exists`);
|
|
240
166
|
}
|
|
241
167
|
}
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (allStrings.has(paddingPlusChar)) {
|
|
249
|
-
throw new Error(`Combination conflict detected: padding "${paddingChar}" + "${charSet[i]}" = "${paddingPlusChar}" already exists in the character set`);
|
|
250
|
-
}
|
|
168
|
+
const charPad = charSet[i] + paddingChar;
|
|
169
|
+
const padChar = paddingChar + charSet[i];
|
|
170
|
+
if (allStrings.has(charPad))
|
|
171
|
+
throw new Error(`Combination conflict: "${charSet[i]}" + padding "${paddingChar}" = "${charPad}"`);
|
|
172
|
+
if (allStrings.has(padChar))
|
|
173
|
+
throw new Error(`Combination conflict: padding "${paddingChar}" + "${charSet[i]}" = "${padChar}"`);
|
|
251
174
|
}
|
|
252
|
-
//
|
|
253
|
-
const
|
|
254
|
-
if (allStrings.has(
|
|
255
|
-
throw new Error(`Combination conflict
|
|
175
|
+
// 패딩 + 패딩 조합 검사
|
|
176
|
+
const doublePad = paddingChar + paddingChar;
|
|
177
|
+
if (allStrings.has(doublePad)) {
|
|
178
|
+
throw new Error(`Combination conflict: double padding "${doublePad}" already exists`);
|
|
256
179
|
}
|
|
257
|
-
//
|
|
258
|
-
// 모든 조합을 검사하면 O(n^3)이므로 일부만 샘플링
|
|
180
|
+
// 3개 조합 검사 (100개 이하만)
|
|
259
181
|
if (charLength >= 2 && requiredLength <= 100) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (allStrings.has(substring) && substring !== charSet[i] && substring !== charSet[j] && substring !== charSet[k]) {
|
|
269
|
-
throw new Error(`Combination conflict detected: substring "${substring}" from "${charSet[i]}" + "${charSet[j]}" + "${charSet[k]}" conflicts with existing character`);
|
|
182
|
+
for (let i = 0; i < limit; i++) {
|
|
183
|
+
for (let j = 0; j < limit; j++) {
|
|
184
|
+
for (let k = 0; k < limit; k++) {
|
|
185
|
+
const combo = charSet[i] + charSet[j] + charSet[k];
|
|
186
|
+
for (let start = 0; start <= combo.length - charLength; start++) {
|
|
187
|
+
const sub = combo.substring(start, start + charLength);
|
|
188
|
+
if (allStrings.has(sub) && sub !== charSet[i] && sub !== charSet[j] && sub !== charSet[k]) {
|
|
189
|
+
throw new Error(`Combination conflict: substring "${sub}" from "${charSet[i]}" + "${charSet[j]}" + "${charSet[k]}"`);
|
|
270
190
|
}
|
|
271
191
|
}
|
|
272
192
|
}
|
|
@@ -275,22 +195,18 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
275
195
|
}
|
|
276
196
|
}
|
|
277
197
|
encode(input, _options) {
|
|
278
|
-
var _a;
|
|
279
198
|
// options는 구버전 호환성을 위해 유지하지만 사용하지 않음
|
|
280
199
|
const bufferInput = typeof input === "string" ? Buffer.from(input, this.encoding) : input;
|
|
281
200
|
// 생성자에서 설정된 값 사용
|
|
282
201
|
const dduLength = this.dduChar.length;
|
|
283
|
-
const effectiveBitLength = this.
|
|
284
|
-
? this.bitLength
|
|
285
|
-
: this.getBitLength(dduLength);
|
|
202
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
286
203
|
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, effectiveBitLength);
|
|
287
204
|
// 문자열 연결 최적화: Array + join 사용
|
|
288
205
|
const resultParts = new Array(dduBinary.length);
|
|
289
206
|
// 각 비트 청크를 변환
|
|
290
207
|
for (let i = 0; i < dduBinary.length; i++) {
|
|
291
208
|
const binaryChunk = dduBinary[i];
|
|
292
|
-
|
|
293
|
-
const charInt = (_a = this.binaryToIntLookup.get(binaryChunk)) !== null && _a !== void 0 ? _a : parseInt(binaryChunk, 2);
|
|
209
|
+
const charInt = this.binaryChunkToIntFn(binaryChunk);
|
|
294
210
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
295
211
|
// 가변 길이 조합 인코딩
|
|
296
212
|
const quotient = Math.floor(charInt / dduLength);
|
|
@@ -322,9 +238,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
322
238
|
let dduBinary = "";
|
|
323
239
|
// 생성자에서 설정된 값 사용
|
|
324
240
|
const dduLength = this.dduChar.length;
|
|
325
|
-
const effectiveBitLength = this.
|
|
326
|
-
? this.bitLength
|
|
327
|
-
: this.getBitLength(dduLength);
|
|
241
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
328
242
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
329
243
|
// 가변 길이 조합 디코딩 (2개씩 읽음)
|
|
330
244
|
for (let i = 0; i < input.length; i += 2) {
|
|
@@ -335,7 +249,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
335
249
|
if (firstIndex === undefined || secondIndex === undefined)
|
|
336
250
|
throw new Error(`Invalid character: ${firstChar} or ${secondChar}`);
|
|
337
251
|
const value = firstIndex * dduLength + secondIndex;
|
|
338
|
-
dduBinary +=
|
|
252
|
+
dduBinary += this.getBinaryFromIndex(value);
|
|
339
253
|
}
|
|
340
254
|
}
|
|
341
255
|
else {
|
|
@@ -345,7 +259,7 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
|
|
|
345
259
|
const charIndex = this.dduBinaryLookup.get(charChunk);
|
|
346
260
|
if (charIndex === undefined)
|
|
347
261
|
throw new Error(`Invalid character: ${charChunk}`);
|
|
348
|
-
dduBinary +=
|
|
262
|
+
dduBinary += this.getBinaryFromIndex(charIndex);
|
|
349
263
|
}
|
|
350
264
|
}
|
|
351
265
|
const decoded = this.dduBinaryToBuffer(dduBinary, paddingBits);
|
|
@@ -9,40 +9,12 @@ 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
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
private initializeCharSet;
|
|
18
|
-
/**
|
|
19
|
-
* 커스텀 charset 처리
|
|
20
|
-
*/
|
|
21
|
-
private processCustomCharSet;
|
|
22
|
-
/**
|
|
23
|
-
* 미리 정의된 charset 처리
|
|
24
|
-
*/
|
|
25
|
-
private processPredefinedCharSet;
|
|
26
|
-
/**
|
|
27
|
-
* 기본 charset 처리
|
|
28
|
-
*/
|
|
29
|
-
private processDefaultCharSet;
|
|
30
|
-
/**
|
|
31
|
-
* fallback charset 가져오기
|
|
32
|
-
*/
|
|
33
|
-
private getFallbackCharSet;
|
|
34
|
-
/**
|
|
35
|
-
* string 또는 배열을 문자 배열로 변환
|
|
36
|
-
*/
|
|
37
|
-
private convertToCharArray;
|
|
38
|
-
/**
|
|
39
|
-
* usePowerOfTwo 옵션 적용
|
|
40
|
-
*/
|
|
41
|
-
private applyPowerOfTwoOption;
|
|
42
|
-
/**
|
|
43
|
-
* 2글자 이상의 문자셋에서 조합으로 인한 중복을 검증
|
|
44
|
-
* 예: ["AB", "CD"] + padding "AB" 또는 "A" + "B" = "AB" 같은 경우 감지
|
|
45
|
-
*/
|
|
16
|
+
private normalizeCharSet;
|
|
17
|
+
private getBinaryFromIndex;
|
|
46
18
|
private validateCombinationDuplicates;
|
|
47
19
|
encode(input: Buffer | string, _options?: DduOptions): string;
|
|
48
20
|
decode(input: string, _options?: DduOptions): string;
|
|
@@ -10,268 +10,186 @@ 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();
|
|
18
|
+
const shouldThrowError = dduOptions?.useBuildErrorReturn ?? false;
|
|
19
|
+
const getCharSetFromSymbol = (symbol) => {
|
|
20
|
+
const cs = getCharSet(symbol);
|
|
21
|
+
if (!cs)
|
|
22
|
+
throw new Error(`CharSet with symbol ${symbol} not found`);
|
|
23
|
+
return cs;
|
|
24
|
+
};
|
|
16
25
|
// charset 초기화
|
|
17
|
-
let
|
|
18
|
-
let finalPadding;
|
|
19
|
-
let requiredLength;
|
|
20
|
-
let bitLength;
|
|
21
|
-
let isPredefined = false;
|
|
26
|
+
let charSet, padding, requiredLength, bitLength, isPredefined;
|
|
22
27
|
try {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const finalDduChar = dduChar ?? dduOptions?.dduChar;
|
|
29
|
+
const finalPadding = paddingChar ?? dduOptions?.paddingChar;
|
|
30
|
+
if (finalDduChar) {
|
|
31
|
+
// 커스텀 charset
|
|
32
|
+
if (!finalPadding)
|
|
33
|
+
throw new Error("paddingChar is required when dduChar or dduOptions.dduChar is provided");
|
|
34
|
+
const arr = typeof finalDduChar === "string" ? [...new Set(finalDduChar.trim())] : finalDduChar;
|
|
35
|
+
const len = dduOptions?.requiredLength ?? arr.length;
|
|
36
|
+
if (arr.length < len)
|
|
37
|
+
throw new Error(`dduChar must be at least ${len} characters long. Provided: ${arr.length}`);
|
|
38
|
+
const usePow2 = dduOptions?.usePowerOfTwo === true || (dduOptions?.usePowerOfTwo === undefined && len > 0 && (len & (len - 1)) === 0);
|
|
39
|
+
if (usePow2) {
|
|
40
|
+
const exp = this.getLargestPowerOfTwoExponent(len);
|
|
41
|
+
const pow2Len = 1 << exp;
|
|
42
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, pow2Len), finalPadding, pow2Len, exp, false];
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, len), finalPadding, len, this.getBitLength(len), false];
|
|
46
|
+
}
|
|
34
47
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
throw new Error(`All characters must have the same length. Expected: ${this.charLength}, but character at index ${i} ("${finalCharSet[i]}") has length ${finalCharSet[i].length}`);
|
|
48
|
+
else if (dduOptions?.dduSetSymbol) {
|
|
49
|
+
// 미리 정의된 charset
|
|
50
|
+
const cs = getCharSetFromSymbol(dduOptions.dduSetSymbol);
|
|
51
|
+
const usePow2 = dduOptions?.usePowerOfTwo === true || (dduOptions?.usePowerOfTwo === undefined && cs.maxRequiredLength > 0 && (cs.maxRequiredLength & (cs.maxRequiredLength - 1)) === 0);
|
|
52
|
+
if (usePow2) {
|
|
53
|
+
const exp = this.getLargestPowerOfTwoExponent(cs.maxRequiredLength);
|
|
54
|
+
const pow2Len = 1 << exp;
|
|
55
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet.slice(0, pow2Len), cs.paddingChar, pow2Len, exp, true];
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// 기본 charset
|
|
63
|
+
const cs = getCharSetFromSymbol(dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.DDU);
|
|
64
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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);
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (shouldThrowError)
|
|
69
|
+
throw error;
|
|
70
|
+
// fallback
|
|
71
|
+
const fallbackSymbol = dduOptions?.dduSetSymbol ?? dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.ONECHARSET;
|
|
72
|
+
const cs = getCharSet(fallbackSymbol) ?? getCharSet(DduSetSymbol.ONECHARSET);
|
|
73
|
+
if (!cs)
|
|
74
|
+
throw new Error(`Critical: No fallback CharSet available`);
|
|
75
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
|
|
71
76
|
}
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
76
|
-
this.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.usePowerOfTwo = isPowerOfTwo || (dduOptions?.usePowerOfTwo ?? true);
|
|
80
|
-
// encoding 설정
|
|
77
|
+
// 정규화 및 검증
|
|
78
|
+
const normalized = this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
|
|
79
|
+
this.dduChar = normalized.charSet;
|
|
80
|
+
this.paddingChar = normalized.padding;
|
|
81
|
+
this.charLength = normalized.charLength;
|
|
82
|
+
this.bitLength = normalized.bitLength;
|
|
83
|
+
this.isPredefinedCharSet = normalized.isPredefined;
|
|
81
84
|
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가 더 효율적 (캐시 미스 감소)
|
|
88
85
|
const dduLength = this.dduChar.length;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
86
|
+
this.usePowerOfTwo = dduLength > 0 && (dduLength & (dduLength - 1)) === 0;
|
|
87
|
+
this.effectiveBitLength = this.usePowerOfTwo ? this.bitLength : this.getBitLength(dduLength);
|
|
88
|
+
this.binaryChunkToIntFn = this.effectiveBitLength <= 26
|
|
89
|
+
? (chunk) => chunk.split('').reduce((v, c) => (v << 1) | (c.charCodeAt(0) & 1), 0)
|
|
90
|
+
: (chunk) => chunk.split('').reduce((v, c) => v * 2 + (c.charCodeAt(0) & 1), 0);
|
|
91
|
+
this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
|
|
92
|
+
if (this.charLength >= 2 && !this.isPredefinedCharSet) {
|
|
93
|
+
this.validateCombinationDuplicates(this.dduChar, this.paddingChar, dduLength);
|
|
98
94
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
* charset 초기화 (커스텀 또는 미리 정의된 charset)
|
|
103
|
-
*/
|
|
104
|
-
initializeCharSet(dduChar, paddingChar, dduOptions) {
|
|
105
|
-
const finalDduChar = dduChar ?? dduOptions?.dduChar;
|
|
106
|
-
const finalPaddingChar = paddingChar ?? dduOptions?.paddingChar;
|
|
107
|
-
// 1. 커스텀 charset 사용
|
|
108
|
-
if (finalDduChar) {
|
|
109
|
-
return this.processCustomCharSet(finalDduChar, finalPaddingChar, dduOptions);
|
|
95
|
+
if (this.effectiveBitLength <= 16) {
|
|
96
|
+
const size = 1 << this.effectiveBitLength;
|
|
97
|
+
this.indexToBinaryCache = Array.from({ length: size }, (_, i) => i.toString(2).padStart(this.effectiveBitLength, "0"));
|
|
110
98
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return this.processPredefinedCharSet(dduOptions.dduSetSymbol, dduOptions);
|
|
99
|
+
else {
|
|
100
|
+
this.indexToBinaryCache = null;
|
|
114
101
|
}
|
|
115
|
-
// 3. 기본 charset 사용
|
|
116
|
-
return this.processDefaultCharSet();
|
|
117
102
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// string 타입을 배열로 변환
|
|
126
|
-
const dduCharArray = this.convertToCharArray(dduChar);
|
|
127
|
-
const dduCharLength = dduOptions?.requiredLength ?? dduCharArray.length;
|
|
128
|
-
if (dduCharArray.length < dduCharLength) {
|
|
129
|
-
throw new Error(`dduChar must be at least ${dduCharLength} characters long. Provided: ${dduCharArray.length}`);
|
|
130
|
-
}
|
|
131
|
-
// usePowerOfTwo 처리
|
|
132
|
-
const result = this.applyPowerOfTwoOption(dduCharArray, paddingChar, dduCharLength, dduOptions?.usePowerOfTwo ?? true);
|
|
133
|
-
return {
|
|
134
|
-
...result,
|
|
135
|
-
isPredefined: false // 커스텀 charset은 검증 필요
|
|
103
|
+
normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions) {
|
|
104
|
+
const applyFallback = () => {
|
|
105
|
+
const fallbackSymbol = dduOptions?.dduSetSymbol ?? dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.ONECHARSET;
|
|
106
|
+
const cs = getCharSet(fallbackSymbol) ?? getCharSet(DduSetSymbol.ONECHARSET);
|
|
107
|
+
if (!cs)
|
|
108
|
+
throw new Error(`Critical: No fallback CharSet available`);
|
|
109
|
+
return { charSet: cs.charSet, padding: cs.paddingChar, requiredLength: cs.maxRequiredLength, bitLength: cs.bitLength, isPredefined: true };
|
|
136
110
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const { charSet, paddingChar, maxRequiredLength, bitLength } = fixedCharSet;
|
|
147
|
-
// usePowerOfTwo 처리
|
|
148
|
-
if (dduOptions?.usePowerOfTwo ?? true) {
|
|
149
|
-
const result = this.applyPowerOfTwoOption(charSet, paddingChar, maxRequiredLength, true);
|
|
150
|
-
return {
|
|
151
|
-
...result,
|
|
152
|
-
isPredefined: true // 미리 정의된 charset은 검증 패스
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
return {
|
|
156
|
-
finalCharSet: charSet,
|
|
157
|
-
finalPadding: paddingChar,
|
|
158
|
-
requiredLength: maxRequiredLength,
|
|
159
|
-
bitLength,
|
|
160
|
-
isPredefined: true // 미리 정의된 charset은 검증 패스
|
|
111
|
+
const validate = (condition, message) => {
|
|
112
|
+
if (condition) {
|
|
113
|
+
if (shouldThrowError)
|
|
114
|
+
throw new Error(message);
|
|
115
|
+
const fb = applyFallback();
|
|
116
|
+
[charSet, padding, requiredLength, bitLength, isPredefined] = [fb.charSet, fb.padding, fb.requiredLength, fb.bitLength, fb.isPredefined];
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
161
120
|
};
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
121
|
+
// 중복 제거 (커스텀 charset만)
|
|
122
|
+
if (!isPredefined) {
|
|
123
|
+
const uniqueChars = new Set(charSet);
|
|
124
|
+
if (uniqueChars.size !== charSet.length) {
|
|
125
|
+
if (shouldThrowError)
|
|
126
|
+
throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${charSet.length}`);
|
|
127
|
+
charSet = Array.from(uniqueChars);
|
|
128
|
+
requiredLength = charSet.length;
|
|
129
|
+
}
|
|
171
130
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
requiredLength: fixedCharSet.maxRequiredLength,
|
|
176
|
-
bitLength: fixedCharSet.bitLength,
|
|
177
|
-
isPredefined: true // 기본 charset도 미리 정의된 것이므로 검증 패스
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* fallback charset 가져오기
|
|
182
|
-
*/
|
|
183
|
-
getFallbackCharSet(dduOptions) {
|
|
184
|
-
const fallbackSymbol = dduOptions?.dduSetSymbol ??
|
|
185
|
-
dduDefaultConstructorOptions.dduSetSymbol ??
|
|
186
|
-
DduSetSymbol.DDU;
|
|
187
|
-
const fixedCharSet = getCharSet(fallbackSymbol);
|
|
188
|
-
if (!fixedCharSet) {
|
|
189
|
-
throw new Error(`Fallback CharSet with symbol ${fallbackSymbol} not found`);
|
|
131
|
+
// 검증
|
|
132
|
+
if (validate(charSet.length < requiredLength, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`)) {
|
|
133
|
+
return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
|
|
190
134
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
*/
|
|
201
|
-
convertToCharArray(input) {
|
|
202
|
-
if (typeof input === "string") {
|
|
203
|
-
return [...new Set(input.trim())].map((c) => c);
|
|
135
|
+
let charLength = charSet[0]?.length ?? 0;
|
|
136
|
+
validate(charLength === 0, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`);
|
|
137
|
+
const invalidIndex = charSet.findIndex(char => char.length !== charLength);
|
|
138
|
+
if (invalidIndex !== -1) {
|
|
139
|
+
if (shouldThrowError)
|
|
140
|
+
throw new Error(`All characters must have the same length. Expected: ${charLength}, but index ${invalidIndex} has length ${charSet[invalidIndex].length}`);
|
|
141
|
+
charSet = charSet.filter(char => char.length === charLength);
|
|
142
|
+
validate(charSet.length < requiredLength, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`);
|
|
143
|
+
charLength = charSet[0]?.length ?? 0;
|
|
204
144
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const powerOfTwoLength = Math.pow(2, powerOfTwoExponent);
|
|
214
|
-
return {
|
|
215
|
-
finalCharSet: charArray.slice(0, powerOfTwoLength),
|
|
216
|
-
finalPadding: padding,
|
|
217
|
-
requiredLength: powerOfTwoLength,
|
|
218
|
-
bitLength: powerOfTwoExponent,
|
|
219
|
-
};
|
|
145
|
+
validate(!charSet.length, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: 0`);
|
|
146
|
+
validate(padding.length !== charLength, `Padding character must have length ${charLength}, got ${padding.length}`);
|
|
147
|
+
if (new Set(charSet).has(padding)) {
|
|
148
|
+
if (shouldThrowError)
|
|
149
|
+
throw new Error(`Padding character "${padding}" cannot be in the character set`);
|
|
150
|
+
charSet = charSet.filter(char => char !== padding);
|
|
151
|
+
validate(charSet.length < requiredLength, `${this.constructor.name} requires at least ${requiredLength} characters. Provided: ${charSet.length}`);
|
|
152
|
+
charLength = charSet[0]?.length ?? 0;
|
|
220
153
|
}
|
|
221
|
-
return {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
bitLength: this.getBitLength(length),
|
|
226
|
-
};
|
|
154
|
+
return { charSet: charSet.slice(0, requiredLength), padding, requiredLength, bitLength, isPredefined, charLength };
|
|
155
|
+
}
|
|
156
|
+
getBinaryFromIndex(index) {
|
|
157
|
+
return this.indexToBinaryCache?.[index] ?? index.toString(2).padStart(this.effectiveBitLength, "0");
|
|
227
158
|
}
|
|
228
|
-
/**
|
|
229
|
-
* 2글자 이상의 문자셋에서 조합으로 인한 중복을 검증
|
|
230
|
-
* 예: ["AB", "CD"] + padding "AB" 또는 "A" + "B" = "AB" 같은 경우 감지
|
|
231
|
-
*/
|
|
232
159
|
validateCombinationDuplicates(charSet, paddingChar, requiredLength) {
|
|
233
160
|
const charLength = charSet[0].length;
|
|
234
|
-
const allStrings = new Set();
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
// 조합이 기존 문자와 중복되는지 확인
|
|
243
|
-
if (allStrings.has(combination)) {
|
|
244
|
-
throw new Error(`Combination conflict detected: "${charSet[i]}" + "${charSet[j]}" = "${combination}" already exists in the character set or padding`);
|
|
161
|
+
const allStrings = new Set([...charSet.slice(0, requiredLength), paddingChar]);
|
|
162
|
+
const limit = Math.min(charSet.length, requiredLength);
|
|
163
|
+
// 2개 조합 검사
|
|
164
|
+
for (let i = 0; i < limit; i++) {
|
|
165
|
+
for (let j = 0; j < limit; j++) {
|
|
166
|
+
const combo = charSet[i] + charSet[j];
|
|
167
|
+
if (allStrings.has(combo)) {
|
|
168
|
+
throw new Error(`Combination conflict: "${charSet[i]}" + "${charSet[j]}" = "${combo}" already exists`);
|
|
245
169
|
}
|
|
246
170
|
}
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (allStrings.has(paddingPlusChar)) {
|
|
254
|
-
throw new Error(`Combination conflict detected: padding "${paddingChar}" + "${charSet[i]}" = "${paddingPlusChar}" already exists in the character set`);
|
|
255
|
-
}
|
|
171
|
+
const charPad = charSet[i] + paddingChar;
|
|
172
|
+
const padChar = paddingChar + charSet[i];
|
|
173
|
+
if (allStrings.has(charPad))
|
|
174
|
+
throw new Error(`Combination conflict: "${charSet[i]}" + padding "${paddingChar}" = "${charPad}"`);
|
|
175
|
+
if (allStrings.has(padChar))
|
|
176
|
+
throw new Error(`Combination conflict: padding "${paddingChar}" + "${charSet[i]}" = "${padChar}"`);
|
|
256
177
|
}
|
|
257
|
-
//
|
|
258
|
-
const
|
|
259
|
-
if (allStrings.has(
|
|
260
|
-
throw new Error(`Combination conflict
|
|
178
|
+
// 패딩 + 패딩 조합 검사
|
|
179
|
+
const doublePad = paddingChar + paddingChar;
|
|
180
|
+
if (allStrings.has(doublePad)) {
|
|
181
|
+
throw new Error(`Combination conflict: double padding "${doublePad}" already exists`);
|
|
261
182
|
}
|
|
262
|
-
//
|
|
263
|
-
// 모든 조합을 검사하면 O(n^3)이므로 일부만 샘플링
|
|
183
|
+
// 3개 조합 검사 (100개 이하만)
|
|
264
184
|
if (charLength >= 2 && requiredLength <= 100) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (allStrings.has(substring) && substring !== charSet[i] && substring !== charSet[j] && substring !== charSet[k]) {
|
|
274
|
-
throw new Error(`Combination conflict detected: substring "${substring}" from "${charSet[i]}" + "${charSet[j]}" + "${charSet[k]}" conflicts with existing character`);
|
|
185
|
+
for (let i = 0; i < limit; i++) {
|
|
186
|
+
for (let j = 0; j < limit; j++) {
|
|
187
|
+
for (let k = 0; k < limit; k++) {
|
|
188
|
+
const combo = charSet[i] + charSet[j] + charSet[k];
|
|
189
|
+
for (let start = 0; start <= combo.length - charLength; start++) {
|
|
190
|
+
const sub = combo.substring(start, start + charLength);
|
|
191
|
+
if (allStrings.has(sub) && sub !== charSet[i] && sub !== charSet[j] && sub !== charSet[k]) {
|
|
192
|
+
throw new Error(`Combination conflict: substring "${sub}" from "${charSet[i]}" + "${charSet[j]}" + "${charSet[k]}"`);
|
|
275
193
|
}
|
|
276
194
|
}
|
|
277
195
|
}
|
|
@@ -284,17 +202,14 @@ export class Ddu64 extends BaseDdu {
|
|
|
284
202
|
const bufferInput = typeof input === "string" ? Buffer.from(input, this.encoding) : input;
|
|
285
203
|
// 생성자에서 설정된 값 사용
|
|
286
204
|
const dduLength = this.dduChar.length;
|
|
287
|
-
const effectiveBitLength = this.
|
|
288
|
-
? this.bitLength
|
|
289
|
-
: this.getBitLength(dduLength);
|
|
205
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
290
206
|
const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, effectiveBitLength);
|
|
291
207
|
// 문자열 연결 최적화: Array + join 사용
|
|
292
208
|
const resultParts = new Array(dduBinary.length);
|
|
293
209
|
// 각 비트 청크를 변환
|
|
294
210
|
for (let i = 0; i < dduBinary.length; i++) {
|
|
295
211
|
const binaryChunk = dduBinary[i];
|
|
296
|
-
|
|
297
|
-
const charInt = this.binaryToIntLookup.get(binaryChunk) ?? parseInt(binaryChunk, 2);
|
|
212
|
+
const charInt = this.binaryChunkToIntFn(binaryChunk);
|
|
298
213
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
299
214
|
// 가변 길이 조합 인코딩
|
|
300
215
|
const quotient = Math.floor(charInt / dduLength);
|
|
@@ -326,9 +241,7 @@ export class Ddu64 extends BaseDdu {
|
|
|
326
241
|
let dduBinary = "";
|
|
327
242
|
// 생성자에서 설정된 값 사용
|
|
328
243
|
const dduLength = this.dduChar.length;
|
|
329
|
-
const effectiveBitLength = this.
|
|
330
|
-
? this.bitLength
|
|
331
|
-
: this.getBitLength(dduLength);
|
|
244
|
+
const effectiveBitLength = this.effectiveBitLength;
|
|
332
245
|
if (this.charLength === 1 && !this.usePowerOfTwo) {
|
|
333
246
|
// 가변 길이 조합 디코딩 (2개씩 읽음)
|
|
334
247
|
for (let i = 0; i < input.length; i += 2) {
|
|
@@ -339,7 +252,7 @@ export class Ddu64 extends BaseDdu {
|
|
|
339
252
|
if (firstIndex === undefined || secondIndex === undefined)
|
|
340
253
|
throw new Error(`Invalid character: ${firstChar} or ${secondChar}`);
|
|
341
254
|
const value = firstIndex * dduLength + secondIndex;
|
|
342
|
-
dduBinary +=
|
|
255
|
+
dduBinary += this.getBinaryFromIndex(value);
|
|
343
256
|
}
|
|
344
257
|
}
|
|
345
258
|
else {
|
|
@@ -349,7 +262,7 @@ export class Ddu64 extends BaseDdu {
|
|
|
349
262
|
const charIndex = this.dduBinaryLookup.get(charChunk);
|
|
350
263
|
if (charIndex === undefined)
|
|
351
264
|
throw new Error(`Invalid character: ${charChunk}`);
|
|
352
|
-
dduBinary +=
|
|
265
|
+
dduBinary += this.getBinaryFromIndex(charIndex);
|
|
353
266
|
}
|
|
354
267
|
}
|
|
355
268
|
const decoded = this.dduBinaryToBuffer(dduBinary, paddingBits);
|