ulid-transform 0.10.2__cp312-cp312-macosx_14_0_arm64.whl → 0.13.1__cp312-cp312-macosx_14_0_arm64.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.
@@ -1,21 +1,41 @@
1
- __version__ = "0.10.2"
1
+ __version__ = "0.13.1"
2
2
 
3
- from .ulid_impl import (
4
- bytes_to_ulid,
5
- bytes_to_ulid_or_none,
6
- ulid_at_time,
7
- ulid_hex,
8
- ulid_now,
9
- ulid_to_bytes,
10
- ulid_to_bytes_or_none,
11
- )
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
+ )
12
29
 
13
30
  __all__ = [
14
- "ulid_now",
31
+ "bytes_to_ulid",
32
+ "bytes_to_ulid_or_none",
15
33
  "ulid_at_time",
34
+ "ulid_at_time_bytes",
16
35
  "ulid_hex",
36
+ "ulid_now",
37
+ "ulid_now_bytes",
17
38
  "ulid_to_bytes",
18
- "bytes_to_ulid",
19
39
  "ulid_to_bytes_or_none",
20
- "bytes_to_ulid_or_none",
40
+ "ulid_to_timestamp",
21
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: ...
@@ -280,6 +280,21 @@ def ulid_hex() -> str:
280
280
  return f"{int(time()*1000):012x}{getrandbits(80):020x}"
281
281
 
282
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
+
283
298
  def ulid_now() -> str:
284
299
  """Generate a ULID."""
285
300
  return ulid_at_time(time())
@@ -302,10 +317,7 @@ def ulid_at_time(timestamp: float) -> str:
302
317
  import ulid
303
318
  ulid.parse(ulid_util.ulid())
304
319
  """
305
- return _encode(
306
- int((timestamp) * 1000).to_bytes(6, byteorder="big")
307
- + int(getrandbits(80)).to_bytes(10, byteorder="big")
308
- )
320
+ return _encode(ulid_at_time_bytes(timestamp))
309
321
 
310
322
 
311
323
  def _encode(ulid_bytes: bytes) -> str:
@@ -420,34 +432,23 @@ def ulid_to_bytes_or_none(ulid: str | None) -> bytes | None:
420
432
  return None
421
433
 
422
434
 
423
- def bytes_to_ulid_or_none(_bytes: bytes | None) -> str | None:
435
+ def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None:
424
436
  """Convert bytes to a ulid."""
425
- if _bytes is None:
437
+ if ulid_bytes is None:
426
438
  return None
427
439
  try:
428
- return bytes_to_ulid(_bytes)
440
+ return bytes_to_ulid(ulid_bytes)
429
441
  except ValueError:
430
442
  return None
431
443
 
432
444
 
433
- try:
434
- from ._ulid_impl import ( # type: ignore[no-redef] # noqa: F811 F401 # pragma: no cover
435
- _bytes_to_ulid as bytes_to_ulid,
436
- )
437
- from ._ulid_impl import ( # type: ignore[no-redef] # noqa: F811 F401 # pragma: no cover
438
- _bytes_to_ulid_or_none as bytes_to_ulid_or_none,
439
- )
440
- from ._ulid_impl import ( # type: ignore[no-redef] # noqa: F811 F401 # pragma: no cover
441
- _ulid_at_time as ulid_at_time,
442
- )
443
- from ._ulid_impl import ( # type: ignore[no-redef] # noqa: F811 F401 # pragma: no cover
444
- _ulid_now as ulid_now,
445
- )
446
- from ._ulid_impl import ( # type: ignore[no-redef] # noqa: F811 F401 # pragma: no cover
447
- _ulid_to_bytes as ulid_to_bytes,
448
- )
449
- from ._ulid_impl import ( # type: ignore[no-redef] # noqa: F811 F401 # pragma: no cover
450
- _ulid_to_bytes_or_none as ulid_to_bytes_or_none,
451
- )
452
- except ImportError: # pragma: no cover
453
- pass # pragma: no cover
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")
@@ -1,41 +1,136 @@
1
1
  # distutils: language = c++
2
- from libcpp.string cimport string
2
+ # cython: language_level=3, c_string_type=str, c_string_encoding=ascii
3
3
 
4
- from time import time
5
- from typing import Optional
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
6
8
 
7
- import cython
9
+ from libc.stdint cimport uint8_t, uint64_t
8
10
 
9
11
 
10
12
  cdef extern from "ulid_wrapper.h":
11
- string _cpp_ulid_at_time(double timestamp)
12
- string _cpp_ulid_to_bytes(const char * ulid_string)
13
- string _cpp_ulid()
14
- string _cpp_bytes_to_ulid(string ulid_bytes)
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
15
21
 
16
22
 
17
- def _ulid_now() -> str:
18
- return _cpp_ulid().decode("ascii")
23
+ def ulid_hex() -> str:
24
+ """Generate a ULID in lowercase hex that will work for a UUID.
19
25
 
20
- def _ulid_at_time(_time: float) -> str:
21
- return _cpp_ulid_at_time(_time).decode("ascii")
26
+ This ulid should not be used for cryptographically secure
27
+ operations.
22
28
 
23
- def _ulid_to_bytes(ulid_str: str) -> bytes:
24
- if len(ulid_str) != 26:
25
- raise ValueError(f"ULID must be a 26 character string: {ulid_str}")
26
- return _cpp_ulid_to_bytes(ulid_str.encode("ascii"))
29
+ This string can be converted with https://github.com/ahawker/ulid
27
30
 
28
- def _bytes_to_ulid(ulid_bytes: bytes) -> str:
29
- if len(ulid_bytes) != 16:
30
- raise ValueError(f"ULID bytes must be 16 bytes: {ulid_bytes!r}")
31
- return _cpp_bytes_to_ulid(ulid_bytes).decode("ascii")
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]
32
38
 
