Go 语言 UUID 库 google/uuid 源码解析:UUID version1 的实现

google/uuid 库地址

关于 UUID 的总体介绍可以查看这篇文章,其包含阅读此篇文章的前置内容。

UUID version 1 在 RFC 4122 文件中定义,其实现基于节点 ID、时钟序列以及当前时间(距离格里历改日【1582年10月15日】 的100纳秒数,具体介绍可以看Go 语言 UUID 库 google/uuid 源码解析:时钟信息)。

目前还没有详细的文章介绍节点 ID 的实现,但是可以知道的是,节点 ID 是利用网络接口硬件地址生成的,定义在 node.go 文件的 setNodeInterface 函数中。其逻辑大致如下:

  1. 如果你指定了网络接口的名称,则它回尝试获取该接口的硬件地址(即 MAC 地址)作为节点 ID。
  2. 如果没有指定,则选择第一个可用接口的硬件地址。
  3. 如果没有可用的硬件地址则会随机生成一个节点 ID。

UUID version 1 在 google/uuid 中的实现则定义在 version1.go 文件中。

函数接口

UUID version 1 定义的接口为 NewUUID(),其返回值为 (UUID, error) 即返回 UUID 序列以及错误信息。其具体代码放在文章末尾,存在困惑的地方,可以看看源码。

具体实现

UUID 的存储结构

首先我们知道 UUID 实际是长 16 字节的序列,其表现是 32 个十六进制数。google 则是将 UUID 序列使用长 16 的字节切片进行存储。其实现如下:

  1. 首先在 uuid.go 文件中声明 type UUID [16]byte 将长 16 的字节切片起别名为 UUID,使其含义更加清晰。
  2. 然后在 version1.go 文件 NewUUID 函数中定义 uuid 变量供后续使用 var uuid UUID

获取时间与时钟序列

时间戳与时钟序列通过 GetTime() 函数直接获取。(GetTime() 的详细介绍可以看 Go 语言 UUID 库 google/uuid 源码解析:时钟信息)。得到两个变量 nowseqnow, seq, err := GetTime()

分割时间信息

首先我们需要知道获取到的 now 类型为 int64 ,即其二进制有 64 位,uuid 中的时间信息会被“切割”为三段:timeHi(16)、timeMid(16)、timeLow(32),具体“切割”如下:

xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
timeHi		    /timeMid		 /timeLow

需要提起知道的是类似 x & 0xff 的代码用于保留低位比特值,抹除高位比特值。示例如下:

x:      10101010 11011011
0xff:   00000000 11111111
-------------------------
&:      00000000 11011011

“切割”实现代码如下:

// 低32位
timeLow := uint32(now & 0xffffffff)
// 高32位中的低16位
timeMid := uint16((now >> 32) & 0xffff)
// 高16位
timeHi := uint16((now >> 48) & 0x0fff)

上述代码详解如下:

  • timeLow
    1. now & 0xffffffff:取 now(int64) 的低 32 位。
    2. uint32(x):将结果转为 uint32。
  • timeMid
    1. now >> 32 将 now(int64) 的高 32 位挪到低 32 位,高 32 位置 0。
    2. (x) & 0xffff 取当前(新)低 32 位中的低 16 位。
    3. uint16(x) 将结果转为 uint 16。
  • timeHi
    1. now >> 48 将 now(int64) 的高 16 位挪到低 16 位,高 48 位置 0。
    2. (x) & 0xfff 取当前(新)低 16 位中的低 12 位。
    3. uint16(x) 将结果转位 uint 16。

之所以 timeHi 只取到低 12 位,是因为需要保留 4 位作为标志位,此次是用于标识 UUID 版本。

我们需要提前知道的是:类似于 x |= 0x1000 的代码,使用于将某个特殊位置为 1 的,此次是将第 13 位(从右往左)置为1:

x:       00000011 00110011
0x1000:  00010000 00000000
--------------------------
|:       00010000 00110011

标识版本代码如下:

timeHi |= 0x1000 // 版本 1

将时间信息和时钟序列放置到 uuid 的正确位置

首先我们需要知道最终的 uuid 结构组成如何:

(🂓代表标志位)

十六进制字符数|8							     |4				  |4  🂓  		   |4			    |12
二进制数     |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|xxxxxxxxxxxxxxxx|xxxxxxxxxxxxxxxx|xxxxxxxxxxxxxxxx|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
具体含义     |timeLow						 |timeMid		  |timeHi		   |seq			    |clockSeq								       

