Files
ag_toolsbox/serial_code/uuidv7/uuid.go
徐涛 7ff09285dd feat(uuidv7): 完善 UUIDv7 功能并更新文档
- 实现 UUIDv7 生成、比较及排序功能
- 新增基于 Base36 和 Base64 的 UUIDv7 编码与解码方法
- 更新 README.md,标记相关功能为已完成状态
2025-10-06 09:27:30 +08:00

259 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package uuidv7
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"sync"
"time"
"archgrid.xyz/ag/toolsbox/serialize/base36"
"archgrid.xyz/ag/toolsbox/serialize/base64"
)
const (
epoch = 1645566142222 // 自定义纪元2022-02-22 22:22:22.222 UTC
nodeBits = 5 // 主机编号容量为032
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的Base64格式字符串
func (c *UUIDv7Components) ToBase64() string {
uuid := c.Bytes()
return base64.ToBase64(uuid[:])
}
// 获取UUIDv7的Base36格式字符串
func (c *UUIDv7Components) ToBase36() string {
uuid := c.Bytes()
return base36.Encode(uuid[:])
}
// 与另一个UUIDv7进行比较按照时间戳、序列号、节点ID进行排序
func (c *UUIDv7Components) Compare(b *UUIDv7Components) int {
if c.Timestamp > b.Timestamp {
return 1
}
if c.Timestamp < b.Timestamp {
return -1
}
if c.Sequence > b.Sequence {
return 1
}
if c.Sequence < b.Sequence {
return -1
}
if c.NodeID > b.NodeID {
return -1
}
if c.NodeID < b.NodeID {
return 1
}
return 0
}
// 对解析后的UUIDv7进行比较按照时间戳、序列号、节点ID进行排序
func CompareUUIDv7(a, b *UUIDv7Components) int {
return a.Compare(b)
}
// 从字节数组解析 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
}
// 从字符串解析 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)
}
// 从Base64格式字符串解析 UUIDv7
func ParseUUIDv7FromBase64(base64Str string) (*UUIDv7Components, error) {
bytes, err := base64.FromBase64(base64Str)
if err != nil {
return nil, fmt.Errorf("无效的Base64格式字符串: %v", err)
}
return ParseUUIDv7FromBytes([16]byte(bytes))
}
// 从Base36格式字符串解析 UUIDv7
func ParseUUIDv7FromBase36(base36Str string) (*UUIDv7Components, error) {
bytes, err := base36.Decode(base36Str)
if err != nil {
return nil, fmt.Errorf("无效的Base36格式字符串: %v", err)
}
return ParseUUIDv7FromBytes([16]byte(bytes))
}