import { randomFillSync } from 'crypto'
const POOL_SIZE_MULTIPLIER = 128
let pool, poolOffset
function fillPool(bytes) {
if (!pool || pool.length < bytes) {
pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER)
randomFillSync(pool)
poolOffset = 0
} else if (poolOffset + bytes > pool.length) {
randomFillSync(pool)
poolOffset = 0
}
poolOffset += bytes
}
function random(bytes) {
// `-=` convert `bytes` to number to prevent `valueOf` abusing
fillPool((bytes -= 0))
return pool.subarray(poolOffset - bytes, poolOffset)
}
function customRandom(alphabet, defaultSize, getRandom) {
// mask: id 생성하기 위해 필요하고 입력한 알파벳의 길이에 가까움
let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
// 랜덤 생성기 호출은 비싼 비용이 들기 때문에 추가적인 바이트를 미리 요청함
// 자리마다 랜덤 알파벳을 추가하지만 bytes[i] & mask 의 인덱스 값이 입력된 알파벳의 길이보다 크면 다시 요청해야함
// 대략적으로 1.6 * size (mask = alphabet.length + 1)
// 1.6일때 최상의 퍼포먼스를 보임
let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)
return (size = defaultSize) => {
let id = ''
while (true) {
let bytes = getRandom(step)
let i = step
while (i--) {
console.log(`bytes[i] & mask - random index: ${bytes[i] & mask} out of ${mask}, do it again if random index === mask after adding '' `)
// 1. 버퍼에 숫자를 가져오고 (두자리)
// 2. mask 는 입력된 알파벳 길이 + 1로
// 3. 두 숫자를 바이트 &를 하면 2진수로 변경하여 둘다 1인 자리에서만 더해짐 = 더해진 수가 mask보다 큰 숫자가 나올 확률은 작음
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND
id += alphabet[bytes[i] & mask] || ''
if (id.length === size) return id
}
}
}
}
function customAlphabet(alphabet, length) {
return customRandom(alphabet, length, random)
}
console.log(customAlphabet('1234567890abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 10)())
console.log(customAlphabet('1234567890abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 10)())