我们只需要按照这个结构以大端序的方式(RFC 变体使用大端序)依此填入即可,实现代码如下:

// 将 timeLow 填充到前 4 个字节
binary.BigEndian.PutUint32(uuid[0:], timeLow)
// 将 timeMid 填充到第4和第5个字节
binary.BigEndian.PutUint16(uuid[4:], timeMid)
// 将 timeHi 填充到第6和第7个字节
binary.BigEndian.PutUint16(uuid[6:], timeHi)
// 将时钟序列填充到第8和第9个字节
binary.BigEndian.PutUint16(uuid[8:], seq)

填充 NodeID

时间和时钟序列填充完毕之后,最后只需填充 NodeID 即可。其基本逻辑为:

  1. 加锁
  2. 如果当前 nodeID 未设置,则通过 setNodeInterface 生成。
  3. 将 nodeID 拷贝至 uuid 的第10到最后一个节点。
  4. 解锁

实现代码如下:

	nodeMu.Lock()if nodeID == zeroID {setNodeInterface("")}copy(uuid[10:], nodeID[:])nodeMu.Unlock()

返回 uuid

最后返回填充好的 uuid 和 nil(error) 即可。return uuid, nil

到这里,完整的 UUID version1 源码解析便完成了,希望你能有所收获。

NewUUID 源码

