当前位置:   article > 正文

Go 语言 UUID 库 google/uuid 源码解析:UUID version1 的实现_func newuuid() (string, error) {

func newuuid() (string, error) {

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
  • 1
  • 2

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

x:      10101010 11011011
0xff:   00000000 11111111
-------------------------
&:      00000000 11011011
  • 1
  • 2
  • 3
  • 4

“切割”实现代码如下:

// 低32位
timeLow := uint32(now & 0xffffffff)
// 高32位中的低16位
timeMid := uint16((now >> 32) & 0xffff)
// 高16位
timeHi := uint16((now >> 48) & 0x0fff)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上述代码详解如下:

  • 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
  • 1
  • 2
  • 3
  • 4

标识版本代码如下:

timeHi |= 0x1000 // 版本 1
  • 1

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/850836
推荐阅读
相关标签