feat(serial_code): 添加UUIDv7生成器实现
新增UUIDv7生成器模块,支持基于自定义纪元的时间戳、节点ID和序列号生成 UUIDv7,并提供Base64、Base36及标准字符串格式的编码与解码功能。 该生成器具备线程安全性和全局单例初始化机制,确保分布式环境下的唯一性。
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
pub mod hail;
|
||||
pub mod uuidv7;
|
||||
|
||||
pub mod uuid {
|
||||
/// 生成一个UUID v4字符串。
|
||||
|
291
src/serial_code/uuidv7.rs
Normal file
291
src/serial_code/uuidv7.rs
Normal 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; // 主机编号容量为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<Mutex<Uuidv7Generator>> = OnceLock::new();
|
||||
|
||||
/// 获取UUIDv7生成器实例,如果实例未初始化,则返回错误。
|
||||
pub fn generator<'a>() -> Result<&'a Mutex<Uuidv7Generator>, 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<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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user