From 07e0d3938ac4e41735ccfb205242e7989c6e6da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Thu, 9 Oct 2025 17:09:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(serial=5Fcode):=20=E6=B7=BB=E5=8A=A0UUIDv7?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=99=A8=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增UUIDv7生成器模块,支持基于自定义纪元的时间戳、节点ID和序列号生成 UUIDv7,并提供Base64、Base36及标准字符串格式的编码与解码功能。 该生成器具备线程安全性和全局单例初始化机制,确保分布式环境下的唯一性。 --- src/serial_code/mod.rs | 1 + src/serial_code/uuidv7.rs | 291 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 src/serial_code/uuidv7.rs diff --git a/src/serial_code/mod.rs b/src/serial_code/mod.rs index 7525964..1d7a45c 100644 --- a/src/serial_code/mod.rs +++ b/src/serial_code/mod.rs @@ -1,4 +1,5 @@ pub mod hail; +pub mod uuidv7; pub mod uuid { /// 生成一个UUID v4字符串。 diff --git a/src/serial_code/uuidv7.rs b/src/serial_code/uuidv7.rs new file mode 100644 index 0000000..9a207bc --- /dev/null +++ b/src/serial_code/uuidv7.rs @@ -0,0 +1,291 @@ +use std::{ + sync::{Mutex, OnceLock}, + thread, + time::Duration, +}; + +use thiserror::Error; +use time::UtcDateTime; + +#[derive(Debug, Error)] +pub enum UuidV7Error { + #[error("UUIDv7 Generator not initialized")] + GeneratorNotInitialized, + #[error("UUIDv7 Node ID exceeded the maximum supports")] + NodeIdExceeded, + #[error("Invalid UUIDv7 string format")] + InvalidStringFormat, + #[error("Invalid Base64 format string")] + InvalidBase64Format, +} + +const EPOCH: i64 = 1645566142222; // 自定义纪元:2022-02-22 22:22:22.222 UTC +const NODE_BITS: u8 = 5; // 主机编号容量为0~32 +const SEQUENCE_BITS: u8 = 18; // 每毫秒生成序列号最大为2^18-1,即262144个 +const MAX_NODE_ID: u16 = (1 << NODE_BITS) - 1; +const MAX_SEQUENCE: u32 = (1 << SEQUENCE_BITS) - 1; +const TIMESTAMP_SHIFTS: u8 = NODE_BITS + SEQUENCE_BITS; +const NODE_SHIFTS: u8 = SEQUENCE_BITS; + +pub struct Uuidv7Generator { + node_id: u16, + last_timestamp: i64, + sequence: u32, +} + +#[allow(dead_code)] +pub struct Uuidv7Components { + timestamp: i64, // 毫秒时间戳(相对于自定义纪元) + node_id: u16, // 5位节点ID + sequence: u32, // 18位序列号 + version: u8, // 版本号(应为7) + variant: u8, // 变体(应为2,表示10xx) + raw_bytes: [u8; 16], // 原始字节 +} + +static GENERATOR: OnceLock> = OnceLock::new(); + +/// 获取UUIDv7生成器实例,如果实例未初始化,则返回错误。 +pub fn generator<'a>() -> Result<&'a Mutex, UuidV7Error> { + GENERATOR.get().ok_or(UuidV7Error::GeneratorNotInitialized) +} + +/// 初始化UUIDv7生成器实例。 +/// +/// - `node_id`:节点ID,取值范围:0~32。 +pub fn init_generator(node_id: u16) -> Result<(), UuidV7Error> { + if node_id > MAX_NODE_ID { + return Err(UuidV7Error::NodeIdExceeded); + } + GENERATOR + .set(Mutex::new(Uuidv7Generator::new(node_id))) + .map_err(|_| UuidV7Error::GeneratorNotInitialized) +} + +impl Uuidv7Generator { + pub fn new(node_id: u16) -> Self { + Self { + node_id, + last_timestamp: EPOCH, + sequence: 0, + } + } + + fn now(&self) -> i64 { + (UtcDateTime::now().unix_timestamp_nanos() / 1_000_000 - EPOCH as i128) as i64 + } + + pub fn next(&mut self) -> Uuidv7Components { + let mut now = self.now(); + if now < self.last_timestamp { + now = self.last_timestamp; + } + if now == self.last_timestamp { + self.sequence += 1; + if self.sequence > MAX_SEQUENCE { + thread::sleep(Duration::from_secs(1)); + now = self.now(); + self.sequence = 0; + } + } else { + self.sequence = 0; + } + self.last_timestamp = now; + Uuidv7Components { + timestamp: now, + node_id: self.node_id, + sequence: self.sequence, + version: 7, + variant: 2, + raw_bytes: [0; 16], + } + } +} + +impl Uuidv7Components { + pub fn bytes(&self) -> [u8; 16] { + let mut uuid = [0u8; 16]; + + // 时间戳(48 位) + let timestamp = (self.timestamp as u64) << TIMESTAMP_SHIFTS; + let seq_and_node = ((self.sequence as u64) << NODE_SHIFTS) | (self.node_id as u64); + + // 写入高位时间戳 + uuid[0] = (timestamp >> 56) as u8; + uuid[1] = (timestamp >> 48) as u8; + uuid[2] = (timestamp >> 40) as u8; + uuid[3] = (timestamp >> 32) as u8; + uuid[4] = (timestamp >> 24) as u8; + uuid[5] = (timestamp >> 16) as u8; + + // 写入低位时间戳 + 版本号(4 位) + uuid[6] = ((timestamp >> 8) as u8) | 0x70; // version 7 + + // 写入 seq + node + uuid[7] = timestamp as u8; + uuid[8] = ((seq_and_node >> 16) as u8) | 0x80; // variant 10xx + uuid[9] = (seq_and_node >> 8) as u8; + uuid[10] = seq_and_node as u8; + + // 剩余位使用随机数填充 + use rand::RngCore; + let mut rng = rand::thread_rng(); + rng.fill_bytes(&mut uuid[11..16]); + + uuid + } + + pub fn to_base64(&self) -> String { + let uuid = self.bytes(); + crate::serialize::to_base64_str(uuid) + } + + pub fn try_from_base64>(uuid_str: S) -> Result { + let bytes = crate::serialize::from_base64_str(uuid_str.as_ref()) + .map_err(|_| UuidV7Error::InvalidBase64Format)?; + + if bytes.len() != 16 { + return Err(UuidV7Error::InvalidStringFormat); + } + + let mut uuid_bytes = [0u8; 16]; + uuid_bytes.copy_from_slice(&bytes); + + Uuidv7Components::try_from(uuid_bytes) + } + + pub fn to_base36(&self) -> String { + let uuid = self.bytes(); + crate::serialize::base36::encode(&uuid) + } + + pub fn try_from_base36>(uuid_str: S) -> Result { + let bytes = crate::serialize::base36::decode(uuid_str.as_ref()) + .map_err(|_| UuidV7Error::InvalidStringFormat)?; + + if bytes.len() != 16 { + return Err(UuidV7Error::InvalidStringFormat); + } + + let mut uuid_bytes = [0u8; 16]; + uuid_bytes.copy_from_slice(&bytes); + + Uuidv7Components::try_from(uuid_bytes) + } +} + +impl ToString for Uuidv7Components { + fn to_string(&self) -> String { + let uuid = self.bytes(); + format!( + "{}-{}-{}-{}-{}", + hex::encode(&uuid[0..4]), + hex::encode(&uuid[4..6]), + hex::encode(&uuid[6..8]), + hex::encode(&uuid[8..10]), + hex::encode(&uuid[10..16]) + ) + } +} + +impl PartialEq for Uuidv7Components { + fn eq(&self, other: &Self) -> bool { + self.timestamp == other.timestamp + && self.node_id == other.node_id + && self.sequence == other.sequence + } +} + +impl Eq for Uuidv7Components {} + +impl PartialOrd for Uuidv7Components { + fn partial_cmp(&self, other: &Self) -> Option { + if self.timestamp != other.timestamp { + return Some(self.timestamp.cmp(&other.timestamp)); + } + + if self.sequence != other.sequence { + return Some(self.sequence.cmp(&other.sequence)); + } + + if self.node_id != other.node_id { + // 注意:这里与时间戳和序列号不同,node_id的比较逻辑是反的 + // 这与Go代码中的Compare方法保持一致 + return Some(other.node_id.cmp(&self.node_id)); + } + + Some(std::cmp::Ordering::Equal) + } +} + +impl Ord for Uuidv7Components { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.partial_cmp(other).unwrap() + } +} + +impl TryFrom<[u8; 16]> for Uuidv7Components { + type Error = UuidV7Error; + + fn try_from(value: [u8; 16]) -> Result { + // 提取时间戳(前48位) + let timestamp = ((value[0] as i64) << 40) + | ((value[1] as i64) << 32) + | ((value[2] as i64) << 24) + | ((value[3] as i64) << 16) + | ((value[4] as i64) << 8) + | (value[5] as i64); + + // 提取版本号(第6字节的高4位) + let version = (value[6] >> 4) as u8; + + // 提取变体(第8字节的高2位) + let variant = (value[8] >> 6) as u8; + + // 提取序列号和节点ID + // 第8字节的低6位 + 第9字节 + 第10字节组成23位 + let seq_and_node = (((value[8] & 0x3F) as i64) << 16) // 第8字节低6位 + | ((value[9] as i64) << 8) // 第9字节 + | (value[10] as i64); // 第10字节 + + // 分离序列号(高18位)和节点ID(低5位) + let sequence = (seq_and_node >> 5) as u32; + let node_id = (seq_and_node & 0x1F) as u16; + + Ok(Uuidv7Components { + timestamp, + node_id, + sequence, + version, + variant, + raw_bytes: value, + }) + } +} + +impl TryFrom for Uuidv7Components { + type Error = UuidV7Error; + + fn try_from(value: String) -> Result { + // 移除连字符 + let clean_str = value.replace("-", ""); + + // 验证长度 + if clean_str.len() != 32 { + return Err(UuidV7Error::InvalidStringFormat); + } + + // 解码十六进制字符串 + let bytes = hex::decode(&clean_str).map_err(|_| UuidV7Error::InvalidStringFormat)?; + + // 转换为数组 + if bytes.len() != 16 { + return Err(UuidV7Error::InvalidStringFormat); + } + + let mut uuid_bytes = [0u8; 16]; + uuid_bytes.copy_from_slice(&bytes); + + Uuidv7Components::try_from(uuid_bytes) + } +}