Programming

Random Id generator

foxlee 2022. 12. 30. 10:03
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)())