Skip to content

Commit 417465e

Browse files
committed
Implement UUID to hex & str without hton functions
1 parent 8cf18dc commit 417465e

File tree

4 files changed

+117
-53
lines changed

4 files changed

+117
-53
lines changed

pgproto.pyx

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ from libc.stdint cimport int8_t, uint8_t, int16_t, uint16_t, \
1717

1818

1919
from . cimport hton
20+
from . cimport tohex
2021

2122
from .debug cimport PG_DEBUG
2223
from . import types as pgproto_types

tohex.h

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#define HEX_PRELUDE \
2+
const char *__hexm = "0123456789abcdef";
3+
4+
#define HEX_1_BYTE(buf, dest) \
5+
{ \
6+
char byte = (buf)[0]; \
7+
(dest)[0] = __hexm[(byte >> 4) & 0x0F]; \
8+
(dest)[1] = __hexm[byte & 0x0F]; \
9+
}
10+
11+
#define HEX_2_BYTES(buf, dest) \
12+
{ \
13+
HEX_1_BYTE(buf, dest) \
14+
HEX_1_BYTE(buf + 1, dest + 2) \
15+
}
16+
17+
#define HEX_4_BYTES(buf, dest) \
18+
{ \
19+
HEX_2_BYTES(buf, dest) \
20+
HEX_2_BYTES(buf + 2, dest + 4) \
21+
}
22+
23+
#define HEX_8_BYTES(buf, dest) \
24+
{ \
25+
HEX_4_BYTES(buf, dest) \
26+
HEX_4_BYTES(buf + 4, dest + 8) \
27+
}
28+
29+
30+
void inline
31+
uuid_to_str(const char *source, char *dest)
32+
{
33+
HEX_PRELUDE
34+
35+
HEX_4_BYTES(source, dest)
36+
dest[8] = '-';
37+
HEX_2_BYTES(source + 4, dest + 9)
38+
dest[13] = '-';
39+
HEX_2_BYTES(source + 6, dest + 14)
40+
dest[18] = '-';
41+
HEX_2_BYTES(source + 8, dest + 19)
42+
dest[23] = '-';
43+
HEX_4_BYTES(source + 10, dest + 24)
44+
HEX_2_BYTES(source + 14, dest + 32)
45+
}
46+
47+
void inline
48+
uuid_to_hex(const char *source, char *dest)
49+
{
50+
HEX_PRELUDE
51+
HEX_8_BYTES(source, dest)
52+
HEX_8_BYTES(source + 8, dest + 16)
53+
}

tohex.pxd

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (C) 2016-present the asyncpg authors and contributors
2+
# <see AUTHORS file>
3+
#
4+
# This module is part of asyncpg and is released under
5+
# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
6+
7+
8+
cdef extern from "./tohex.h":
9+
cdef void uuid_to_str(const char *source, char *dest)
10+
cdef void uuid_to_hex(const char *source, char *dest)

uuid.pyx

+53-53
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,44 @@
11
import functools
22
import uuid
33

4-
from libc.stdint cimport uint64_t, uint8_t, int8_t
4+
from libc.stdint cimport uint8_t, int8_t
55
from libc.string cimport memcpy, strncmp
66

77

88
# A more efficient UUID type implementation
9-
# (6-7x faster than the uuid.UUID).
10-
9+
# (6-7x faster than the starndard uuid.UUID):
10+
#
11+
# -= Benchmark results (less is better): =-
12+
#
13+
# std_UUID(bytes): 1.2368
14+
# c_UUID(bytes): * 0.1645 (7.52x)
15+
# object(): 0.1483
16+
#
17+
# std_UUID(str): 1.8038
18+
# c_UUID(str): * 0.2313 (7.80x)
19+
#
20+
# str(std_UUID()): 1.4625
21+
# str(c_UUID()): * 0.2681 (5.46x)
22+
# str(object()): 0.5975
23+
#
24+
# std_UUID().bytes: 0.3508
25+
# c_UUID().bytes: * 0.1068 (3.28x)
26+
#
27+
# std_UUID().int: 0.0871
28+
# c_UUID().int: * 0.0856
29+
#
30+
# std_UUID().hex: 0.4871
31+
# c_UUID().hex: * 0.1405
32+
#
33+
# hash(std_UUID()): 0.3635
34+
# hash(c_UUID()): * 0.1564 (2.32x)
35+
#
36+
# dct[std_UUID()]: 0.3319
37+
# dct[c_UUID()]: * 0.1570 (2.11x)
38+
#
39+
# std_UUID() ==: 0.3478
40+
# c_UUID() ==: * 0.0915 (3.80x)
1141

12-
cdef const char *_hexmap = b"0123456789abcdef"
1342

