@ddunigma/node 1.1.3 → 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.
@@ -15,393 +15,181 @@ export class Ddu64 extends BaseDdu {
15
15
  indexToBinaryCache;
16
16
  constructor(dduChar, paddingChar, dduOptions) {
17
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
+ };
18
25
  // charset 초기화
19
- let finalCharSet;
20
- let finalPadding;
21
- let requiredLength;
22
- let bitLength;
23
- let isPredefined = false;
26
+ let charSet, padding, requiredLength, bitLength, isPredefined;
24
27
  try {
25
- const result = this.initializeCharSet(dduChar, paddingChar, dduOptions);
26
- finalCharSet = [...result.finalCharSet];
27
- finalPadding = result.finalPadding;
28
- requiredLength = result.requiredLength;
29
- bitLength = result.bitLength;
30
- isPredefined = result.isPredefined;
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
+ }
47
+ }
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];
65
+ }
31
66
  }
32
67
  catch (error) {
33
- // useBuildErrorReturn이 true일 경우 에러를 그대로 throw
34
- if (dduOptions?.useBuildErrorReturn ?? false) {
68
+ if (shouldThrowError)
35
69
  throw error;
36
- }
37
- // fallback 처리
38
- const fallbackResult = this.getFallbackCharSet(dduOptions);
39
- finalCharSet = [...fallbackResult.finalCharSet];
40
- finalPadding = fallbackResult.finalPadding;
41
- requiredLength = fallbackResult.requiredLength;
42
- bitLength = fallbackResult.bitLength;
43
- isPredefined = true; // fallback도 predefined charset 사용
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];
44
76
  }
45
- const shouldThrowError = dduOptions?.useBuildErrorReturn ?? false;
46
- const normalized = this.normalizeCharSet({
47
- charSet: finalCharSet,
48
- padding: finalPadding,
49
- requiredLength,
50
- bitLength,
51
- isPredefined,
52
- shouldThrowError,
53
- dduOptions,
54
- });
77
+ // 정규화 검증
78
+ const normalized = this.normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions);
55
79
  this.dduChar = normalized.charSet;
56
80
  this.paddingChar = normalized.padding;
57
81
  this.charLength = normalized.charLength;
58
82
  this.bitLength = normalized.bitLength;
59
83
  this.isPredefinedCharSet = normalized.isPredefined;
84
+ this.encoding = dduOptions?.encoding ?? this.defaultEncoding;
60
85
  const dduLength = this.dduChar.length;
61
86
  this.usePowerOfTwo = dduLength > 0 && (dduLength & (dduLength - 1)) === 0;
62
- this.effectiveBitLength = this.usePowerOfTwo
63
- ? this.bitLength
64
- : this.getBitLength(dduLength);
65
- const canUseBitwise = this.effectiveBitLength <= 26;
66
- if (canUseBitwise) {
67
- this.binaryChunkToIntFn = (chunk) => {
68
- let value = 0;
69
- for (let i = 0; i < chunk.length; i++) {
70
- value = (value << 1) | (chunk.charCodeAt(i) & 1);
71
- }
72
- return value;
73
- };
74
- }
75
- else {
76
- this.binaryChunkToIntFn = (chunk) => {
77
- let value = 0;
78
- for (let i = 0; i < chunk.length; i++) {
79
- value = value * 2 + (chunk.charCodeAt(i) & 1);
80
- }
81
- return value;
82
- };
83
- }
84
- this.encoding = dduOptions?.encoding ?? this.defaultEncoding;
85
- this.dduChar.forEach((char, index) => {
86
- this.dduBinaryLookup.set(char, index);
87
- });
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));
88
92
  if (this.charLength >= 2 && !this.isPredefinedCharSet) {
89
- this.validateCombinationDuplicates(this.dduChar, this.paddingChar, this.dduChar.length);
93
+ this.validateCombinationDuplicates(this.dduChar, this.paddingChar, dduLength);
90
94
  }
