mal-packet-weaver
C++20 packet serialization/deserialization library.
Loading...
Searching...
No Matches
uuid.hpp
Go to the documentation of this file.
1// based on https://github.com/crashoz/uuid_v4
2
3#pragma once
11{
12
20 void inline m128itos(__m128i x, char *mem)
21 {
22 // Expand each byte in x to two bytes in res
23 // i.e. 0x12345678 -> 0x0102030405060708
24 // Then translate each byte to its hex ascii representation
25 // i.e. 0x0102030405060708 -> 0x3132333435363738
26 const __m256i mask = _mm256_set1_epi8(0x0F);
27 const __m256i add = _mm256_set1_epi8(0x06);
28 const __m256i alpha_mask = _mm256_set1_epi8(0x10);
29 const __m256i alpha_offset = _mm256_set1_epi8(0x57);
30
31 __m256i a = _mm256_castsi128_si256(x);
32 __m256i as = _mm256_srli_epi64(a, 4);
33 __m256i lo = _mm256_unpacklo_epi8(as, a);
34 __m128i hi = _mm256_castsi256_si128(_mm256_unpackhi_epi8(as, a));
35 __m256i c = _mm256_inserti128_si256(lo, hi, 1);
36 __m256i d = _mm256_and_si256(c, mask);
37 __m256i alpha = _mm256_slli_epi64(_mm256_and_si256(_mm256_add_epi8(d, add), alpha_mask), 3);
38 __m256i offset = _mm256_blendv_epi8(_mm256_slli_epi64(add, 3), alpha_offset, alpha);
39 __m256i res = _mm256_add_epi8(d, offset);
40
41 // Add dashes between blocks as specified in RFC-4122
42 // 8-4-4-4-12
43 const __m256i dash_shuffle = _mm256_set_epi32(0x0b0a0908, 0x07060504, 0x80030201, 0x00808080, 0x0d0c800b,
44 0x0a090880, 0x07060504, 0x03020100);
45 const __m256i dash =
46 _mm256_set_epi64x(0x0000000000000000ull, 0x2d000000002d0000ull, 0x00002d000000002d, 0x0000000000000000ull);
47
48 __m256i resd = _mm256_shuffle_epi8(res, dash_shuffle);
49 resd = _mm256_or_si256(resd, dash);
50
51 _mm256_storeu_si256((__m256i *)mem, endianness::betole256(resd));
52 *(uint16_t *)(mem + 16) = endianness::betole16(_mm256_extract_epi16(res, 7));
53 *(uint32_t *)(mem + 32) = endianness::betole32(_mm256_extract_epi32(res, 7));
54 }
55
63 __m128i inline stom128i(const char *mem)
64 {
65 // Remove dashes and pack hex ascii bytes in a 256-bits int
66 const __m256i dash_shuffle = _mm256_set_epi32(0x80808080, 0x0f0e0d0c, 0x0b0a0908, 0x06050403, 0x80800f0e,
67 0x0c0b0a09, 0x07060504, 0x03020100);
68
69 __m256i x = endianness::betole256(_mm256_loadu_si256((__m256i *)mem));
70 x = _mm256_shuffle_epi8(x, dash_shuffle);
71 x = _mm256_insert_epi16(x, endianness::betole16(*(uint16_t *)(mem + 16)), 7);
72 x = _mm256_insert_epi32(x, endianness::betole32(*(uint32_t *)(mem + 32)), 7);
73
74 // Build a mask to apply a different offset to alphas and digits
75 const __m256i sub = _mm256_set1_epi8(0x2F);
76 const __m256i mask = _mm256_set1_epi8(0x20);
77 const __m256i alpha_offset = _mm256_set1_epi8(0x28);
78 const __m256i digits_offset = _mm256_set1_epi8(0x01);
79 const __m256i unweave = _mm256_set_epi32(0x0f0d0b09, 0x0e0c0a08, 0x07050301, 0x06040200, 0x0f0d0b09, 0x0e0c0a08,
80 0x07050301, 0x06040200);
81 const __m256i shift = _mm256_set_epi32(0x00000000, 0x00000004, 0x00000000, 0x00000004, 0x00000000, 0x00000004,
82 0x00000000, 0x00000004);
83
84 // Translate ascii bytes to their value
85 // i.e. 0x3132333435363738 -> 0x0102030405060708
86 // Shift hi-digits
87 // i.e. 0x0102030405060708 -> 0x1002300450067008
88 // Horizontal add
89 // i.e. 0x1002300450067008 -> 0x12345678
90 __m256i a = _mm256_sub_epi8(x, sub);
91 __m256i alpha = _mm256_slli_epi64(_mm256_and_si256(a, mask), 2);
92 __m256i sub_mask = _mm256_blendv_epi8(digits_offset, alpha_offset, alpha);
93 a = _mm256_sub_epi8(a, sub_mask);
94 a = _mm256_shuffle_epi8(a, unweave);
95 a = _mm256_sllv_epi32(a, shift);
96 a = _mm256_hadd_epi32(a, _mm256_setzero_si256());
97 a = _mm256_permute4x64_epi64(a, 0b00001000);
98
99 return _mm256_castsi256_si128(a);
100 }
101
105 class UUID
106 {
107 public:
111 UUID() {}
112
113 UUID(const UUID &other)
114 {
115 __m128i x = _mm_load_si128((__m128i *)other.data);
116 _mm_store_si128((__m128i *)data, x);
117 }
118
122 UUID(__m128i uuid) { _mm_store_si128((__m128i *)data, uuid); }
123
127 UUID(uint64_t x, uint64_t y)
128 {
129 __m128i z = _mm_set_epi64x(x, y);
130 _mm_store_si128((__m128i *)data, z);
131 }
132
136 UUID(const uint8_t *bytes)
137 {
138 __m128i x = _mm_loadu_si128((__m128i *)bytes);
139 _mm_store_si128((__m128i *)data, x);
140 }
141
145 explicit UUID(const std::string &bytes)
146 {
147 __m128i x = endianness::betole128(_mm_loadu_si128((__m128i *)bytes.data()));
148 _mm_store_si128((__m128i *)data, x);
149 }
150
154 static UUID fromStrFactory(const std::string &s) { return fromStrFactory(s.c_str()); }
155
156 static UUID fromStrFactory(const char *raw) { return UUID(stom128i(raw)); }
157
158 void fromStr(const char *raw) { _mm_store_si128((__m128i *)data, stom128i(raw)); }
159
163 UUID &operator=(const UUID &other)
164 {
165 if (&other == this)
166 {
167 return *this;
168 }
169 __m128i x = _mm_load_si128((__m128i *)other.data);
170 _mm_store_si128((__m128i *)data, x);
171 return *this;
172 }
173
177 friend bool operator==(const UUID &lhs, const UUID &rhs)
178 {
179 __m128i x = _mm_load_si128((__m128i *)lhs.data);
180 __m128i y = _mm_load_si128((__m128i *)rhs.data);
181
182 __m128i neq = _mm_xor_si128(x, y);
183 return _mm_test_all_zeros(neq, neq);
184 }
185
189 friend bool operator<(const UUID &lhs, const UUID &rhs)
190 {
191 // There are no trivial 128-bits comparisons in SSE/AVX
192 // It's faster to compare two uint64_t
193 uint64_t *x = (uint64_t *)lhs.data;
194 uint64_t *y = (uint64_t *)rhs.data;
195 return *x < *y || (*x == *y && *(x + 1) < *(y + 1));
196 }
197
198 friend bool operator!=(const UUID &lhs, const UUID &rhs) { return !(lhs == rhs); }
199 friend bool operator>(const UUID &lhs, const UUID &rhs) { return rhs < lhs; }
200 friend bool operator<=(const UUID &lhs, const UUID &rhs) { return !(lhs > rhs); }
201 friend bool operator>=(const UUID &lhs, const UUID &rhs) { return !(lhs < rhs); }
202
206 std::string bytes() const
207 {
208 std::string mem;
209 bytes(mem);
210 return mem;
211 }
212
213 void bytes(std::string &out) const
214 {
215 out.resize(sizeof(data));
216 bytes((char *)out.data());
217 }
218
219 void bytes(char *bytes) const
220 {
221 __m128i x = endianness::betole128(_mm_load_si128((__m128i *)data));
222 _mm_storeu_si128((__m128i *)bytes, x);
223 }
224
228 std::string str() const
229 {
230 std::string mem;
231 str(mem);
232 return mem;
233 }
234
235 void str(std::string &s) const
236 {
237 s.resize(36);
238 str((char *)s.data());
239 }
240
241 void str(char *res) const
242 {
243 __m128i x = _mm_load_si128((__m128i *)data);
244 m128itos(x, res);
245 }
246
247 friend std::ostream &operator<<(std::ostream &stream, const UUID &uuid) { return stream << uuid.str(); }
248
249 friend std::istream &operator>>(std::istream &stream, UUID &uuid)
250 {
251 std::string s;
252 stream >> s;
253 uuid = fromStrFactory(s);
254 return stream;
255 }
256
257 size_t hash() const { return *((uint64_t *)data) ^ *((uint64_t *)data + 8); }
258
259 private:
260 alignas(128) uint8_t data[16];
261 };
262
268 template <typename RNG>
270 {
271 public:
276 : generator(new RNG(std::random_device()())),
277 distribution(std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max())
278 {
279 }
280
284 UUIDGenerator(uint64_t seed)
285 : generator(new RNG(seed)),
286 distribution(std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max())
287 {
288 }
289
294 : generator(gen), distribution(std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max())
295 {
296 }
297
302 {
303 // The two masks set the uuid version (4) and variant (1)
304 const __m128i and_mask = _mm_set_epi64x(0xFFFFFFFFFFFFFF3Full, 0xFF0FFFFFFFFFFFFFull);
305 const __m128i or_mask = _mm_set_epi64x(0x0000000000000080ull, 0x0040000000000000ull);
306 __m128i n = _mm_set_epi64x(distribution(*generator), distribution(*generator));
307 __m128i uuid = _mm_or_si128(_mm_and_si128(n, and_mask), or_mask);
308
309 return UUID(uuid);
310 }
311
312 private:
313 std::shared_ptr<RNG> generator;
314 std::uniform_int_distribution<uint64_t> distribution;
315 };
316} // namespace mal_toolkit::uuid
317
318namespace std
319{
320 template <>
321 struct hash<mal_toolkit::uuid::UUID>
322 {
326 size_t operator()(const mal_toolkit::uuid::UUID &uuid) const { return uuid.hash(); }
327 };
328} // namespace std
Generates UUIDv4 from a provided random generator (c++11 random module). std::mt19937_64 is highly re...
Definition uuid.hpp:270
UUID getUUID()
Generates a new UUID.
Definition uuid.hpp:301
std::uniform_int_distribution< uint64_t > distribution
Definition uuid.hpp:314
UUIDGenerator(RNG &gen)
Constructor with a user-provided random generator.
Definition uuid.hpp:293
UUIDGenerator(uint64_t seed)
Constructor with a specified seed for the random generator.
Definition uuid.hpp:284
UUIDGenerator()
Default constructor.
Definition uuid.hpp:275
std::shared_ptr< RNG > generator
Definition uuid.hpp:313
UUIDv4 (random 128-bits) RFC-4122.
Definition uuid.hpp:106
friend bool operator>=(const UUID &lhs, const UUID &rhs)
Definition uuid.hpp:201
UUID(__m128i uuid)
Constructs an UUID from a 128-bits unsigned int.
Definition uuid.hpp:122
friend bool operator==(const UUID &lhs, const UUID &rhs)
Equality comparison operator.
Definition uuid.hpp:177
friend bool operator>(const UUID &lhs, const UUID &rhs)
Definition uuid.hpp:199
size_t hash() const
Definition uuid.hpp:257
void str(char *res) const
Definition uuid.hpp:241
friend bool operator<=(const UUID &lhs, const UUID &rhs)
Definition uuid.hpp:200
UUID(const uint8_t *bytes)
Constructs an UUID from a byte array.
Definition uuid.hpp:136
UUID(const UUID &other)
Definition uuid.hpp:113
friend std::ostream & operator<<(std::ostream &stream, const UUID &uuid)
Definition uuid.hpp:247
UUID(uint64_t x, uint64_t y)
Constructs an UUID from two 64-bit integers.
Definition uuid.hpp:127
void bytes(std::string &out) const
Definition uuid.hpp:213
std::string bytes() const
Serializes the UUID to a byte string (16 bytes).
Definition uuid.hpp:206
UUID & operator=(const UUID &other)
Assignment operator.
Definition uuid.hpp:163
static UUID fromStrFactory(const char *raw)
Definition uuid.hpp:156
UUID(const std::string &bytes)
Constructs an UUID from a byte string.
Definition uuid.hpp:145
UUID()
Default constructor.
Definition uuid.hpp:111
void bytes(char *bytes) const
Definition uuid.hpp:219
void fromStr(const char *raw)
Definition uuid.hpp:158
friend std::istream & operator>>(std::istream &stream, UUID &uuid)
Definition uuid.hpp:249
std::string str() const
Converts the UUID to its string representation.
Definition uuid.hpp:228
void str(std::string &s) const
Definition uuid.hpp:235
friend bool operator<(const UUID &lhs, const UUID &rhs)
Less than comparison operator.
Definition uuid.hpp:189
static UUID fromStrFactory(const std::string &s)
Static factory to parse an UUID from its string representation.
Definition uuid.hpp:154
friend bool operator!=(const UUID &lhs, const UUID &rhs)
Definition uuid.hpp:198
Provides functions and utilities for handling endianness conversions.
__m128i betole128(__m128i x)
Converts a 128-bit value between big-endian and little-endian using SSE2 intrinsics.
uint16_t betole16(uint16_t x)
Converts a 16-bit value between big-endian and little-endian.
uint32_t betole32(uint32_t x)
Converts a 32-bit value between big-endian and little-endian.
__m256i betole256(__m256i x)
Converts a 256-bit value between big-endian and little-endian using AVX2 intrinsics.
void m128itos(__m128i x, char *mem)
Converts a 128-bits unsigned int to an UUIDv4 string representation using SIMD via AVX2.
Definition uuid.hpp:20
__m128i stom128i(const char *mem)
Converts an UUIDv4 string representation to a 128-bits unsigned int using SIMD via AVX2.
Definition uuid.hpp:63
Contains a collection of tools and utilities provided by the MAL Toolkit library.
Definition backoffs.hpp:7
STL namespace.
Provides utilities for working with Universally Unique Identifiers (UUIDs).
size_t operator()(const mal_toolkit::uuid::UUID &uuid) const
Hashes an UUID instance.
Definition uuid.hpp:326