mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
gh-89083: add support for UUID version 6 (RFC 9562) (#120650)
Add support for generating UUIDv6 objects according to RFC 9562, §5.6 [1]. The functionality is provided by the `uuid.uuid6()` function which takes as inputs an optional 48-bit hardware address and an optional 14-bit clock sequence. The UUIDv6 temporal fields are ordered differently than those of UUIDv1, thereby providing improved database locality. [1]: https://www.rfc-editor.org/rfc/rfc9562.html#section-5.6 --------- Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
214562ed4d
commit
990ad272f6
5 changed files with 235 additions and 17 deletions
63
Lib/uuid.py
63
Lib/uuid.py
|
@ -1,8 +1,8 @@
|
|||
r"""UUID objects (universally unique identifiers) according to RFC 4122/9562.
|
||||
|
||||
This module provides immutable UUID objects (class UUID) and the functions
|
||||
uuid1(), uuid3(), uuid4(), uuid5(), and uuid8() for generating version 1, 3,
|
||||
4, 5, and 8 UUIDs as specified in RFC 4122/9562.
|
||||
uuid1(), uuid3(), uuid4(), uuid5(), uuid6(), and uuid8() for generating
|
||||
version 1, 3, 4, 5, 6, and 8 UUIDs as specified in RFC 4122/9562.
|
||||
|
||||
If all you want is a unique ID, you should probably call uuid1() or uuid4().
|
||||
Note that uuid1() may compromise privacy since it creates a UUID containing
|
||||
|
@ -101,6 +101,7 @@ _RFC_4122_VERSION_1_FLAGS = ((1 << 76) | (0x8000 << 48))
|
|||
_RFC_4122_VERSION_3_FLAGS = ((3 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_4_FLAGS = ((4 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_5_FLAGS = ((5 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_6_FLAGS = ((6 << 76) | (0x8000 << 48))
|
||||
_RFC_4122_VERSION_8_FLAGS = ((8 << 76) | (0x8000 << 48))
|
||||
|
||||
|
||||
|
@ -127,7 +128,9 @@ class UUID:
|
|||
|
||||
fields a tuple of the six integer fields of the UUID,
|
||||
which are also available as six individual attributes
|
||||
and two derived attributes:
|
||||
and two derived attributes. The time_* attributes are
|
||||
only relevant to version 1, while the others are only
|
||||
relevant to versions 1 and 6:
|
||||
|
||||
time_low the first 32 bits of the UUID
|
||||
time_mid the next 16 bits of the UUID
|
||||
|
@ -353,8 +356,19 @@ class UUID:
|
|||
|
||||
@property
|
||||
def time(self):
|
||||
return (((self.time_hi_version & 0x0fff) << 48) |
|
||||
(self.time_mid << 32) | self.time_low)
|
||||
if self.version == 6:
|
||||
# time_hi (32) | time_mid (16) | ver (4) | time_lo (12) | ... (64)
|
||||
time_hi = self.int >> 96
|
||||
time_lo = (self.int >> 64) & 0x0fff
|
||||
return time_hi << 28 | (self.time_mid << 12) | time_lo
|
||||
else:
|
||||
# time_lo (32) | time_mid (16) | ver (4) | time_hi (12) | ... (64)
|
||||
#
|
||||
# For compatibility purposes, we do not warn or raise when the
|
||||
# version is not 1 (timestamp is irrelevant to other versions).
|
||||
time_hi = (self.int >> 64) & 0x0fff
|
||||
time_lo = self.int >> 96
|
||||
return time_hi << 48 | (self.time_mid << 32) | time_lo
|
||||
|
||||
@property
|
||||
def clock_seq(self):
|
||||
|
@ -756,6 +770,44 @@ def uuid5(namespace, name):
|
|||
int_uuid_5 |= _RFC_4122_VERSION_5_FLAGS
|
||||
return UUID._from_int(int_uuid_5)
|
||||
|
||||
_last_timestamp_v6 = None
|
||||
|
||||
def uuid6(node=None, clock_seq=None):
|
||||
"""Similar to :func:`uuid1` but where fields are ordered differently
|
||||
for improved DB locality.
|
||||
|
||||
More precisely, given a 60-bit timestamp value as specified for UUIDv1,
|
||||
for UUIDv6 the first 48 most significant bits are stored first, followed
|
||||
by the 4-bit version (same position), followed by the remaining 12 bits
|
||||
of the original 60-bit timestamp.
|
||||
"""
|
||||
global _last_timestamp_v6
|
||||
import time
|
||||
nanoseconds = time.time_ns()
|
||||
# 0x01b21dd213814000 is the number of 100-ns intervals between the
|
||||
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
|
||||
timestamp = nanoseconds // 100 + 0x01b21dd213814000
|
||||
if _last_timestamp_v6 is not None and timestamp <= _last_timestamp_v6:
|
||||
timestamp = _last_timestamp_v6 + 1
|
||||
_last_timestamp_v6 = timestamp
|
||||
if clock_seq is None:
|
||||
import random
|
||||
clock_seq = random.getrandbits(14) # instead of stable storage
|
||||
time_hi_and_mid = (timestamp >> 12) & 0xffff_ffff_ffff
|
||||
time_lo = timestamp & 0x0fff # keep 12 bits and clear version bits
|
||||
clock_s = clock_seq & 0x3fff # keep 14 bits and clear variant bits
|
||||
if node is None:
|
||||
node = getnode()
|
||||
# --- 32 + 16 --- -- 4 -- -- 12 -- -- 2 -- -- 14 --- 48
|
||||
# time_hi_and_mid | version | time_lo | variant | clock_seq | node
|
||||
int_uuid_6 = time_hi_and_mid << 80
|
||||
int_uuid_6 |= time_lo << 64
|
||||
int_uuid_6 |= clock_s << 48
|
||||
int_uuid_6 |= node & 0xffff_ffff_ffff
|
||||
# by construction, the variant and version bits are already cleared
|
||||
int_uuid_6 |= _RFC_4122_VERSION_6_FLAGS
|
||||
return UUID._from_int(int_uuid_6)
|
||||
|
||||
def uuid8(a=None, b=None, c=None):
|
||||
"""Generate a UUID from three custom blocks.
|
||||
|
||||
|
@ -788,6 +840,7 @@ def main():
|
|||
"uuid3": uuid3,
|
||||
"uuid4": uuid4,
|
||||
"uuid5": uuid5,
|
||||
"uuid6": uuid6,
|
||||
"uuid8": uuid8,
|
||||
}
|
||||
uuid_namespace_funcs = ("uuid3", "uuid5")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue