Skip to content

Commit b395ff9

Browse files
committed
Add C implementation of UUID stringification
1 parent 3416e7c commit b395ff9

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

Lib/test/test_uuid.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ def test_UUID(self):
224224
self.uuid.UUID(urn)]:
225225
# Test all conversions and properties of the UUID object.
226226
equal(str(u), string)
227+
if hasattr(self.uuid, "_c_uuid_str_method"):
228+
# If we have the C accelerator, test both implementations.
229+
equal(self.uuid._py_uuid_str_method(u), string)
230+
equal(self.uuid._c_uuid_str_method(u), string)
227231
equal(int(u), integer)
228232
equal(u.bytes, bytes)
229233
equal(u.bytes_le, bytes_le)

Lib/uuid.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,12 @@ def _netstat_getnode():
635635
_generate_time_safe = getattr(_uuid, "generate_time_safe", None)
636636
_has_stable_extractable_node = _uuid.has_stable_extractable_node
637637
_UuidCreate = getattr(_uuid, "UuidCreate", None)
638+
_uuid_int_to_str = getattr(_uuid, "uuid_int_to_str", None)
639+
_py_uuid_str_method = UUID.__str__
640+
if _uuid_int_to_str is not None:
641+
def _c_uuid_str_method(self):
642+
return _uuid_int_to_str(self.int)
643+
UUID.__str__ = _c_uuid_str_method
638644
except ImportError:
639645
_uuid = None
640646
_generate_time_safe = None
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve performance of :meth:`uuid.UUID.__str__ <object.__str__>` by adding
2+
a C implementation in _uuidmodule.

Modules/_uuidmodule.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@
33
* DCE compatible Universally Unique Identifier library.
44
*/
55

6-
// Need limited C API version 3.13 for Py_mod_gil
7-
#include "pyconfig.h" // Py_GIL_DISABLED
8-
#ifndef Py_GIL_DISABLED
9-
# define Py_LIMITED_API 0x030d0000
10-
#endif
11-
6+
#define Py_BUILD_CORE_MODULE
127
#include "Python.h"
8+
#include "pycore_long.h" // _PyLong_AsByteArray
139
#if defined(HAVE_UUID_H)
1410
// AIX, FreeBSD, libuuid with pkgconf
1511
#include <uuid.h>
@@ -90,6 +86,42 @@ py_windows_has_stable_node(void)
9086
}
9187
#endif /* MS_WINDOWS */
9288

89+
static PyObject *
90+
py_uuid_int_to_str(PyObject *Py_UNUSED(context), PyObject *uuid_int)
91+
{
92+
if (!PyLong_Check(uuid_int)) {
93+
PyErr_SetString(PyExc_TypeError, "uuid_int must be an integer");
94+
return NULL;
95+
}
96+
97+
unsigned char bytes[16];
98+
if (_PyLong_AsByteArray((PyLongObject *)uuid_int, bytes, 16, 0, 0, 1) < 0) {
99+
return NULL;
100+
}
101+
102+
PyObject *result = PyUnicode_New(36, 127);
103+
if (result == NULL) {
104+
return NULL;
105+
}
106+
assert(PyUnicode_KIND(result) == PyUnicode_1BYTE_KIND);
107+
Py_UCS1 *str = PyUnicode_1BYTE_DATA(result);
108+
static const Py_UCS1 hex_digits[] = "0123456789abcdef";
109+
110+
int pos = 0;
111+
for (int i = 0; i < 16; i++) {
112+
if (i == 4 || i == 6 || i == 8 || i == 10) {
113+
str[pos++] = '-';
114+
}
115+
unsigned char byte = bytes[i];
116+
str[pos++] = hex_digits[byte >> 4];
117+
str[pos++] = hex_digits[byte & 0x0f];
118+
}
119+
#ifdef Py_DEBUG
120+
assert(pos == 36);
121+
assert(_PyUnicode_CheckConsistency(result, 1));
122+
#endif
123+
return result;
124+
}
93125

94126
static int
95127
uuid_exec(PyObject *module)
@@ -129,6 +161,7 @@ static PyMethodDef uuid_methods[] = {
129161
#if defined(MS_WINDOWS)
130162
{"UuidCreate", py_UuidCreate, METH_NOARGS, NULL},
131163
#endif
164+
{"uuid_int_to_str", py_uuid_int_to_str, METH_O, NULL},
132165
{NULL, NULL, 0, NULL} /* sentinel */
133166
};
134167

0 commit comments

Comments
 (0)