91
95
  if (this.effectiveBitLength <= 16) {
92
- const cacheSize = 1 << this.effectiveBitLength;
93
- this.indexToBinaryCache = new Array(cacheSize);
94
- for (let i = 0; i < cacheSize; i++) {
95
- this.indexToBinaryCache[i] = i
96
- .toString(2)
97
- .padStart(this.effectiveBitLength, "0");
98
- }
96
+ const size = 1 << this.effectiveBitLength;
97
+ this.indexToBinaryCache = Array.from({ length: size }, (_, i) => i.toString(2).padStart(this.effectiveBitLength, "0"));
99
98
  }
100
99
  else {
101
100
  this.indexToBinaryCache = null;
102
101
  }
103
102
  }
104
- /**
105
- * charset 초기화 (커스텀 또는 미리 정의된 charset)
106
- */
107
- initializeCharSet(dduChar, paddingChar, dduOptions) {
108
- const finalDduChar = dduChar ?? dduOptions?.dduChar;
109
- const finalPaddingChar = paddingChar ?? dduOptions?.paddingChar;
110
- // 1. 커스텀 charset 사용
111
- if (finalDduChar) {
112
- return this.processCustomCharSet(finalDduChar, finalPaddingChar, dduOptions);
113
- }
114
- // 2. 미리 정의된 charset 사용
115
- if (dduOptions?.dduSetSymbol) {
116
- return this.processPredefinedCharSet(dduOptions.dduSetSymbol, dduOptions);
117
- }
118
- // 3. 기본 charset 사용
119
- return this.processDefaultCharSet();
120
- }
121
- /**
122
- * 커스텀 charset 처리
123
- */
124
- processCustomCharSet(dduChar, paddingChar, dduOptions) {
125
- if (!paddingChar) {
126
- throw new Error("paddingChar is required when dduChar or dduOptions.dduChar is provided");
127
- }
128
- // string 타입을 배열로 변환
129
- const dduCharArray = this.convertToCharArray(dduChar);
130
- const dduCharLength = dduOptions?.requiredLength ?? dduCharArray.length;
131
- if (dduCharArray.length < dduCharLength) {
132
- throw new Error(`dduChar must be at least ${dduCharLength} characters long. Provided: ${dduCharArray.length}`);
133
- }
134
- // usePowerOfTwo 처리
135
- // charArray의 길이가 2의 제곱수이거나 option에 usePowerOfTwo가 명시적으로 true일 때만 적용
136
- const shouldUsePowerOfTwo = dduOptions?.usePowerOfTwo === true ||
137
- (dduOptions?.usePowerOfTwo === undefined && this.isPowerOfTwo(dduCharLength));
138
- const result = this.applyPowerOfTwoOption(dduCharArray, paddingChar, dduCharLength, shouldUsePowerOfTwo);
139
- return {
140
- ...result,
141
- isPredefined: false // 커스텀 charset은 검증 필요
142
- };
143
- }
144
- /**
145
- * 미리 정의된 charset 처리
146
- */
147
- processPredefinedCharSet(symbol, dduOptions) {
148
- const fixedCharSet = getCharSet(symbol);
149
- if (!fixedCharSet) {
150
- throw new Error(`CharSet with symbol ${symbol} not found`);
151
- }
152
- const { charSet, paddingChar, maxRequiredLength, bitLength } = fixedCharSet;
153
- // usePowerOfTwo 처리
154
- // charArray의 길이가 2의 제곱수이거나 option에 usePowerOfTwo가 명시적으로 true일 때만 적용
155
- const shouldUsePowerOfTwo = dduOptions?.usePowerOfTwo === true ||
156
- (dduOptions?.usePowerOfTwo === undefined && this.isPowerOfTwo(maxRequiredLength));
157
- if (shouldUsePowerOfTwo) {
158
- const result = this.applyPowerOfTwoOption(charSet, paddingChar, maxRequiredLength, true);
159
- return {
160
- ...result,
161
- isPredefined: true // 미리 정의된 charset은 검증 패스
162
- };
163
- }
164
- return {
165
- finalCharSet: charSet,
166
- finalPadding: paddingChar,
167
- requiredLength: maxRequiredLength,
168
- bitLength,
169
- isPredefined: true // 미리 정의된 charset은 검증 패스
170
- };
171
- }
172
- /**
173
- * 기본 charset 처리
174
- */
175
- processDefaultCharSet() {
176
- const defaultSymbol = dduDefaultConstructorOptions.dduSetSymbol ?? DduSetSymbol.DDU;
177
- const fixedCharSet = getCharSet(defaultSymbol);
178
- if (!fixedCharSet) {
179
- throw new Error(`Default CharSet with symbol ${defaultSymbol} not found`);
180
- }
181
- return {
182
- finalCharSet: fixedCharSet.charSet,
183
- finalPadding: fixedCharSet.paddingChar,
184
- requiredLength: fixedCharSet.maxRequiredLength,
185
- bitLength: fixedCharSet.bitLength,
186
- isPredefined: true // 기본 charset도 미리 정의된 것이므로 검증 패스
187
- };
188
- }
189
- /**
190
- * fallback charset 가져오기
191
- * 우선순위: dduOptions.dduSetSymbol > dduDefaultConstructorOptions.dduSetSymbol > DduSetSymbol.ONECHARSET
192
- */
193
- getFallbackCharSet(dduOptions) {
194
- // 우선순위에 따라 fallback symbol 결정
195
- const fallbackSymbol = dduOptions?.dduSetSymbol ??
196
- dduDefaultConstructorOptions.dduSetSymbol ??
197
- DduSetSymbol.ONECHARSET; // DDU 대신 ONECHARSET 사용
198
- const fixedCharSet = getCharSet(fallbackSymbol);
199
- if (!fixedCharSet) {
200
- // 최후의 fallback: ONECHARSET
201
- const lastResort = getCharSet(DduSetSymbol.ONECHARSET);
202
- if (!lastResort) {
203
- throw new Error(`Critical: No fallback CharSet available`);
204
- }
205
- return {
206
- finalCharSet: lastResort.charSet,
207
- finalPadding: lastResort.paddingChar,
208
- requiredLength: lastResort.maxRequiredLength,
209
- bitLength: lastResort.bitLength,
210
- };
211
- }
212
- return {
213
- finalCharSet: fixedCharSet.charSet,
214
- finalPadding: fixedCharSet.paddingChar,
215
- requiredLength: fixedCharSet.maxRequiredLength,
216
- bitLength: fixedCharSet.bitLength,
217
- };
218
- }
219
- normalizeCharSet(params) {
220
- let currentCharSet = [...params.charSet];
221
- let currentPadding = params.padding;
222
- let currentRequiredLength = params.requiredLength;
223
- let currentBitLength = params.bitLength;
224
- let currentIsPredefined = params.isPredefined;
225
- const { shouldThrowError, dduOptions } = params;
226
- let fallbackCache = null;
227
- const getFallback = () => {
228
- if (!fallbackCache) {
229
- const fallback = this.getFallbackCharSet(dduOptions);
230
- fallbackCache = {
231
- finalCharSet: [...fallback.finalCharSet],
232
- finalPadding: fallback.finalPadding,
233
- requiredLength: fallback.requiredLength,
234
- bitLength: fallback.bitLength,
235
- };
236
- }
237
- return fallbackCache;
238
- };
103
+ normalizeCharSet(charSet, padding, requiredLength, bitLength, isPredefined, shouldThrowError, dduOptions) {
239
104
  const applyFallback = () => {
240
- const fallback = getFallback();
241
- currentCharSet = [...fallback.finalCharSet];
242
- currentPadding = fallback.finalPadding;
243
- currentRequiredLength = fallback.requiredLength;
244
- currentBitLength = fallback.bitLength;
245
- currentIsPredefined = true;
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 };
246
110
  };
247
- const ensureMinLength = () => {
248
- if (currentCharSet.length < currentRequiredLength) {
249
- if (shouldThrowError) {
250
- throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: ${currentCharSet.length}`);
251
- }
252
- applyFallback();
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;
253
118
  }
119
+ return false;
254
120
  };
255
- if (!currentIsPredefined) {
256
- const uniqueChars = new Set(currentCharSet);
257
- if (uniqueChars.size !== currentCharSet.length) {
258
- if (shouldThrowError) {
259
- throw new Error(`Character set contains duplicate characters. Unique: ${uniqueChars.size}, Total: ${currentCharSet.length}`);
260
- }
261
- currentCharSet = Array.from(uniqueChars);
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;
262
129
  }
263
130
  }
264
- ensureMinLength();
265
- let charLength = currentCharSet[0]?.length ?? 0;
266
- if (charLength === 0) {
267
- if (shouldThrowError) {
268
- throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: ${currentCharSet.length}`);
269
- }
270
- applyFallback();
271
- charLength = currentCharSet[0]?.length ?? 0;
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);
272
134
  }
