@ddunigma/node 1.1.7 → 1.1.9

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.
@@ -15,6 +15,10 @@ export declare class Ddu64 extends BaseDdu {
15
15
  private readonly indexToBinaryCache;
16
16
  constructor(dduChar?: string[] | string, paddingChar?: string, dduOptions?: DduConstructorOptions);
17
17
  private normalizeCharSet;
18
+ private resolveInitialCharSet;
19
+ private getFallbackCharSet;
20
+ private shouldUsePowerOfTwo;
21
+ private getCharSetOrThrow;
18
22
  private getBinaryFromIndex;
19
23
  private validateCombinationDuplicates;
20
24
  encode(input: Buffer | string, _options?: DduOptions): string;
@@ -6,86 +6,18 @@ 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, _d, _e, _f, _g;
9
+ var _a, _b;
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
- };
19
- // charset 초기화
20
- let charSet, padding, requiredLength, bitLength, isPredefined;
21
- try {
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(`[Ddu64 Constructor] paddingChar is required when dduChar is provided. Received: dduChar=${typeof finalDduChar}, paddingChar=${finalPadding}`);
28
- }
29
- // 문자열을 배열로 변환 (중복 제거 없이)
30
- const arr = typeof finalDduChar === "string" ? [...finalDduChar.trim()] : finalDduChar;
31
- // 중복 검사 (useBuildErrorReturn이 true일 때만)
32
- if (shouldThrowError) {
33
- const uniqueChars = new Set(arr);
34
- if (uniqueChars.size !== arr.length) {
35
- const duplicates = arr.filter((char, index) => arr.indexOf(char) !== index);
36
- throw new Error(`[Ddu64 Constructor] Character set contains duplicate characters. Total: ${arr.length}, Unique: ${uniqueChars.size}, Duplicates: [${[...new Set(duplicates)].join(', ')}]`);
37
- }
38
- }
39
- const len = (_b = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.requiredLength) !== null && _b !== void 0 ? _b : arr.length;
40
- if (arr.length < len) {
41
- throw new Error(`[Ddu64 Constructor] Insufficient characters in charset. Required: ${len}, Provided: ${arr.length}`);
42
- }
43
- 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);
44
- if (usePow2) {
45
- const exp = this.getLargestPowerOfTwoExponent(len);
46
- const pow2Len = 1 << exp;
47
- [charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, pow2Len), finalPadding, pow2Len, exp, false];
48
- }
49
- else {
50
- [charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, len), finalPadding, len, this.getBitLength(len), false];
51
- }
52
- }
53
- else if (dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduSetSymbol) {
54
- // 미리 정의된 charset
55
- const cs = getCharSetFromSymbol(dduOptions.dduSetSymbol);
56
- 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);
57
- if (usePow2) {
58
- const exp = this.getLargestPowerOfTwoExponent(cs.maxRequiredLength);
59
- const pow2Len = 1 << exp;
60
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet.slice(0, pow2Len), cs.paddingChar, pow2Len, exp, true];
61
- }
62
- else {
63
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
64
- }
65
- }
66
- else {
67
- // 기본 charset
68
- const cs = getCharSetFromSymbol((_c = types_1.dduDefaultConstructorOptions.dduSetSymbol) !== null && _c !== void 0 ? _c : types_1.DduSetSymbol.DDU);
69
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
70
- }
71
- }
72
- catch (error) {
73
- if (shouldThrowError)
74
- throw error;
75
- // fallback
76
- 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;
77
- const cs = (_f = (0, charSets_1.getCharSet)(fallbackSymbol)) !== null && _f !== void 0 ? _f : (0, charSets_1.getCharSet)(types_1.DduSetSymbol.ONECHARSET);
78
- if (!cs)
79
- throw new Error(`Critical: No fallback CharSet available`);
80
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
81
- }
13
+ const { charSet, padding, requiredLength, bitLength, isPredefined, } = this.resolveInitialCharSet(dduChar, paddingChar, dduOptions, shouldThrowError);
82
14
  // 정규화 및 검증
83
15
  const normalized = this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
84
16
  this.dduChar = normalized.charSet;
85
17
  this.paddingChar = normalized.padding;
86
18
  this.charLength = normalized.charLength;
87
19
  this.isPredefinedCharSet = normalized.isPredefined;
88
- this.encoding = (_g = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.encoding) !== null && _g !== void 0 ? _g : this.defaultEncoding;
20
+ this.encoding = (_b = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.encoding) !== null && _b !== void 0 ? _b : this.defaultEncoding;
89
21
  const dduLength = this.dduChar.length;
90
22
  const recalculatedBitLength = this.getBitLength(dduLength);
91
23
  this.usePowerOfTwo = dduLength > 0 && (dduLength & (dduLength - 1)) === 0;
@@ -96,7 +28,13 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
96
28
  this.maxBinaryValue = Math.pow(2, this.effectiveBitLength);
97
29
  // 성능 최적화: 단일 구현 사용 (벤치마크 결과 Method2가 더 빠르고 안정적)
98
30
  // Method2는 32비트 이상에서도 오버플로우 없이 정확한 결과 제공
99
- this.binaryChunkToIntFn = (chunk) => chunk.split('').reduce((v, c) => v * 2 + (c.charCodeAt(0) & 1), 0);
31
+ this.binaryChunkToIntFn = (chunk) => {
32
+ let value = 0;
33
+ for (let i = 0; i < chunk.length; i++) {
34
+ value = value * 2 + (chunk.charCodeAt(i) & 1);
35
+ }
36
+ return value;
37
+ };
100
38
  this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
101
39
  if (this.charLength === 1 && !this.isPredefinedCharSet) {
102
40
  this.validateCombinationDuplicates(this.dduChar, this.paddingChar, dduLength);
@@ -109,70 +47,180 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
109
47
  }
110
48
  }
