一般地,我们把随机数生成算法分为密码学上安全的伪随机数发生器(Cryptographically-Secure Pseudo-Random Number Generator)和(密码学上不安全的)伪随机数发生器(Pseudo-Random Number Generator)。前者的例子有环境噪声(os.urandom())后者的例子有MT19937(random.getrandbits())
def uuid1(node=None, clock_seq=None): """Generate a UUID from a host ID, sequence number, and the current time. If 'node' is not given, getnode() is used to obtain the hardware address. If 'clock_seq' is given, it is used as the sequence number; otherwise a random 14-bit sequence number is chosen."""
# When the system provides a version-1 UUID generator, use it (but don't # use UuidCreate here because its UUIDs don't conform to RFC 4122). if _generate_time_safe is not None and node is clock_seq is None: uuid_time, safely_generated = _generate_time_safe() try: is_safe = SafeUUID(safely_generated) except ValueError: is_safe = SafeUUID.unknown return UUID(bytes=uuid_time, is_safe=is_safe)
global _last_timestamp 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 is not None and timestamp <= _last_timestamp: timestamp = _last_timestamp + 1 _last_timestamp = timestamp if clock_seq is None: import random clock_seq = random.getrandbits(14) # instead of stable storage time_low = timestamp & 0xffffffff time_mid = (timestamp >> 32) & 0xffff time_hi_version = (timestamp >> 48) & 0x0fff clock_seq_low = clock_seq & 0xff clock_seq_hi_variant = (clock_seq >> 8) & 0x3f if node is None: node = getnode() return UUID(fields=(time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node), version=1)
def getnode(): """Get the hardware address as a 48-bit positive integer.
The first time this runs, it may launch a separate program, which could be quite slow. If all attempts to obtain the hardware address fail, we choose a random 48-bit number with its eighth bit set to 1 as recommended in RFC 4122. """ global _node if _node is not None: return _node
for getter in _GETTERS + [_random_getnode]: try: _node = getter() except: continue if (_node is not None) and (0 <= _node < (1 << 48)): return _node assert False, '_random_getnode() returned invalid value: {}'.format(_node)
def _random_getnode(): """Get a random node ID.""" # RFC 4122, $4.1.6 says "For systems with no IEEE address, a randomly or # pseudo-randomly generated value may be used; see Section 4.5. The # multicast bit must be set in such addresses, in order that they will # never conflict with addresses obtained from network cards." # # The "multicast bit" of a MAC address is defined to be "the least # significant bit of the first octet". This works out to be the 41st bit # counting from 1 being the least significant bit, or 1<<40. # # See https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast import random return random.getrandbits(48) | (1 << 40)
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.
* 'a' is the first 48-bit chunk of the UUID (octets 0-5); * 'b' is the mid 12-bit chunk (octets 6-7); * 'c' is the last 62-bit chunk (octets 8-15).
When a value is not specified, a pseudo-random value is generated. """ if a is None: import random a = random.getrandbits(48) if b is None: import random b = random.getrandbits(12) if c is None: import random c = random.getrandbits(62) int_uuid_8 = (a & 0xffff_ffff_ffff) << 80 int_uuid_8 |= (b & 0xfff) << 64 int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff # by construction, the variant and version bits are already cleared int_uuid_8 |= _RFC_4122_VERSION_8_FLAGS return UUID._from_int(int_uuid_8)
def uuid3(namespace, name): """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" if isinstance(name, str): name = bytes(name, "utf-8") import hashlib h = hashlib.md5(namespace.bytes + name, usedforsecurity=False) int_uuid_3 = int.from_bytes(h.digest()) int_uuid_3 &= _RFC_4122_CLEARFLAGS_MASK int_uuid_3 |= _RFC_4122_VERSION_3_FLAGS return UUID._from_int(int_uuid_3)
def uuid5(namespace, name): """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" if isinstance(name, str): name = bytes(name, "utf-8") import hashlib h = hashlib.sha1(namespace.bytes + name, usedforsecurity=False) int_uuid_5 = int.from_bytes(h.digest()[:16]) int_uuid_5 &= _RFC_4122_CLEARFLAGS_MASK int_uuid_5 |= _RFC_4122_VERSION_5_FLAGS return UUID._from_int(int_uuid_5)
def uuid7(): """Generate a UUID from a Unix timestamp in milliseconds and random bits.
UUIDv7 objects feature monotonicity within a millisecond. """ # --- 48 --- -- 4 -- --- 12 --- -- 2 -- --- 30 --- - 32 - # unix_ts_ms | version | counter_hi | variant | counter_lo | random # # 'counter = counter_hi | counter_lo' is a 42-bit counter constructed # with Method 1 of RFC 9562, §6.2, and its MSB is set to 0. # # 'random' is a 32-bit random value regenerated for every new UUID. # # If multiple UUIDs are generated within the same millisecond, the LSB # of 'counter' is incremented by 1. When overflowing, the timestamp is # advanced and the counter is reset to a random 42-bit integer with MSB # set to 0.
if _last_timestamp_v7 is None or timestamp_ms > _last_timestamp_v7: counter, tail = _uuid7_get_counter_and_tail() else: if timestamp_ms < _last_timestamp_v7: timestamp_ms = _last_timestamp_v7 + 1 # advance the 42-bit counter counter = _last_counter_v7 + 1 if counter > 0x3ff_ffff_ffff: # advance the 48-bit timestamp timestamp_ms += 1 counter, tail = _uuid7_get_counter_and_tail() else: # 32-bit random data tail = int.from_bytes(os.urandom(4))
unix_ts_ms = timestamp_ms & 0xffff_ffff_ffff counter_msbs = counter >> 30 # keep 12 counter's MSBs and clear variant bits counter_hi = counter_msbs & 0x0fff # keep 30 counter's LSBs and clear version bits counter_lo = counter & 0x3fff_ffff # ensure that the tail is always a 32-bit integer (by construction, # it is already the case, but future interfaces may allow the user # to specify the random tail) tail &= 0xffff_ffff
int_uuid_7 = unix_ts_ms << 80 int_uuid_7 |= counter_hi << 64 int_uuid_7 |= counter_lo << 32 int_uuid_7 |= tail # by construction, the variant and version bits are already cleared int_uuid_7 |= _RFC_4122_VERSION_7_FLAGS res = UUID._from_int(int_uuid_7)
# defer global update until all computations are done _last_timestamp_v7 = timestamp_ms _last_counter_v7 = counter return res