feat(serial_code): 添加UUIDv7生成器实现

新增UUIDv7生成器模块,支持基于自定义纪元的时间戳、节点ID和序列号生成
UUIDv7,并提供Base64、Base36及标准字符串格式的编码与解码功能。
该生成器具备线程安全性和全局单例初始化机制,确保分布式环境下的唯一性。
This commit is contained in:
徐涛
2025-10-09 17:09:41 +08:00
parent 21892e977f
commit 07e0d3938a
2 changed files with 292 additions and 0 deletions

View File

@@ -1,4 +1,5 @@
pub mod hail;
pub mod uuidv7;
pub mod uuid {
/// 生成一个UUID v4字符串。

291
src/serial_code/uuidv7.rs Normal file
View File

@@ -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; // 主机编号容量为032
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<Mutex<Uuidv7Generator>> = OnceLock::new();
/// 获取UUIDv7生成器实例如果实例未初始化则返回错误。
pub fn generator<'a>() -> Result<&'a Mutex<Uuidv7Generator>, UuidV7Error> {
GENERATOR.get().ok_or(UuidV7Error::GeneratorNotInitialized)
}
/// 初始化UUIDv7生成器实例。
///
/// - `node_id`节点ID取值范围032。
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<S: AsRef<str>>(uuid_str: S) -> Result<Self, UuidV7Error> {
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<S: AsRef<str>>(uuid_str: S) -> Result<Self, UuidV7Error> {
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<std::cmp::Ordering> {
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<Self, Self::Error> {
// 提取时间戳前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<String> for Uuidv7Components {
type Error = UuidV7Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
// 移除连字符
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)
}
}