33
- def _ulid_to_bytes_or_none(ulid_str: Optional[str]) -> Optional[bytes]:
34
- if ulid_str is None or len(ulid_str) != 26:
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:
35
107
  return None
36
- return _cpp_ulid_to_bytes(ulid_str.encode("ascii"))
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
+
37
112
 
38
- def _bytes_to_ulid_or_none(ulid_bytes: Optional[bytes]) -> Optional[str]:
113
+ def bytes_to_ulid_or_none(ulid_bytes: bytes | None) -> str | None:
114
+ """Convert bytes to a ulid."""
39
115
  if ulid_bytes is None or len(ulid_bytes) != 16:
40
116
  return None
41
- return _cpp_bytes_to_ulid(ulid_bytes).decode("ascii")
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)
@@ -1,32 +1,94 @@
1
1
  #include "ulid_wrapper.h"
2
2
  #include "ulid.hh"
3
3
 
4
- using namespace std;
4
+ /**
5
+ * Generate a new text ULID and write it to the provided buffer.
6
+ * The buffer is NOT null-terminated.
7
+ */
8
+ void _cpp_ulid(char dst[26]) {
9
+ ulid::ULID ulid;
10
+ ulid::EncodeTimeSystemClockNow(ulid);
11
+ ulid::EncodeEntropyRand(ulid);
12
+ ulid::MarshalTo(ulid, dst);
13
+ }
5
14
 
