新增 UUIDv7 生成器实现,支持基于时间戳、节点 ID 和序列号的唯一标识符生成。 包含初始化、生成、解析以及错误处理等相关逻辑,并提供字节和字符串两种形式的 序列化与反序列化方法。同时添加了对生成器未初始化和节点 ID 超限的错误定义。
226 lines
5.9 KiB
Go
226 lines
5.9 KiB
Go
package uuidv7
|
||
|
||
import (
|
||
"crypto/rand"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
const (
|
||
epoch = 1645566142222 // 自定义纪元:2022-02-22 22:22:22.222 UTC
|
||
nodeBits = 5 // 主机编号容量为0~32
|
||
sequenceBits = 18 // 每毫秒生成序列号最大为2^18-1,即262144个
|
||
maxNodeID = (1 << nodeBits) - 1
|
||
maxSequence = (1 << sequenceBits) - 1
|
||
timestampShift = nodeBits + sequenceBits
|
||
nodeShift = sequenceBits
|
||
)
|
||
|
||
type UUIDv7Generator struct {
|
||
locker sync.Mutex
|
||
nodeID int64
|
||
lastTime int64
|
||
sequence int64
|
||
}
|
||
|
||
type UUIDv7Components struct {
|
||
Timestamp int64 // 毫秒时间戳(相对于自定义纪元)
|
||
NodeID int64 // 5位节点ID
|
||
Sequence int64 // 18位序列号
|
||
Version int // 版本号(应为7)
|
||
Variant int // 变体(应为2,表示10xx)
|
||
RawBytes [16]byte // 原始字节
|
||
}
|
||
|
||
var generatorInstance *UUIDv7Generator
|
||
|
||
// 获取UUIDv7生成器实例。如果实例尚未初始化,则返回错误。
|
||
func Generator() (*UUIDv7Generator, error) {
|
||
if generatorInstance == nil {
|
||
return nil, &UUIDv7GeneratorNotInitializedError{}
|
||
}
|
||
return generatorInstance, nil
|
||
}
|
||
|
||
// 指定一个主机编号,并完成UUIDv7生成器的初始化。如果主机编号超出范围,则返回错误。
|
||
func Initialize(hostSerial int64) error {
|
||
if hostSerial < 0 || hostSerial > maxNodeID {
|
||
return &UUIDv7NodeIDExceededError{}
|
||
}
|
||
generatorInstance = &UUIDv7Generator{
|
||
nodeID: hostSerial,
|
||
lastTime: epoch,
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 获取当前时间对应的新纪元毫秒时间戳。
|
||
func (g *UUIDv7Generator) Now() int64 {
|
||
return time.Now().UnixMilli() - epoch
|
||
}
|
||
|
||
// 按顺序生成一个UUIDv7序号组件。
|
||
func (g *UUIDv7Generator) Next() UUIDv7Components {
|
||
g.locker.Lock()
|
||
defer g.locker.Unlock()
|
||
now := g.Now()
|
||
if now < g.lastTime {
|
||
// 防止时间回拔的情况,发生回拔时,直接使用上一次的时间戳
|
||
now = g.lastTime
|
||
}
|
||
if now == g.lastTime {
|
||
g.sequence++
|
||
if g.sequence > maxSequence {
|
||
time.Sleep(time.Millisecond)
|
||
now = g.Now()
|
||
g.sequence = 0
|
||
}
|
||
} else {
|
||
g.sequence = 0
|
||
}
|
||
g.lastTime = now
|
||
return UUIDv7Components{
|
||
Timestamp: now,
|
||
NodeID: g.nodeID,
|
||
Sequence: g.sequence,
|
||
Version: 7,
|
||
Variant: 2,
|
||
}
|
||
}
|
||
|
||
// 读取随机字节
|
||
func (c *UUIDv7Components) readRandom(bytes []byte) error {
|
||
_, err := rand.Read(bytes)
|
||
return err
|
||
}
|
||
|
||
// 获取UUIDv7原始字节数组
|
||
func (c *UUIDv7Components) Bytes() [16]byte {
|
||
// 构造 UUID
|
||
var uuid [16]byte
|
||
// 时间戳(48 位)
|
||
timestamp := uint64(c.Timestamp) << timestampShift
|
||
seqAndNode := (uint64(c.Sequence) << nodeShift) | uint64(c.NodeID)
|
||
// 写入高位时间戳
|
||
uuid[0] = byte(timestamp >> 56)
|
||
uuid[1] = byte(timestamp >> 48)
|
||
uuid[2] = byte(timestamp >> 40)
|
||
uuid[3] = byte(timestamp >> 32)
|
||
uuid[4] = byte(timestamp >> 24)
|
||
uuid[5] = byte(timestamp >> 16)
|
||
// 写入低位时间戳 + 版本号(4 位)
|
||
uuid[6] = byte((timestamp >> 8) | 0x70) // version 7
|
||
uuid[7] = byte(timestamp)
|
||
// 写入 seq + node
|
||
uuid[8] = byte((seqAndNode >> 16) | 0x80) // variant 10xx
|
||
uuid[9] = byte(seqAndNode >> 8)
|
||
uuid[10] = byte(seqAndNode)
|
||
// 剩余位全为 0(可扩展为更多节点 ID 或随机数)
|
||
randomBytes := make([]byte, 5)
|
||
if err := c.readRandom(randomBytes); err != nil {
|
||
// 如果随机数生成失败,使用时间相关值作为后备
|
||
t := time.Now().UnixNano()
|
||
randomBytes[0] = byte(t)
|
||
randomBytes[1] = byte(t >> 8)
|
||
randomBytes[2] = byte(t >> 16)
|
||
randomBytes[3] = byte(t >> 24)
|
||
randomBytes[4] = byte(c.Sequence)
|
||
}
|
||
copy(uuid[11:16], randomBytes)
|
||
return uuid
|
||
}
|
||
|
||
// 获取UUIDv7字符串
|
||
func (c *UUIDv7Components) String() string {
|
||
uuid := c.Bytes()
|
||
return fmt.Sprintf("%x-%x-%x-%x-%x",
|
||
uuid[0:4],
|
||
uuid[4:6],
|
||
uuid[6:8],
|
||
uuid[8:10],
|
||
uuid[10:16])
|
||
}
|
||
|
||
// 对解析后的UUIDv7进行比较,按照时间戳、序列号、节点ID进行排序
|
||
func CompareUUIDv7(a, b *UUIDv7Components) int {
|
||
if a.Timestamp > b.Timestamp {
|
||
return 1
|
||
}
|
||
if a.Timestamp < b.Timestamp {
|
||
return -1
|
||
}
|
||
|
||
if a.Sequence > b.Sequence {
|
||
return 1
|
||
}
|
||
if a.Sequence < b.Sequence {
|
||
return -1
|
||
}
|
||
|
||
if a.NodeID > b.NodeID {
|
||
return -1
|
||
}
|
||
if a.NodeID < b.NodeID {
|
||
return 1
|
||
}
|
||
|
||
return 0
|
||
}
|
||
|
||
// 与另一个UUIDv7进行比较,按照时间戳、序列号、节点ID进行排序
|
||
func (c *UUIDv7Components) Compare(b *UUIDv7Components) int {
|
||
return CompareUUIDv7(c, b)
|
||
}
|
||
|
||
// ParseUUIDv7FromBytes 从字节数组解析 UUIDv7
|
||
func ParseUUIDv7FromBytes(data [16]byte) (*UUIDv7Components, error) {
|
||
// 提取时间戳(前48位)
|
||
timestamp := (int64(data[0]) << 40) |
|
||
(int64(data[1]) << 32) |
|
||
(int64(data[2]) << 24) |
|
||
(int64(data[3]) << 16) |
|
||
(int64(data[4]) << 8) |
|
||
int64(data[5])
|
||
// 提取版本号(第6字节的高4位)
|
||
version := int(data[6] >> 4)
|
||
// 提取变体(第8字节的高2位)
|
||
variant := int(data[8] >> 6)
|
||
// 提取序列号和节点ID
|
||
// 第8字节的低2位 + 第9字节 + 第10字节的高3位组成23位
|
||
seqAndNode := (int64(data[8]&0x3F) << 16) | // 第8字节低6位
|
||
(int64(data[9]) << 8) | // 第9字节
|
||
int64(data[10]) // 第10字节
|
||
// 分离序列号(高18位)和节点ID(低5位)
|
||
sequence := seqAndNode >> 5
|
||
nodeID := seqAndNode & 0x1F
|
||
return &UUIDv7Components{
|
||
Timestamp: timestamp,
|
||
NodeID: nodeID,
|
||
Sequence: sequence,
|
||
Version: version,
|
||
Variant: variant,
|
||
RawBytes: data,
|
||
}, nil
|
||
}
|
||
|
||
// ParseUUIDv7FromString 从字符串解析 UUIDv7
|
||
func ParseUUIDv7FromString(uuidStr string) (*UUIDv7Components, error) {
|
||
// 移除连字符并验证长度
|
||
cleanStr := strings.ReplaceAll(uuidStr, "-", "")
|
||
if len(cleanStr) != 32 {
|
||
return nil, fmt.Errorf("无效的UUIDv7字符串长度")
|
||
}
|
||
// 解码十六进制字符串
|
||
bytes, err := hex.DecodeString(cleanStr)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("无效的UUIDv7字符串格式: %v", err)
|
||
}
|
||
// 转换为数组
|
||
var uuidBytes [16]byte
|
||
copy(uuidBytes[:], bytes)
|
||
return ParseUUIDv7FromBytes(uuidBytes)
|
||
}
|