273
- const invalidIndex = currentCharSet.findIndex((char) => char.length !== charLength);
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);
274
138
  if (invalidIndex !== -1) {
275
- if (shouldThrowError) {
276
- throw new Error(`All characters must have the same length. Expected: ${charLength}, but character at index ${invalidIndex} ("${currentCharSet[invalidIndex]}") has length ${currentCharSet[invalidIndex].length}`);
277
- }
278
- currentCharSet = currentCharSet.filter((char) => char.length === charLength);
279
- ensureMinLength();
280
- charLength = currentCharSet[0]?.length ?? 0;
281
- }
282
- if (!currentCharSet.length) {
283
- if (shouldThrowError) {
284
- throw new Error(`${this.constructor.name} requires at least ${currentRequiredLength} characters in the character set. Provided: 0`);
285
- }
286
- applyFallback();
287
- charLength = currentCharSet[0]?.length ?? 0;
288
- }
289
- if (currentPadding.length !== charLength) {
290
- if (shouldThrowError) {
291
- throw new Error(`Padding character must have the same length as the characters. Expected: ${charLength}, but padding character has length ${currentPadding.length}`);
292
- }
293
- applyFallback();
294
- charLength = currentCharSet[0]?.length ?? 0;
295
- }
296
- const charSetLookup = new Set(currentCharSet);
297
- if (charSetLookup.has(currentPadding)) {
298
- if (shouldThrowError) {
299
- throw new Error(`Padding character "${currentPadding}" cannot be in the character set`);
300
- }
301
- currentCharSet = currentCharSet.filter((char) => char !== currentPadding);
302
- ensureMinLength();
303
- charLength = currentCharSet[0]?.length ?? 0;
304
- }
305
- currentCharSet = currentCharSet.slice(0, currentRequiredLength);
306
- return {
307
- charSet: currentCharSet,
308
- padding: currentPadding,
309
- requiredLength: currentRequiredLength,
310
- bitLength: currentBitLength,
311
- isPredefined: currentIsPredefined,
312
- charLength,
313
- };
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;
144
+ }
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;
153
+ }
154
+ return { charSet: charSet.slice(0, requiredLength), padding, requiredLength, bitLength, isPredefined, charLength };
314
155
  }