111
49
  normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions) {
112
- var _a, _b, _c, _d, _e, _f;
113
- const applyFallback = () => {
114
- var _a, _b, _c;
115
- 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;
116
- const cs = (_c = (0, charSets_1.getCharSet)(fallbackSymbol)) !== null && _c !== void 0 ? _c : (0, charSets_1.getCharSet)(types_1.DduSetSymbol.ONECHARSET);
117
- if (!cs)
118
- throw new Error(`Critical: No fallback CharSet available`);
119
- return { charSet: cs.charSet, padding: cs.paddingChar, requiredLength: cs.maxRequiredLength, bitLength: cs.bitLength, isPredefined: true };
120
- };
121
- const validate = (condition, message) => {
122
- if (condition) {
50
+ const applyFallback = () => this.getFallbackCharSet(dduOptions);
51
+ while (true) {
52
+ const ensure = (condition, message) => {
53
+ if (!condition)
54
+ return false;
123
55
  if (shouldThrowError)
124
56
  throw new Error(message);
125
- const fb = applyFallback();
126
- [charSet, padding, requiredLength, bitLength, isPredefined] = [fb.charSet, fb.padding, fb.requiredLength, fb.bitLength, fb.isPredefined];
57
+ ({
58
+ charSet,
59
+ padding,
60
+ requiredLength,
61
+ bitLength,
62
+ isPredefined,
63
+ } = applyFallback());
127
64
  return true;
65
+ };
66
+ const uniqueChars = new Set(charSet);
67
+ if (uniqueChars.size !== charSet.length) {
68
+ const duplicates = charSet.filter((char, index) => charSet.indexOf(char) !== index);
69
+ const errorMsg = `[Ddu64 normalizeCharSet] Character set contains duplicate characters. Total: ${charSet.length}, Unique: ${uniqueChars.size}, Duplicates: [${[
70
+ ...new Set(duplicates),
71
+ ].join(", ")}]`;
72
+ if (shouldThrowError)
73
+ throw new Error(errorMsg);
74
+ charSet = Array.from(uniqueChars);
75
+ if (!isPredefined) {
76
+ requiredLength = charSet.length;
77
+ }
128
78
  }
129
- return false;
130
- };
131
- // 중복 제거 (모든 charset에 적용)
132
- const uniqueChars = new Set(charSet);
133
- if (uniqueChars.size !== charSet.length) {
134
- const duplicates = charSet.filter((char, index) => charSet.indexOf(char) !== index);
135
- const errorMsg = `[Ddu64 normalizeCharSet] Character set contains duplicate characters. Total: ${charSet.length}, Unique: ${uniqueChars.size}, Duplicates: [${[...new Set(duplicates)].join(', ')}]`;
136
- if (shouldThrowError)
137
- throw new Error(errorMsg);
138
- // 중복 제거 후 계속 진행
139
- charSet = Array.from(uniqueChars);
140
- if (!isPredefined) {
141
- requiredLength = charSet.length;
79
+ if (ensure(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters. Required: ${requiredLength}, Provided: ${charSet.length}`)) {
80
+ continue;
142
81
  }
82
+ if (ensure(requiredLength < 2, `[Ddu64 normalizeCharSet] At least 2 unique characters are required. Provided: ${requiredLength}`)) {
83
+ continue;
84
+ }
85
+ if (ensure(bitLength <= 0, `[Ddu64 normalizeCharSet] Invalid bit length (${bitLength}) for charset size ${requiredLength}`)) {
86
+ continue;
87
+ }
88
+ if (ensure(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset. Required: ${requiredLength} characters`)) {
89
+ continue;
90
+ }
91
+ let charLength = charSet[0].length;
92
+ const invalidIndex = charSet.findIndex((char) => char.length !== charLength);
93
+ if (invalidIndex !== -1) {
94
+ if (shouldThrowError) {
95
+ throw new Error(`[Ddu64 normalizeCharSet] Inconsistent character length. Expected: ${charLength}, but character at index ${invalidIndex} ("${charSet[invalidIndex]}") has length ${charSet[invalidIndex].length}`);
96
+ }
97
+ charSet = charSet.filter((char) => char.length === charLength);
98
+ if (ensure(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after filtering. Required: ${requiredLength}, Remaining: ${charSet.length}`)) {
99
+ continue;
100
+ }
101
+ if (ensure(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset after filtering`)) {
102
+ continue;
103
+ }
104
+ charLength = charSet[0].length;
105
+ }
106
+ if (ensure(padding.length !== charLength, `[Ddu64 normalizeCharSet] Padding character length mismatch. Expected: ${charLength}, Got: ${padding.length} (padding: "${padding}")`)) {
107
+ continue;
108
+ }
109
+ if (charSet.includes(padding)) {
110
+ if (shouldThrowError) {
111
+ throw new Error(`[Ddu64 normalizeCharSet] Padding character "${padding}" conflicts with charset. Padding must not be in the character set.`);
112
+ }
113
+ charSet = charSet.filter((char) => char !== padding);
114
+ if (ensure(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after removing padding conflict. Required: ${requiredLength}, Remaining: ${charSet.length}`)) {
115
+ continue;
116
+ }
117
+ if (ensure(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset after removing padding conflict`)) {
118
+ continue;
119
+ }
120
+ charLength = charSet[0].length;
121
+ }
122
+ return {
123
+ charSet: charSet.slice(0, requiredLength),
124
+ padding,
125
+ requiredLength,
126
+ bitLength,
127
+ isPredefined,
128
+ charLength,
129
+ };
143
130
  }
144
- // 검증
145
- if (validate(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters. Required: ${requiredLength}, Provided: ${charSet.length}`)) {
146
- return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
147
- }
148
- if (validate(requiredLength < 2, `[Ddu64 normalizeCharSet] At least 2 unique characters are required. Provided: ${requiredLength}`)) {
149
- return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
131
+ }
132
+ resolveInitialCharSet(dduChar, paddingChar, dduOptions, shouldThrowError) {
133
+ var _a, _b;
134
+ const finalize = (set, padding, length, baseBitLength, predefined = false) => {
135
+ const usePow2 = this.shouldUsePowerOfTwo(length, dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.usePowerOfTwo);
136
+ if (usePow2 && length > 0) {
137
+ const exponent = this.getLargestPowerOfTwoExponent(length);
138
+ const pow2Length = 1 << exponent;
139
+ return {
140
+ charSet: set.slice(0, pow2Length),
141
+ padding,
142
+ requiredLength: pow2Length,
143
+ bitLength: exponent,
144
+ isPredefined: predefined,
145
+ };
146
+ }
147
+ const computedBitLength = baseBitLength !== null && baseBitLength !== void 0 ? baseBitLength : (length > 0 ? this.getBitLength(length) : 0);
148
+ return {
149
+ charSet: set.slice(0, length),
150
+ padding,
151
+ requiredLength: length,
152
+ bitLength: computedBitLength,
153
+ isPredefined: predefined,
154
+ };
155
+ };
156
+ const fallback = () => this.getFallbackCharSet(dduOptions);
157
+ try {
158
+ const finalDduChar = dduChar !== null && dduChar !== void 0 ? dduChar : dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduChar;
159
+ const finalPadding = paddingChar !== null && paddingChar !== void 0 ? paddingChar : dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.paddingChar;
160
+ if (finalDduChar) {
161
+ if (!finalPadding) {
162
+ throw new Error(`[Ddu64 Constructor] paddingChar is required when dduChar is provided. Received: dduChar=${typeof finalDduChar}, paddingChar=${finalPadding}`);
163
+ }
164
+ const arr = typeof finalDduChar === "string"
165
+ ? [...finalDduChar.trim()]
166
+ : [...finalDduChar];
167
+ if (shouldThrowError) {
168
+ const uniqueChars = new Set(arr);
169
+ if (uniqueChars.size !== arr.length) {
170
+ const duplicates = arr.filter((char, index) => arr.indexOf(char) !== index);
171
+ throw new Error(`[Ddu64 Constructor] Character set contains duplicate characters. Total: ${arr.length}, Unique: ${uniqueChars.size}, Duplicates: [${[
172
+ ...new Set(duplicates),
173
+ ].join(", ")}]`);
174
+ }
175
+ }
176
+ const len = (_a = dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.requiredLength) !== null && _a !== void 0 ? _a : arr.length;
177
+ if (arr.length < len) {
178
+ throw new Error(`[Ddu64 Constructor] Insufficient characters in charset. Required: ${len}, Provided: ${arr.length}`);
179
+ }
180
+ return finalize(arr, finalPadding, len, undefined, false);
181
+ }
182
+ if (dduOptions === null || dduOptions === void 0 ? void 0 : dduOptions.dduSetSymbol) {
183
+ const cs = this.getCharSetOrThrow(dduOptions.dduSetSymbol);
184
+ return finalize(cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true);
185
+ }
186
+ const defaultSymbol = (_b = types_1.dduDefaultConstructorOptions.dduSetSymbol) !== null && _b !== void 0 ? _b : types_1.DduSetSymbol.DDU;
187
+ const cs = this.getCharSetOrThrow(defaultSymbol);
188
+ return finalize(cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true);
150
189
  }
151
- if (validate(bitLength <= 0, `[Ddu64 normalizeCharSet] Invalid bit length (${bitLength}) for charset size ${requiredLength}`)) {
152
- return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
190
+ catch (error) {
191
+ if (shouldThrowError)
192
+ throw error;
193
+ return fallback();
153
194
  }
154
- let charLength = (_b = (_a = charSet[0]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
155
- validate(charLength === 0, `[Ddu64 normalizeCharSet] Empty charset. Required: ${requiredLength} characters`);
156
- const invalidIndex = charSet.findIndex(char => char.length !== charLength);
157
- if (invalidIndex !== -1) {
158
- if (shouldThrowError) {
159
- throw new Error(`[Ddu64 normalizeCharSet] Inconsistent character length. Expected: ${charLength}, but character at index ${invalidIndex} ("${charSet[invalidIndex]}") has length ${charSet[invalidIndex].length}`);
160
- }
161
- charSet = charSet.filter(char => char.length === charLength);
162
- validate(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after filtering. Required: ${requiredLength}, Remaining: ${charSet.length}`);
163
- charLength = (_d = (_c = charSet[0]) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
195
+ }
196
+ getFallbackCharSet(dduOptions) {
197
+ var _a, _b, _c;
198
+ 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;
199
+ const cs = (_c = (0, charSets_1.getCharSet)(fallbackSymbol)) !== null && _c !== void 0 ? _c : (0, charSets_1.getCharSet)(types_1.DduSetSymbol.ONECHARSET);
200
+ if (!cs)
201
+ throw new Error(`Critical: No fallback CharSet available`);
202
+ return {
203
+ charSet: cs.charSet,
204
+ padding: cs.paddingChar,
205
+ requiredLength: cs.maxRequiredLength,
206
+ bitLength: cs.bitLength,
207
+ isPredefined: true,
208
+ };
209
+ }
210
+ shouldUsePowerOfTwo(length, preference) {
211
+ if (preference === true) {
212
+ return length > 0;
164
213
  }
165
- validate(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset after validation`);
166
- validate(padding.length !== charLength, `[Ddu64 normalizeCharSet] Padding character length mismatch. Expected: ${charLength}, Got: ${padding.length} (padding: "${padding}")`);
167
- if (new Set(charSet).has(padding)) {
168
- if (shouldThrowError) {
169
- throw new Error(`[Ddu64 normalizeCharSet] Padding character "${padding}" conflicts with charset. Padding must not be in the character set.`);
170
- }
171
- charSet = charSet.filter(char => char !== padding);
172
- validate(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after removing padding conflict. Required: ${requiredLength}, Remaining: ${charSet.length}`);
173
- charLength = (_f = (_e = charSet[0]) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0;
214
+ if (preference === false) {
215
+ return false;
174
216
  }
175
- return { charSet: charSet.slice(0, requiredLength), padding, requiredLength, bitLength, isPredefined, charLength };
217
+ return length > 0 && (length & (length - 1)) === 0;
218
+ }
219
+ getCharSetOrThrow(symbol) {
220
+ const cs = (0, charSets_1.getCharSet)(symbol);
221
+ if (!cs)
222
+ throw new Error(`CharSet with symbol ${symbol} not found`);
223
+ return cs;
176
224
  }
177
225
  getBinaryFromIndex(index) {
178
226
  var _a, _b;
@@ -213,27 +261,23 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
213
261
  encode(input, _options) {
214
262
  // options는 구버전 호환성을 위해 유지하지만 사용하지 않음
215
263
  const bufferInput = typeof input === "string" ? Buffer.from(input, this.encoding) : input;
216
- // 생성자에서 설정된 사용
217
- const dduLength = this.dduChar.length;
218
- const effectiveBitLength = this.effectiveBitLength;
219
- const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, effectiveBitLength);
220
- // 문자열 연결 최적화: Array + join 사용
264
+ const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, this.effectiveBitLength);
221
265
  const resultParts = new Array(dduBinary.length);
222
- // 비트 청크를 변환
223
- for (let i = 0; i < dduBinary.length; i++) {
224
- const binaryChunk = dduBinary[i];
225
- const charInt = this.binaryChunkToIntFn(binaryChunk);
226
- if (!this.usePowerOfTwo) {
227
- // 가변 길이 조합 인코딩 (멀티바이트 문자도 지원)
228
- const quotient = Math.floor(charInt / dduLength);
229
- const remainder = charInt % dduLength;
230
- resultParts[i] = this.dduChar[quotient] + this.dduChar[remainder];
231
- }
232
- else {
233
- // 고정 길이 직접 매핑
266
+ if (this.usePowerOfTwo) {
267
+ for (let i = 0; i < dduBinary.length; i++) {
268
+ const charInt = this.binaryChunkToIntFn(dduBinary[i]);
234
269
  resultParts[i] = this.dduChar[charInt];
235
270
  }
236
271
  }
272
+ else {
273
+ const dduLength = this.dduChar.length;
274
+ for (let i = 0; i < dduBinary.length; i++) {
275
+ const value = this.binaryChunkToIntFn(dduBinary[i]);
276
+ const quotient = Math.floor(value / dduLength);
277
+ const remainder = value % dduLength;
278
+ resultParts[i] = this.dduChar[quotient] + this.dduChar[remainder];
279
+ }
280
+ }
237
281
  let resultString = resultParts.join("");
238
282
  // 패딩 비트 정보를 padChar + 패딩비트수 형태로 추가
239
283
  if (padding > 0) {
@@ -262,13 +306,14 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
262
306
  input = input.substring(0, padCharIndex);
263
307
  }
264
308
  }
265
- let dduBinary = "";
309
+ const binaryParts = [];
310
+ const charLength = this.charLength;
266
311
  const dduLength = this.dduChar.length;
267
312
  if (!this.usePowerOfTwo) {
268
- const chunkSize = this.charLength * 2;
313
+ const chunkSize = charLength * 2;
269
314
  for (let i = 0; i < input.length; i += chunkSize) {
270
- const firstChar = input.slice(i, i + this.charLength);
271
- const secondChar = input.slice(i + this.charLength, i + chunkSize);
315
+ const firstChar = input.slice(i, i + charLength);
316
+ const secondChar = input.slice(i + charLength, i + chunkSize);
272
317
  const firstIndex = this.dduBinaryLookup.get(firstChar);
273
318
  const secondIndex = this.dduBinaryLookup.get(secondChar);
274
319
  if (firstIndex === undefined || secondIndex === undefined) {
@@ -279,23 +324,23 @@ class Ddu64 extends BaseDdu_1.BaseDdu {
279
324
  if (value >= this.maxBinaryValue) {
280
325
  throw new Error(`[Ddu64 decode] Invalid character combination detected. Calculated value ${value} exceeds binary range ${this.maxBinaryValue - 1}.`);
281
326
  }
282
- dduBinary += this.getBinaryFromIndex(value);
327
+ binaryParts.push(this.getBinaryFromIndex(value));
283
328
  }
284
329
  }
285
330
  else {
286
- for (let i = 0; i < input.length; i += this.charLength) {
287
- const charChunk = input.slice(i, i + this.charLength);
331
+ for (let i = 0; i < input.length; i += charLength) {
332
+ const charChunk = input.slice(i, i + charLength);
288
333
  const charIndex = this.dduBinaryLookup.get(charChunk);
289
334
  if (charIndex === undefined) {
290
- throw new Error(`[Ddu64 decode] Invalid character in encoded string. Character: "${charChunk}", Position: ${i}, Charset size: ${dduLength}, Character length: ${this.charLength}`);
335
+ throw new Error(`[Ddu64 decode] Invalid character in encoded string. Character: "${charChunk}", Position: ${i}, Charset size: ${dduLength}, Character length: ${charLength}`);
291
336
  }
292
337
  if (charIndex >= this.maxBinaryValue) {
293
338
  throw new Error(`[Ddu64 decode] Invalid binary index ${charIndex}. Allowed range: 0-${this.maxBinaryValue - 1}`);
294
339
  }
295
- dduBinary += this.getBinaryFromIndex(charIndex);
340
+ binaryParts.push(this.getBinaryFromIndex(charIndex));
296
341
  }
297
342
  }
298
- return this.dduBinaryToBuffer(dduBinary, paddingBits);
343
+ return this.dduBinaryToBuffer(binaryParts.join(""), paddingBits);
299
344
  }
300
345
  decode(input, _options) {
301
346
  return this.decodeToBuffer(input, _options).toString(this.encoding);
@@ -15,6 +15,10 @@ export declare class Ddu64 extends BaseDdu {
15
15
  private readonly indexToBinaryCache;
16
16
  constructor(dduChar?: string[] | string, paddingChar?: string, dduOptions?: DduConstructorOptions);
17
17
  private normalizeCharSet;
18
+ private resolveInitialCharSet;
19
+ private getFallbackCharSet;
20
+ private shouldUsePowerOfTwo;
21
+ private getCharSetOrThrow;
18
22
  private getBinaryFromIndex;
19
23
  private validateCombinationDuplicates;
20
24
  encode(input: Buffer | string, _options?: DduOptions): string;
@@ -17,75 +17,7 @@ export class Ddu64 extends BaseDdu {
17
17
  constructor(dduChar, paddingChar, dduOptions) {
18
18
  super();
19
19
  const shouldThrowError = dduOptions?.useBuildErrorReturn ?? false;
20
- const getCharSetFromSymbol = (symbol) => {
21
- const cs = getCharSet(symbol);
22
- if (!cs)
23
- throw new Error(`CharSet with symbol ${symbol} not found`);
24
- return cs;
25
- };
26
- // charset 초기화
27
- let charSet, padding, requiredLength, bitLength, isPredefined;
28
- try {
29
- const finalDduChar = dduChar ?? dduOptions?.dduChar;
30
- const finalPadding = paddingChar ?? dduOptions?.paddingChar;
31
- if (finalDduChar) {
32
- // 커스텀 charset
33
- if (!finalPadding) {
34
- throw new Error(`[Ddu64 Constructor] paddingChar is required when dduChar is provided. Received: dduChar=${typeof finalDduChar}, paddingChar=${finalPadding}`);
35
- }
36
- // 문자열을 배열로 변환 (중복 제거 없이)
37
- const arr = typeof finalDduChar === "string" ? [...finalDduChar.trim()] : finalDduChar;
38
- // 중복 검사 (useBuildErrorReturn이 true일 때만)
39
- if (shouldThrowError) {
40
- const uniqueChars = new Set(arr);
41
- if (uniqueChars.size !== arr.length) {
42
- const duplicates = arr.filter((char, index) => arr.indexOf(char) !== index);
43
- throw new Error(`[Ddu64 Constructor] Character set contains duplicate characters. Total: ${arr.length}, Unique: ${uniqueChars.size}, Duplicates: [${[...new Set(duplicates)].join(', ')}]`);
44
- }
45
- }
46
- const len = dduOptions?.requiredLength ?? arr.length;
47
- if (arr.length < len) {
48
- throw new Error(`[Ddu64 Constructor] Insufficient characters in charset. Required: ${len}, Provided: ${arr.length}`);
49
- }
50
- const usePow2 = dduOptions?.usePowerOfTwo === true || (dduOptions?.usePowerOfTwo === undefined && len > 0 && (len & (len - 1)) === 0);
51
- if (usePow2) {
52
- const exp = this.getLargestPowerOfTwoExponent(len);
53
- const pow2Len = 1 << exp;
54
- [charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, pow2Len), finalPadding, pow2Len, exp, false];
55
- }
56
- else {
57
- [charSet, padding, requiredLength, bitLength, isPredefined] = [arr.slice(0, len), finalPadding, len, this.getBitLength(len), false];
58
- }
59
- }
60
- else if (dduOptions?.dduSetSymbol) {
61
- // 미리 정의된 charset
62
- const cs = getCharSetFromSymbol(dduOptions.dduSetSymbol);
63
- const usePow2 = dduOptions?.usePowerOfTwo === true || (dduOptions?.usePowerOfTwo === undefined && cs.maxRequiredLength > 0 && (cs.maxRequiredLength & (cs.maxRequiredLength - 1)) === 0);
64
- if (usePow2) {
65
- const exp = this.getLargestPowerOfTwoExponent(cs.maxRequiredLength);
66
- const pow2Len = 1 << exp;
67
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet.slice(0, pow2Len), cs.paddingChar, pow2Len, exp, true];
68
- }
69
- else {
70
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
71
- }
72
- }
73
- else {
74
- // 기본 charset
75
- const cs = getCharSetFromSymbol(dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.DDU);
76
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
77
- }
78
- }
79
- catch (error) {
80
- if (shouldThrowError)
81
- throw error;
82
- // fallback
83
- const fallbackSymbol = dduOptions?.dduSetSymbol ?? dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.ONECHARSET;
84
- const cs = getCharSet(fallbackSymbol) ?? getCharSet(DduSetSymbol.ONECHARSET);
85
- if (!cs)
86
- throw new Error(`Critical: No fallback CharSet available`);
87
- [charSet, padding, requiredLength, bitLength, isPredefined] = [cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true];
88
- }
20
+ const { charSet, padding, requiredLength, bitLength, isPredefined, } = this.resolveInitialCharSet(dduChar, paddingChar, dduOptions, shouldThrowError);
89
21
  // 정규화 및 검증
90
22
  const normalized = this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
91
23
  this.dduChar = normalized.charSet;
@@ -103,7 +35,13 @@ export class Ddu64 extends BaseDdu {
103
35
  this.maxBinaryValue = 2 ** this.effectiveBitLength;
104
36
  // 성능 최적화: 단일 구현 사용 (벤치마크 결과 Method2가 더 빠르고 안정적)
105
37
  // Method2는 32비트 이상에서도 오버플로우 없이 정확한 결과 제공
106
- this.binaryChunkToIntFn = (chunk) => chunk.split('').reduce((v, c) => v * 2 + (c.charCodeAt(0) & 1), 0);
38
+ this.binaryChunkToIntFn = (chunk) => {
39
+ let value = 0;
40
+ for (let i = 0; i < chunk.length; i++) {
41
+ value = value * 2 + (chunk.charCodeAt(i) & 1);
42
+ }
43
+ return value;
44
+ };
107
45
  this.dduChar.forEach((char, index) => this.dduBinaryLookup.set(char, index));
108
46
  if (this.charLength === 1 && !this.isPredefinedCharSet) {
109
47
  this.validateCombinationDuplicates(this.dduChar, this.paddingChar, dduLength);
@@ -116,68 +54,180 @@ export class Ddu64 extends BaseDdu {
116
54
  }
117
55
  }
118
56
  normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions) {
119
- const applyFallback = () => {
120
- const fallbackSymbol = dduOptions?.dduSetSymbol ?? dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.ONECHARSET;
121
- const cs = getCharSet(fallbackSymbol) ?? getCharSet(DduSetSymbol.ONECHARSET);
122
- if (!cs)
123
- throw new Error(`Critical: No fallback CharSet available`);
124
- return { charSet: cs.charSet, padding: cs.paddingChar, requiredLength: cs.maxRequiredLength, bitLength: cs.bitLength, isPredefined: true };
125
- };
126
- const validate = (condition, message) => {
127
- if (condition) {
57
+ const applyFallback = () => this.getFallbackCharSet(dduOptions);
58
+ while (true) {
59
+ const ensure = (condition, message) => {
60
+ if (!condition)
61
+ return false;
128
62
  if (shouldThrowError)
129
63
  throw new Error(message);
130
- const fb = applyFallback();
131
- [charSet, padding, requiredLength, bitLength, isPredefined] = [fb.charSet, fb.padding, fb.requiredLength, fb.bitLength, fb.isPredefined];
64
+ ({
65
+ charSet,
66
+ padding,
67
+ requiredLength,
68
+ bitLength,
69
+ isPredefined,
70
+ } = applyFallback());
132
71
  return true;
72
+ };
73
+ const uniqueChars = new Set(charSet);
74
+ if (uniqueChars.size !== charSet.length) {
75
+ const duplicates = charSet.filter((char, index) => charSet.indexOf(char) !== index);
76
+ const errorMsg = `[Ddu64 normalizeCharSet] Character set contains duplicate characters. Total: ${charSet.length}, Unique: ${uniqueChars.size}, Duplicates: [${[
77
+ ...new Set(duplicates),
78
+ ].join(", ")}]`;
79
+ if (shouldThrowError)
80
+ throw new Error(errorMsg);
81
+ charSet = Array.from(uniqueChars);
82
+ if (!isPredefined) {
83
+ requiredLength = charSet.length;
84
+ }
133
85
  }
134
- return false;
135
- };
136
- // 중복 제거 (모든 charset에 적용)
137
- const uniqueChars = new Set(charSet);
138
- if (uniqueChars.size !== charSet.length) {
139
- const duplicates = charSet.filter((char, index) => charSet.indexOf(char) !== index);
140
- const errorMsg = `[Ddu64 normalizeCharSet] Character set contains duplicate characters. Total: ${charSet.length}, Unique: ${uniqueChars.size}, Duplicates: [${[...new Set(duplicates)].join(', ')}]`;
141
- if (shouldThrowError)
142
- throw new Error(errorMsg);
143
- // 중복 제거 후 계속 진행
144
- charSet = Array.from(uniqueChars);
145
- if (!isPredefined) {
146
- requiredLength = charSet.length;
86
+ if (ensure(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters. Required: ${requiredLength}, Provided: ${charSet.length}`)) {
87
+ continue;
147
88
  }
89
+ if (ensure(requiredLength < 2, `[Ddu64 normalizeCharSet] At least 2 unique characters are required. Provided: ${requiredLength}`)) {
90
+ continue;
91
+ }
92
+ if (ensure(bitLength <= 0, `[Ddu64 normalizeCharSet] Invalid bit length (${bitLength}) for charset size ${requiredLength}`)) {
93
+ continue;
94
+ }
95
+ if (ensure(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset. Required: ${requiredLength} characters`)) {
96
+ continue;
97
+ }
98
+ let charLength = charSet[0].length;
99
+ const invalidIndex = charSet.findIndex((char) => char.length !== charLength);
100
+ if (invalidIndex !== -1) {
101
+ if (shouldThrowError) {
102
+ throw new Error(`[Ddu64 normalizeCharSet] Inconsistent character length. Expected: ${charLength}, but character at index ${invalidIndex} ("${charSet[invalidIndex]}") has length ${charSet[invalidIndex].length}`);
103
+ }
104
+ charSet = charSet.filter((char) => char.length === charLength);
105
+ if (ensure(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after filtering. Required: ${requiredLength}, Remaining: ${charSet.length}`)) {
106
+ continue;
107
+ }
108
+ if (ensure(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset after filtering`)) {
109
+ continue;
110
+ }
111
+ charLength = charSet[0].length;
112
+ }
113
+ if (ensure(padding.length !== charLength, `[Ddu64 normalizeCharSet] Padding character length mismatch. Expected: ${charLength}, Got: ${padding.length} (padding: "${padding}")`)) {
114
+ continue;
115
+ }
116
+ if (charSet.includes(padding)) {
117
+ if (shouldThrowError) {
118
+ throw new Error(`[Ddu64 normalizeCharSet] Padding character "${padding}" conflicts with charset. Padding must not be in the character set.`);
119
+ }
120
+ charSet = charSet.filter((char) => char !== padding);
121
+ if (ensure(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after removing padding conflict. Required: ${requiredLength}, Remaining: ${charSet.length}`)) {
122
+ continue;
123
+ }
124
+ if (ensure(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset after removing padding conflict`)) {
125
+ continue;
126
+ }
127
+ charLength = charSet[0].length;
128
+ }
129
+ return {
130
+ charSet: charSet.slice(0, requiredLength),
131
+ padding,
132
+ requiredLength,
133
+ bitLength,
134
+ isPredefined,
135
+ charLength,
136
+ };
148
137
  }
149
- // 검증
150
- if (validate(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters. Required: ${requiredLength}, Provided: ${charSet.length}`)) {
151
- return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
152
- }
153
- if (validate(requiredLength < 2, `[Ddu64 normalizeCharSet] At least 2 unique characters are required. Provided: ${requiredLength}`)) {
154
- return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
138
+ }
139
+ resolveInitialCharSet(dduChar, paddingChar, dduOptions, shouldThrowError) {
140
+ const finalize = (set, padding, length, baseBitLength, predefined = false) => {
141
+ const usePow2 = this.shouldUsePowerOfTwo(length, dduOptions?.usePowerOfTwo);
142
+ if (usePow2 && length > 0) {
143
+ const exponent = this.getLargestPowerOfTwoExponent(length);
144
+ const pow2Length = 1 << exponent;
145
+ return {
146
+ charSet: set.slice(0, pow2Length),
147
+ padding,
148
+ requiredLength: pow2Length,
149
+ bitLength: exponent,
150
+ isPredefined: predefined,
151
+ };
152
+ }
153
+ const computedBitLength = baseBitLength ?? (length > 0 ? this.getBitLength(length) : 0);
154
+ return {
155
+ charSet: set.slice(0, length),
156
+ padding,
157
+ requiredLength: length,
158
+ bitLength: computedBitLength,
159
+ isPredefined: predefined,
160
+ };
161
+ };
162
+ const fallback = () => this.getFallbackCharSet(dduOptions);
163
+ try {
164
+ const finalDduChar = dduChar ?? dduOptions?.dduChar;
165
+ const finalPadding = paddingChar ?? dduOptions?.paddingChar;
166
+ if (finalDduChar) {
167
+ if (!finalPadding) {
168
+ throw new Error(`[Ddu64 Constructor] paddingChar is required when dduChar is provided. Received: dduChar=${typeof finalDduChar}, paddingChar=${finalPadding}`);
169
+ }
170
+ const arr = typeof finalDduChar === "string"
171
+ ? [...finalDduChar.trim()]
172
+ : [...finalDduChar];
173
+ if (shouldThrowError) {
174
+ const uniqueChars = new Set(arr);
175
+ if (uniqueChars.size !== arr.length) {
176
+ const duplicates = arr.filter((char, index) => arr.indexOf(char) !== index);
177
+ throw new Error(`[Ddu64 Constructor] Character set contains duplicate characters. Total: ${arr.length}, Unique: ${uniqueChars.size}, Duplicates: [${[
178
+ ...new Set(duplicates),
179
+ ].join(", ")}]`);
180
+ }
181
+ }
182
+ const len = dduOptions?.requiredLength ?? arr.length;
183
+ if (arr.length < len) {
184
+ throw new Error(`[Ddu64 Constructor] Insufficient characters in charset. Required: ${len}, Provided: ${arr.length}`);
185
+ }
186
+ return finalize(arr, finalPadding, len, undefined, false);
187
+ }
188
+ if (dduOptions?.dduSetSymbol) {
189
+ const cs = this.getCharSetOrThrow(dduOptions.dduSetSymbol);
190
+ return finalize(cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true);
191
+ }
192
+ const defaultSymbol = dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.DDU;
193
+ const cs = this.getCharSetOrThrow(defaultSymbol);
194
+ return finalize(cs.charSet, cs.paddingChar, cs.maxRequiredLength, cs.bitLength, true);
155
195
  }
156
- if (validate(bitLength <= 0, `[Ddu64 normalizeCharSet] Invalid bit length (${bitLength}) for charset size ${requiredLength}`)) {
157
- return this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
196
+ catch (error) {
197
+ if (shouldThrowError)
198
+ throw error;
199
+ return fallback();
158
200
  }
159
- let charLength = charSet[0]?.length ?? 0;
160
- validate(charLength === 0, `[Ddu64 normalizeCharSet] Empty charset. Required: ${requiredLength} characters`);
161
- const invalidIndex = charSet.findIndex(char => char.length !== charLength);
162
- if (invalidIndex !== -1) {
163
- if (shouldThrowError) {
164
- throw new Error(`[Ddu64 normalizeCharSet] Inconsistent character length. Expected: ${charLength}, but character at index ${invalidIndex} ("${charSet[invalidIndex]}") has length ${charSet[invalidIndex].length}`);
165
- }
166
- charSet = charSet.filter(char => char.length === charLength);
167
- validate(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after filtering. Required: ${requiredLength}, Remaining: ${charSet.length}`);
168
- charLength = charSet[0]?.length ?? 0;
201
+ }
202
+ getFallbackCharSet(dduOptions) {
203
+ const fallbackSymbol = dduOptions?.dduSetSymbol ??
204
+ dduDefaultConstructorOptions.dduSetSymbol ??
205
+ DduSetSymbol.ONECHARSET;
206
+ const cs = getCharSet(fallbackSymbol) ?? getCharSet(DduSetSymbol.ONECHARSET);
207
+ if (!cs)
208
+ throw new Error(`Critical: No fallback CharSet available`);
209
+ return {
210
+ charSet: cs.charSet,
211
+ padding: cs.paddingChar,
212
+ requiredLength: cs.maxRequiredLength,
213
+ bitLength: cs.bitLength,
214
+ isPredefined: true,
215
+ };
216
+ }
217
+ shouldUsePowerOfTwo(length, preference) {
218
+ if (preference === true) {
219
+ return length > 0;
169
220
  }
170
- validate(!charSet.length, `[Ddu64 normalizeCharSet] Empty charset after validation`);
171
- validate(padding.length !== charLength, `[Ddu64 normalizeCharSet] Padding character length mismatch. Expected: ${charLength}, Got: ${padding.length} (padding: "${padding}")`);
172
- if (new Set(charSet).has(padding)) {
173
- if (shouldThrowError) {
174
- throw new Error(`[Ddu64 normalizeCharSet] Padding character "${padding}" conflicts with charset. Padding must not be in the character set.`);
175
- }
176
- charSet = charSet.filter(char => char !== padding);
177
- validate(charSet.length < requiredLength, `[Ddu64 normalizeCharSet] Insufficient characters after removing padding conflict. Required: ${requiredLength}, Remaining: ${charSet.length}`);
178
- charLength = charSet[0]?.length ?? 0;
221
+ if (preference === false) {
222
+ return false;
179
223
  }
180
- return { charSet: charSet.slice(0, requiredLength), padding, requiredLength, bitLength, isPredefined, charLength };
224
+ return length > 0 && (length & (length - 1)) === 0;
225
+ }
226
+ getCharSetOrThrow(symbol) {
227
+ const cs = getCharSet(symbol);
228
+ if (!cs)
229
+ throw new Error(`CharSet with symbol ${symbol} not found`);
230
+ return cs;
181
231
  }
182
232
  getBinaryFromIndex(index) {
183
233
  if (index < 0 || index >= this.maxBinaryValue) {
@@ -217,27 +267,23 @@ export class Ddu64 extends BaseDdu {
217
267
  encode(input, _options) {
218
268
  // options는 구버전 호환성을 위해 유지하지만 사용하지 않음
219
269
  const bufferInput = typeof input === "string" ? Buffer.from(input, this.encoding) : input;
220
- // 생성자에서 설정된 사용
221
- const dduLength = this.dduChar.length;
222
- const effectiveBitLength = this.effectiveBitLength;
223
- const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, effectiveBitLength);
224
- // 문자열 연결 최적화: Array + join 사용
270
+ const { dduBinary, padding } = this.bufferToDduBinary(bufferInput, this.effectiveBitLength);
225
271
  const resultParts = new Array(dduBinary.length);
226
- // 비트 청크를 변환
227
- for (let i = 0; i < dduBinary.length; i++) {
228
- const binaryChunk = dduBinary[i];
229
- const charInt = this.binaryChunkToIntFn(binaryChunk);
230
- if (!this.usePowerOfTwo) {
231
- // 가변 길이 조합 인코딩 (멀티바이트 문자도 지원)
232
- const quotient = Math.floor(charInt / dduLength);
233
- const remainder = charInt % dduLength;
234
- resultParts[i] = this.dduChar[quotient] + this.dduChar[remainder];
235
- }
236
- else {
237
- // 고정 길이 직접 매핑
272
+ if (this.usePowerOfTwo) {
273
+ for (let i = 0; i < dduBinary.length; i++) {
274
+ const charInt = this.binaryChunkToIntFn(dduBinary[i]);
238
275
  resultParts[i] = this.dduChar[charInt];
239
276
  }
240
277
  }
278
+ else {
279
+ const dduLength = this.dduChar.length;
280
+ for (let i = 0; i < dduBinary.length; i++) {
281
+ const value = this.binaryChunkToIntFn(dduBinary[i]);
282
+ const quotient = Math.floor(value / dduLength);
283
+ const remainder = value % dduLength;
284
+ resultParts[i] = this.dduChar[quotient] + this.dduChar[remainder];
285
+ }
286
+ }
241
287
  let resultString = resultParts.join("");
242
288
  // 패딩 비트 정보를 padChar + 패딩비트수 형태로 추가
243
289
  if (padding > 0) {
@@ -266,13 +312,14 @@ export class Ddu64 extends BaseDdu {
266
312
  input = input.substring(0, padCharIndex);
267
313
  }
268
314
  }
269
- let dduBinary = "";
315
+ const binaryParts = [];
316
+ const charLength = this.charLength;
270
317
  const dduLength = this.dduChar.length;
271
318
  if (!this.usePowerOfTwo) {
272
- const chunkSize = this.charLength * 2;
319
+ const chunkSize = charLength * 2;
273
320
  for (let i = 0; i < input.length; i += chunkSize) {
274
- const firstChar = input.slice(i, i + this.charLength);
275
- const secondChar = input.slice(i + this.charLength, i + chunkSize);
321
+ const firstChar = input.slice(i, i + charLength);
322
+ const secondChar = input.slice(i + charLength, i + chunkSize);
276
323
  const firstIndex = this.dduBinaryLookup.get(firstChar);
277
324
  const secondIndex = this.dduBinaryLookup.get(secondChar);
278
325
  if (firstIndex === undefined || secondIndex === undefined) {
@@ -283,23 +330,23 @@ export class Ddu64 extends BaseDdu {
283
330
  if (value >= this.maxBinaryValue) {
284
331
  throw new Error(`[Ddu64 decode] Invalid character combination detected. Calculated value ${value} exceeds binary range ${this.maxBinaryValue - 1}.`);
285
332
  }
286
- dduBinary += this.getBinaryFromIndex(value);
333
+ binaryParts.push(this.getBinaryFromIndex(value));
287
334
  }
288
335
  }
289
336
  else {
290
- for (let i = 0; i < input.length; i += this.charLength) {
291
- const charChunk = input.slice(i, i + this.charLength);
337
+ for (let i = 0; i < input.length; i += charLength) {
338
+ const charChunk = input.slice(i, i + charLength);
292
339
  const charIndex = this.dduBinaryLookup.get(charChunk);
293
340
  if (charIndex === undefined) {
294
- throw new Error(`[Ddu64 decode] Invalid character in encoded string. Character: "${charChunk}", Position: ${i}, Charset size: ${dduLength}, Character length: ${this.charLength}`);
341
+ throw new Error(`[Ddu64 decode] Invalid character in encoded string. Character: "${charChunk}", Position: ${i}, Charset size: ${dduLength}, Character length: ${charLength}`);
295
342
  }
296
343
  if (charIndex >= this.maxBinaryValue) {
297
344
  throw new Error(`[Ddu64 decode] Invalid binary index ${charIndex}. Allowed range: 0-${this.maxBinaryValue - 1}`);
298
345
  }
299
- dduBinary += this.getBinaryFromIndex(charIndex);
346
+ binaryParts.push(this.getBinaryFromIndex(charIndex));
300
347
  }
301
348
  }
302
- return this.dduBinaryToBuffer(dduBinary, paddingBits);
349
+ return this.dduBinaryToBuffer(binaryParts.join(""), paddingBits);
303
350
  }
304
351
  decode(input, _options) {
305
352
  return this.decodeToBuffer(input, _options).toString(this.encoding);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ddunigma/node",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/mjs/index.js",
6
6
  "types": "dist/index.d.ts",