@ddunigma/node 2.1.1 → 3.0.0
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 +178 -229
- package/dist/DduStream-NP3CMCKK.js +2 -0
- package/dist/chunk-FVBZKVPE.js +4 -0
- package/dist/index.cjs +7 -0
- package/dist/index.d.cts +784 -0
- package/dist/index.d.ts +784 -0
- package/dist/index.js +5 -0
- package/package.json +39 -16
- package/dist/cjs/base/BaseDdu.d.ts +0 -53
- package/dist/cjs/base/BaseDdu.js +0 -29
- package/dist/cjs/base/index.d.ts +0 -1
- package/dist/cjs/base/index.js +0 -5
- package/dist/cjs/charSets/index.d.ts +0 -18
- package/dist/cjs/charSets/index.js +0 -4385
- package/dist/cjs/encoders/Ddu64.d.ts +0 -302
- package/dist/cjs/encoders/Ddu64.js +0 -1308
- package/dist/cjs/encoders/index.d.ts +0 -1
- package/dist/cjs/encoders/index.js +0 -5
- package/dist/cjs/index.d.ts +0 -5
- package/dist/cjs/index.js +0 -16
- package/dist/cjs/package.json +0 -3
- package/dist/cjs/types/DduDefaultTypes.d.ts +0 -3
- package/dist/cjs/types/DduDefaultTypes.js +0 -17
- package/dist/cjs/types/DduEnums.d.ts +0 -6
- package/dist/cjs/types/DduEnums.js +0 -10
- package/dist/cjs/types/DduInterface.d.ts +0 -91
- package/dist/cjs/types/DduInterface.js +0 -2
- package/dist/cjs/types/index.d.ts +0 -3
- package/dist/cjs/types/index.js +0 -19
- package/dist/cjs/utils/CharsetBuilder.d.ts +0 -129
- package/dist/cjs/utils/CharsetBuilder.js +0 -246
- package/dist/cjs/utils/DduPipeline.d.ts +0 -114
- package/dist/cjs/utils/DduPipeline.js +0 -229
- package/dist/cjs/utils/DduStream.d.ts +0 -64
- package/dist/cjs/utils/DduStream.js +0 -221
- package/dist/cjs/utils/crypto.d.ts +0 -30
- package/dist/cjs/utils/crypto.js +0 -84
- package/dist/cjs/utils/index.d.ts +0 -3
- package/dist/cjs/utils/index.js +0 -12
- package/dist/mjs/base/BaseDdu.d.ts +0 -53
- package/dist/mjs/base/BaseDdu.js +0 -23
- package/dist/mjs/base/index.d.ts +0 -1
- package/dist/mjs/base/index.js +0 -1
- package/dist/mjs/charSets/index.d.ts +0 -18
- package/dist/mjs/charSets/index.js +0 -4380
- package/dist/mjs/encoders/Ddu64.d.ts +0 -302
- package/dist/mjs/encoders/Ddu64.js +0 -1343
- package/dist/mjs/encoders/index.d.ts +0 -1
- package/dist/mjs/encoders/index.js +0 -1
- package/dist/mjs/index.d.ts +0 -5
- package/dist/mjs/index.js +0 -4
- package/dist/mjs/package.json +0 -3
- package/dist/mjs/types/DduDefaultTypes.d.ts +0 -3
- package/dist/mjs/types/DduDefaultTypes.js +0 -14
- package/dist/mjs/types/DduEnums.d.ts +0 -6
- package/dist/mjs/types/DduEnums.js +0 -7
- package/dist/mjs/types/DduInterface.d.ts +0 -91
- package/dist/mjs/types/DduInterface.js +0 -1
- package/dist/mjs/types/index.d.ts +0 -3
- package/dist/mjs/types/index.js +0 -3
- package/dist/mjs/utils/CharsetBuilder.d.ts +0 -129
- package/dist/mjs/utils/CharsetBuilder.js +0 -241
- package/dist/mjs/utils/DduPipeline.d.ts +0 -114
- package/dist/mjs/utils/DduPipeline.js +0 -223
- package/dist/mjs/utils/DduStream.d.ts +0 -64
- package/dist/mjs/utils/DduStream.js +0 -225
- package/dist/mjs/utils/crypto.d.ts +0 -30
- package/dist/mjs/utils/crypto.js +0 -78
- package/dist/mjs/utils/index.d.ts +0 -3
- package/dist/mjs/utils/index.js +0 -3
package/README.md
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
# ddunigma Node
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@ddunigma/node)
|
|
4
4
|
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
Node.js implementation of [ddunigma](https://github.com/i3l3/ddunigma) (Python original)
|
|
8
|
-
|
|
9
5
|
커스텀 charset을 사용하는 Base64 스타일 인코더/디코더 라이브러리입니다.
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
V2 추가사항
|
|
12
8
|
|
|
13
|
-
-
|
|
14
|
-
- [@i3ls](https://github.com/i3l3)
|
|
15
|
-
- [@gunu3371](https://github.com/gunu3371)
|
|
16
|
-
- Original Repository: [ddunigma](https://github.com/i3l3/ddunigma)
|
|
9
|
+
- 이제 한글 종성 결합 시스템을 활용하여 8개 기본 문자 × 8개 종성으로 64가지 조합을 만들어, 6비트를 한 글자로 표현합니다.
|
|
17
10
|
|
|
18
11
|
## Requirements
|
|
19
12
|
|
|
20
|
-
- **Node.js >=
|
|
13
|
+
- **Node.js >= 18.0.0**
|
|
21
14
|
|
|
22
15
|
## Install
|
|
23
16
|
|
|
@@ -25,136 +18,143 @@ Node.js implementation of [ddunigma](https://github.com/i3l3/ddunigma) (Python o
|
|
|
25
18
|
npm install @ddunigma/node
|
|
26
19
|
```
|
|
27
20
|
|
|
28
|
-
##
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Ddu64, DduSetSymbol } from "@ddunigma/node";
|
|
25
|
+
|
|
26
|
+
// V2 (기본, 한글 종성 결합 64개)
|
|
27
|
+
const ddu = new Ddu64();
|
|
28
|
+
ddu.encode("안녕하세요"); // "뎯땩잇땨뎪뎨잇잉뎯욱잇우뎯땨읶뎨뎯땩듂잊"
|
|
29
|
+
ddu.decode("뎯땩잇땨뎪뎨잇잉뎯욱잇우뎯땨읶뎨뎯땩듂잊"); // "안녕하세요"
|
|
30
|
+
|
|
31
|
+
// V1 (구버전 호환, 8개 문자 쌍 방식)
|
|
32
|
+
const dduV1 = new Ddu64(undefined, undefined, { dduSetSymbol: DduSetSymbol.DDU_V1 });
|
|
33
|
+
dduV1.encode("안녕하세요"); // ".우땨땨이?땨뜌.이.뜌이?이!.우우땨이?우뜌.우땨뜌이이.뜌.우땨땨!이이야"
|
|
34
|
+
dduV1.decode(".우땨땨이?땨뜌.이.뜌이?이!.우우땨이?우뜌.우땨뜌이이.뜌.우땨땨!이이야"); // "안녕하세요"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 기본 사용법
|
|
29
40
|
|
|
30
|
-
###
|
|
41
|
+
### 인코딩/디코딩
|
|
31
42
|
|
|
32
43
|
```typescript
|
|
33
44
|
import { Ddu64 } from "@ddunigma/node";
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
const encoder = new Ddu64("우따야", "뭐");
|
|
37
|
-
|
|
38
|
-
const text = "Hello World!";
|
|
39
|
-
const encoded = encoder.encode(text);
|
|
40
|
-
console.log(encoded); // 우따야뭐우따... (인코딩된 문자열)
|
|
46
|
+
const encoder = new Ddu64();
|
|
41
47
|
|
|
48
|
+
const encoded = encoder.encode("Hello World!");
|
|
42
49
|
const decoded = encoder.decode(encoded);
|
|
43
|
-
console.log(decoded); // "Hello World!"
|
|
44
50
|
```
|
|
45
51
|
|
|
46
|
-
###
|
|
52
|
+
### 커스텀 charset
|
|
47
53
|
|
|
48
54
|
```typescript
|
|
49
|
-
|
|
55
|
+
// 문자열 또는 배열로 charset 지정
|
|
56
|
+
const encoder = new Ddu64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "=");
|
|
50
57
|
|
|
51
|
-
//
|
|
52
|
-
const
|
|
53
|
-
|
|
58
|
+
// 종성 결합 커스텀 charset (dduChar × codaChar 동적 생성)
|
|
59
|
+
const encoder2 = new Ddu64(["가", "나", "다", "라"], "뭐", {
|
|
60
|
+
codaChar: ["", "ㄱ", "ㄲ", "ㄷ"], // 4×4 = 16개 조합
|
|
54
61
|
});
|
|
62
|
+
```
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
const encoder2 = new Ddu64(undefined, undefined, {
|
|
58
|
-
dduSetSymbol: DduSetSymbol.DDU,
|
|
59
|
-
});
|
|
64
|
+
### 프리셋
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
| Symbol | 문자 수 | 비트 | 설명 |
|
|
67
|
+
| ------------ | ------- | ---- | ------------------------------------ |
|
|
68
|
+
| `DDU` | 64 | 6 | 한글 종성 결합 (8 기본문자 × 8 종성) |
|
|
69
|
+
| `DDU_V1` | 8 | 3 | 구버전 호환 (뜌땨이우야!?.) |
|
|
70
|
+
| `ONECHARSET` | 64 | 6 | 영문 + 숫자 + 특수문자 |
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
dduSetSymbol: DduSetSymbol.THREECHARSET,
|
|
69
|
-
});
|
|
72
|
+
```typescript
|
|
73
|
+
import { Ddu64, DduSetSymbol } from "@ddunigma/node";
|
|
70
74
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
const encoder = new Ddu64(undefined, undefined, {
|
|
76
|
+
dduSetSymbol: DduSetSymbol.ONECHARSET,
|
|
77
|
+
});
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 고급 기능
|
|
83
|
+
|
|
84
|
+
### 압축
|
|
85
|
+
|
|
86
|
+
deflate(기본) 또는 brotli 압축을 지원합니다. 디코딩 시 자동으로 압축 여부를 감지합니다.
|
|
77
87
|
|
|
78
88
|
```typescript
|
|
79
|
-
|
|
89
|
+
const encoder = new Ddu64();
|
|
80
90
|
|
|
81
|
-
//
|
|
82
|
-
const
|
|
91
|
+
// 호출 시 옵션으로 지정
|
|
92
|
+
const encoded = encoder.encode(longText, { compress: true });
|
|
93
|
+
const decoded = encoder.decode(encoded);
|
|
94
|
+
|
|
95
|
+
// 생성자에서 기본 활성화
|
|
96
|
+
const compressEncoder = new Ddu64(undefined, undefined, {
|
|
83
97
|
compress: true,
|
|
98
|
+
compressionAlgorithm: "brotli",
|
|
99
|
+
compressionLevel: 6,
|
|
84
100
|
});
|
|
85
|
-
|
|
86
|
-
// 또는 encode 호출 시 압축 옵션 지정
|
|
87
|
-
const text = "반복되는 긴 텍스트...".repeat(100);
|
|
88
|
-
const encoded = encoder.encode(text, { compress: true });
|
|
89
|
-
const decoded = encoder.decode(encoded); // 자동으로 압축 해제
|
|
90
101
|
```
|
|
91
102
|
|
|
92
|
-
###
|
|
103
|
+
### 암호화
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
import { Ddu64 } from "@ddunigma/node";
|
|
105
|
+
AES-256-GCM 암호화를 내장합니다. 동일한 키로 생성된 인코더만 복호화할 수 있습니다.
|
|
96
106
|
|
|
107
|
+
```typescript
|
|
97
108
|
const encoder = new Ddu64(undefined, undefined, {
|
|
98
|
-
|
|
99
|
-
urlSafe: true, // +, /, = 를 URL 안전 문자로 변환
|
|
109
|
+
encryptionKey: "my-secret-key",
|
|
100
110
|
});
|
|
101
111
|
|
|
102
|
-
const encoded = encoder.encode("
|
|
103
|
-
|
|
112
|
+
const encoded = encoder.encode("비밀 메시지");
|
|
113
|
+
const decoded = encoder.decode(encoded);
|
|
104
114
|
```
|
|
105
115
|
|
|
106
|
-
### 체크섬
|
|
116
|
+
### 체크섬
|
|
107
117
|
|
|
108
|
-
|
|
109
|
-
import { Ddu64 } from "@ddunigma/node";
|
|
118
|
+
CRC32 체크섬으로 데이터 무결성을 검증합니다.
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
});
|
|
120
|
+
```typescript
|
|
121
|
+
const encoder = new Ddu64();
|
|
114
122
|
|
|
115
|
-
const encoded = encoder.encode(
|
|
116
|
-
const decoded = encoder.decode(encoded
|
|
117
|
-
//
|
|
123
|
+
const encoded = encoder.encode(data, { checksum: true });
|
|
124
|
+
const decoded = encoder.decode(encoded, { checksum: true });
|
|
125
|
+
// 데이터 변조 시 에러 발생
|
|
118
126
|
```
|
|
119
127
|
|
|
120
|
-
###
|
|
128
|
+
### URL-Safe
|
|
121
129
|
|
|
122
|
-
|
|
123
|
-
import { Ddu64 } from "@ddunigma/node";
|
|
130
|
+
`+`, `/`, `=` 를 URL 안전 문자(`-`, `_`, `.`)로 변환합니다.
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
```typescript
|
|
133
|
+
const encoder = new Ddu64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "=", {
|
|
134
|
+
urlSafe: true,
|
|
127
135
|
});
|
|
128
|
-
|
|
129
|
-
const encoded = encoder.encode("Secret message!");
|
|
130
|
-
const decoded = encoder.decode(encoded); // 자동으로 복호화
|
|
131
136
|
```
|
|
132
137
|
|
|
138
|
+
> charset/padding에 `-`, `_`, `.` 가 포함되면 urlSafe를 활성화할 수 없습니다.
|
|
139
|
+
|
|
133
140
|
### 청크 분할
|
|
134
141
|
|
|
135
142
|
```typescript
|
|
136
|
-
import { Ddu64 } from "@ddunigma/node";
|
|
137
|
-
|
|
138
143
|
const encoder = new Ddu64();
|
|
139
144
|
|
|
140
|
-
const encoded = encoder.encode(
|
|
141
|
-
chunkSize: 76,
|
|
142
|
-
chunkSeparator: "\n",
|
|
145
|
+
const encoded = encoder.encode(data, {
|
|
146
|
+
chunkSize: 76,
|
|
147
|
+
chunkSeparator: "\n",
|
|
143
148
|
});
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
const decoded = encoder.decode(encoded); // 자동으로 줄바꿈 제거
|
|
149
|
+
// 디코딩 시 구분자 자동 제거
|
|
147
150
|
```
|
|
148
151
|
|
|
149
|
-
### 비동기
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
import { Ddu64 } from "@ddunigma/node";
|
|
152
|
+
### 비동기 처리
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
대용량 데이터에서 이벤트 루프 블로킹을 방지합니다.
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
const encoded = await encoder.encodeAsync(
|
|
156
|
+
```typescript
|
|
157
|
+
const encoded = await encoder.encodeAsync(largeBuffer);
|
|
158
158
|
const decoded = await encoder.decodeAsync(encoded);
|
|
159
159
|
const buffer = await encoder.decodeToBufferAsync(encoded);
|
|
160
160
|
```
|
|
@@ -162,81 +162,51 @@ const buffer = await encoder.decodeToBufferAsync(encoded);
|
|
|
162
162
|
### 진행률 콜백
|
|
163
163
|
|
|
164
164
|
```typescript
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
encoder.encode(largeData, {
|
|
170
|
-
onProgress: (info) => {
|
|
171
|
-
console.log(`진행률: ${info.percent}%`);
|
|
172
|
-
console.log(`처리됨: ${info.processedBytes}/${info.totalBytes}`);
|
|
165
|
+
encoder.encode(data, {
|
|
166
|
+
onProgress: ({ percent, stage }) => {
|
|
167
|
+
console.log(`${percent}% (${stage})`);
|
|
173
168
|
},
|
|
174
169
|
});
|
|
175
170
|
```
|
|
176
171
|
|
|
177
|
-
###
|
|
172
|
+
### 통계
|
|
178
173
|
|
|
179
174
|
```typescript
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const encoder = new Ddu64();
|
|
183
|
-
|
|
184
|
-
const stats = encoder.getStats("Test data", { compress: true });
|
|
185
|
-
console.log(stats);
|
|
186
|
-
// {
|
|
187
|
-
// originalSize: 9,
|
|
188
|
-
// encodedSize: 12,
|
|
189
|
-
// compressedSize: 17,
|
|
190
|
-
// compressionRatio: 1.89,
|
|
191
|
-
// expansionRatio: 1.33,
|
|
192
|
-
// charsetSize: 64,
|
|
193
|
-
// bitLength: 6
|
|
194
|
-
// }
|
|
175
|
+
const stats = encoder.getStats(data, { compress: true });
|
|
176
|
+
// { originalSize, encodedSize, compressedSize, compressionRatio, expansionRatio, charsetSize, bitLength }
|
|
195
177
|
```
|
|
196
178
|
|
|
197
179
|
### Zip Bomb 방어
|
|
198
180
|
|
|
199
181
|
```typescript
|
|
200
|
-
import { Ddu64 } from "@ddunigma/node";
|
|
201
|
-
|
|
202
182
|
const encoder = new Ddu64(undefined, undefined, {
|
|
203
183
|
maxDecodedBytes: 10 * 1024 * 1024, // 10MB
|
|
204
184
|
maxDecompressedBytes: 50 * 1024 * 1024, // 50MB
|
|
205
185
|
});
|
|
206
|
-
|
|
207
|
-
// 제한 초과 시 에러 발생
|
|
208
186
|
```
|
|
209
187
|
|
|
210
188
|
---
|
|
211
189
|
|
|
212
190
|
## CharsetBuilder
|
|
213
191
|
|
|
214
|
-
커스텀 charset을
|
|
192
|
+
커스텀 charset을 빌더 패턴으로 생성합니다.
|
|
215
193
|
|
|
216
194
|
```typescript
|
|
217
195
|
import { CharsetBuilder } from "@ddunigma/node";
|
|
218
196
|
|
|
219
|
-
// 유니코드
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// Base64 문자셋
|
|
223
|
-
const chars2 = CharsetBuilder.base64().build();
|
|
197
|
+
// 유니코드 범위
|
|
198
|
+
CharsetBuilder.fromUnicodeRange(0x4e00, 0x4e3f).build();
|
|
224
199
|
|
|
225
|
-
// 혼동 문자 제외
|
|
226
|
-
|
|
200
|
+
// Base64에서 혼동 문자 제외
|
|
201
|
+
CharsetBuilder.base64().excludeConfusing().build();
|
|
227
202
|
|
|
228
|
-
// 2의 제곱수로 제한
|
|
229
|
-
|
|
203
|
+
// 2의 제곱수로 제한 + 시드 셔플
|
|
204
|
+
CharsetBuilder.fromString("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
|
230
205
|
.limitToPowerOfTwo()
|
|
231
|
-
.
|
|
206
|
+
.shuffle(12345)
|
|
207
|
+
.build();
|
|
232
208
|
|
|
233
|
-
//
|
|
234
|
-
const chars5 = CharsetBuilder.base64().excludeUrlUnsafe().build();
|
|
235
|
-
|
|
236
|
-
// 시드 기반 셔플
|
|
237
|
-
const chars6 = CharsetBuilder.base64().shuffle(12345).build();
|
|
238
|
-
|
|
239
|
-
// 패딩 문자와 함께 빌드
|
|
209
|
+
// 패딩 문자 자동 선택
|
|
240
210
|
const { charset, padding } = CharsetBuilder.base64().buildWithPadding();
|
|
241
211
|
```
|
|
242
212
|
|
|
@@ -244,44 +214,37 @@ const { charset, padding } = CharsetBuilder.base64().buildWithPadding();
|
|
|
244
214
|
|
|
245
215
|
## DduPipeline
|
|
246
216
|
|
|
247
|
-
|
|
217
|
+
압축 → 암호화 → 인코딩을 체이닝하고, `reverse()`로 역순 복원합니다.
|
|
248
218
|
|
|
249
219
|
```typescript
|
|
250
220
|
import { DduPipeline, Ddu64 } from "@ddunigma/node";
|
|
251
221
|
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
// 압축 → 암호화 → 인코딩 파이프라인
|
|
255
|
-
const pipeline = new DduPipeline()
|
|
256
|
-
.compress()
|
|
257
|
-
.encrypt("my-secret-key")
|
|
258
|
-
.encode(encoder);
|
|
259
|
-
|
|
260
|
-
const encoded = pipeline.processToString("Hello World!");
|
|
222
|
+
const pipeline = new DduPipeline().compress(6, "brotli").encrypt("secret-key").encode(new Ddu64());
|
|
261
223
|
|
|
262
|
-
|
|
224
|
+
const encoded = pipeline.processToString("Hello");
|
|
263
225
|
const decoded = pipeline.reverse().processToString(encoded);
|
|
264
226
|
```
|
|
265
227
|
|
|
266
228
|
---
|
|
267
229
|
|
|
268
|
-
## 스트림
|
|
230
|
+
## 스트림
|
|
269
231
|
|
|
270
|
-
대용량
|
|
232
|
+
대용량 파일을 메모리 효율적으로 처리합니다. 스트림 헤더로 압축/암호화를 자동 감지합니다.
|
|
271
233
|
|
|
272
234
|
```typescript
|
|
273
235
|
import { Ddu64, createEncodeStream, createDecodeStream } from "@ddunigma/node";
|
|
274
236
|
import fs from "fs";
|
|
275
237
|
|
|
276
|
-
const encoder = new Ddu64(
|
|
238
|
+
const encoder = new Ddu64(undefined, undefined, {
|
|
239
|
+
compress: true,
|
|
240
|
+
encryptionKey: "stream-key",
|
|
241
|
+
});
|
|
277
242
|
|
|
278
|
-
// 인코딩 스트림
|
|
279
243
|
fs.createReadStream("input.bin")
|
|
280
244
|
.pipe(createEncodeStream(encoder))
|
|
281
|
-
.pipe(fs.createWriteStream("output.
|
|
245
|
+
.pipe(fs.createWriteStream("output.ddu"));
|
|
282
246
|
|
|
283
|
-
|
|
284
|
-
fs.createReadStream("output.txt")
|
|
247
|
+
fs.createReadStream("output.ddu")
|
|
285
248
|
.pipe(createDecodeStream(encoder))
|
|
286
249
|
.pipe(fs.createWriteStream("restored.bin"));
|
|
287
250
|
```
|
|
@@ -292,99 +255,85 @@ fs.createReadStream("output.txt")
|
|
|
292
255
|
|
|
293
256
|
### `new Ddu64(dduChar?, paddingChar?, options?)`
|
|
294
257
|
|
|
295
|
-
인코더 인스턴스를 생성합니다.
|
|
296
|
-
|
|
297
|
-
**Parameters:**
|
|
298
|
-
|
|
299
258
|
| Parameter | Type | Description |
|
|
300
259
|
| ------------- | ----------------------- | ------------------------ |
|
|
301
260
|
| `dduChar` | `string \| string[]` | charset 문자열 또는 배열 |
|
|
302
261
|
| `paddingChar` | `string` | 패딩 문자 |
|
|
303
262
|
| `options` | `DduConstructorOptions` | 옵션 객체 |
|
|
304
263
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
| Option | Type
|
|
308
|
-
| ---------------------- |
|
|
309
|
-
| `dduSetSymbol` | `DduSetSymbol`
|
|
310
|
-
| `
|
|
311
|
-
| `
|
|
312
|
-
| `
|
|
313
|
-
| `
|
|
314
|
-
| `
|
|
315
|
-
| `
|
|
316
|
-
| `
|
|
317
|
-
| `
|
|
318
|
-
| `
|
|
319
|
-
| `
|
|
320
|
-
| `
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
|
329
|
-
|
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
264
|
+
**생성자 옵션:**
|
|
265
|
+
|
|
266
|
+
| Option | Type | Default | Description |
|
|
267
|
+
| ---------------------- | ----------------------- | ----------- | ----------------------------- |
|
|
268
|
+
| `dduSetSymbol` | `DduSetSymbol` | `DDU` | 프리셋 심볼 |
|
|
269
|
+
| `codaChar` | `string[]` | — | 종성 배열 (동적 charset 생성) |
|
|
270
|
+
| `encoding` | `BufferEncoding` | `'utf-8'` | 문자열 인코딩 |
|
|
271
|
+
| `compress` | `boolean` | `false` | 기본 압축 활성화 |
|
|
272
|
+
| `compressionAlgorithm` | `"deflate" \| "brotli"` | `'deflate'` | 압축 알고리즘 |
|
|
273
|
+
| `compressionLevel` | `number` | `6` | 압축 레벨 |
|
|
274
|
+
| `urlSafe` | `boolean` | `false` | URL-Safe 모드 |
|
|
275
|
+
| `encryptionKey` | `string` | — | AES-256-GCM 암호화 키 |
|
|
276
|
+
| `checksum` | `boolean` | `false` | CRC32 체크섬 |
|
|
277
|
+
| `chunkSize` | `number` | — | 청크 분할 크기 |
|
|
278
|
+
| `chunkSeparator` | `string` | `'\n'` | 청크 구분자 |
|
|
279
|
+
| `maxDecodedBytes` | `number` | `67108864` | 최대 디코딩 바이트 (64MB) |
|
|
280
|
+
| `maxDecompressedBytes` | `number` | `67108864` | 최대 압축해제 바이트 (64MB) |
|
|
281
|
+
| `throwOnError` | `boolean` | `false` | 초기화 오류 시 throw |
|
|
282
|
+
| `useRepeatPadding` | `boolean` | `false` | 패딩 문자 반복 방식 |
|
|
283
|
+
| `usePowerOfTwo` | `boolean` | `true` | 2의 제곱수 charset 강제 |
|
|
284
|
+
|
|
285
|
+
**메서드:**
|
|
286
|
+
|
|
287
|
+
| Method | Return | Description |
|
|
288
|
+
| ---------------------------------------- | ----------------- | -------------------- |
|
|
289
|
+
| `encode(data, options?)` | `string` | 인코딩 |
|
|
290
|
+
| `decode(encoded, options?)` | `string` | 디코딩 |
|
|
291
|
+
| `decodeToBuffer(encoded, options?)` | `Buffer` | Buffer로 디코딩 |
|
|
292
|
+
| `encodeAsync(data, options?)` | `Promise<string>` | 비동기 인코딩 |
|
|
293
|
+
| `decodeAsync(encoded, options?)` | `Promise<string>` | 비동기 디코딩 |
|
|
294
|
+
| `decodeToBufferAsync(encoded, options?)` | `Promise<Buffer>` | 비동기 Buffer 디코딩 |
|
|
295
|
+
| `getStats(data, options?)` | `DduEncodeStats` | 인코딩 통계 |
|
|
296
|
+
| `getCharSetInfo()` | `CharSetInfo` | charset 정보 |
|
|
297
|
+
|
|
298
|
+
**encode/decode 옵션 (DduOptions):**
|
|
299
|
+
|
|
300
|
+
| Option | Type | Description |
|
|
301
|
+
| ---------------------- | ----------------------- | -------------------- |
|
|
302
|
+
| `compress` | `boolean` | 압축 사용 |
|
|
303
|
+
| `compressionAlgorithm` | `"deflate" \| "brotli"` | 압축 알고리즘 |
|
|
304
|
+
| `compressionLevel` | `number` | 압축 레벨 |
|
|
305
|
+
| `checksum` | `boolean` | 체크섬 추가/검증 |
|
|
306
|
+
| `chunkSize` | `number` | 청크 분할 크기 |
|
|
307
|
+
| `chunkSeparator` | `string` | 청크 구분자 |
|
|
308
|
+
| `maxDecodedBytes` | `number` | 최대 디코딩 바이트 |
|
|
309
|
+
| `maxDecompressedBytes` | `number` | 최대 압축해제 바이트 |
|
|
310
|
+
| `onProgress` | `function` | 진행률 콜백 |
|
|
343
311
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
비동기로 데이터를 인코딩합니다.
|
|
347
|
-
|
|
348
|
-
### `decodeAsync(encoded, options?): Promise<string>`
|
|
349
|
-
|
|
350
|
-
비동기로 데이터를 디코딩합니다.
|
|
351
|
-
|
|
352
|
-
### `decodeToBufferAsync(encoded, options?): Promise<Buffer>`
|
|
353
|
-
|
|
354
|
-
비동기로 Buffer로 디코딩합니다.
|
|
355
|
-
|
|
356
|
-
### `getStats(data, options?): DduEncodeStats`
|
|
312
|
+
---
|
|
357
313
|
|
|
358
|
-
|
|
314
|
+
## Testing
|
|
359
315
|
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
compressedSize?: number; // 압축된 크기
|
|
365
|
-
compressionRatio?: number; // 압축률 (0-1)
|
|
366
|
-
expansionRatio: number; // 인코딩 확장 비율
|
|
367
|
-
charsetSize: number; // charset 크기
|
|
368
|
-
bitLength: number; // 비트 길이
|
|
369
|
-
}
|
|
316
|
+
```bash
|
|
317
|
+
pnpm test # 전체 테스트
|
|
318
|
+
pnpm test:watch # 워치 모드
|
|
319
|
+
pnpm test:coverage # 커버리지
|
|
370
320
|
```
|
|
371
321
|
|
|
372
|
-
|
|
322
|
+
## Build & Verify
|
|
373
323
|
|
|
374
|
-
|
|
324
|
+
```bash
|
|
325
|
+
pnpm build
|
|
326
|
+
pnpm lint
|
|
327
|
+
pnpm bench
|
|
328
|
+
pnpm pack:check
|
|
329
|
+
```
|
|
375
330
|
|
|
376
331
|
---
|
|
377
332
|
|
|
378
|
-
##
|
|
379
|
-
|
|
380
|
-
| Symbol | 문자 수 | 비트 길이 | 설명 |
|
|
381
|
-
| -------------- | ------- | --------- | ------------------------- |
|
|
382
|
-
| `DDU` | 8 | 3 | 한글 + 특수문자 기본 세트 |
|
|
383
|
-
| `ONECHARSET` | 64 | 6 | 영문 + 숫자 + 특수문자 |
|
|
384
|
-
| `TWOCHARSET` | 1024 | 10 | 2글자 조합 세트 |
|
|
385
|
-
| `THREECHARSET` | 32768 | 15 | 3글자 조합 세트 |
|
|
333
|
+
## Credits
|
|
386
334
|
|
|
387
|
-
|
|
335
|
+
- Original: [@i3ls](https://github.com/i3l3), [@gunu3371](https://github.com/gunu3371)
|
|
336
|
+
- Repository: [ddunigma](https://github.com/i3l3/ddunigma)
|
|
388
337
|
|
|
389
338
|
## License
|
|
390
339
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import {Transform,PassThrough}from'stream';import {createBrotliCompress,constants,createDeflate,createBrotliDecompress,createInflate}from'zlib';var N=["-","_","."],E={"":0,\u3131:1,\u3132:2,\u3133:3,\u3134:4,\u3135:5,\u3136:6,\u3137:7,\u3139:8,\u313A:9,\u313B:10,\u313C:11,\u313D:12,\u313E:13,\u313F:14,\u3140:15,\u3141:16,\u3142:17,\u3144:18,\u3145:19,\u3146:20,\u3147:21,\u3148:22,\u314A:23,\u314B:24,\u314C:25,\u314D:26,\u314E:27};function x(n,e){let r=n.charCodeAt(0);if(r<44032||r>55203)return n;let t=r-(r-44032)%28,i=E[e]??0;return String.fromCharCode(t+i)}function P(n,e){let r=new Array(n.length*e.length),t=0;for(let i of n)for(let o of e)r[t++]=x(i,o);return r}var z=(()=>{let n=new Uint32Array(256);for(let e=0;e<256;e++){let r=e;for(let t=0;t<8;t++)r=r>>>1^(r&1?3988292384:0);n[e]=r;}return n})(),H="ELYSIA",K="GRISEO",b="CHK",W="ENC";function F(n){return n.length===0?n:n.replaceAll("+","-").replaceAll("/","_").replaceAll("=",".")}function j(n){return n.length===0?n:n.replaceAll("-","+").replaceAll("_","/").replaceAll(".","=")}function U(n,e,r){if(e<=0||n.length<=e)return n;let t=Math.ceil(n.length/e),i=new Array(t*2-1),o=0;for(let s=0;s<n.length;s+=e)i[o++]=n.slice(s,s+e),s+e<n.length&&(i[o++]=r);return i.join("")}function h(n,e){if(n.length===0)return n;let r=n.replace(/[\r\n]/g,""),t=e&&e!==`
|
|
2
|
+
`&&e!==`\r
|
|
3
|
+
`&&e!=="\r"?e:"";return t.length>0&&r.includes(t)&&(r=r.replaceAll(t,"")),r}function J(n){let e=4294967295;for(let r=0;r<n.length;r++)e=e>>>8^z[(e^n[r])&255];return ((e^4294967295)>>>0).toString(16).padStart(8,"0")}function $(n){let e=n.lastIndexOf(b);if(e===-1)return {data:n,checksum:null};let r=n.slice(e+b.length);return r.length!==8||!/^[0-9a-f]+$/i.test(r)?{data:n,checksum:null}:{data:n.slice(0,e),checksum:r.toLowerCase()}}function C(n,e){let t=n===void 0||!Number.isFinite(n)?6:Math.floor(n);return Math.min(e==="brotli"?11:9,Math.max(0,t))}var c="DDS1";function v(n){return n.length*2+c.length+2}function k(n,e){let r=e.compressionAlgorithm==="brotli"?"B":e.compressionAlgorithm==="deflate"?"D":"N",t=e.encrypted?"1":"0";return `${n}${c}${r}${t}${n}`}function R(n,e){let r=v(e);if(n.length<r||!n.startsWith(e))return null;let t=e.length;if(n.slice(t,t+c.length)!==c)throw new Error("[DduStream decode] Invalid stream header magic");let o=n[t+c.length],s=n[t+c.length+1];if(n.slice(r-e.length,r)!==e)throw new Error("[DduStream decode] Invalid stream header terminator");let a=o==="B"?"brotli":o==="D"?"deflate":o==="N"?void 0:null;if(a===null)throw new Error("[DduStream decode] Invalid stream header compression flag");if(s!=="0"&&s!=="1")throw new Error("[DduStream decode] Invalid stream header encryption flag");return {compressionAlgorithm:a,encrypted:s==="1"}}var m=class extends Transform{prefix;emittedPrefix=false;constructor(e){super(),this.prefix=e;}_transform(e,r,t){try{this.emittedPrefix||(this.push(this.prefix),this.emittedPrefix=!0),this.push(e),t();}catch(i){t(i);}}};function _(n,e){let r=new PassThrough,t=r.write.bind(r);return r.write=function(i,o,s){return typeof o=="function"?n.write(i,o):n.write(i,o,s)},r.end=function(i,o,s){return typeof i=="function"?n.end(i):typeof o=="function"?n.end(i,o):n.end(i,o,s)},e.on("data",i=>t(i)),e.on("end",()=>r.push(null)),e.on("error",i=>r.destroy(i)),n.on("error",i=>r.destroy(i)),r}function A(n){if(n.length===0)return new PassThrough;for(let r=0;r<n.length-1;r++)n[r].pipe(n[r+1]);let e=_(n[0],n[n.length-1]);for(let r of n)r.on("error",t=>{e.destroy(t);});return e}var p=class extends Transform{encoder;buffers;bufferOffset;totalLength;chunkSize;footerCompressionAlgorithm;footerEncrypted;constructor(e,r){super({...r,readableObjectMode:false,writableObjectMode:false}),this.encoder=e;let t=e.getCharSetInfo();this.buffers=[],this.bufferOffset=0,this.totalLength=0,this.footerCompressionAlgorithm=r?.compress??t.defaultCompress?r?.compressionAlgorithm??t.defaultCompressionAlgorithm:void 0,this.footerEncrypted=(r?.encrypt??true)&&t.hasEncryptionKey,this.chunkSize=this.calculateChunkSize(t.bitLength);}calculateChunkSize(e){let r=(o,s)=>s===0?o:r(s,o%s),t=8*e/r(8,e),i=Math.max(1,Math.floor(4096/t));return t*i}_transform(e,r,t){try{for(this.buffers.push(e),this.totalLength+=e.length;this.totalLength>=this.chunkSize;){let i=this.readBytes(this.chunkSize),o=this.encodeChunk(i,!1);this.push(o);}t();}catch(i){t(i);}}_flush(e){try{if(this.totalLength>0){let r=this.readBytes(this.totalLength),t=this.encodeChunk(r,!0);this.push(t);}e();}catch(r){e(r);}}encodeChunk(e,r){return this.encoder.encodeRawBuffer(e,{compressionAlgorithm:r?this.footerCompressionAlgorithm:void 0,encrypted:r?this.footerEncrypted:false,omitFooter:!r})}readBytes(e){if(e<=0||this.totalLength<e)return Buffer.alloc(0);let r=this.buffers[0];if(this.buffers.length===1&&r&&this.bufferOffset===0&&r.length===e)return this.buffers=[],this.totalLength=0,r;let t=Buffer.allocUnsafe(e),i=0;for(;i<e&&this.buffers.length>0;){let o=this.buffers[0],s=o.length-this.bufferOffset,f=Math.min(e-i,s);o.copy(t,i,this.bufferOffset,this.bufferOffset+f),i+=f,this.bufferOffset+=f,this.bufferOffset>=o.length&&(this.buffers.shift(),this.bufferOffset=0);}return this.totalLength-=e,t}},g=class extends Transform{encoder;options;normalizedBuffer;chunkSize;chunkSeparator;separatorTail;constructor(e,r){super({...r,readableObjectMode:false,writableObjectMode:false}),this.encoder=e,this.options={...r,compress:false,checksum:false,encrypt:false},this.normalizedBuffer="";let t=e.getCharSetInfo();this.chunkSeparator=r?.chunkSeparator??t.defaultChunkSeparator,this.separatorTail="",this.chunkSize=1024;}_transform(e,r,t){try{this.appendNormalizedChunk(e.toString("utf-8"));let i=100;for(;this.normalizedBuffer.length>=this.chunkSize+i;){let o=this.normalizedBuffer.slice(0,this.chunkSize);this.normalizedBuffer=this.normalizedBuffer.slice(this.chunkSize);let s=this.encoder.decodeToBuffer(o,this.options);this.push(s);}t();}catch(i){t(i);}}_flush(e){try{if(this.separatorTail.length>0&&(this.normalizedBuffer+=h(this.separatorTail,this.chunkSeparator),this.separatorTail=""),this.normalizedBuffer.length>0){let r=this.encoder.decodeStreamToBuffer(this.normalizedBuffer,this.options);this.push(r),this.normalizedBuffer="";}e();}catch(r){e(r);}}appendNormalizedChunk(e){if(e.length===0)return;let r=Math.max(0,this.chunkSeparator.length-1);if(r===0){this.normalizedBuffer+=h(e,this.chunkSeparator);return}let t=this.separatorTail+e;if(t.length<=r){this.separatorTail=t;return}let i=t.length-r,o=t.slice(0,i);this.separatorTail=t.slice(i),this.normalizedBuffer+=h(o,this.chunkSeparator);}},y=class extends Transform{encoder;options;normalizedBuffer;chunkSeparator;separatorTail;paddingChar;headerLength;mode;innerStream;constructor(e,r){super({...r,readableObjectMode:false,writableObjectMode:false}),this.encoder=e,this.options={...r},this.normalizedBuffer="";let t=e.getCharSetInfo();this.paddingChar=t.paddingChar,this.headerLength=v(t.paddingChar),this.chunkSeparator=r?.chunkSeparator??t.defaultChunkSeparator,this.separatorTail="",this.mode="pending",this.innerStream=null;}_transform(e,r,t){try{if(this.appendNormalizedChunk(e.toString("utf-8")),this.maybeInitializeMode(!1),this.mode==="pipeline"){this.flushBufferedToInner(t);return}t();}catch(i){t(i);}}_flush(e){try{if(this.separatorTail.length>0&&(this.normalizedBuffer+=h(this.separatorTail,this.chunkSeparator),this.separatorTail=""),this.maybeInitializeMode(!0),this.mode==="pipeline"){this.finishInnerPipeline(e);return}if(this.normalizedBuffer.length>0){let r=this.encoder.decodeStreamToBuffer(this.normalizedBuffer,this.options);this.push(r),this.normalizedBuffer="";}e();}catch(r){e(r);}}appendNormalizedChunk(e){if(e.length===0)return;let r=Math.max(0,this.chunkSeparator.length-1);if(r===0){this.normalizedBuffer+=h(e,this.chunkSeparator);return}let t=this.separatorTail+e;if(t.length<=r){this.separatorTail=t;return}let i=t.length-r,o=t.slice(0,i);this.separatorTail=t.slice(i),this.normalizedBuffer+=h(o,this.chunkSeparator);}maybeInitializeMode(e){if(this.mode!=="pending")return;let r=this.paddingChar.length;if(this.normalizedBuffer.length<r){e&&this.normalizedBuffer.length>0&&(this.mode="legacy");return}if(!this.normalizedBuffer.startsWith(this.paddingChar)){this.mode="legacy";return}if(this.normalizedBuffer.length<this.headerLength){if(e)throw new Error("[DduStream decode] Incomplete stream header");return}let t=this.normalizedBuffer.slice(0,this.headerLength),i=R(t,this.paddingChar);if(!i){this.mode="legacy";return}this.mode="pipeline",this.normalizedBuffer=this.normalizedBuffer.slice(this.headerLength),this.initializeInnerPipeline(i);}initializeInnerPipeline(e){if(e.encrypted&&!this.encoder.getCharSetInfo().hasEncryptionKey)throw new Error("[DduStream decode] Encrypted stream requires an encryptionKey");let r=L(this.encoder,{...this.options,streamAutoDetect:false,compress:!!e.compressionAlgorithm,compressionAlgorithm:e.compressionAlgorithm,encrypt:e.encrypted});r.on("data",t=>{this.push(t);}),r.on("error",t=>{this.destroy(t);}),this.innerStream=r;}flushBufferedToInner(e){if(!this.innerStream||this.normalizedBuffer.length===0){e();return}let r=this.normalizedBuffer;if(this.normalizedBuffer="",!this.innerStream.write(Buffer.from(r,"utf-8"))){this.innerStream.once("drain",()=>e());return}e();}finishInnerPipeline(e){if(!this.innerStream){e();return}let r=this.innerStream,t=()=>{r.once("end",()=>e()),r.end();};if(this.normalizedBuffer.length>0){let i=this.normalizedBuffer;if(this.normalizedBuffer="",!r.write(Buffer.from(i,"utf-8"))){r.once("drain",t);return}}t();}};function Z(n,e){let r=n.getCharSetInfo(),t=e?.compress??r.defaultCompress,i=(e?.encrypt??true)&&r.hasEncryptionKey,o=e?.streamAutoDetect!==false,s=t?e?.compressionAlgorithm??r.defaultCompressionAlgorithm:void 0,f=new p(n,e),a=[];if(t){let l=s??r.defaultCompressionAlgorithm,u=l==="brotli",S=C(e?.compressionLevel??r.defaultCompressionLevel,l),T=u?createBrotliCompress({params:{[constants.BROTLI_PARAM_QUALITY]:Math.min(11,Math.max(0,S))}}):createDeflate({level:Math.min(9,Math.max(0,S))});a.push(T);}if(i){let l=n.createEncryptionStream();l&&a.push(l);}return a.push(f),o&&a.push(new m(k(r.paddingChar,{compressionAlgorithm:s,encrypted:i}))),a.length===1?f:A(a)}function L(n,e){if(e?.streamAutoDetect??true)return new y(n,e);let t=n.getCharSetInfo(),i=e?.compress??t.defaultCompress,o=(e?.encrypt??true)&&t.hasEncryptionKey,s=new g(n,e),f=[s];if(o){let a=n.createDecryptionStream();a&&f.push(a);}if(i){let u=(e?.compressionAlgorithm??t.defaultCompressionAlgorithm)==="brotli"?createBrotliDecompress():createInflate();f.push(u);}return f.length===1?s:A(f)}export{N as a,P as b,H as c,K as d,b as e,W as f,F as g,j as h,U as i,h as j,J as k,$ as l,C as m,p as n,g as o,Z as p,L as q};//# sourceMappingURL=chunk-FVBZKVPE.js.map
|
|
4
|
+
//# sourceMappingURL=chunk-FVBZKVPE.js.map
|