1 /* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
2
3 #ifndef MPT_UUID_UUID_HPP
4 #define MPT_UUID_UUID_HPP
5
6
7
8 #include "mpt/base/constexpr_throw.hpp"
9 #include "mpt/base/macros.hpp"
10 #include "mpt/base/integer.hpp"
11 #include "mpt/base/memory.hpp"
12 #include "mpt/base/namespace.hpp"
13 #include "mpt/endian/integer.hpp"
14 #include "mpt/format/default_formatter.hpp"
15 #include "mpt/format/simple.hpp"
16 #include "mpt/parse/parse.hpp"
17 #include "mpt/random/random.hpp"
18 #include "mpt/string/types.hpp"
19 #include "mpt/string/utility.hpp"
20
21 #if MPT_OS_WINDOWS
22 #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600)
23 #include <guiddef.h>
24 #endif // _WIN32_WINNT
25 #include <objbase.h>
26 #include <rpc.h>
27 #endif // MPT_OS_WINDOWS
28
29
30
31 namespace mpt {
32 inline namespace MPT_INLINE_NS {
33
34
35 // Microsoft on-disk layout
36 struct GUIDms {
37 uint32le Data1;
38 uint16le Data2;
39 uint16le Data3;
40 uint64be Data4; // yes, big endian here
41 };
declare_binary_safe(const GUIDms &)42 constexpr bool declare_binary_safe(const GUIDms &) {
43 return true;
44 }
45 static_assert(mpt::check_binary_size<GUIDms>(16));
46
47 // RFC binary format
48 struct UUIDbin {
49 uint32be Data1;
50 uint16be Data2;
51 uint16be Data3;
52 uint64be Data4;
53 };
declare_binary_safe(const UUIDbin &)54 constexpr bool declare_binary_safe(const UUIDbin &) {
55 return true;
56 }
57 static_assert(mpt::check_binary_size<UUIDbin>(16));
58
59
60
61 struct UUID {
62 private:
63 uint32 Data1;
64 uint16 Data2;
65 uint16 Data3;
66 uint64 Data4;
67
68 public:
GetData1mpt::MPT_INLINE_NS::UUID69 MPT_CONSTEXPRINLINE uint32 GetData1() const noexcept {
70 return Data1;
71 }
GetData2mpt::MPT_INLINE_NS::UUID72 MPT_CONSTEXPRINLINE uint16 GetData2() const noexcept {
73 return Data2;
74 }
GetData3mpt::MPT_INLINE_NS::UUID75 MPT_CONSTEXPRINLINE uint16 GetData3() const noexcept {
76 return Data3;
77 }
GetData4mpt::MPT_INLINE_NS::UUID78 MPT_CONSTEXPRINLINE uint64 GetData4() const noexcept {
79 return Data4;
80 }
81
82 public:
GetData64_1mpt::MPT_INLINE_NS::UUID83 MPT_CONSTEXPRINLINE uint64 GetData64_1() const noexcept {
84 return (static_cast<uint64>(Data1) << 32) | (static_cast<uint64>(Data2) << 16) | (static_cast<uint64>(Data3) << 0);
85 }
GetData64_2mpt::MPT_INLINE_NS::UUID86 MPT_CONSTEXPRINLINE uint64 GetData64_2() const noexcept {
87 return Data4;
88 }
89
90 public:
91 // xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx
92 // <--32-->-<16>-<16>-<-------64------>
IsNilmpt::MPT_INLINE_NS::UUID93 MPT_CONSTEXPRINLINE bool IsNil() const noexcept {
94 return (Data1 == 0) && (Data2 == 0) && (Data3 == 0) && (Data4 == 0);
95 }
IsValidmpt::MPT_INLINE_NS::UUID96 MPT_CONSTEXPRINLINE bool IsValid() const noexcept {
97 return (Data1 != 0) || (Data2 != 0) || (Data3 != 0) || (Data4 != 0);
98 }
Variantmpt::MPT_INLINE_NS::UUID99 MPT_CONSTEXPRINLINE uint8 Variant() const noexcept {
100 return Nn() >> 4u;
101 }
Versionmpt::MPT_INLINE_NS::UUID102 MPT_CONSTEXPRINLINE uint8 Version() const noexcept {
103 return Mm() >> 4u;
104 }
IsRFC4122mpt::MPT_INLINE_NS::UUID105 MPT_CONSTEXPRINLINE bool IsRFC4122() const noexcept {
106 return (Variant() & 0xcu) == 0x8u;
107 }
108
109 private:
Mmmpt::MPT_INLINE_NS::UUID110 MPT_CONSTEXPRINLINE uint8 Mm() const noexcept {
111 return static_cast<uint8>((Data3 >> 8) & 0xffu);
112 }
Nnmpt::MPT_INLINE_NS::UUID113 MPT_CONSTEXPRINLINE uint8 Nn() const noexcept {
114 return static_cast<uint8>((Data4 >> 56) & 0xffu);
115 }
MakeRFC4122mpt::MPT_INLINE_NS::UUID116 void MakeRFC4122(uint8 version) noexcept {
117 // variant
118 uint8 Nn = static_cast<uint8>((Data4 >> 56) & 0xffu);
119 Data4 &= 0x00ffffffffffffffull;
120 Nn &= ~(0xc0u);
121 Nn |= 0x80u;
122 Data4 |= static_cast<uint64>(Nn) << 56;
123 // version
124 version &= 0x0fu;
125 uint8 Mm = static_cast<uint8>((Data3 >> 8) & 0xffu);
126 Data3 &= 0x00ffu;
127 Mm &= ~(0xf0u);
128 Mm |= (version << 4u);
129 Data3 |= static_cast<uint16>(Mm) << 8;
130 }
131 #if MPT_OS_WINDOWS
132 private:
UUIDFromWin32mpt::MPT_INLINE_NS::UUID133 static mpt::UUID UUIDFromWin32(::UUID uuid) {
134 return mpt::UUID(uuid.Data1, uuid.Data2, uuid.Data3, (static_cast<uint64>(0) | (static_cast<uint64>(uuid.Data4[0]) << 56) | (static_cast<uint64>(uuid.Data4[1]) << 48) | (static_cast<uint64>(uuid.Data4[2]) << 40) | (static_cast<uint64>(uuid.Data4[3]) << 32) | (static_cast<uint64>(uuid.Data4[4]) << 24) | (static_cast<uint64>(uuid.Data4[5]) << 16) | (static_cast<uint64>(uuid.Data4[6]) << 8) | (static_cast<uint64>(uuid.Data4[7]) << 0)));
135 }
UUIDToWin32mpt::MPT_INLINE_NS::UUID136 static ::UUID UUIDToWin32(mpt::UUID uuid) {
137 ::UUID result = ::UUID();
138 result.Data1 = uuid.GetData1();
139 result.Data2 = uuid.GetData2();
140 result.Data3 = uuid.GetData3();
141 result.Data4[0] = static_cast<uint8>(uuid.GetData4() >> 56);
142 result.Data4[1] = static_cast<uint8>(uuid.GetData4() >> 48);
143 result.Data4[2] = static_cast<uint8>(uuid.GetData4() >> 40);
144 result.Data4[3] = static_cast<uint8>(uuid.GetData4() >> 32);
145 result.Data4[4] = static_cast<uint8>(uuid.GetData4() >> 24);
146 result.Data4[5] = static_cast<uint8>(uuid.GetData4() >> 16);
147 result.Data4[6] = static_cast<uint8>(uuid.GetData4() >> 8);
148 result.Data4[7] = static_cast<uint8>(uuid.GetData4() >> 0);
149 return result;
150 }
151
152 public:
UUIDmpt::MPT_INLINE_NS::UUID153 explicit UUID(::UUID uuid) {
154 *this = UUIDFromWin32(uuid);
155 }
operator ::UUIDmpt::MPT_INLINE_NS::UUID156 operator ::UUID() const {
157 return UUIDToWin32(*this);
158 }
159 #endif // MPT_OS_WINDOWS
160 private:
NibbleFromCharmpt::MPT_INLINE_NS::UUID161 static MPT_CONSTEXPRINLINE uint8 NibbleFromChar(char x) {
162 return ('0' <= x && x <= '9') ? static_cast<uint8>(x - '0' + 0) : ('a' <= x && x <= 'z') ? static_cast<uint8>(x - 'a' + 10)
163 : ('A' <= x && x <= 'Z') ? static_cast<uint8>(x - 'A' + 10)
164 : mpt::constexpr_throw<uint8>(std::domain_error(""));
165 }
ByteFromHexmpt::MPT_INLINE_NS::UUID166 static MPT_CONSTEXPRINLINE uint8 ByteFromHex(char x, char y) {
167 return static_cast<uint8>(uint8(0) | (NibbleFromChar(x) << 4) | (NibbleFromChar(y) << 0));
168 }
ParseHex16mpt::MPT_INLINE_NS::UUID169 static MPT_CONSTEXPRINLINE uint16 ParseHex16(const char * str) {
170 return static_cast<uint16>(uint16(0) | (static_cast<uint16>(ByteFromHex(str[0], str[1])) << 8) | (static_cast<uint16>(ByteFromHex(str[2], str[3])) << 0));
171 }
ParseHex32mpt::MPT_INLINE_NS::UUID172 static MPT_CONSTEXPRINLINE uint32 ParseHex32(const char * str) {
173 return static_cast<uint32>(uint32(0) | (static_cast<uint32>(ByteFromHex(str[0], str[1])) << 24) | (static_cast<uint32>(ByteFromHex(str[2], str[3])) << 16) | (static_cast<uint32>(ByteFromHex(str[4], str[5])) << 8) | (static_cast<uint32>(ByteFromHex(str[6], str[7])) << 0));
174 }
175
176 public:
ParseLiteralmpt::MPT_INLINE_NS::UUID177 static MPT_CONSTEXPRINLINE UUID ParseLiteral(const char * str, std::size_t len) {
178 return (len == 36 && str[8] == '-' && str[13] == '-' && str[18] == '-' && str[23] == '-') ? mpt::UUID(
179 ParseHex32(str + 0),
180 ParseHex16(str + 9),
181 ParseHex16(str + 14),
182 uint64(0)
183 | (static_cast<uint64>(ParseHex16(str + 19)) << 48)
184 | (static_cast<uint64>(ParseHex16(str + 24)) << 32)
185 | (static_cast<uint64>(ParseHex32(str + 28)) << 0))
186 : mpt::constexpr_throw<mpt::UUID>(std::domain_error(""));
187 }
188
189 public:
UUIDmpt::MPT_INLINE_NS::UUID190 MPT_CONSTEXPRINLINE UUID() noexcept
191 : Data1(0)
192 , Data2(0)
193 , Data3(0)
194 , Data4(0) {
195 return;
196 }
UUIDmpt::MPT_INLINE_NS::UUID197 MPT_CONSTEXPRINLINE explicit UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4) noexcept
198 : Data1(Data1)
199 , Data2(Data2)
200 , Data3(Data3)
201 , Data4(Data4) {
202 return;
203 }
UUIDmpt::MPT_INLINE_NS::UUID204 explicit UUID(UUIDbin uuid) {
205 Data1 = uuid.Data1.get();
206 Data2 = uuid.Data2.get();
207 Data3 = uuid.Data3.get();
208 Data4 = uuid.Data4.get();
209 }
UUIDmpt::MPT_INLINE_NS::UUID210 explicit UUID(GUIDms guid) {
211 Data1 = guid.Data1.get();
212 Data2 = guid.Data2.get();
213 Data3 = guid.Data3.get();
214 Data4 = guid.Data4.get();
215 }
operator UUIDbinmpt::MPT_INLINE_NS::UUID216 operator UUIDbin() const {
217 UUIDbin result{};
218 result.Data1 = GetData1();
219 result.Data2 = GetData2();
220 result.Data3 = GetData3();
221 result.Data4 = GetData4();
222 return result;
223 }
operator GUIDmsmpt::MPT_INLINE_NS::UUID224 operator GUIDms() const {
225 GUIDms result{};
226 result.Data1 = GetData1();
227 result.Data2 = GetData2();
228 result.Data3 = GetData3();
229 result.Data4 = GetData4();
230 return result;
231 }
232
233 public:
234 // Create a UUID
235 template <typename Trng>
Generatempt::MPT_INLINE_NS::UUID236 static UUID Generate(Trng & rng) {
237 #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
238 #if (_WIN32_WINNT >= 0x0602)
239 ::GUID guid = ::GUID();
240 HRESULT result = CoCreateGuid(&guid);
241 if (result != S_OK) {
242 return mpt::UUID::RFC4122Random(rng);
243 }
244 return mpt::UUID::UUIDFromWin32(guid);
245 #else
246 return mpt::UUID::RFC4122Random(rng);
247 #endif
248 #elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
249 ::UUID uuid = ::UUID();
250 RPC_STATUS status = ::UuidCreate(&uuid);
251 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
252 return mpt::UUID::RFC4122Random(rng);
253 }
254 status = RPC_S_OK;
255 if (UuidIsNil(&uuid, &status) != FALSE) {
256 return mpt::UUID::RFC4122Random(rng);
257 }
258 if (status != RPC_S_OK) {
259 return mpt::UUID::RFC4122Random(rng);
260 }
261 return mpt::UUID::UUIDFromWin32(uuid);
262 #else
263 return RFC4122Random(rng);
264 #endif
265 }
266 // Create a UUID that contains local, traceable information.
267 // Safe for local use. May be faster.
268 template <typename Trng>
GenerateLocalUseOnlympt::MPT_INLINE_NS::UUID269 static UUID GenerateLocalUseOnly(Trng & rng) {
270 #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
271 #if (_WIN32_WINNT >= 0x0602)
272 ::GUID guid = ::GUID();
273 HRESULT result = CoCreateGuid(&guid);
274 if (result != S_OK) {
275 return mpt::UUID::RFC4122Random(rng);
276 }
277 return mpt::UUID::UUIDFromWin32(guid);
278 #else
279 return mpt::UUID::RFC4122Random(rng);
280 #endif
281 #elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
282 #if _WIN32_WINNT >= 0x0501
283 // Available since Win2000, but we check for WinXP in order to not use this
284 // function in Win32old builds. It is not available on some non-fully
285 // patched Win98SE installs in the wild.
286 ::UUID uuid = ::UUID();
287 RPC_STATUS status = ::UuidCreateSequential(&uuid);
288 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
289 return Generate(rng);
290 }
291 status = RPC_S_OK;
292 if (UuidIsNil(&uuid, &status) != FALSE) {
293 return mpt::UUID::RFC4122Random(rng);
294 }
295 if (status != RPC_S_OK) {
296 return mpt::UUID::RFC4122Random(rng);
297 }
298 return mpt::UUID::UUIDFromWin32(uuid);
299 #else
300 // Fallback to ::UuidCreate is safe as ::UuidCreateSequential is only a
301 // tiny performance optimization.
302 return Generate(rng);
303 #endif
304 #else
305 return RFC4122Random(rng);
306 #endif
307 }
308 // Create a RFC4122 Random UUID.
309 template <typename Trng>
RFC4122Randommpt::MPT_INLINE_NS::UUID310 static UUID RFC4122Random(Trng & prng) {
311 UUID result;
312 result.Data1 = mpt::random<uint32>(prng);
313 result.Data2 = mpt::random<uint16>(prng);
314 result.Data3 = mpt::random<uint16>(prng);
315 result.Data4 = mpt::random<uint64>(prng);
316 result.MakeRFC4122(4);
317 return result;
318 }
319 friend UUID UUIDRFC4122NamespaceV3(const UUID & ns, const mpt::ustring & name);
320 friend UUID UUIDRFC4122NamespaceV5(const UUID & ns, const mpt::ustring & name);
321
322 public:
323 // General UUID<->string conversion.
324 // The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72
FromStringmpt::MPT_INLINE_NS::UUID325 static UUID FromString(const mpt::ustring & str) {
326 std::vector<mpt::ustring> segments = mpt::split<mpt::ustring>(str, MPT_ULITERAL("-"));
327 if (segments.size() != 5) {
328 return UUID();
329 }
330 if (segments[0].length() != 8) {
331 return UUID();
332 }
333 if (segments[1].length() != 4) {
334 return UUID();
335 }
336 if (segments[2].length() != 4) {
337 return UUID();
338 }
339 if (segments[3].length() != 4) {
340 return UUID();
341 }
342 if (segments[4].length() != 12) {
343 return UUID();
344 }
345 UUID result;
346 result.Data1 = mpt::ConvertHexStringTo<uint32>(segments[0]);
347 result.Data2 = mpt::ConvertHexStringTo<uint16>(segments[1]);
348 result.Data3 = mpt::ConvertHexStringTo<uint16>(segments[2]);
349 result.Data4 = mpt::ConvertHexStringTo<uint64>(segments[3] + segments[4]);
350 return result;
351 }
ToAStringmpt::MPT_INLINE_NS::UUID352 std::string ToAString() const {
353 return std::string()
354 + mpt::format<std::string>::hex0<8>(GetData1())
355 + std::string("-")
356 + mpt::format<std::string>::hex0<4>(GetData2())
357 + std::string("-")
358 + mpt::format<std::string>::hex0<4>(GetData3())
359 + std::string("-")
360 + mpt::format<std::string>::hex0<4>(static_cast<uint16>(GetData4() >> 48))
361 + std::string("-")
362 + mpt::format<std::string>::hex0<4>(static_cast<uint16>(GetData4() >> 32))
363 + mpt::format<std::string>::hex0<8>(static_cast<uint32>(GetData4() >> 0));
364 }
ToUStringmpt::MPT_INLINE_NS::UUID365 mpt::ustring ToUString() const {
366 return mpt::ustring()
367 + mpt::format<mpt::ustring>::hex0<8>(GetData1())
368 + MPT_USTRING("-")
369 + mpt::format<mpt::ustring>::hex0<4>(GetData2())
370 + MPT_USTRING("-")
371 + mpt::format<mpt::ustring>::hex0<4>(GetData3())
372 + MPT_USTRING("-")
373 + mpt::format<mpt::ustring>::hex0<4>(static_cast<uint16>(GetData4() >> 48))
374 + MPT_USTRING("-")
375 + mpt::format<mpt::ustring>::hex0<4>(static_cast<uint16>(GetData4() >> 32))
376 + mpt::format<mpt::ustring>::hex0<8>(static_cast<uint32>(GetData4() >> 0));
377 }
378 };
379
operator ==(const mpt::UUID & a,const mpt::UUID & b)380 MPT_CONSTEXPRINLINE bool operator==(const mpt::UUID & a, const mpt::UUID & b) noexcept {
381 return (a.GetData1() == b.GetData1()) && (a.GetData2() == b.GetData2()) && (a.GetData3() == b.GetData3()) && (a.GetData4() == b.GetData4());
382 }
383
operator !=(const mpt::UUID & a,const mpt::UUID & b)384 MPT_CONSTEXPRINLINE bool operator!=(const mpt::UUID & a, const mpt::UUID & b) noexcept {
385 return (a.GetData1() != b.GetData1()) || (a.GetData2() != b.GetData2()) || (a.GetData3() != b.GetData3()) || (a.GetData4() != b.GetData4());
386 }
387
388
389 namespace uuid_literals {
390
operator ""_uuid(const char * str,std::size_t len)391 MPT_CONSTEXPRINLINE mpt::UUID operator"" _uuid(const char * str, std::size_t len) {
392 return mpt::UUID::ParseLiteral(str, len);
393 }
394
395 } // namespace uuid_literals
396
397
398 template <typename Tstring>
uuid_to_string(mpt::UUID uuid)399 inline Tstring uuid_to_string(mpt::UUID uuid) {
400 return mpt::transcode<Tstring>(uuid.ToUString());
401 }
402
403 template <>
uuid_to_string(mpt::UUID uuid)404 inline std::string uuid_to_string<std::string>(mpt::UUID uuid) {
405 return uuid.ToAString();
406 }
407
408 template <typename Tstring, typename T, std::enable_if_t<std::is_same<T, mpt::UUID>::value, bool> = true>
format_value_default(const T & x)409 inline Tstring format_value_default(const T & x) {
410 return mpt::transcode<Tstring>(mpt::uuid_to_string<typename mpt::select_format_string_type<Tstring>::type>(x));
411 }
412
413
414 } // namespace MPT_INLINE_NS
415 } // namespace mpt
416
417
418
419 #endif // MPT_UUID_UUID_HPP
420