1443
cdef char _hextable[256]
1544
_hextable[:] = [
@@ -27,14 +56,7 @@ _hextable[:] = [
2756
]
2857

2958

30-
cdef inline char i64_to_hex(uint64_t num, char *s):
31-
cdef uint8_t i
32-
33-
for i in range(15, -1, -1):
34-
s[i] = _hexmap[num & 0x0F]
35-
num >>= 4
36-
37-
return 0
59+
cdef std_UUID = uuid.UUID
3860

3961

4062
cdef pg_uuid_bytes_from_str(str u, char *out):
@@ -142,26 +164,18 @@ cdef class UUID(__UUIDReplaceMe):
142164
return uuid.SafeUUID.unknown
143165

144166
def __str__(self):
145-
cdef:
146-
uint64_t u
147-
char out[36]
148-
149-
u = <uint64_t>hton.unpack_int64(self._data)
150-
i64_to_hex(u, out)
151-
u = <uint64_t>hton.unpack_int64(self._data + 8)
152-
i64_to_hex(u, out + 20)
153-
154-
memcpy(out + 14, out + 12, 4)
155-
memcpy(out + 9, out + 8, 4)
156-
memcpy(out + 19, out + 20, 4)
157-
out[8] = b'-'
158-
out[13] = b'-'
159-
out[18] = b'-'
160-
out[23] = b'-'
161-
167+
cdef char out[36]
168+
tohex.uuid_to_str(self._data, out)
162169
return cpythonx.PyUnicode_FromKindAndData(
163170
cpythonx.PyUnicode_1BYTE_KIND, <void*>out, 36)
164171

172+
@property
173+
def hex(self):
174+
cdef char out[32]
175+
tohex.uuid_to_hex(self._data, out)
176+
return cpythonx.PyUnicode_FromKindAndData(
177+
cpythonx.PyUnicode_1BYTE_KIND, <void*>out, 32)
178+
165179
def __repr__(self):
166180
return f"UUID('{self}')"
167181

@@ -171,42 +185,42 @@ cdef class UUID(__UUIDReplaceMe):
171185
def __eq__(self, other):
172186
if type(other) is UUID:
173187
return strncmp(self._data, (<UUID>other)._data, 16) == 0
174-
if isinstance(other, uuid.UUID):
188+
if isinstance(other, std_UUID):
175189
return self.int == other.int
176190
return NotImplemented
177191

178192
def __ne__(self, other):
179193
if type(other) is UUID:
180194
return strncmp(self._data, (<UUID>other)._data, 16) != 0
181-
if isinstance(other, uuid.UUID):
195+
if isinstance(other, std_UUID):
182196
return self.int != other.int
183197
return NotImplemented
184198

185199
def __lt__(self, other):
186200
if type(other) is UUID:
187201
return strncmp(self._data, (<UUID>other)._data, 16) < 0
188-
if isinstance(other, uuid.UUID):
202+
if isinstance(other, std_UUID):
189203
return self.int < other.int
190204
return NotImplemented
191205

192206
def __gt__(self, other):
193207
if type(other) is UUID:
194208
return strncmp(self._data, (<UUID>other)._data, 16) > 0
195-
if isinstance(other, uuid.UUID):
209+
if isinstance(other, std_UUID):
196210
return self.int > other.int
197211
return NotImplemented
198212

199213
def __le__(self, other):
200214
if type(other) is UUID:
201215
return strncmp(self._data, (<UUID>other)._data, 16) <= 0
202-
if isinstance(other, uuid.UUID):
216+
if isinstance(other, std_UUID):
203217
return self.int <= other.int
204218
return NotImplemented
205219

206220
def __ge__(self, other):
207221
if type(other) is UUID:
208222
return strncmp(self._data, (<UUID>other)._data, 16) >= 0
209-
if isinstance(other, uuid.UUID):
223+
if isinstance(other, std_UUID):
210224
return self.int >= other.int
211225
return NotImplemented
212226

@@ -268,20 +282,6 @@ cdef class UUID(__UUIDReplaceMe):
268282
def node(self):
269283
return self.int & 0xffffffffffff
270284

271-
@property
272-
def hex(self):
273-
cdef:
274-
uint64_t u
275-
char out[32]
276-
277-
u = <uint64_t>hton.unpack_int64(self._data)
278-
i64_to_hex(u, out)
279-
u = <uint64_t>hton.unpack_int64(self._data + 8)
280-
i64_to_hex(u, out + 16)
281-
282-
return cpythonx.PyUnicode_FromKindAndData(
283-
cpythonx.PyUnicode_1BYTE_KIND, <void*>out, 32)
284-
285285
@property
286286
def urn(self):
287287
return 'urn:uuid:' + str(self)
@@ -329,10 +329,10 @@ cdef class UUID(__UUIDReplaceMe):
329329
#
330330
assert UUID.__bases__[0] is __UUIDReplaceMe
331331
assert UUID.__mro__[1] is __UUIDReplaceMe
332-
cpython.Py_INCREF(uuid.UUID)
333-
cpython.PyTuple_SET_ITEM(UUID.__bases__, 0, uuid.UUID)
334-
cpython.Py_INCREF(uuid.UUID)
335-
cpython.PyTuple_SET_ITEM(UUID.__mro__, 1, uuid.UUID)
332+
cpython.Py_INCREF(std_UUID)
333+
cpython.PyTuple_SET_ITEM(UUID.__bases__, 0, std_UUID)
334+
cpython.Py_INCREF(std_UUID)
335+
cpython.PyTuple_SET_ITEM(UUID.__mro__, 1, std_UUID)
336336
# </hack>
337337

338338

0 commit comments

Comments
 (0)