UNPKG

ph-utils

Version:

js 开发工具集,前后端都可以使用(commonjs和es module)

171 lines (170 loc) 6.09 kB
/** 雪花ID, 推荐在全局构造一个对象用于生成id */ export class SnowflakeID { /** * 构造函数 * * @param machineId 机器标识,默认为1 * @param epoch 时间戳起始值,默认为1288834974657 */ constructor(machineId = 1, epoch = 1288834974657) { /** 版本号, 默认为: 0 */ this.version = 0; this.machineId = BigInt(machineId); this.epoch = BigInt(epoch); this._lastTimestamp = BigInt(0); this._sequence = BigInt(0); } _getTimestamp() { return BigInt(Date.now()); } _waitNextMillis(cTimestamp) { let timestamp = cTimestamp; while (timestamp <= this._lastTimestamp) { timestamp = BigInt(Date.now()); } return timestamp; } /** * 生成雪花ID * * @returns 返回生成的唯一ID字符串 * @throws 如果时钟回退,抛出错误 */ generate() { let cTimestamp = this._getTimestamp(); if (cTimestamp < this._lastTimestamp) { throw new Error("Clock moved backwards"); } if (cTimestamp === this._lastTimestamp) { // TODO: increment sequence // 雪花id,末尾: 12位序列号(二进制12个1等于16进制4095),最多4096个(从0到4095) this._sequence = (this._sequence + 1n) & BigInt("0xFFF"); if (this._sequence === 0n) { // 同一时间戳,序列号溢出,等待下一毫秒 cTimestamp = this._waitNextMillis(cTimestamp); } } else { this._sequence = BigInt(0); } this._lastTimestamp = cTimestamp; const timeDiff = cTimestamp - this.epoch; let id = (timeDiff << BigInt(22)) | (this.machineId << BigInt(12)) | this._sequence; let idstr = id.toString().padStart(19, "0"); return `${this.version}${idstr}`; } parse(snowflakeID, epoch, includeVersion = true) { let version = undefined; if (includeVersion) { snowflakeID = snowflakeID.substring(1); version = snowflakeID.substring(0, 1); } let epochTime = epoch || Number(this.epoch); const id = BigInt(snowflakeID); // 1FFFFFFFFFF 就是二进制的41个1, 雪花id前面41为为时间戳,间隔部分 let timeDiff = (id >> BigInt(22)) & BigInt("0x1FFFFFFFFFF"); const flowTime = Number(timeDiff) + epochTime; // 雪花id中间10位为机器标识, 0x3FF(二进制10个1) 进行位运算 const machineId = (id >> BigInt(12)) & BigInt("0x3FF"); const sequence = id & BigInt("0xFFF"); return { value: snowflakeID, timeOffset: timeDiff, timestamp: flowTime, machineId: machineId, sequence: sequence, epoch: epochTime, version: version, }; } } /** 将uuid转换为更简单的唯一标记id */ export class ShortUUID { /** * 构造函数,用于初始化字母表 * @param {string} [alphabet] - 可选参数,用于指定自定义字母表 * 如果提供了alphabet参数,则将其设置为实例的字母表属性 * 如果未提供alphabet参数,则使用默认值 */ constructor(alphabet) { this.alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; if (alphabet) { this.alphabet = alphabet; } } /** * 将UUID字符串进行编码处理 * @param uuid - 需要编码的UUID字符串 * @returns 编码后的字符串 */ encode(uuid, alphabet) { // 将UUID字符串转换为整数 const uuidInt = this._uuidHexToInt(uuid); // 将整数转换为特定格式的字符串 return this._intToString(uuidInt, alphabet); } /** * 解码短UUID字符串,将其转换为UUID整数和十六进制格式 * @param shortUUID - 需要解码的短UUID字符串 * @returns 返回包含UUID整数和十六进制格式的对象 */ decode(shortUUID, alphabet) { const uuidInt = this._stringToInt(shortUUID, alphabet); return { uuidInt: uuidInt, uuid: this._uuidIntToHex(uuidInt), }; } _stringToInt(str, alphabet) { if (!alphabet) alphabet = this.alphabet; const alphabetlen = BigInt(alphabet.length); let result = BigInt(0); let multiplier = BigInt(1); const strlen = str.length; for (let i = strlen - 1; i >= 0; i--) { const char = str[i]; const index = alphabet.indexOf(char); if (index === -1) { throw new Error(`Character "${char}" not found in alphabet`); } result += BigInt(index) * multiplier; multiplier *= alphabetlen; } return result; } _intToString(uuidInt, alphabet) { if (!alphabet) alphabet = this.alphabet; const alphabetlen = BigInt(alphabet.length); let num = uuidInt; const result = []; // uuidInt = d₀ × 1(base的0次方) + d₁ × base + d₂ × base²... while (num) { const index = Number(num % alphabetlen); num = num / alphabetlen; result.push(alphabet.charAt(index)); } // 倒序,保证高位在前 return result.reverse().join(""); } _uuidHexToInt(uuid) { const uuidHex = uuid.replaceAll("-", ""); return BigInt(`0x${uuidHex}`); } _uuidIntToHex(uuidInt) { const uuidHexStr = uuidInt.toString(16); if (!/^[0-9a-fA-F]{32}$/.test(uuidHexStr)) { throw new Error("Invalid UUID string (must be 32 hex characters)"); } return [ uuidHexStr.slice(0, 8), uuidHexStr.slice(8, 12), uuidHexStr.slice(12, 16), uuidHexStr.slice(16, 20), uuidHexStr.slice(20), ].join("-"); } }