ulid-transform 1.0.1__cp313-cp313-musllinux_1_2_i686.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ulid_transform/__init__.py +41 -0
- ulid_transform/__init__.pyi +10 -0
- ulid_transform/_py_ulid_impl.py +454 -0
- ulid_transform/_ulid_impl.cpython-313-i386-linux-musl.so +0 -0
- ulid_transform/_ulid_impl.pyx +136 -0
- ulid_transform/py.typed +0 -0
- ulid_transform/ulid.hh +17 -0
- ulid_transform/ulid_struct.hh +716 -0
- ulid_transform/ulid_uint128.hh +555 -0
- ulid_transform/ulid_wrapper.cpp +94 -0
- ulid_transform/ulid_wrapper.h +13 -0
- ulid_transform-1.0.1.dist-info/LICENSE +22 -0
- ulid_transform-1.0.1.dist-info/METADATA +107 -0
- ulid_transform-1.0.1.dist-info/RECORD +17 -0
- ulid_transform-1.0.1.dist-info/WHEEL +4 -0
- ulid_transform.libs/libgcc_s-f3fb5a36.so.1 +0 -0
- ulid_transform.libs/libstdc++-d2a021ba.so.6.0.32 +0 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
__version__ = "1.0.1"
|
2
|
+
|
3
|
+
try:
|
4
|
+
from ._ulid_impl import (
|
5
|
+
bytes_to_ulid,
|
6
|
+
bytes_to_ulid_or_none,
|
7
|
+
ulid_at_time,
|
8
|
+
ulid_at_time_bytes,
|
9
|
+
ulid_hex,
|
10
|
+
ulid_now,
|
11
|
+
ulid_now_bytes,
|
12
|
+
ulid_to_bytes,
|
13
|
+
ulid_to_bytes_or_none,
|
14
|
+
ulid_to_timestamp,
|
15
|
+
)
|
16
|
+
except ImportError:
|
17
|
+
from ._py_ulid_impl import (
|
18
|
+
bytes_to_ulid,
|
19
|
+
bytes_to_ulid_or_none,
|
20
|
+
ulid_at_time,
|
21
|
+
ulid_at_time_bytes,
|
22
|
+
ulid_hex,
|
23
|
+
ulid_now,
|
24
|
+
ulid_now_bytes,
|
25
|
+
ulid_to_bytes,
|
26
|
+
ulid_to_bytes_or_none,
|
27
|
+
ulid_to_timestamp,
|
28
|
+
)
|
29
|
+
|
30
|
+
__all__ = [
|
31
|
+
"bytes_to_ulid",
|
32
|
+
"bytes_to_ulid_or_none",
|
33
|
+
"ulid_at_time",
|
34
|
+
"ulid_at_time_bytes",
|
35
|
+
"ulid_hex",
|
36
|
+
"ulid_now",
|
37
|
+
"ulid_now_bytes",
|
38
|
+
"ulid_to_bytes",
|
39
|
+
"ulid_to_bytes_or_none",
|
40
|
+
"ulid_to_timestamp",
|
41
|
+
]
|
@@ -0,0 +1,10 @@
|
|
1
|
+
def ulid_hex() -> str: ...
|
2
|
+
def ulid_at_time_bytes(timestamp: float) -> bytes: ...
|
3
|
+
def ulid_now_bytes() -> bytes: ...
|
4
|
+
def ulid_now() -> str: ...
|
5
|
+
def ulid_at_time(timestamp: float) -> str: ...
|
6
|
+
def ulid_to_bytes(value: str) -> bytes: ...
|
7
|
+
def bytes_to_ulid(value: bytes) -> str: ...
|
8
|
+
def ulid_to_bytes_or_none(ulid: str | None) -> bytes | None: ...
|
9
|
+
def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None: ...
|
10
|
+
def ulid_to_timestamp(ulid: str | bytes) -> int: ...
|
@@ -0,0 +1,454 @@
|
|
1
|
+
import array
|
2
|
+
from random import getrandbits
|
3
|
+
from time import time
|
4
|
+
|
5
|
+
# From https://github.com/ahawker/ulid/blob/06289583e9de4286b4d80b4ad000d137816502ca/ulid/base32.py#L102
|
6
|
+
#: Array that maps encoded string char byte values to enable O(1) lookups.
|
7
|
+
_DECODE = array.array(
|
8
|
+
"B",
|
9
|
+
(
|
10
|
+
0xFF,
|
11
|
+
0xFF,
|
12
|
+
0xFF,
|
13
|
+
0xFF,
|
14
|
+
0xFF,
|
15
|
+
0xFF,
|
16
|
+
0xFF,
|
17
|
+
0xFF,
|
18
|
+
0xFF,
|
19
|
+
0xFF,
|
20
|
+
0xFF,
|
21
|
+
0xFF,
|
22
|
+
0xFF,
|
23
|
+
0xFF,
|
24
|
+
0xFF,
|
25
|
+
0xFF,
|
26
|
+
0xFF,
|
27
|
+
0xFF,
|
28
|
+
0xFF,
|
29
|
+
0xFF,
|
30
|
+
0xFF,
|
31
|
+
0xFF,
|
32
|
+
0xFF,
|
33
|
+
0xFF,
|
34
|
+
0xFF,
|
35
|
+
0xFF,
|
36
|
+
0xFF,
|
37
|
+
0xFF,
|
38
|
+
0xFF,
|
39
|
+
0xFF,
|
40
|
+
0xFF,
|
41
|
+
0xFF,
|
42
|
+
0xFF,
|
43
|
+
0xFF,
|
44
|
+
0xFF,
|
45
|
+
0xFF,
|
46
|
+
0xFF,
|
47
|
+
0xFF,
|
48
|
+
0xFF,
|
49
|
+
0xFF,
|
50
|
+
0xFF,
|
51
|
+
0xFF,
|
52
|
+
0xFF,
|
53
|
+
0xFF,
|
54
|
+
0xFF,
|
55
|
+
0xFF,
|
56
|
+
0xFF,
|
57
|
+
0xFF,
|
58
|
+
0x00,
|
59
|
+
0x01,
|
60
|
+
0x02,
|
61
|
+
0x03,
|
62
|
+
0x04,
|
63
|
+
0x05,
|
64
|
+
0x06,
|
65
|
+
0x07,
|
66
|
+
0x08,
|
67
|
+
0x09,
|
68
|
+
0xFF,
|
69
|
+
0xFF,
|
70
|
+
0xFF,
|
71
|
+
0xFF,
|
72
|
+
0xFF,
|
73
|
+
0xFF,
|
74
|
+
0xFF,
|
75
|
+
0x0A,
|
76
|
+
0x0B,
|
77
|
+
0x0C,
|
78
|
+
0x0D,
|
79
|
+
0x0E,
|
80
|
+
0x0F,
|
81
|
+
0x10,
|
82
|
+
0x11,
|
83
|
+
0x01,
|
84
|
+
0x12,
|
85
|
+
0x13,
|
86
|
+
0x01,
|
87
|
+
0x14,
|
88
|
+
0x15,
|
89
|
+
0x00,
|
90
|
+
0x16,
|
91
|
+
0x17,
|
92
|
+
0x18,
|
93
|
+
0x19,
|
94
|
+
0x1A,
|
95
|
+
0xFF,
|
96
|
+
0x1B,
|
97
|
+
0x1C,
|
98
|
+
0x1D,
|
99
|
+
0x1E,
|
100
|
+
0x1F,
|
101
|
+
0xFF,
|
102
|
+
0xFF,
|
103
|
+
0xFF,
|
104
|
+
0xFF,
|
105
|
+
0xFF,
|
106
|
+
0xFF,
|
107
|
+
0x0A,
|
108
|
+
0x0B,
|
109
|
+
0x0C,
|
110
|
+
0x0D,
|
111
|
+
0x0E,
|
112
|
+
0x0F,
|
113
|
+
0x10,
|
114
|
+
0x11,
|
115
|
+
0x01,
|
116
|
+
0x12,
|
117
|
+
0x13,
|
118
|
+
0x01,
|
119
|
+
0x14,
|
120
|
+
0x15,
|
121
|
+
0x00,
|
122
|
+
0x16,
|
123
|
+
0x17,
|
124
|
+
0x18,
|
125
|
+
0x19,
|
126
|
+
0x1A,
|
127
|
+
0xFF,
|
128
|
+
0x1B,
|
129
|
+
0x1C,
|
130
|
+
0x1D,
|
131
|
+
0x1E,
|
132
|
+
0x1F,
|
133
|
+
0xFF,
|
134
|
+
0xFF,
|
135
|
+
0xFF,
|
136
|
+
0xFF,
|
137
|
+
0xFF,
|
138
|
+
0xFF,
|
139
|
+
0xFF,
|
140
|
+
0xFF,
|
141
|
+
0xFF,
|
142
|
+
0xFF,
|
143
|
+
0xFF,
|
144
|
+
0xFF,
|
145
|
+
0xFF,
|
146
|
+
0xFF,
|
147
|
+
0xFF,
|
148
|
+
0xFF,
|
149
|
+
0xFF,
|
150
|
+
0xFF,
|
151
|
+
0xFF,
|
152
|
+
0xFF,
|
153
|
+
0xFF,
|
154
|
+
0xFF,
|
155
|
+
0xFF,
|
156
|
+
0xFF,
|
157
|
+
0xFF,
|
158
|
+
0xFF,
|
159
|
+
0xFF,
|
160
|
+
0xFF,
|
161
|
+
0xFF,
|
162
|
+
0xFF,
|
163
|
+
0xFF,
|
164
|
+
0xFF,
|
165
|
+
0xFF,
|
166
|
+
0xFF,
|
167
|
+
0xFF,
|
168
|
+
0xFF,
|
169
|
+
0xFF,
|
170
|
+
0xFF,
|
171
|
+
0xFF,
|
172
|
+
0xFF,
|
173
|
+
0xFF,
|
174
|
+
0xFF,
|
175
|
+
0xFF,
|
176
|
+
0xFF,
|
177
|
+
0xFF,
|
178
|
+
0xFF,
|
179
|
+
0xFF,
|
180
|
+
0xFF,
|
181
|
+
0xFF,
|
182
|
+
0xFF,
|
183
|
+
0xFF,
|
184
|
+
0xFF,
|
185
|
+
0xFF,
|
186
|
+
0xFF,
|
187
|
+
0xFF,
|
188
|
+
0xFF,
|
189
|
+
0xFF,
|
190
|
+
0xFF,
|
191
|
+
0xFF,
|
192
|
+
0xFF,
|
193
|
+
0xFF,
|
194
|
+
0xFF,
|
195
|
+
0xFF,
|
196
|
+
0xFF,
|
197
|
+
0xFF,
|
198
|
+
0xFF,
|
199
|
+
0xFF,
|
200
|
+
0xFF,
|
201
|
+
0xFF,
|
202
|
+
0xFF,
|
203
|
+
0xFF,
|
204
|
+
0xFF,
|
205
|
+
0xFF,
|
206
|
+
0xFF,
|
207
|
+
0xFF,
|
208
|
+
0xFF,
|
209
|
+
0xFF,
|
210
|
+
0xFF,
|
211
|
+
0xFF,
|
212
|
+
0xFF,
|
213
|
+
0xFF,
|
214
|
+
0xFF,
|
215
|
+
0xFF,
|
216
|
+
0xFF,
|
217
|
+
0xFF,
|
218
|
+
0xFF,
|
219
|
+
0xFF,
|
220
|
+
0xFF,
|
221
|
+
0xFF,
|
222
|
+
0xFF,
|
223
|
+
0xFF,
|
224
|
+
0xFF,
|
225
|
+
0xFF,
|
226
|
+
0xFF,
|
227
|
+
0xFF,
|
228
|
+
0xFF,
|
229
|
+
0xFF,
|
230
|
+
0xFF,
|
231
|
+
0xFF,
|
232
|
+
0xFF,
|
233
|
+
0xFF,
|
234
|
+
0xFF,
|
235
|
+
0xFF,
|
236
|
+
0xFF,
|
237
|
+
0xFF,
|
238
|
+
0xFF,
|
239
|
+
0xFF,
|
240
|
+
0xFF,
|
241
|
+
0xFF,
|
242
|
+
0xFF,
|
243
|
+
0xFF,
|
244
|
+
0xFF,
|
245
|
+
0xFF,
|
246
|
+
0xFF,
|
247
|
+
0xFF,
|
248
|
+
0xFF,
|
249
|
+
0xFF,
|
250
|
+
0xFF,
|
251
|
+
0xFF,
|
252
|
+
0xFF,
|
253
|
+
0xFF,
|
254
|
+
0xFF,
|
255
|
+
0xFF,
|
256
|
+
0xFF,
|
257
|
+
0xFF,
|
258
|
+
0xFF,
|
259
|
+
0xFF,
|
260
|
+
0xFF,
|
261
|
+
0xFF,
|
262
|
+
0xFF,
|
263
|
+
0xFF,
|
264
|
+
0xFF,
|
265
|
+
0xFF,
|
266
|
+
),
|
267
|
+
)
|
268
|
+
|
269
|
+
|
270
|
+
def ulid_hex() -> str:
|
271
|
+
"""Generate a ULID in lowercase hex that will work for a UUID.
|
272
|
+
|
273
|
+
This ulid should not be used for cryptographically secure
|
274
|
+
operations.
|
275
|
+
|
276
|
+
This string can be converted with https://github.com/ahawker/ulid
|
277
|
+
|
278
|
+
ulid.from_uuid(uuid.UUID(ulid_hex))
|
279
|
+
"""
|
280
|
+
return f"{int(time()*1000):012x}{getrandbits(80):020x}"
|
281
|
+
|
282
|
+
|
283
|
+
def ulid_at_time_bytes(timestamp: float) -> bytes:
|
284
|
+
"""Generate an ULID as 16 bytes that will work for a UUID.
|
285
|
+
|
286
|
+
uuid.UUID(bytes=ulid_bytes)
|
287
|
+
"""
|
288
|
+
return int(timestamp * 1000).to_bytes(6, byteorder="big") + int(
|
289
|
+
getrandbits(80)
|
290
|
+
).to_bytes(10, byteorder="big")
|
291
|
+
|
292
|
+
|
293
|
+
def ulid_now_bytes() -> bytes:
|
294
|
+
"""Generate an ULID as 16 bytes that will work for a UUID."""
|
295
|
+
return ulid_at_time_bytes(time())
|
296
|
+
|
297
|
+
|
298
|
+
def ulid_now() -> str:
|
299
|
+
"""Generate a ULID."""
|
300
|
+
return ulid_at_time(time())
|
301
|
+
|
302
|
+
|
303
|
+
def ulid_at_time(timestamp: float) -> str:
|
304
|
+
"""Generate a ULID.
|
305
|
+
|
306
|
+
This ulid should not be used for cryptographically secure
|
307
|
+
operations.
|
308
|
+
|
309
|
+
01AN4Z07BY 79KA1307SR9X4MV3
|
310
|
+
|----------| |----------------|
|
311
|
+
Timestamp Randomness
|
312
|
+
48bits 80bits
|
313
|
+
|
314
|
+
This string can be loaded directly with https://github.com/ahawker/ulid
|
315
|
+
|
316
|
+
import ulid_transform as ulid_util
|
317
|
+
import ulid
|
318
|
+
ulid.parse(ulid_util.ulid())
|
319
|
+
"""
|
320
|
+
return _encode(ulid_at_time_bytes(timestamp))
|
321
|
+
|
322
|
+
|
323
|
+
def _encode(ulid_bytes: bytes) -> str:
|
324
|
+
# This is base32 crockford encoding with the loop unrolled for performance
|
325
|
+
#
|
326
|
+
# This code is adapted from:
|
327
|
+
# https://github.com/ahawker/ulid/blob/06289583e9de4286b4d80b4ad000d137816502ca/ulid/base32.py#L102
|
328
|
+
#
|
329
|
+
enc = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
330
|
+
return (
|
331
|
+
enc[(ulid_bytes[0] & 224) >> 5]
|
332
|
+
+ enc[ulid_bytes[0] & 31]
|
333
|
+
+ enc[(ulid_bytes[1] & 248) >> 3]
|
334
|
+
+ enc[((ulid_bytes[1] & 7) << 2) | ((ulid_bytes[2] & 192) >> 6)]
|
335
|
+
+ enc[((ulid_bytes[2] & 62) >> 1)]
|
336
|
+
+ enc[((ulid_bytes[2] & 1) << 4) | ((ulid_bytes[3] & 240) >> 4)]
|
337
|
+
+ enc[((ulid_bytes[3] & 15) << 1) | ((ulid_bytes[4] & 128) >> 7)]
|
338
|
+
+ enc[(ulid_bytes[4] & 124) >> 2]
|
339
|
+
+ enc[((ulid_bytes[4] & 3) << 3) | ((ulid_bytes[5] & 224) >> 5)]
|
340
|
+
+ enc[ulid_bytes[5] & 31]
|
341
|
+
+ enc[(ulid_bytes[6] & 248) >> 3]
|
342
|
+
+ enc[((ulid_bytes[6] & 7) << 2) | ((ulid_bytes[7] & 192) >> 6)]
|
343
|
+
+ enc[(ulid_bytes[7] & 62) >> 1]
|
344
|
+
+ enc[((ulid_bytes[7] & 1) << 4) | ((ulid_bytes[8] & 240) >> 4)]
|
345
|
+
+ enc[((ulid_bytes[8] & 15) << 1) | ((ulid_bytes[9] & 128) >> 7)]
|
346
|
+
+ enc[(ulid_bytes[9] & 124) >> 2]
|
347
|
+
+ enc[((ulid_bytes[9] & 3) << 3) | ((ulid_bytes[10] & 224) >> 5)]
|
348
|
+
+ enc[ulid_bytes[10] & 31]
|
349
|
+
+ enc[(ulid_bytes[11] & 248) >> 3]
|
350
|
+
+ enc[((ulid_bytes[11] & 7) << 2) | ((ulid_bytes[12] & 192) >> 6)]
|
351
|
+
+ enc[(ulid_bytes[12] & 62) >> 1]
|
352
|
+
+ enc[((ulid_bytes[12] & 1) << 4) | ((ulid_bytes[13] & 240) >> 4)]
|
353
|
+
+ enc[((ulid_bytes[13] & 15) << 1) | ((ulid_bytes[14] & 128) >> 7)]
|
354
|
+
+ enc[(ulid_bytes[14] & 124) >> 2]
|
355
|
+
+ enc[((ulid_bytes[14] & 3) << 3) | ((ulid_bytes[15] & 224) >> 5)]
|
356
|
+
+ enc[ulid_bytes[15] & 31]
|
357
|
+
)
|
358
|
+
|
359
|
+
|
360
|
+
def ulid_to_bytes(value: str) -> bytes:
|
361
|
+
"""Decode a ulid to bytes."""
|
362
|
+
if len(value) != 26:
|
363
|
+
raise ValueError(f"ULID must be a 26 character string: {value}")
|
364
|
+
encoded = value.encode("ascii")
|
365
|
+
decoding = _DECODE
|
366
|
+
return bytes(
|
367
|
+
(
|
368
|
+
((decoding[encoded[0]] << 5) | decoding[encoded[1]]) & 0xFF,
|
369
|
+
((decoding[encoded[2]] << 3) | (decoding[encoded[3]] >> 2)) & 0xFF,
|
370
|
+
(
|
371
|
+
(decoding[encoded[3]] << 6)
|
372
|
+
| (decoding[encoded[4]] << 1)
|
373
|
+
| (decoding[encoded[5]] >> 4)
|
374
|
+
)
|
375
|
+
& 0xFF,
|
376
|
+
((decoding[encoded[5]] << 4) | (decoding[encoded[6]] >> 1)) & 0xFF,
|
377
|
+
(
|
378
|
+
(decoding[encoded[6]] << 7)
|
379
|
+
| (decoding[encoded[7]] << 2)
|
380
|
+
| (decoding[encoded[8]] >> 3)
|
381
|
+
)
|
382
|
+
& 0xFF,
|
383
|
+
((decoding[encoded[8]] << 5) | (decoding[encoded[9]])) & 0xFF,
|
384
|
+
((decoding[encoded[10]] << 3) | (decoding[encoded[11]] >> 2)) & 0xFF,
|
385
|
+
(
|
386
|
+
(decoding[encoded[11]] << 6)
|
387
|
+
| (decoding[encoded[12]] << 1)
|
388
|
+
| (decoding[encoded[13]] >> 4)
|
389
|
+
)
|
390
|
+
& 0xFF,
|
391
|
+
((decoding[encoded[13]] << 4) | (decoding[encoded[14]] >> 1)) & 0xFF,
|
392
|
+
(
|
393
|
+
(decoding[encoded[14]] << 7)
|
394
|
+
| (decoding[encoded[15]] << 2)
|
395
|
+
| (decoding[encoded[16]] >> 3)
|
396
|
+
)
|
397
|
+
& 0xFF,
|
398
|
+
((decoding[encoded[16]] << 5) | (decoding[encoded[17]])) & 0xFF,
|
399
|
+
((decoding[encoded[18]] << 3) | (decoding[encoded[19]] >> 2)) & 0xFF,
|
400
|
+
(
|
401
|
+
(decoding[encoded[19]] << 6)
|
402
|
+
| (decoding[encoded[20]] << 1)
|
403
|
+
| (decoding[encoded[21]] >> 4)
|
404
|
+
)
|
405
|
+
& 0xFF,
|
406
|
+
((decoding[encoded[21]] << 4) | (decoding[encoded[22]] >> 1)) & 0xFF,
|
407
|
+
(
|
408
|
+
(decoding[encoded[22]] << 7)
|
409
|
+
| (decoding[encoded[23]] << 2)
|
410
|
+
| (decoding[encoded[24]] >> 3)
|
411
|
+
)
|
412
|
+
& 0xFF,
|
413
|
+
((decoding[encoded[24]] << 5) | (decoding[encoded[25]])) & 0xFF,
|
414
|
+
)
|
415
|
+
)
|
416
|
+
|
417
|
+
|
418
|
+
def bytes_to_ulid(value: bytes) -> str:
|
419
|
+
"""Encode bytes to a ulid."""
|
420
|
+
if len(value) != 16:
|
421
|
+
raise ValueError(f"ULID bytes must be 16 bytes: {value!r}")
|
422
|
+
return _encode(value)
|
423
|
+
|
424
|
+
|
425
|
+
def ulid_to_bytes_or_none(ulid: str | None) -> bytes | None:
|
426
|
+
"""Convert an ulid to bytes."""
|
427
|
+
if ulid is None:
|
428
|
+
return None
|
429
|
+
try:
|
430
|
+
return ulid_to_bytes(ulid)
|
431
|
+
except ValueError:
|
432
|
+
return None
|
433
|
+
|
434
|
+
|
435
|
+
def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None:
|
436
|
+
"""Convert bytes to a ulid."""
|
437
|
+
if ulid_bytes is None:
|
438
|
+
return None
|
439
|
+
try:
|
440
|
+
return bytes_to_ulid(ulid_bytes)
|
441
|
+
except ValueError:
|
442
|
+
return None
|
443
|
+
|
444
|
+
|
445
|
+
def ulid_to_timestamp(ulid: str | bytes) -> int:
|
446
|
+
"""
|
447
|
+
Get the timestamp from a ULID.
|
448
|
+
The returned value is in milliseconds since the UNIX epoch.
|
449
|
+
"""
|
450
|
+
if not isinstance(ulid, bytes):
|
451
|
+
ulid_bytes = ulid_to_bytes(ulid)
|
452
|
+
else:
|
453
|
+
ulid_bytes = ulid
|
454
|
+
return int.from_bytes(b"\x00\x00" + ulid_bytes[:6], "big")
|
Binary file
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# distutils: language = c++
|
2
|
+
# cython: language_level=3, c_string_type=str, c_string_encoding=ascii
|
3
|
+
|
4
|
+
# The `<bytes>xxx[:N]` syntax is required for two reasons:
|
5
|
+
# 1. When working with "ULID bytes", the buffer may contain NULs.
|
6
|
+
# 2. When working with ULID text, the buffer is exactly 26 bytes long and not NUL-terminated.
|
7
|
+
# See https://github.com/cython/cython/issues/3234
|
8
|
+
|
9
|
+
from libc.stdint cimport uint8_t, uint64_t
|
10
|
+
|
11
|
+
|
12
|
+
cdef extern from "ulid_wrapper.h":
|
13
|
+
void _cpp_ulid(char dst[26]) nogil
|
14
|
+
void _cpp_ulid_bytes(uint8_t dst[16]) nogil
|
15
|
+
void _cpp_ulid_at_time(double epoch_time, char dst[26]) nogil
|
16
|
+
void _cpp_ulid_at_time_bytes(double epoch_time, uint8_t dst[16]) nogil
|
17
|
+
void _cpp_ulid_to_bytes(const char ulid_string[26], uint8_t dst[16]) nogil
|
18
|
+
void _cpp_bytes_to_ulid(const uint8_t b[16], char * dst) nogil
|
19
|
+
void _cpp_hexlify_16(const uint8_t b[16], char dst[32]) nogil
|
20
|
+
uint64_t _cpp_bytes_to_timestamp(const uint8_t b[16]) nogil
|
21
|
+
|
22
|
+
|
23
|
+
def ulid_hex() -> str:
|
24
|
+
"""Generate a ULID in lowercase hex that will work for a UUID.
|
25
|
+
|
26
|
+
This ulid should not be used for cryptographically secure
|
27
|
+
operations.
|
28
|
+
|
29
|
+
This string can be converted with https://github.com/ahawker/ulid
|
30
|
+
|
31
|
+
ulid.from_uuid(uuid.UUID(ulid_hex))
|
32
|
+
"""
|
33
|
+
cdef unsigned char ulid_bytes_buf[16]
|
34
|
+
_cpp_ulid_bytes(ulid_bytes_buf)
|
35
|
+
cdef char ulid_hex_buf[32]
|
36
|
+
_cpp_hexlify_16(ulid_bytes_buf, ulid_hex_buf)
|
37
|
+
return <str>ulid_hex_buf[:32]
|
38
|
+
|
39
|
+
|
40
|
+
def ulid_now_bytes() -> bytes:
|
41
|
+
"""Generate an ULID as 16 bytes that will work for a UUID."""
|
42
|
+
cdef unsigned char ulid_bytes_buf[16]
|
43
|
+
_cpp_ulid_bytes(ulid_bytes_buf)
|
44
|
+
return <bytes>ulid_bytes_buf[:16]
|
45
|
+
|
46
|
+
|
47
|
+
def ulid_at_time_bytes(timestamp: float) -> bytes:
|
48
|
+
"""Generate an ULID as 16 bytes that will work for a UUID.
|
49
|
+
|
50
|
+
uuid.UUID(bytes=ulid_bytes)
|
51
|
+
"""
|
52
|
+
cdef unsigned char ulid_bytes_buf[16]
|
53
|
+
_cpp_ulid_at_time_bytes(timestamp, ulid_bytes_buf)
|
54
|
+
return <bytes>ulid_bytes_buf[:16]
|
55
|
+
|
56
|
+
|
57
|
+
def ulid_now() -> str:
|
58
|
+
"""Generate a ULID."""
|
59
|
+
cdef char ulid_text_buf[26]
|
60
|
+
_cpp_ulid(ulid_text_buf)
|
61
|
+
return <str>ulid_text_buf[:26]
|
62
|
+
|
63
|
+
|
64
|
+
def ulid_at_time(timestamp: float) -> str:
|
65
|
+
"""Generate a ULID.
|
66
|
+
|
67
|
+
This ulid should not be used for cryptographically secure
|
68
|
+
operations.
|
69
|
+
|
70
|
+
01AN4Z07BY 79KA1307SR9X4MV3
|
71
|
+
|----------| |----------------|
|
72
|
+
Timestamp Randomness
|
73
|
+
48bits 80bits
|
74
|
+
|
75
|
+
This string can be loaded directly with https://github.com/ahawker/ulid
|
76
|
+
|
77
|
+
import ulid_transform as ulid_util
|
78
|
+
import ulid
|
79
|
+
ulid.parse(ulid_util.ulid())
|
80
|
+
"""
|
81
|
+
cdef char ulid_text_buf[26]
|
82
|
+
_cpp_ulid_at_time(timestamp, ulid_text_buf)
|
83
|
+
return <str>ulid_text_buf[:26]
|
84
|
+
|
85
|
+
|
86
|
+
def ulid_to_bytes(value: str) -> bytes:
|
87
|
+
"""Decode a ulid to bytes."""
|
88
|
+
if len(value) != 26:
|
89
|
+
raise ValueError(f"ULID must be a 26 character string: {value}")
|
90
|
+
cdef unsigned char ulid_bytes_buf[16]
|
91
|
+
_cpp_ulid_to_bytes(value, ulid_bytes_buf)
|
92
|
+
return <bytes>ulid_bytes_buf[:16]
|
93
|
+
|
94
|
+
|
95
|
+
def bytes_to_ulid(value: bytes) -> str:
|
96
|
+
"""Encode bytes to a ulid."""
|
97
|
+
if len(value) != 16:
|
98
|
+
raise ValueError(f"ULID bytes must be 16 bytes: {value!r}")
|
99
|
+
cdef char ulid_text_buf[26]
|
100
|
+
_cpp_bytes_to_ulid(value, ulid_text_buf)
|
101
|
+
return <str>ulid_text_buf[:26]
|
102
|
+
|
103
|
+
|
104
|
+
def ulid_to_bytes_or_none(ulid: str | None) -> bytes | None:
|
105
|
+
"""Convert an ulid to bytes."""
|
106
|
+
if ulid is None or len(ulid) != 26:
|
107
|
+
return None
|
108
|
+
cdef unsigned char ulid_bytes_buf[16]
|
109
|
+
_cpp_ulid_to_bytes(ulid, ulid_bytes_buf)
|
110
|
+
return <bytes>ulid_bytes_buf[:16]
|
111
|
+
|
112
|
+
|
113
|
+
def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None:
|
114
|
+
"""Convert bytes to a ulid."""
|
115
|
+
if ulid_bytes is None or len(ulid_bytes) != 16:
|
116
|
+
return None
|
117
|
+
cdef char ulid_text_buf[26]
|
118
|
+
_cpp_bytes_to_ulid(ulid_bytes, ulid_text_buf)
|
119
|
+
return <str>ulid_text_buf[:26]
|
120
|
+
|
121
|
+
|
122
|
+
def ulid_to_timestamp(ulid: str | bytes) -> int:
|
123
|
+
"""
|
124
|
+
Get the timestamp from a ULID.
|
125
|
+
The returned value is in milliseconds since the UNIX epoch.
|
126
|
+
"""
|
127
|
+
cdef unsigned char ulid_bytes_buf[16]
|
128
|
+
if not isinstance(ulid, bytes):
|
129
|
+
if len(ulid) != 26:
|
130
|
+
raise ValueError(f"ULID must be a 26 character string: {ulid}")
|
131
|
+
_cpp_ulid_to_bytes(ulid, ulid_bytes_buf)
|
132
|
+
return _cpp_bytes_to_timestamp(ulid_bytes_buf)
|
133
|
+
else:
|
134
|
+
if len(ulid) != 16:
|
135
|
+
raise ValueError(f"ULID bytes must be 16 bytes: {ulid!r}")
|
136
|
+
return _cpp_bytes_to_timestamp(ulid)
|
ulid_transform/py.typed
ADDED
File without changes
|
ulid_transform/ulid.hh
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
// Originally from https://github.com/suyash/ulid
|
2
|
+
|
3
|
+
#ifndef ULID_HH
|
4
|
+
#define ULID_HH
|
5
|
+
|
6
|
+
// http://stackoverflow.com/a/23981011
|
7
|
+
#ifdef __SIZEOF_INT128__
|
8
|
+
#define ULIDUINT128
|
9
|
+
#endif
|
10
|
+
|
11
|
+
#ifdef ULIDUINT128
|
12
|
+
#include "ulid_uint128.hh"
|
13
|
+
#else
|
14
|
+
#include "ulid_struct.hh"
|
15
|
+
#endif // ULIDUINT128
|
16
|
+
|
17
|
+
#endif // ULID_HH
|