blob: e957071a4ecef9542a8b401740c93b73d4f2d6d3 [file]
/*
* Python UUID module:
* - wraps libuuid or Windows rpcrt4.dll.
* - implements fast version of the uuid.py:UUID class.
* - re-implements uuid4() and uuid7() functions with improved performance
* by virtue of them being implemented in C and better entropy fetching
* strategy.
*
* DCE compatible Universally Unique Identifier library.
*/
#ifndef Py_BUILD_CORE_BUILTIN
# define Py_BUILD_CORE_MODULE 1
#endif
#include "Python.h"
#include "pycore_critical_section.h" // _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED()
#include "pycore_long.h" // _PyLong_AsByteArray(), _PyLong_GetOne()
#include "pycore_pylifecycle.h" // _PyOS_URandom()
#include "pycore_runtime_init.h" // _Py_ID()
#include "pycore_strhex.h" // _Py_strhex()
#if defined(HAVE_UUID_H)
// AIX, FreeBSD, libuuid with pkgconf
#include <uuid.h>
#elif defined(HAVE_UUID_UUID_H)
// libuuid without pkgconf
#include <uuid/uuid.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h> // getpid()
#endif
#ifdef HAVE_PROCESS_H
# include <process.h> // getpid()
#endif
#ifdef MS_WINDOWS
# include <windows.h> // GetCurrentProcessId()
#endif
#ifdef MS_WINDOWS
#include <rpc.h>
#ifndef RPC_S_OK
#define RPC_S_OK 0L
#endif
#endif
#include <string.h> // memcpy(), memset(), strncmp()
#ifndef MS_WINDOWS
/*[clinic input]
module _uuid
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7cbed123a45a3859]*/
static PyObject *
py_uuid_generate_time_safe(PyObject *Py_UNUSED(context),
PyObject *Py_UNUSED(ignored))
{
uuid_t uuid;
#ifdef HAVE_UUID_GENERATE_TIME_SAFE
int res;
res = uuid_generate_time_safe(uuid);
return Py_BuildValue("y#i", (const char *) uuid, sizeof(uuid), res);
#elif defined(HAVE_UUID_CREATE)
uint32_t status;
uuid_create(&uuid, &status);
# if defined(HAVE_UUID_ENC_BE)
unsigned char buf[sizeof(uuid)];
uuid_enc_be(buf, &uuid);
return Py_BuildValue("y#i", buf, sizeof(uuid), (int) status);
# else
return Py_BuildValue("y#i", (const char *) &uuid, sizeof(uuid), (int) status);
# endif /* HAVE_UUID_CREATE */
#else /* HAVE_UUID_GENERATE_TIME_SAFE */
uuid_generate_time(uuid);
return Py_BuildValue("y#O", (const char *) uuid, sizeof(uuid), Py_None);
#endif /* HAVE_UUID_GENERATE_TIME_SAFE */
}
#else /* MS_WINDOWS */
static PyObject *
py_UuidCreate(PyObject *Py_UNUSED(context),
PyObject *Py_UNUSED(ignored))
{
UUID uuid;
RPC_STATUS res;
Py_BEGIN_ALLOW_THREADS
res = UuidCreateSequential(&uuid);
Py_END_ALLOW_THREADS
switch (res) {
case RPC_S_OK:
case RPC_S_UUID_LOCAL_ONLY:
case RPC_S_UUID_NO_ADDRESS:
/*
All success codes, but the latter two indicate that the UUID is random
rather than based on the MAC address. If the OS can't figure this out,
neither can we, so we'll take it anyway.
*/
return Py_BuildValue("y#", (const char *)&uuid, sizeof(uuid));
}
PyErr_SetFromWindowsErr(res);
return NULL;
}
static int
py_windows_has_stable_node(void)
{
UUID uuid;
RPC_STATUS res;
Py_BEGIN_ALLOW_THREADS
res = UuidCreateSequential(&uuid);
Py_END_ALLOW_THREADS
return res == RPC_S_OK;
}
#endif /* MS_WINDOWS */
#define UUID_SIZE 16
typedef struct uuidobject {
PyObject_HEAD
uint8_t bytes[UUID_SIZE];
Py_hash_t cached_hash;
PyObject *is_safe;
PyObject *weakreflist;
} uuidobject;
#define uuidobject_CAST(OP) ((uuidobject *)(OP))
// UUID Structure per RFC 9562:
//
// A UUID is 128 bits (16 bytes) represented as:
//
// String: xx xx xx xx - xx xx - Mx xx - Nx xx - xx xx xx xx xx xx
// Byte pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
//
// Time-based layouts in big-endian order (field widths in bits):
//
// v1: time_low(32) | time_mid(16) | version(4) | time_high(12) |
// variant(2) | clock_seq(14) | node(48)
// v6: time_high(32) | time_mid(16) | version(4) | time_low(12) |
// variant(2) | clock_seq(14) | node(48)
// v7: unix_ts_ms(48) | version(4) | rand_a(12) | variant(2) | rand_b(62)
//
// v1 and v6 encode the same 60-bit timestamp in 100-nanosecond intervals
// since 1582-10-15, with different field ordering. v7 uses milliseconds
// since the Unix epoch. Its rand_a and rand_b fields hold random/counter
// bits, with no clock sequence or node field.
//
// M is the version field: the most significant 4 bits of byte 6.
//
// N contains the variant: the most significant variable bits of byte 8.
// 0xxx: Reserved for NCS compatibility
// 10xx: RFC 4122/9562 (standard)
// 110x: Reserved for Microsoft compatibility
// 111x: Reserved for future definition
#define RANDOM_BUF_SIZE 256
#define MAX_FREE_LIST_SIZE 32
/* State of the _uuid module */
typedef struct {
PyTypeObject *uuid_type;
PyObject *safe_uuid;
PyObject *uint128_max;
PyObject *random_func;
PyObject *random_size_int;
PyObject *time_func;
// A freelist for uuid objects -- 15-20% performance boost.
uuidobject *freelist;
size_t freelist_size;
// We overfetch entropy to speed up successive uuid generations;
// this enables 10x peformance boost.
uint8_t random_buf[RANDOM_BUF_SIZE];
uint64_t random_idx;
uint64_t random_last_pid;
// UUID v7 state
uint64_t last_timestamp_v7;
uint64_t last_counter_v7;
uint8_t last_timestamp_v7_init;
} uuid_state;
#include "clinic/_uuidmodule.c.h"
/*[clinic input]
class _uuid.UUID "uuidobject *" "&uuid_type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b62c7d736c192881]*/
// Forward declarations
static int from_hex(uuidobject *self, PyObject *hex);
static int from_bytes_le(uuidobject *self, PyObject *bytes_le);
static int from_int(uuidobject *self, PyObject *int_value, int validate);
static int from_fields(uuidobject *self, PyObject *fields);
static PyObject *get_SafeUUID(uuid_state *state);
static uint64_t
uuid_getpid(void)
{
#if !defined(MS_WINDOWS) || defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)
return (uint64_t)getpid();
#else
return (uint64_t)GetCurrentProcessId();
#endif
}
static struct PyModuleDef uuidmodule;
static inline uuid_state *
get_uuid_state(PyObject *mod)
{
uuid_state *state = PyModule_GetState(mod);
assert(state != NULL);
return state;
}
static inline uuid_state *
get_uuid_state_by_cls(PyTypeObject *cls)
{
PyObject *module = PyType_GetModuleByDef(cls, &uuidmodule);
assert(module != NULL);
uuid_state *state = get_uuid_state(module);
assert(state != NULL);
return state;
}
// Forward declaration
static PyObject *uuid_from_bytes_array(PyTypeObject *type, uint8_t bytes[UUID_SIZE]);
static int
gen_time(uuid_state *state, PyTime_t *time)
{
if (state->time_func == NULL) {
return PyTime_Time(time);
}
PyObject *time_func = Py_NewRef(state->time_func);
PyObject *ret = PyObject_CallNoArgs(time_func);
Py_DECREF(time_func);
if (ret == NULL) {
return -1;
}
if (!PyLong_CheckExact(ret)) {
PyErr_SetString(PyExc_ValueError, "time_func must return int");
Py_DECREF(ret);
return -1;
}
int res = PyLong_AsInt64(ret, time);
Py_DECREF(ret);
return res;
}
static int
gen_random_lock_held(PyObject *module, uuid_state *state, uint8_t *bytes,
Py_ssize_t size)
{
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(module);
// Overfetching & caching entropy improves the performance 10x.
// There's a precedent with NodeJS doing exact same thing for
// improving performance of their UUID implementation.
uint64_t pid = uuid_getpid();
if (pid != state->random_last_pid) {
// The main concern to take core of with caching entropy is handling
// fork -- we don't want the child process to share any entropy with
// us. Luckily getpid() is fast.
state->random_last_pid = pid;
state->random_idx = RANDOM_BUF_SIZE;
}
if (state->random_idx + size <= RANDOM_BUF_SIZE) {
memcpy(bytes, state->random_buf + state->random_idx, size);
state->random_idx += size;
}
else {
if (state->random_idx < RANDOM_BUF_SIZE) {
// We exhaustively consume cached entropy. We do that
// because we have tests that compare Python and C
// implementations and it's important that they see incoming
// entropy as a continuous stream.
// The overhead here must be negligible, but we want the same
// code to be run in production and in tests.
Py_ssize_t partial = RANDOM_BUF_SIZE - state->random_idx;
memcpy(bytes, state->random_buf + state->random_idx, partial);
bytes += partial;
size -= partial;
}
// Reserve the cached bytes before a refill can release the module's
// critical section. Another call must not consume the same bytes.
state->random_idx = RANDOM_BUF_SIZE;
uint8_t random_buf[RANDOM_BUF_SIZE];
if (state->random_func != NULL) {
PyObject *random_func = Py_NewRef(state->random_func);
PyObject *buf = PyObject_CallOneArg(
random_func, state->random_size_int);
Py_DECREF(random_func);
if (buf == NULL) {
return -1;
}
if (!PyBytes_CheckExact(buf)) {
PyErr_SetString(PyExc_ValueError, "random_func must return bytes");
Py_DECREF(buf);
return -1;
}
if (PyBytes_Size(buf) != (Py_ssize_t)RANDOM_BUF_SIZE) {
PyErr_Format(
PyExc_ValueError,
"random_func must return bytes of length %zd exactly",
(Py_ssize_t)RANDOM_BUF_SIZE
);
Py_DECREF(buf);
return -1;
}
memcpy(random_buf, PyBytes_AsString(buf), sizeof(random_buf));
Py_DECREF(buf);
}
else {
// _PyOS_URandom() releases the thread state. Fill a private buffer
// so concurrent refills cannot write into the same memory.
if (_PyOS_URandom(random_buf, sizeof(random_buf)) < 0) {
return -1;
}
}
// We hold the caller's module lock again after fetching entropy.
// Replace the cache, discarding any unused bytes from a concurrent refill.
memcpy(state->random_buf, random_buf, sizeof(random_buf));
memcpy(bytes, state->random_buf, size);
state->random_idx = size;
}
return 0;
}
/*[clinic input]
@critical_section
_uuid.uuid4
Generate a random UUID (version 4).
[clinic start generated code]*/
static PyObject *
_uuid_uuid4_impl(PyObject *module)
/*[clinic end generated code: output=b835af30d9d6efc5 input=9cfb3a3b71c25cdf]*/
{
uuid_state *state = get_uuid_state(module);
uint8_t bytes[UUID_SIZE];
if (gen_random_lock_held(module, state, bytes, sizeof(bytes)) < 0) {
return NULL;
}
// Set version (4) and variant
bytes[6] = (uint8_t)((bytes[6] & 0x0f) | 0x40);
bytes[8] = (uint8_t)((bytes[8] & 0x3f) | 0x80);
return uuid_from_bytes_array(state->uuid_type, bytes);
}
static inline int
uuid7_get_counter_and_tail(PyObject *module, uuid_state *state,
uint64_t *counter, uint8_t *tail)
{
uint8_t rand_bytes[10];
if (gen_random_lock_held(module, state, rand_bytes, sizeof(rand_bytes)) < 0) {
return -1;
}
*counter = (((uint64_t)rand_bytes[0] & 0x01) << 40) |
((uint64_t)rand_bytes[1] << 32) |
((uint64_t)rand_bytes[2] << 24) |
((uint64_t)rand_bytes[3] << 16) |
((uint64_t)rand_bytes[4] << 8) |
((uint64_t)rand_bytes[5]);
memcpy(tail, rand_bytes + 6, 4);
return 0;
}
// The module's critical section protects the cached entropy and UUIDv7 state.
// It may be suspended during entropy fetching or a Python testing hook.
/*[clinic input]
@critical_section
_uuid.uuid7
Generate a UUID from a Unix timestamp in milliseconds and random bits.
UUIDv7 objects feature monotonicity within a millisecond.
[clinic start generated code]*/
static PyObject *
_uuid_uuid7_impl(PyObject *module)
/*[clinic end generated code: output=f301accc11162c91 input=88514d61dc785108]*/
{
uuid_state *state = get_uuid_state(module);
uint8_t bytes[UUID_SIZE];
uint64_t timestamp_ms, counter;
PyTime_t pytime;
if (gen_time(state, &pytime) < 0) {
return NULL;
}
uint64_t last_timestamp, last_counter;
uint8_t last_timestamp_init;
do {
last_timestamp = state->last_timestamp_v7;
last_counter = state->last_counter_v7;
last_timestamp_init = state->last_timestamp_v7_init;
timestamp_ms = (uint64_t)(pytime / 1000000);
if (!last_timestamp_init || timestamp_ms > last_timestamp) {
if (uuid7_get_counter_and_tail(module, state, &counter, bytes + 12) < 0) {
return NULL;
}
}
else {
if (timestamp_ms < last_timestamp) {
timestamp_ms = last_timestamp + 1;
}
// advance the 42-bit counter
counter = last_counter + 1;
if (counter > 0x3FFFFFFFFFF) {
// advance the 48-bit timestamp
timestamp_ms += 1;
if (uuid7_get_counter_and_tail(module, state, &counter, bytes + 12) < 0) {
return NULL;
}
}
else {
// This is the common fast path, we only need 4 bytes of entropy
// 32-bit random data
if (gen_random_lock_held(module, state, bytes + 12, 4) < 0) {
return NULL;
}
}
}
// A refill can suspend the critical section and let another call
// advance the state. Recompute instead of committing a stale counter.
} while (last_timestamp != state->last_timestamp_v7 ||
last_counter != state->last_counter_v7 ||
last_timestamp_init != state->last_timestamp_v7_init);
timestamp_ms &= 0xFFFFFFFFFFFF;
bytes[0] = (uint8_t)(timestamp_ms >> 40);
bytes[1] = (uint8_t)(timestamp_ms >> 32);
bytes[2] = (uint8_t)(timestamp_ms >> 24);
bytes[3] = (uint8_t)(timestamp_ms >> 16);
bytes[4] = (uint8_t)(timestamp_ms >> 8);
bytes[5] = (uint8_t)timestamp_ms;
uint16_t counter_hi = (uint16_t)((counter >> 30) & 0x0FFF);
bytes[6] = (uint8_t)(0x70 | (counter_hi >> 8)); // Version 7 = 0111
bytes[7] = (uint8_t)counter_hi;
uint16_t counter_mid = (uint16_t)((counter >> 16) & 0x3FFF);
bytes[8] = (uint8_t)(0x80 | (counter_mid >> 8)); // Variant = 10
bytes[9] = (uint8_t)counter_mid;
uint16_t counter_lo = (uint16_t)(counter & 0xFFFF);
bytes[10] = (uint8_t)(counter_lo >> 8);
bytes[11] = (uint8_t)counter_lo;
state->last_timestamp_v7_init = 1;
state->last_timestamp_v7 = timestamp_ms;
state->last_counter_v7 = counter;
return uuid_from_bytes_array(state->uuid_type, bytes);
}
/*[clinic input]
@critical_section
_uuid._install_c_hooks
*
random_func: object
time_func: object
[clinic start generated code]*/
static PyObject *
_uuid__install_c_hooks_impl(PyObject *module, PyObject *random_func,
PyObject *time_func)
/*[clinic end generated code: output=884aa6e91b2ea832 input=6c5017297067e2ea]*/
{
uuid_state *state = get_uuid_state(module);
// Reset bufferred entropy -- tests need to always start fresh
// when the random function is changed. Reset last_timestamp_v7 --
// important for repeatable tests
state->random_idx = RANDOM_BUF_SIZE;
state->last_timestamp_v7_init = 0;
if (random_func == Py_None) {
Py_CLEAR(state->random_func);
}
else {
Py_XSETREF(state->random_func, Py_NewRef(random_func));
}
if (time_func == Py_None) {
Py_CLEAR(state->time_func);
}
else {
Py_XSETREF(state->time_func, Py_NewRef(time_func));
}
Py_RETURN_NONE;
}
/*[clinic input]
_uuid.UUID.__init__
hex: object = None
bytes: object = None
bytes_le: object = None
fields: object = None
int: object = None
version: object = None
*
is_safe: object = NULL
UUID is a fast base implementation type for uuid.UUID.
[clinic start generated code]*/
static int
_uuid_UUID___init___impl(uuidobject *self, PyObject *hex, PyObject *bytes,
PyObject *bytes_le, PyObject *fields,
PyObject *int_value, PyObject *version,
PyObject *is_safe)
/*[clinic end generated code: output=6d100803f5ecd751 input=23c754454cc11724]*/
{
// Explicit None values do not select a UUID input form.
int passed = 0;
if (hex != Py_None) passed++;
if (bytes != Py_None) passed++;
if (bytes_le != Py_None) passed++;
if (fields != Py_None) passed++;
if (int_value != Py_None) passed++;
if (passed != 1) {
PyErr_SetString(
PyExc_TypeError,
"one of the hex, bytes, bytes_le, fields, or int arguments must be given"
);
return -1;
}
if (hex != Py_None) {
if (from_hex(self, hex) < 0) {
return -1;
}
}
else if (bytes != Py_None) {
if (!PyBytes_Check(bytes)) {
PyErr_SetString(PyExc_TypeError, "bytes must be a bytes object");
return -1;
}
if (PyBytes_GET_SIZE(bytes) != UUID_SIZE) {
PyErr_SetString(
PyExc_ValueError,
"bytes is not a 16-char string"
);
return -1;
}
memcpy(self->bytes, PyBytes_AS_STRING(bytes), sizeof(self->bytes));
}
else if (bytes_le != Py_None) {
if (from_bytes_le(self, bytes_le) < 0) {
return -1;
}
}
else if (fields != Py_None) {
if (from_fields(self, fields) < 0) {
return -1;
}
}
else if (int_value != Py_None) {
if (from_int(self, int_value, 1) < 0) {
return -1;
}
}
else {
Py_UNREACHABLE();
}
if (version != Py_None) {
long version_num = PyLong_AsLong(version);
if (version_num == -1 && PyErr_Occurred()) {
return -1;
}
if (version_num < 1 || version_num > 8) {
PyErr_SetString(PyExc_ValueError, "illegal version number");
return -1;
}
// Clear variant bits (keep only lower 6 bits of byte 8)
self->bytes[8] &= 0x3f; // 0011 1111
// Clear version bits (keep only lower 4 bits of byte 6)
self->bytes[6] &= 0x0f; // 0000 1111
// Set the variant to RFC 4122/9562 (binary 10xx xxxx)
self->bytes[8] |= 0x80; // 1000 0000
// Set the version number (upper 4 bits of byte 6)
self->bytes[6] |= (uint8_t)(version_num << 4);
}
Py_XSETREF(self->is_safe, Py_XNewRef(is_safe));
return 0;
}
static const uint8_t INT_TO_HEX[] = "0123456789abcdef";
// Lookup table for hex character to value conversion
// -1 for invalid characters
static const int8_t HEX_TO_INT[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1, 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
static inline void
byte_to_hex(uint8_t byte, char *hex)
{
hex[0] = INT_TO_HEX[(byte >> 4) & 0xf];
hex[1] = INT_TO_HEX[byte & 0xf];
}
static int
from_hex(uuidobject *self, PyObject *hex)
{
Py_ssize_t size;
const char *start = PyUnicode_AsUTF8AndSize(hex, &size);
if (start == NULL) {
return -1;
}
uint8_t ch;
uint8_t acc, acc_set;
int8_t part;
int i, j;
// Reimplement `hex = hex.replace('urn:', '').replace('uuid:', '')`
if (size > 0 && start[0] == 'u') {
if (size >= 9 && (strncmp(start, "urn:uuid:", 9) == 0 ||
strncmp(start, "uuid:urn:", 9) == 0)) {
start += 9;
size -= 9;
}
else if (size >= 4 && strncmp(start, "urn:", 4) == 0) {
start += 4;
size -= 4;
}
else if (size >= 5 && strncmp(start, "uuid:", 5) == 0) {
start += 5;
size -= 5;
}
}
// Reimplement `hex = hex.strip('{}')`
while (size >= 1 && (start[0] == '{' || start[0] == '}')) {
start++;
size -= 1;
}
while (size >= 1 && (start[size - 1] == '{' || start[size - 1] == '}')) {
size -= 1;
}
if (size < 32) {
PyErr_SetString(
PyExc_ValueError,
"badly formed hexadecimal UUID string"
);
return -1;
}
acc_set = 0;
j = 0;
for (i = 0; i < size; i++) {
ch = (uint8_t)start[i];
if (ch == '-') {
continue;
}
part = HEX_TO_INT[ch];
if (part == -1) {
PyErr_SetString(
PyExc_ValueError,
"badly formed hexadecimal UUID string"
);
return -1;
}
if (acc_set) {
acc |= (uint8_t)part;
self->bytes[j] = acc;
acc_set = 0;
j++;
}
else {
acc = (uint8_t)(part << 4);
acc_set = 1;
}
if (j > UUID_SIZE || (j == UUID_SIZE && acc_set)) {
PyErr_Format(PyExc_ValueError,
"invalid UUID '%s': decodes to more than 16 bytes",
hex);
return -1;
}
}
if (j != UUID_SIZE) {
PyErr_Format(PyExc_ValueError,
"invalid UUID '%s': decodes to less than 16 bytes",
hex);
return -1;
}
return 0;
}
static int
from_bytes_le(uuidobject *self, PyObject *bytes_le)
{
if (!PyBytes_Check(bytes_le)) {
PyErr_SetString(PyExc_TypeError, "bytes_le must be a bytes object");
return -1;
}
if (PyBytes_GET_SIZE(bytes_le) != UUID_SIZE) {
PyErr_SetString(PyExc_ValueError,
"bytes_le is not a 16-char string");
return -1;
}
// Convert from little-endian to big-endian UUID format
// UUID fields in little-endian order need to be byte-swapped:
// - time_low (4 bytes)
// - time_mid (2 bytes)
// - time_hi_version (2 bytes)
// - clock_seq_hi_variant (1 byte) - no swap needed
// - clock_seq_low (1 byte) - no swap needed
// - node (6 bytes) - no swap needed
const unsigned char *src = (const unsigned char *)PyBytes_AS_STRING(bytes_le);
unsigned char *dst = (unsigned char *)self->bytes;
// Swap time_low (bytes 0-3)
dst[0] = src[3];
dst[1] = src[2];
dst[2] = src[1];
dst[3] = src[0];
// Swap time_mid (bytes 4-5)
dst[4] = src[5];
dst[5] = src[4];
// Swap time_hi_version (bytes 6-7)
dst[6] = src[7];
dst[7] = src[6];
// Copy clock_seq and node as-is (bytes 8-15)
memcpy(dst + 8, src + 8, 8);
return 0;
}
static int
validate_int(uuid_state *state, PyObject *int_value)
{
if (!PyLong_Check(int_value)) {
PyErr_SetString(PyExc_TypeError, "value must be an integer");
return -1;
}
int cmp = PyLong_IsNegative(int_value);
if (cmp < 0) {
return -1;
}
if (cmp == 1) {
PyErr_SetString(PyExc_ValueError,
"int is out of range (need a 128-bit value)");
return -1;
}
// Check if it's greater than max (2^128 - 1)
cmp = PyObject_RichCompareBool(int_value, state->uint128_max, Py_GT);
if (cmp < 0) {
return -1;
}
if (cmp == 1) {
PyErr_SetString(PyExc_ValueError,
"int is out of range (need a 128-bit value)");
return -1;
}
return 0;
}
static int
from_int(uuidobject *self, PyObject *int_value, int validate)
{
// Convert a 128-bit integer to UUID bytes (big-endian)
uuid_state *state = get_uuid_state_by_cls(Py_TYPE(self));
if (validate && validate_int(state, int_value) < 0) {
return -1;
}
return _PyLong_AsByteArray(
(PyLongObject *)int_value,
(unsigned char *)self->bytes,
sizeof(self->bytes),
0, // big-endian
0, // unsigned
1 // with_exceptions
);
}
static int
extract_field(
PyObject *fields,
int field_num,
uint64_t max_value,
const char *error_msg,
uint64_t *result
) {
PyObject *field = PySequence_GetItem(fields, field_num);
if (field == NULL) {
return -1;
}
if (!PyLong_Check(field)) {
PyErr_Format(PyExc_TypeError, "field %d must be an integer", field_num);
goto fail;
}
uint64_t value;
if (PyLong_AsUInt64(field, &value) < 0) {
goto fail;
}
if (value > max_value) {
PyErr_SetString(PyExc_ValueError, error_msg);
goto fail;
}
*result = value;
Py_DECREF(field);
return 0;
fail:
Py_DECREF(field);
return -1;
}
static int
from_fields(uuidobject *self, PyObject *fields)
{
// Validate the length without replacing errors raised by __len__.
Py_ssize_t len = PySequence_Size(fields);
if (len < 0) {
return -1;
}
if (len != 6) {
PyErr_SetString(PyExc_ValueError, "fields is not a 6-tuple");
return -1;
}
#define EXTRACT_FIELD(FIELD_NUM, MAX_VALUE, ERROR_MSG, TYPE, NAME) \
TYPE NAME; \
do { \
uint64_t NAME##_extracted; \
if (extract_field(fields, FIELD_NUM, MAX_VALUE, ERROR_MSG, \
&NAME##_extracted) < 0) \
{ \
return -1; \
} \
NAME = (TYPE)NAME##_extracted; \
} while (0)
EXTRACT_FIELD(
0, (1ULL << 32) - 1, "field 1 out of range (need a 32-bit value)",
uint32_t, time_low
);
EXTRACT_FIELD(
1, (1ULL << 16) - 1, "field 2 out of range (need a 16-bit value)",
uint16_t, time_mid
);
EXTRACT_FIELD(
2, (1ULL << 16) - 1, "field 3 out of range (need a 16-bit value)",
uint16_t, time_hi_version
);
EXTRACT_FIELD(
3, (1ULL << 8) - 1, "field 4 out of range (need an 8-bit value)",
uint8_t, clock_seq_hi_variant
);
EXTRACT_FIELD(
4, (1ULL << 8) - 1, "field 5 out of range (need an 8-bit value)",
uint8_t, clock_seq_low
);
EXTRACT_FIELD(
5, (1ULL << 48) - 1, "field 6 out of range (need a 48-bit value)",
uint64_t, node
);
#undef EXTRACT_FIELD
self->bytes[0] = (uint8_t)(time_low >> 24);
self->bytes[1] = (uint8_t)(time_low >> 16);
self->bytes[2] = (uint8_t)(time_low >> 8);
self->bytes[3] = (uint8_t)time_low;
self->bytes[4] = (uint8_t)(time_mid >> 8);
self->bytes[5] = (uint8_t)time_mid;
self->bytes[6] = (uint8_t)(time_hi_version >> 8);
self->bytes[7] = (uint8_t)time_hi_version;
self->bytes[8] = clock_seq_hi_variant;
self->bytes[9] = clock_seq_low;
self->bytes[10] = (uint8_t)(node >> 40);
self->bytes[11] = (uint8_t)(node >> 32);
self->bytes[12] = (uint8_t)(node >> 24);
self->bytes[13] = (uint8_t)(node >> 16);
self->bytes[14] = (uint8_t)(node >> 8);
self->bytes[15] = (uint8_t)node;
return 0;
}
static PyObject *
get_int(uuidobject *self)
{
return _PyLong_FromByteArray(
(unsigned char *)self->bytes, sizeof(self->bytes), 0, 0);
}
static PyObject *
get_uuidmod_attr(const char *name)
{
return PyImport_ImportModuleAttrString("uuid", name);
}
static PyObject *
get_SafeUUID(uuid_state *state)
{
if (state->safe_uuid != NULL) {
return Py_NewRef(state->safe_uuid);
}
state->safe_uuid = get_uuidmod_attr("SafeUUID");
return Py_XNewRef(state->safe_uuid);
}
static uuidobject *
make_uuid(PyTypeObject *type)
{
uuidobject *self = NULL;
uuid_state *state = get_uuid_state_by_cls(type);
if (type != state->uuid_type) {
// Allocate the subclass's full instance size and enable GC tracking.
self = uuidobject_CAST(PyType_GenericAlloc(type, 0));
if (self == NULL) {
return NULL;
}
goto init;
}
Py_BEGIN_CRITICAL_SECTION(type);
if (state->freelist_size > 0) {
self = state->freelist;
state->freelist = uuidobject_CAST(self->weakreflist);
state->freelist_size--;
}
Py_END_CRITICAL_SECTION();
if (self != NULL) {
// Deallocation releases the type reference even when caching the object,
// so the freelist does not keep the type and its module alive. Restore
// that reference and reinitialize the object before returning it.
PyObject_Init((PyObject *)self, type);
}
else {
self = PyObject_New(uuidobject, type);
if (self == NULL) {
return NULL;
}
}
init:
self->is_safe = NULL;
self->weakreflist = NULL;
self->cached_hash = -1;
return self;
}
static PyObject *
Uuid_alloc(PyTypeObject *type, Py_ssize_t Py_UNUSED(nitems))
{
uuidobject *self = make_uuid(type);
if (self == NULL) {
return NULL;
}
memset(self->bytes, 0, sizeof(self->bytes));
return (PyObject *)self;
}
static void
Uuid_dealloc(PyObject *obj)
{
PyTypeObject *type = Py_TYPE(obj);
uuidobject *uuid = uuidobject_CAST(obj);
if (uuid->weakreflist != NULL) {
PyObject_ClearWeakRefs(obj);
}
Py_CLEAR(uuid->is_safe);
int skip_dealloc = 0;
// Only the base UUID type uses the freelist. Do not search a subclass's
// MRO: GC may already have cleared its base type's module reference.
PyObject *module = PyType_GetModule_DuringGC(type);
if (module == NULL) {
goto dealloc;
}
assert(PyModule_Check(module));
if (PyModule_GetDef(module) != &uuidmodule) {
goto dealloc;
}
uuid_state *state = get_uuid_state(module);
if (type != state->uuid_type) {
goto dealloc;
}
Py_BEGIN_CRITICAL_SECTION(type);
if (state->freelist_size < MAX_FREE_LIST_SIZE) {
uuidobject *head = state->freelist;
state->freelist = uuid;
uuid->weakreflist = (PyObject *)head;
state->freelist_size++;
skip_dealloc = 1;
}
Py_END_CRITICAL_SECTION();
dealloc:
if (!skip_dealloc) {
type->tp_free(uuid);
}
// Only live instances own a reference to the heap type. Keeping that
// reference in the freelist would prevent the module from being collected.
Py_DECREF(type);
}
static PyObject *
Uuid_get_int(PyObject *op, void *Py_UNUSED(closure))
{
return get_int(uuidobject_CAST(op));
}
static PyObject *
Uuid_get_is_safe(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
uuid_state *state = get_uuid_state_by_cls(Py_TYPE(self));
if (self->is_safe == NULL) {
PyObject *SafeUUID = get_SafeUUID(state);
if (SafeUUID == NULL) {
return NULL;
}
PyObject *ret = PyObject_GetAttrString(SafeUUID, "unknown");
Py_DECREF(SafeUUID);
return ret;
}
return Py_NewRef(self->is_safe);
}
static PyObject *
Uuid_get_hex(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return _Py_strhex((const char *)self->bytes, sizeof(self->bytes));
}
static PyObject *
Uuid_get_variant(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
uint8_t variant_byte = self->bytes[8];
// xxx - three high bits of variant_byte are unknown
if (!(variant_byte & 0x80)) { // & 0b1000_0000
// 0xx - RESERVED_NCS
return get_uuidmod_attr("RESERVED_NCS");
}
// 1xx -- we know that high bit must be 1
if (!(variant_byte & 0x40)) { // & 0b0100_0000
// 10x - RFC_4122
return get_uuidmod_attr("RFC_4122");
}
// 11x -- we know that two high bits are 1
if (!(variant_byte & 0x20)) { // & 0b0010_0000
// 110 - RESERVED_MICROSOFT
return get_uuidmod_attr("RESERVED_MICROSOFT");
}
// 111 -- we know that all three high bits are 1 - RESERVED_FUTURE
return get_uuidmod_attr("RESERVED_FUTURE");
}
static int
is_rfc_4122(uuidobject *self)
{
return (self->bytes[8] & 0xc0) == 0x80;
}
static long
get_version(uuidobject *self)
{
// RFC_4122 is when bit 7 is set (0x80) and bit 6 is not set (0x40)
// 0xc0 = 0b11000000
// 0x80 = 0b10000000
if (!is_rfc_4122(self)) {
return 0;
}
return (self->bytes[6] >> 4) & 0xf;
}
static PyObject *
Uuid_get_version(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
if (!is_rfc_4122(self)) {
Py_RETURN_NONE;
}
return PyLong_FromLong(get_version(self));
}
static inline uint32_t
get_time_low(uuidobject *self)
{
return ((uint32_t)self->bytes[0] << 24) |
((uint32_t)self->bytes[1] << 16) |
((uint32_t)self->bytes[2] << 8) |
((uint32_t)self->bytes[3]);
}
static inline uint16_t
get_time_mid(uuidobject *self)
{
return (uint16_t)((self->bytes[4] << 8) | self->bytes[5]);
}
static inline uint16_t
get_time_hi_version(uuidobject *self)
{
return (uint16_t)((self->bytes[6] << 8) | self->bytes[7]);
}
static inline uint8_t
get_clock_seq_hi_variant(uuidobject *self)
{
return self->bytes[8];
}
static inline uint8_t
get_clock_seq_low(uuidobject *self)
{
return self->bytes[9];
}
static inline uint64_t
get_node(uuidobject *self)
{
return ((uint64_t)self->bytes[10] << 40) |
((uint64_t)self->bytes[11] << 32) |
((uint64_t)self->bytes[12] << 24) |
((uint64_t)self->bytes[13] << 16) |
((uint64_t)self->bytes[14] << 8) |
((uint64_t)self->bytes[15]);
}
static PyObject *
Uuid_get_time_low(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return PyLong_FromUnsignedLong(get_time_low(self));
}
static PyObject *
Uuid_get_time_mid(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return PyLong_FromUnsignedLong(get_time_mid(self));
}
static PyObject *
Uuid_get_time_hi_version(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return PyLong_FromUnsignedLong(get_time_hi_version(self));
}
static PyObject *
Uuid_get_clock_seq_hi_variant(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return PyLong_FromUnsignedLong(get_clock_seq_hi_variant(self));
}
static PyObject *
Uuid_get_clock_seq_low(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return PyLong_FromUnsignedLong(get_clock_seq_low(self));
}
static PyObject *
Uuid_get_time(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
long version = get_version(self);
if (version == 6) {
// UUID v6: time_hi (32) | time_mid (16) | ver (4) | time_lo (12) | ... (64)
uint32_t time_hi = get_time_low(self);
uint16_t time_mid = get_time_mid(self);
uint16_t time_lo = (uint16_t)(((self->bytes[6] & 0x0f) << 8) |
self->bytes[7]);
uint64_t time = ((uint64_t)time_hi << 28) |
((uint64_t)time_mid << 12) |
(uint64_t)time_lo;
return PyLong_FromUnsignedLongLong(time);
}
else if (version == 7) {
// UUID v7: unix_ts_ms (48) | ... (80)
// First 6 bytes are the 48-bit timestamp
uint64_t unix_ts_ms = ((uint64_t)self->bytes[0] << 40) |
((uint64_t)self->bytes[1] << 32) |
((uint64_t)self->bytes[2] << 24) |
((uint64_t)self->bytes[3] << 16) |
((uint64_t)self->bytes[4] << 8) |
((uint64_t)self->bytes[5]);
return PyLong_FromUnsignedLongLong(unix_ts_ms);
}
else {
// UUID v1 and others: time_lo (32) | time_mid (16) | ver (4)
// | time_hi (12) | ... (64)
uint32_t time_lo = get_time_low(self);
uint16_t time_mid = get_time_mid(self);
uint16_t time_hi = (uint16_t)(((self->bytes[6] & 0x0f) << 8) |
self->bytes[7]);
uint64_t time = ((uint64_t)time_hi << 48) |
((uint64_t)time_mid << 32) |
(uint64_t)time_lo;
return PyLong_FromUnsignedLongLong(time);
}
}
static PyObject *
Uuid_get_clock_seq(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
// clock_seq_hi_variant (byte 8) & 0x3f, then clock_seq_low (byte 9)
uint16_t clock_seq = (uint16_t)(((get_clock_seq_hi_variant(self) & 0x3f) << 8) |
get_clock_seq_low(self));
return PyLong_FromUnsignedLong(clock_seq);
}
static PyObject *
Uuid_get_node(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return PyLong_FromUnsignedLongLong(get_node(self));
}
static PyObject *
Uuid_get_bytes(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
return PyBytes_FromStringAndSize((const char *)self->bytes, sizeof(self->bytes));
}
static PyObject *
Uuid_get_bytes_le(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
// UUID fields in little-endian order need to be byte-swapped:
// - time_low (4 bytes) - reversed
// - time_mid (2 bytes) - reversed
// - time_hi_version (2 bytes) - reversed
// - clock_seq and node (8 bytes) - unchanged
unsigned char bytes_le[UUID_SIZE];
// Reverse time_low (bytes 0-3)
bytes_le[0] = self->bytes[3];
bytes_le[1] = self->bytes[2];
bytes_le[2] = self->bytes[1];
bytes_le[3] = self->bytes[0];
// Reverse time_mid (bytes 4-5)
bytes_le[4] = self->bytes[5];
bytes_le[5] = self->bytes[4];
// Reverse time_hi_version (bytes 6-7)
bytes_le[6] = self->bytes[7];
bytes_le[7] = self->bytes[6];
// Copy clock_seq and node as-is (bytes 8-15)
memcpy(bytes_le + 8, self->bytes + 8, 8);
return PyBytes_FromStringAndSize((const char *)bytes_le, sizeof(bytes_le));
}
static PyObject *
Uuid_get_fields(PyObject *op, void *Py_UNUSED(closure))
{
uuidobject *self = uuidobject_CAST(op);
uint32_t time_low = get_time_low(self);
uint16_t time_mid = get_time_mid(self);
uint16_t time_hi_version = get_time_hi_version(self);
uint8_t clock_seq_hi_variant = get_clock_seq_hi_variant(self);
uint8_t clock_seq_low = get_clock_seq_low(self);
uint64_t node = get_node(self);
// Build and return the tuple
return Py_BuildValue(
"(kHHBBK)",
(unsigned long)time_low,
(unsigned short)time_mid,
(unsigned short)time_hi_version,
(unsigned char)clock_seq_hi_variant,
(unsigned char)clock_seq_low,
(unsigned long long)node
);
}
static PyObject *
uuid_from_bytes_array(PyTypeObject *type, uint8_t bytes[UUID_SIZE])
{
uuidobject *self = make_uuid(type);
if (self == NULL) {
return NULL;
}
memcpy(self->bytes, bytes, sizeof(self->bytes));
return (PyObject *)self;
}
static PyObject *
Uuid_nb_int(PyObject *self)
{
return get_int(uuidobject_CAST(self));
}
static PyObject *
Uuid_richcompare(PyObject *self, PyObject *other, int op)
{
uuid_state *state = get_uuid_state_by_cls(Py_TYPE(self));
if (!PyObject_TypeCheck(other, state->uuid_type)) {
Py_RETURN_NOTIMPLEMENTED;
}
uuidobject *uuid_self = uuidobject_CAST(self);
uuidobject *uuid_other = uuidobject_CAST(other);
int cmp = memcmp(uuid_self->bytes, uuid_other->bytes, sizeof(uuid_self->bytes));
int result;
switch (op) {
case Py_EQ:
result = (cmp == 0);
break;
case Py_NE:
result = (cmp != 0);
break;
case Py_LT:
result = (cmp < 0);
break;
case Py_LE:
result = (cmp <= 0);
break;
case Py_GT:
result = (cmp > 0);
break;
case Py_GE:
result = (cmp >= 0);
break;
default:
Py_RETURN_NOTIMPLEMENTED;
}
if (result) {
Py_RETURN_TRUE;
}
else {
Py_RETURN_FALSE;
}
}
static PyObject *
Uuid_str(PyObject *self)
{
uuidobject *uuid = uuidobject_CAST(self);
// UUID string format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (36 chars)
PyObject *result = PyUnicode_New(36, 127);
if (result == NULL) {
return NULL;
}
char *str = (char *)PyUnicode_1BYTE_DATA(result);
for (int i = 0; i < 4; i++) {
byte_to_hex(uuid->bytes[i], &str[i * 2]);
}
str[8] = '-';
for (int i = 4; i < 6; i++) {
byte_to_hex(uuid->bytes[i], &str[9 + (i - 4) * 2]);
}
str[13] = '-';
for (int i = 6; i < 8; i++) {
byte_to_hex(uuid->bytes[i], &str[14 + (i - 6) * 2]);
}
str[18] = '-';
for (int i = 8; i < 10; i++) {
byte_to_hex(uuid->bytes[i], &str[19 + (i - 8) * 2]);
}
str[23] = '-';
for (int i = 10; i < UUID_SIZE; i++) {
byte_to_hex(uuid->bytes[i], &str[24 + (i - 10) * 2]);
}
return result;
}
static PyObject *
Uuid_repr(PyObject *self)
{
// Get the class name (sadly can't use tp_name -- we don't need the full name)
PyObject *cls_name = PyObject_GetAttrString((PyObject *)Py_TYPE(self), "__name__");
if (cls_name == NULL) {
return NULL;
}
PyObject *repr = PyUnicode_FromFormat("%U('%S')", cls_name, self);
Py_DECREF(cls_name);
return repr;
}
static int
Uuid_setattr(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(name),
PyObject *Py_UNUSED(value))
{
PyErr_SetString(PyExc_TypeError, "UUID objects are immutable");
return -1;
}
static PyObject *
Uuid_get_urn(PyObject *op, void *Py_UNUSED(closure))
{
PyObject *str_obj = Uuid_str(op);
if (str_obj == NULL) {
return NULL;
}
PyObject *urn = PyUnicode_FromFormat("urn:uuid:%U", str_obj);
Py_DECREF(str_obj);
return urn;
}
static Py_hash_t
Uuid_hash(PyObject *op)
{
uuidobject *uuid = uuidobject_CAST(op);
// UUIDs are very often used in dicts/sets, so it makes
// sense to cache the computed hash (like we do for str).
if (uuid->cached_hash == -1) {
uuid->cached_hash = Py_HashBuffer(uuid->bytes, sizeof(uuid->bytes));
}
return uuid->cached_hash;
}
/*[clinic input]
@classmethod
_uuid.UUID._from_int
value: object
/
Create a UUID from an integer value. Internal use only.
[clinic start generated code]*/
static PyObject *
_uuid_UUID__from_int_impl(PyTypeObject *type, PyObject *value)
/*[clinic end generated code: output=05af0cfa4805fcae input=3f472ebfd07bbf50]*/
{
uuid_state *state = get_uuid_state_by_cls(type);
if (validate_int(state, value) < 0) {
return NULL;
}
uuidobject *self = make_uuid(type);
if (self == NULL) {
return NULL;
}
if (from_int(self, value, 0) < 0) {
// We validated before creating an instance, so now we don't need to
// validate again
return NULL;
}
return (PyObject *)self;
}
static PyGetSetDef Uuid_getset[] = {
{"int", Uuid_get_int, NULL, "UUID as a 128-bit integer", NULL},
{"is_safe", Uuid_get_is_safe, NULL, "UUID safety status", NULL},
{"fields", Uuid_get_fields, NULL, "UUID as a 6-tuple", NULL},
{"hex", Uuid_get_hex, NULL, "UUID as a 32-character hex string", NULL},
{"urn", Uuid_get_urn, NULL, "UUID as a URN", NULL},
{"variant", Uuid_get_variant, NULL, "UUID variant", NULL},
{"version", Uuid_get_version, NULL, "UUID version", NULL},
{"time_low", Uuid_get_time_low, NULL, "Time low field (32 bits)", NULL},
{"time_mid", Uuid_get_time_mid, NULL, "Time mid field (16 bits)", NULL},
{"bytes", Uuid_get_bytes, NULL, "UUID as a 16-byte string", NULL},
{"bytes_le", Uuid_get_bytes_le, NULL,
"UUID as a 16-byte string in little-endian byte order", NULL},
{"time_hi_version", Uuid_get_time_hi_version, NULL,
"Time high and version field (16 bits)", NULL},
{"clock_seq_hi_variant", Uuid_get_clock_seq_hi_variant, NULL,
"Clock sequence high and variant field (8 bits)", NULL},
{"clock_seq_low", Uuid_get_clock_seq_low, NULL,
"Clock sequence low field (8 bits)", NULL},
{"time", Uuid_get_time, NULL,
"Time field (60 bits for v1/v6, 48 bits for v7)", NULL},
{"clock_seq", Uuid_get_clock_seq, NULL, "Clock sequence field (14 bits)", NULL},
{"node", Uuid_get_node, NULL, "Node field (48 bits)", NULL},
{NULL}
};
/*[clinic input]
_uuid.UUID.__getstate__
Return the UUID's state for pickling.
[clinic start generated code]*/
static PyObject *
_uuid_UUID___getstate___impl(uuidobject *self)
/*[clinic end generated code: output=f9278a4d28ccac91 input=4b471ae24b705e8e]*/
{
PyObject *dict = PyDict_New();
if (dict == NULL) {
return NULL;
}
// Always add 'int' key
PyObject *int_value = get_int(self);
if (int_value == NULL) {
Py_DECREF(dict);
return NULL;
}
if (PyDict_SetItem(dict, &_Py_ID(int), int_value) < 0) {
Py_DECREF(int_value);
Py_DECREF(dict);
return NULL;
}
Py_DECREF(int_value);
if (PyDict_SetItem(dict, &_Py_ID(is_safe), self->is_safe) < 0) {
Py_DECREF(dict);
return NULL;
}
return dict;
}
/*[clinic input]
_uuid.UUID.__setstate__
state: object
/
Restore the UUID's state from pickling.
Expects a mapping with 'int' and optionally 'is_safe' keys.
[clinic start generated code]*/
static PyObject *
_uuid_UUID___setstate___impl(uuidobject *self, PyObject *state)
/*[clinic end generated code: output=cdf6bd4a2a680b3f input=fc65bdad0a8d6211]*/
{
// Get and set the 'int' value
PyObject *int_value = PyObject_GetItem(state, &_Py_ID(int));
if (int_value == NULL) {
return NULL;
}
int result = from_int(self, int_value, 1);
Py_DECREF(int_value);
if (result < 0) {
return NULL;
}
int has_is_safe = PySequence_Contains(state, &_Py_ID(is_safe));
if (has_is_safe < 0) {
return NULL;
}
PyObject *is_safe = has_is_safe
? PyObject_GetItem(state, &_Py_ID(is_safe))
: Py_NewRef(Py_None);
if (is_safe == NULL) {
return NULL;
}
uuid_state *module_state = get_uuid_state_by_cls(Py_TYPE(self));
PyObject *SafeUUID = get_SafeUUID(module_state);
if (SafeUUID == NULL) {
Py_DECREF(is_safe);
return NULL;
}
PyObject *safety = PyObject_CallOneArg(SafeUUID, is_safe);
Py_DECREF(SafeUUID);
Py_DECREF(is_safe);
if (safety == NULL) {
return NULL;
}
Py_XSETREF(self->is_safe, safety);
Py_RETURN_NONE;
}
static PyObject *
compute_uuid_max(void)
{
// Compute `(1 << 128) - 1`
PyObject *shift = PyLong_FromLong(128);
if (shift == NULL) {
return NULL;
}
PyObject *shifted = PyNumber_Lshift(_PyLong_GetOne(), shift);
Py_DECREF(shift);
if (shifted == NULL) {
return NULL;
}
PyObject *result = PyNumber_Subtract(shifted, _PyLong_GetOne());
Py_DECREF(shifted);
return result;
}
static PyMethodDef Uuid_methods[] = {
_UUID_UUID__FROM_INT_METHODDEF
_UUID_UUID___GETSTATE___METHODDEF
_UUID_UUID___SETSTATE___METHODDEF
{NULL, NULL}
};
static PyMemberDef Uuid_members[] = {
{"__weaklistoffset__", Py_T_PYSSIZET, offsetof(uuidobject, weakreflist), Py_READONLY},
{NULL}
};
static PyType_Slot Uuid_slots[] = {
{Py_tp_alloc, Uuid_alloc},
{Py_tp_dealloc, Uuid_dealloc},
{Py_tp_getattro, PyObject_GenericGetAttr},
{Py_tp_setattro, Uuid_setattr},
{Py_tp_getset, Uuid_getset},
{Py_tp_members, Uuid_members},
{Py_tp_init, _uuid_UUID___init__},
{Py_tp_doc, (void *)_uuid_UUID___init____doc__},
{Py_tp_str, Uuid_str},
{Py_tp_repr, Uuid_repr},
{Py_tp_hash, Uuid_hash},
{Py_tp_richcompare, Uuid_richcompare},
{Py_nb_int, Uuid_nb_int},
{Py_tp_methods, Uuid_methods},
{0, NULL},
};
static PyType_Spec Uuid_spec = {
// We use "uuid.UUID" here and not "_uuid.UUID" to have full
// compatibility with old pickled UUIDs. There's no workaround
// if we want both to produce compatible pickles that can be read
// by older Pythons (using ancient pickle protocol verions) and
// restore from pickles produced by old Python versions.
.name = "uuid.UUID",
.basicsize = sizeof(uuidobject),
.flags = (
Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE
| Py_TPFLAGS_HEAPTYPE
| Py_TPFLAGS_IMMUTABLETYPE
),
.slots = Uuid_slots,
};
static int
module_traverse(PyObject *mod, visitproc visit, void *arg)
{
uuid_state *state = get_uuid_state(mod);
Py_VISIT(state->uuid_type);
Py_VISIT(state->safe_uuid);
Py_VISIT(state->uint128_max);
Py_VISIT(state->random_func);
Py_VISIT(state->time_func);
Py_VISIT(state->random_size_int);
return 0;
}
static int
module_clear(PyObject *mod)
{
uuid_state *state = get_uuid_state(mod);
Py_CLEAR(state->uuid_type);
Py_CLEAR(state->safe_uuid);
Py_CLEAR(state->uint128_max);
Py_CLEAR(state->random_func);
Py_CLEAR(state->time_func);
Py_CLEAR(state->random_size_int);
if (state->freelist != NULL) {
while (state->freelist != NULL) {
uuidobject *cur = state->freelist;
state->freelist = uuidobject_CAST(cur->weakreflist);
PyObject_Free(cur);
}
state->freelist = NULL;
state->freelist_size = 0;
}
return 0;
}
static void
module_free(void *mod)
{
(void)module_clear((PyObject *)mod);
}
static int
uuid_exec(PyObject *module)
{
uuid_state *state = get_uuid_state(module);
#define ADD_INT(NAME, VALUE) \
do { \
if (PyModule_AddIntConstant(module, (NAME), (VALUE)) < 0) { \
return -1; \
} \
} while (0)
assert(sizeof(uuid_t) == UUID_SIZE);
#if defined(MS_WINDOWS)
ADD_INT("has_uuid_generate_time_safe", 0);
#elif defined(HAVE_UUID_GENERATE_TIME_SAFE)
ADD_INT("has_uuid_generate_time_safe", 1);
#else
ADD_INT("has_uuid_generate_time_safe", 0);
#endif
#if defined(MS_WINDOWS)
ADD_INT("has_stable_extractable_node", py_windows_has_stable_node());
#elif defined(HAVE_UUID_GENERATE_TIME_SAFE_STABLE_MAC)
ADD_INT("has_stable_extractable_node", 1);
#else
ADD_INT("has_stable_extractable_node", 0);
#endif
#undef ADD_INT
state->uuid_type = (PyTypeObject *)PyType_FromModuleAndSpec(
module,
&Uuid_spec,
NULL
);
if (state->uuid_type == NULL) {
return -1;
}
if (PyModule_AddType(module, state->uuid_type) < 0) {
return -1;
}
state->safe_uuid = NULL;
state->uint128_max = compute_uuid_max();
if (state->uint128_max == NULL) {
return -1;
}
state->last_timestamp_v7 = 0;
state->last_timestamp_v7_init = 0;
state->last_counter_v7 = 0;
state->random_last_pid = uuid_getpid();
state->time_func = NULL;
state->random_func = NULL;
state->random_size_int = PyLong_FromSize_t((Py_ssize_t)RANDOM_BUF_SIZE);
if (state->random_size_int == NULL) {
return -1;
}
state->random_idx = RANDOM_BUF_SIZE;
state->freelist = NULL;
state->freelist_size = 0;
return 0;
}
static PyMethodDef uuid_methods[] = {
_UUID_UUID4_METHODDEF
_UUID_UUID7_METHODDEF
_UUID__INSTALL_C_HOOKS_METHODDEF
#if defined(HAVE_UUID_UUID_H) || defined(HAVE_UUID_H)
{"generate_time_safe", py_uuid_generate_time_safe, METH_NOARGS, NULL},
#endif
#if defined(MS_WINDOWS)
{"UuidCreate", py_UuidCreate, METH_NOARGS, NULL},
#endif
{NULL, NULL, 0, NULL}
};
static PyModuleDef_Slot uuid_slots[] = {
{Py_mod_exec, uuid_exec},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
{0, NULL}
};
static struct PyModuleDef uuidmodule = {
PyModuleDef_HEAD_INIT,
.m_name = "_uuid",
.m_size = sizeof(uuid_state),
.m_methods = uuid_methods,
.m_traverse = module_traverse,
.m_clear = module_clear,
.m_slots = uuid_slots,
.m_free = module_free,
};
PyMODINIT_FUNC
PyInit__uuid(void)
{
return PyModuleDef_Init(&uuidmodule);
}