func NewUUID() (UUID, error) {var uuid UUIDnow, seq, err := GetTime()if err != nil {return uuid, err}// 标志位//   🂓// xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx// timeHi		   /timeMid			/timeLow// 低32位timeLow := uint32(now & 0xffffffff)// 高32位中的低16位timeMid := uint16((now >> 32) & 0xffff)// 高16位timeHi := uint16((now >> 48) & 0x0fff)// 将第4位置为1,作为标志位,标志为版本号1timeHi |= 0x1000 // 版本 1// 8							   /4				/4  🂓  		  /4			   /12 											    /16进制字符数// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/二进制数// timeLow						   /timeMid			/timeHi			 /seq			  /clockSeq									       /具体含义binary.BigEndian.PutUint32(uuid[0:], timeLow)binary.BigEndian.PutUint16(uuid[4:], timeMid)binary.BigEndian.PutUint16(uuid[6:], timeHi)binary.BigEndian.PutUint16(uuid[8:], seq)nodeMu.Lock()if nodeID == zeroID {setNodeInterface("")}copy(uuid[10:], nodeID[:])nodeMu.Unlock()return uuid, nil
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/42084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器BMC基础知识总结

前言 因为对硬件方面不太理解,所以打算先从服务器开始学习,也想和大家一起分享一下,有什么不对的地方可以纠正一下哦!谢谢啦!互相学习共同成长~ 1.BMC是什么? 官方解释:BMC全名Baseboard Mana…

【深度学习】-WASB-调试说明

要改这么几个地方: 代码仓库:/Desktop/code/python_project/WASB-SBDT-main/ 篮球数据集xx_xx_11.xml只保留最后一个11.xml 并把11下直接放置11 video: 这里的东西被我改了,要以仓库为准

SpringSecurity中文文档(Servlet X.509 Run-As)

X.509 Authentication X. 509证书身份验证的最常见用途是在使用 SSL 时验证服务器的身份,最常见的用法是在浏览器中使用 HTTPS。浏览器会自动检查服务器提供的证书是否已由其维护的受信任证书颁发机构列表中的一个颁发(数字签名)。 您还可以使用带有“相互身份验证…

Java与Selenium配置及常见报错解决方法

Java与Selenium配置及常见报错解决方法 1. 简介 Java与Selenium是自动化测试中常用的工具和技术。正确配置开发环境并处理可能出现的问题,对于顺利开展测试工作至关重要。本文将为您详细介绍Java与Selenium的配置过程,并提供常见报错的解决方法。 2. …

中英双语介绍英国伟大的作家:查尔斯·狄更斯(Charles Dickens)

中文版 查尔斯狄更斯简介 查尔斯狄更斯(Charles Dickens,1812年2月7日-1870年6月9日)是19世纪英国最著名的小说家之一,以其丰富的文学作品和独特的写作风格闻名于世。他的作品不仅在文学界具有重要地位,而…

SpringSecurity中@PreAuthorize(“hasRole(‘ROLE_USER‘)“) 不起作用的原因

PreAuthorize(“hasRole(‘ROLE_USER’)”) 不起作用的原因可能确实是用户信息中没有包含正确的角色信息,但也可能由其他几个因素导致。以下是一些可能的原因: 1、用户信息中确实没有角色信息: 如果 Authentication 对象中的 GrantedAuthor…

一气之下,关闭成都400多人的游戏公司

关注卢松松,会经常给你分享一些我的经验和观点。 最近,多益网络宣布关闭成都公司,在未来三年内,关闭成都所有的相关公司。原因竟然是输掉了劳动仲裁,赔偿员工38万多,然后一气之下要退出成都,…

【Python】简易书籍管理程序

之前简单介绍了有关Python的面向对象中的类,这次就围绕"类"和json格式来做一个简单的代码程序,并且在后面还附带了数据。 import json import re import timeclass BookManager:def __init__(self):self.books {} # 使用字典存储书籍信息&a…

Python基础之协程

文章目录 1 协程1.1 简介1.2 协程优势&分类1.2.1 优势1.2.2 分类 1.3 generator协程1.4 gevent协程1.5 asyncio1.5.1 简介1.5.2 asyncio函数1.5.3 async\await1.5.4 asyncio基本操作1.5.4.1 asyncio协程对象1.5.4.2 task对象1.5.4.3 future对象1.5.4.4 绑定回调1.5.4.5 异步…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的生日聚会(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 &#x1f…

使用antd的<Form/>组件获取富文本编辑器输入的数据

前端开发中,嵌入富文本编辑器时,可以通过富文本编辑器自身的事件处理函数将数据传输给后端。有时候,场景稍微复杂点,比如一个输入页面除了要保存富文本编辑器的内容到后端,可能还有一些其他输入组件获取到的数据也一并…

Mac搭建anaconda环境并安装深度学习库

1. 下载anaconda安装包 根据自己的操作系统不同,选择不同的安装包Anaconda3-2024.06-1-MacOSX-x86_64.pkg,我用的还是旧的intel所以下载这个,https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/,如果mac用的是M1&#xff0…

GuLi商城-商品服务-API-品牌管理-云存储开通与使用

这里学习下阿里云对象存储 地址:对象存储 OSS_云存储服务_企业数据管理_存储-阿里云 登录支付宝账号,找到了我以前开通的阿里云对象存储 熟悉下API 文档中心 简介_对象存储(OSS)-阿里云帮助中心 我们将用这种方式上传阿里云OSS

SprongBoot3整合Knife4j实现在线接口文档

大家好,我是晓凡。 写在前面 在上一篇文章,我们详细介绍了SpringBoot3 怎么整合SpringDoc实现在线接口文档。但是,有不少小伙伴 都觉得接口界面太丑了。有没有什么更美观一点的UI界面呢? 当然是有的了,毕竟这是一个…

那些年,学过的屠龙术

朱泙漫学屠龙于支离益,单千金之家,三年技成而无所用其巧。 程序员的技能,比如: Windows平台编程,从入行业时至今,没有做过相关的业务,相关的知识只能当成谈资。 Windows平台下应用的crash问题&…

基于Android Studio电影购票系统

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 主要实为了方便用户随时随地进行电影购票。在配色方面选择了一些富有电影元素的颜色。主要能够实现的功能与流程为: 1.用户首先需要注册用户名填写密码。 2.用户可以用之前注册的用户名和密码进行登录。 3.登…

数据库——数据库性能优化

title: 数据库——数据库性能优化 date: 2024-07-06 12:26:21 tags: 数据库 categories: 数据库 cover: /image/T1.jpg description: 数据库——MySQL的性能优化 数据库优化 系统的吞吐量瓶颈往往出现在数据库的 访问速度 上,随着应用程序的运行,数据库…

【密码学】密码学体系

密码学体系是信息安全领域的基石,它主要分为两大类:对称密码体制和非对称密码体制。 一、对称密码体制(Symmetric Cryptography) 在对称密码体制中,加密和解密使用相同的密钥。这意味着发送方和接收方都必须事先拥有这…

kubeadm离线部署kubernetesv1.30.0

背景:最近由于docker image获取镜像受限的问题,以及公司内部部署kubernetes受限于内部网络无法访问公网的问题,对于离线部署kubernetes成为不是十分方便。谨以此文仅供参考。 kubernetes部署节点信息 kubernetes版本 1.30.0 操作系统版本&a…

springboot2.7.6 集成swagger

在 Spring Boot 2.7.6 版本中集成 Swagger 的步骤相对直接,主要涉及添加依赖、编写配置以及在控制器中添加文档注解几个环节。 下面是集成 Swagger 的基本步骤: 1. 添加依赖 首先,在pom.xml文件中添加 Swagger 相关依赖。 对于 Spring Boot…