315
156
  getBinaryFromIndex(index) {
316
- const cache = this.indexToBinaryCache;
317
- if (cache && index < cache.length) {
318
- return cache[index];
319
- }
320
- return index.toString(2).padStart(this.effectiveBitLength, "0");
321
- }
322
- /**
323
- * string 또는 배열을 문자 배열로 변환
324
- */
325
- convertToCharArray(input) {
326
- if (typeof input === "string") {
327
- return [...new Set(input.trim())].map((c) => c);
328
- }
329
- return input;
157
+ return this.indexToBinaryCache?.[index] ?? index.toString(2).padStart(this.effectiveBitLength, "0");
330
158
  }
331
- /**
332
- * 숫자가 2의 제곱수인지 확인
333
- */
334
- isPowerOfTwo(n) {
335
- return n > 0 && (n & (n - 1)) === 0;
336
- }
337
- /**
338
- * usePowerOfTwo 옵션 적용
339
- */
340
- applyPowerOfTwoOption(charArray, padding, length, usePowerOfTwo) {
341
- if (usePowerOfTwo) {
342
- const powerOfTwoExponent = this.getLargestPowerOfTwoExponent(length);
343
- const powerOfTwoLength = Math.pow(2, powerOfTwoExponent);
344
- return {
345
- finalCharSet: charArray.slice(0, powerOfTwoLength),
346
- finalPadding: padding,
347
- requiredLength: powerOfTwoLength,
348
- bitLength: powerOfTwoExponent,
349
- };
350
- }
351
- return {
352
- finalCharSet: charArray.slice(0, length),
353
- finalPadding: padding,
354
- requiredLength: length,
355
- bitLength: this.getBitLength(length),
356
- };
357
- }
358
- /**
359
- * 2글자 이상의 문자셋에서 조합으로 인한 중복을 검증
360
- * 예: ["AB", "CD"] + padding "AB" 또는 "A" + "B" = "AB" 같은 경우 감지
361
- */
362
159
  validateCombinationDuplicates(charSet, paddingChar, requiredLength) {
363
160
  const charLength = charSet[0].length;
364
- const allStrings = new Set();
365
- // 1. 기본 문자셋과 패딩 문자 추가
366
- charSet.slice(0, requiredLength).forEach((char) => allStrings.add(char));
367
- allStrings.add(paddingChar);
368
- // 2. 2개 조합 검사 (charLength * 2 길이)
369
- for (let i = 0; i < Math.min(charSet.length, requiredLength); i++) {
370
- for (let j = 0; j < Math.min(charSet.length, requiredLength); j++) {
371
- const combination = charSet[i] + charSet[j];
372
- // 조합이 기존 문자와 중복되는지 확인
373
- if (allStrings.has(combination)) {
374
- 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`);
375
169
  }
376
170
  }
377
- // 문자 + 패딩 조합 검사
378
- const charPlusPadding = charSet[i] + paddingChar;
379
- const paddingPlusChar = paddingChar + charSet[i];
380
- if (allStrings.has(charPlusPadding)) {
381
- throw new Error(`Combination conflict detected: "${charSet[i]}" + padding "${paddingChar}" = "${charPlusPadding}" already exists in the character set`);
382
- }
383
- if (allStrings.has(paddingPlusChar)) {
384
- throw new Error(`Combination conflict detected: padding "${paddingChar}" + "${charSet[i]}" = "${paddingPlusChar}" already exists in the character set`);
385
- }
386
- }
387
- // 3. 패딩 + 패딩 조합 검사
388
- const doublePadding = paddingChar + paddingChar;
389
- if (allStrings.has(doublePadding)) {
390
- throw new Error(`Combination conflict detected: padding "${paddingChar}" + padding "${paddingChar}" = "${doublePadding}" already exists in the character set`);
391
- }
392
- // 4. 3개 이상 조합 검사 (선택적, 성능을 위해 샘플링)
393
- // 모든 조합을 검사하면 O(n^3)이므로 일부만 샘플링
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}"`);
177
+ }
178
+ // 패딩 + 패딩 조합 검사
179
+ const doublePad = paddingChar + paddingChar;
180
+ if (allStrings.has(doublePad)) {
181
+ throw new Error(`Combination conflict: double padding "${doublePad}" already exists`);
182
+ }
183
+ // 3개 조합 검사 (100개 이하만)
394
184
  if (charLength >= 2 && requiredLength <= 100) {
395
- // 100개 이하일 때만 전체 검사
396
- for (let i = 0; i < Math.min(charSet.length, requiredLength); i++) {
397
- for (let j = 0; j < Math.min(charSet.length, requiredLength); j++) {
398
- for (let k = 0; k < Math.min(charSet.length, requiredLength); k++) {
399
- const combination = charSet[i] + charSet[j] + charSet[k];
400
- // 조합의 부분 문자열이 기존 문자와 중복되는지 확인
401
- for (let start = 0; start < combination.length - charLength + 1; start++) {
402
- const substring = combination.substring(start, start + charLength);
403
- if (allStrings.has(substring) && substring !== charSet[i] && substring !== charSet[j] && substring !== charSet[k]) {
404
- 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]}"`);
405
193
  }
406
194
  }
407
195
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ddunigma/node",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/mjs/index.js",
6
6
  "types": "dist/index.d.ts",