6
- std::string _cpp_ulid() {
15
+ /**
16
+ * Generate a new binary ULID and write it to the provided buffer.
17
+ */
18
+ void _cpp_ulid_bytes(uint8_t dst[16]) {
7
19
  ulid::ULID ulid;
8
20
  ulid::EncodeTimeSystemClockNow(ulid);
9
21
  ulid::EncodeEntropyRand(ulid);
10
- return ulid::Marshal(ulid);
22
+ ulid::MarshalBinaryTo(ulid, dst);
23
+ }
24
+
25
+ /**
26
+ * Generate a new text ULID at the provided epoch time and write it to the provided buffer.
27
+ * The buffer is NOT null-terminated.
28
+ */
29
+ void _cpp_ulid_at_time(double epoch_time, char dst[26]) {
30
+ ulid::ULID ulid;
31
+ ulid::EncodeTimestamp(static_cast<int64_t>(epoch_time*1000), ulid);
32
+ ulid::EncodeEntropyRand(ulid);
33
+ ulid::MarshalTo(ulid, dst);
11
34
  }
12
35
 
13
- std::string _cpp_ulid_at_time(double epoch_time) {
36
+ /**
37
+ * Generate a new binary ULID at the provided epoch time and write it to the provided buffer.
38
+ */
39
+ void _cpp_ulid_at_time_bytes(double epoch_time, uint8_t dst[16]) {
14
40
  ulid::ULID ulid;
15
41
  ulid::EncodeTimestamp(static_cast<int64_t>(epoch_time*1000), ulid);
16
42
  ulid::EncodeEntropyRand(ulid);
17
- return ulid::Marshal(ulid);
43
+ ulid::MarshalBinaryTo(ulid, dst);
18
44
  }
19
45
 
20
- std::string _cpp_ulid_to_bytes(const char * ulid_string) {
46
+ /**
47
+ * Convert a text ULID to a binary ULID.
48
+ * The buffer passed in must contain at least 26 bytes.
49
+ * Invalid data will result in undefined behavior.
50
+ */
51
+ void _cpp_ulid_to_bytes(const char * ulid_string, uint8_t dst[16]) {
21
52
  ulid::ULID ulid;
22
53
  ulid::UnmarshalFrom(ulid_string, ulid);
23
- std::vector<uint8_t> data = ulid::MarshalBinary(ulid);
24
- std::string str(reinterpret_cast<char *>(data.data()), data.size());
25
- return str;
54
+ ulid::MarshalBinaryTo(ulid, dst);
55
+ }
56
+
57
+ /**
58
+ * Convert a binary ULID to a text ULID.
59
+ * The buffer passed in must contain at least 16 bytes.
60
+ * The output buffer will NOT be null-terminated.
61
+ */
62
+ void _cpp_bytes_to_ulid(const uint8_t b[16], char dst[26]) {
63
+ ulid::ULID ulid;
64
+ ulid::UnmarshalBinaryFrom(b, ulid);
65
+ ulid::MarshalTo(ulid, dst);
66
+ }
67
+
68
+ /**
69
+ * Convert a buffer of exactly 16 bytes to 32 hex characters.
70
+ * The output buffer will NOT be null-terminated.
71
+ */
72
+ void _cpp_hexlify_16(const uint8_t b[16], char dst[32]) {
73
+ static const char hexdigits[17] = "0123456789abcdef";
74
+ int in_index, out_index;
75
+ for (in_index = out_index = 0; in_index < 16; in_index++) {
76
+ uint8_t c = b[in_index];
77
+ dst[out_index++] = hexdigits[c >> 4];
78
+ dst[out_index++] = hexdigits[c & 0x0f];
79
+ }
26
80
  }
27
81
 
28
- std::string _cpp_bytes_to_ulid(std::string bytes_string) {
29
- std::vector<uint8_t> data(bytes_string.begin(), bytes_string.end());
30
- ulid::ULID ulid = ulid::UnmarshalBinary(data);
31
- return ulid::Marshal(ulid);
82
+ /**
83
+ * Interpret the first 6 bytes of a binary ULID as a timestamp.
84
+ */
85
+ uint64_t _cpp_bytes_to_timestamp(const uint8_t b[16]) {
86
+ uint64_t timestamp = 0;
87
+ timestamp |= static_cast<uint64_t>(b[0]) << 40;
88
+ timestamp |= static_cast<uint64_t>(b[1]) << 32;
89
+ timestamp |= static_cast<uint64_t>(b[2]) << 24;
90
+ timestamp |= static_cast<uint64_t>(b[3]) << 16;
91
+ timestamp |= static_cast<uint64_t>(b[4]) << 8;
92
+ timestamp |= static_cast<uint64_t>(b[5]);
93
+ return timestamp;
32
94
  }
@@ -1,11 +1,13 @@
1
- #include <string>
2
-
3
1
  #ifndef ULID_WRAPPER_H
4
2
  #define ULID_WRAPPER_H
3
+ #include <stdint.h>
5
4
 
6
- std::string _cpp_ulid();
7
- std::string _cpp_ulid_at_time(double timestamp);
8
- std::string _cpp_ulid_to_bytes(const char * ulid_string);
9
- std::string _cpp_bytes_to_ulid(std::string bytes_string);
10
-
5
+ void _cpp_ulid(char dst[26]);
6
+ void _cpp_ulid_bytes(uint8_t dst[16]);
7
+ void _cpp_ulid_at_time(double epoch_time, char dst[26]);
8
+ void _cpp_ulid_at_time_bytes(double epoch_time, uint8_t dst[16]);
9
+ void _cpp_ulid_to_bytes(const char * ulid_string, uint8_t dst[16]);
10
+ void _cpp_bytes_to_ulid(const uint8_t b[16], char dst[26]);
11
+ void _cpp_hexlify_16(const uint8_t b[16], char dst[32]);
12
+ uint64_t _cpp_bytes_to_timestamp(const uint8_t b[16]);
11
13
  #endif
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ulid-transform
3
- Version: 0.10.2
3
+ Version: 0.13.1
4
4
  Summary: Create and transform ULIDs
5
5
  Home-page: https://github.com/bdraco/ulid-transform
6
6
  License: MIT
@@ -0,0 +1,15 @@
1
+ ulid_transform/_ulid_impl.pyx,sha256=C64Ca_ydLglYYadIVgrQvMMVKPbpc6nfaOHTFK66ORU,4441
2
+ ulid_transform/__init__.pyi,sha256=eRmwQvXnwgj1mhdVokznDZVwOl2Xr_5tEAertTjkI5s,468
3
+ ulid_transform/ulid.hh,sha256=cCMqxiPh9iw5hOxvu4czd2I6Rgnl7z9hc9c5a78qLLI,295
4
+ ulid_transform/__init__.py,sha256=hRTpkzXAE-Z3sy9NNxkssew2iau-8OrhEbz0pJ01skI,854
5
+ ulid_transform/ulid_uint128.hh,sha256=W_EkG8AmjhKdzkl6Wz3t5XY11sE7LgCqKBmhdxjjQ90,14815
6
+ ulid_transform/ulid_wrapper.cpp,sha256=om3LXOJRI1zksRm3p03hFecssOdHlW0GGo8VJ9j3fIE,2798
7
+ ulid_transform/_py_ulid_impl.py,sha256=4fiV3uTS9JI8mTKPGJDKaBQIB4R9jYRjTSSCmYID_5I,10224
8
+ ulid_transform/ulid_struct.hh,sha256=vPUTL0wOvm0YRtLbuVZCYLyDaFmTA_WdLASQ786n4FQ,19309
9
+ ulid_transform/_ulid_impl.cpython-312-darwin.so,sha256=Xj3DndA7pzIG9rMQ4iBihXY-T9fZltzdfZJBNGSB6Fg,103168
10
+ ulid_transform/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ ulid_transform/ulid_wrapper.h,sha256=uDDC1AY2BkyHRkXoKso_qHK7IVrmkyZwqE2g7JtYdRQ,506
12
+ ulid_transform-0.13.1.dist-info/RECORD,,
13
+ ulid_transform-0.13.1.dist-info/LICENSE,sha256=gieJjxNidEKH1VXLqbV9tuqnmcnqvMqt5xcmktkn5I4,1072
14
+ ulid_transform-0.13.1.dist-info/WHEEL,sha256=yhFWDruQeL16y7AZmR-YzCRfHXG6441RPQ3U5xtqaLo,106
15
+ ulid_transform-0.13.1.dist-info/METADATA,sha256=LMB1c85FCU4KgDAl5v_F5ikJuHNmGWHc5aXMOIYNuyY,5389