1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QUUID_H
5#define QUUID_H
6
7#include <QtCore/qcompare.h>
8#include <QtCore/qendian.h>
9#include <QtCore/qstring.h>
10#include <QtCore/qsystemdetection.h>
11
12#if defined(Q_OS_WIN) || defined(Q_QDOC)
13#ifndef GUID_DEFINED
14#define GUID_DEFINED
15typedef struct _GUID
16{
17 ulong Data1;
18 ushort Data2;
19 ushort Data3;
20 uchar Data4[8];
21} GUID, *REFGUID, *LPGUID;
22#endif
23#endif
24
25#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
26Q_FORWARD_DECLARE_CF_TYPE(CFUUID);
27Q_FORWARD_DECLARE_OBJC_CLASS(NSUUID);
28#endif
29
30QT_BEGIN_NAMESPACE
31
32class Q_CORE_EXPORT QUuid
33{
34 QUuid(Qt::Initialization) {}
35public:
36 enum Variant {
37 VarUnknown =-1,
38 NCS = 0, // 0 - -
39 DCE = 2, // 1 0 -
40 Microsoft = 6, // 1 1 0
41 Reserved = 7 // 1 1 1
42 };
43
44 enum Version {
45 VerUnknown =-1,
46 Time = 1, // 0 0 0 1
47 EmbeddedPOSIX = 2, // 0 0 1 0
48 Md5 = 3, // 0 0 1 1
49 Name = Md5,
50 Random = 4, // 0 1 0 0
51 Sha1 = 5, // 0 1 0 1
52 UnixEpoch = 7, // 0 1 1 1
53 };
54
55 enum StringFormat {
56 WithBraces = 0,
57 WithoutBraces = 1,
58 Id128 = 3
59 };
60
61 union alignas(16) Id128Bytes {
62 quint8 data[16];
63 quint16 data16[8];
64 quint32 data32[4];
65 quint64 data64[2];
66#if defined(QT_COMPILER_SUPPORTS_INT128)
67QT_WARNING_PUSH
68QT_WARNING_DISABLE_GCC("-Wpedantic") // ISO C++ does not support ‘__int128’ for ‘data128’
69 unsigned __int128 data128[1];
70QT_WARNING_POP
71#elif defined(QT_SUPPORTS_INT128)
72# error "struct QUuid::Id128Bytes should not depend on QT_SUPPORTS_INT128 for ABI reasons."
73# error "Adjust the declaration of the `data128` member above so it is always defined if it's " \
74 "supported by the current compiler/architecture in any configuration."
75#endif
76
77 constexpr explicit operator QByteArrayView() const noexcept
78 {
79 return QByteArrayView(data, sizeof(data));
80 }
81
82 friend constexpr Id128Bytes qbswap(Id128Bytes b) noexcept
83 {
84 // 128-bit byte swap
85 auto b0 = qbswap(source: b.data64[0]);
86 auto b1 = qbswap(source: b.data64[1]);
87 b.data64[0] = b1;
88 b.data64[1] = b0;
89 return b;
90 }
91 };
92
93 constexpr QUuid() noexcept : data1(0), data2(0), data3(0), data4{0,0,0,0,0,0,0,0} {}
94
95 constexpr QUuid(uint l, ushort w1, ushort w2, uchar b1, uchar b2, uchar b3,
96 uchar b4, uchar b5, uchar b6, uchar b7, uchar b8) noexcept
97 : data1(l), data2(w1), data3(w2), data4{b1, b2, b3, b4, b5, b6, b7, b8} {}
98 explicit inline QUuid(Id128Bytes id128, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept;
99
100 explicit QUuid(QAnyStringView string) noexcept
101 : QUuid{fromString(string)} {}
102 static QUuid fromString(QAnyStringView string) noexcept;
103#if QT_CORE_REMOVED_SINCE(6, 3)
104 explicit QUuid(const QString &);
105 static QUuid fromString(QStringView string) noexcept;
106 static QUuid fromString(QLatin1StringView string) noexcept;
107 explicit QUuid(const char *);
108 explicit QUuid(const QByteArray &);
109#endif
110 QString toString(StringFormat mode = WithBraces) const;
111 QByteArray toByteArray(StringFormat mode = WithBraces) const;
112 inline Id128Bytes toBytes(QSysInfo::Endian order = QSysInfo::BigEndian) const noexcept;
113 QByteArray toRfc4122() const;
114
115 static inline QUuid fromBytes(const void *bytes, QSysInfo::Endian order = QSysInfo::BigEndian);
116#if QT_CORE_REMOVED_SINCE(6, 3)
117 static QUuid fromRfc4122(const QByteArray &);
118#endif
119 static QUuid fromRfc4122(QByteArrayView) noexcept;
120
121#if QT_CORE_REMOVED_SINCE(6, 9)
122 bool isNull() const noexcept;
123#endif
124 constexpr bool isNull(QT6_DECL_NEW_OVERLOAD) const noexcept
125 {
126#if defined(__cpp_lib_bit_cast) && defined(QT_SUPPORTS_INT128)
127 return std::bit_cast<quint128>(*this) == 0;
128#else
129 // QNX fails to compile
130 // data4[0] == 0 && data4[1] == 0 && ...
131 // in constexpr context, so rewrite it using a loop. This way we have
132 // only single data4[i] != 0 check at each iteration
133 for (size_t i = 0; i < 8; ++i) {
134 if (data4[i] != 0)
135 return false;
136 }
137 return data1 == 0 && data2 == 0 && data3 == 0;
138#endif
139 }
140
141#ifdef QT_SUPPORTS_INT128
142 static constexpr QUuid fromUInt128(quint128 uuid, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept;
143 constexpr quint128 toUInt128(QSysInfo::Endian order = QSysInfo::BigEndian) const noexcept;
144#endif
145
146private:
147 friend constexpr bool comparesEqual(const QUuid &lhs, const QUuid &rhs) noexcept
148 {
149 return is_eq(o: compareThreeWay_helper(lhs, rhs));
150 }
151 static constexpr Qt::strong_ordering
152 compareThreeWay_helper(const QUuid &lhs, const QUuid &rhs) noexcept
153 {
154#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
155 if (const auto c = Qt::compareThreeWay(lhs: lhs.data1, rhs: rhs.data1); !is_eq(o: c))
156 return c;
157 if (const auto c = Qt::compareThreeWay(lhs: lhs.data2, rhs: rhs.data2); !is_eq(o: c))
158 return c;
159 if (const auto c = Qt::compareThreeWay(lhs: lhs.data3, rhs: rhs.data3); !is_eq(o: c))
160 return c;
161#elif defined(__cpp_lib_bit_cast) && defined(QT_SUPPORTS_INT128)
162 quint128 lu = qFromBigEndian(std::bit_cast<quint128>(lhs));
163 quint128 ru = qFromBigEndian(std::bit_cast<quint128>(rhs));
164 return Qt::compareThreeWay(lu, ru);
165#else
166 auto make_int = [](const QUuid &u) {
167 quint64 result = quint64(u.data3) << 48;
168 result |= quint64(u.data2) << 32;
169 return qFromBigEndian(result | u.data1);
170 };
171 if (const auto c = Qt::compareThreeWay(make_int(lhs), make_int(rhs)); !is_eq(c))
172 return c;
173#endif
174 for (unsigned i = 0; i < sizeof(lhs.data4); ++i) {
175 if (const auto c = Qt::compareThreeWay(lhs: lhs.data4[i], rhs: rhs.data4[i]); !is_eq(o: c))
176 return c;
177 }
178 return Qt::strong_ordering::equal;
179 }
180 friend constexpr Qt::strong_ordering compareThreeWay(const QUuid &lhs, const QUuid &rhs) noexcept
181 {
182#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
183 // Keep the old sorting order from before Qt 6.8, which sorted first on
184 // variant(). We don't need the exact algorithm to achieve same results.
185 auto fastVariant = [](const QUuid &uuid) {
186 quint8 v = uuid.data4[0];
187 // i.e.: return v >= Microsoft ? v : v >= DCE ? DCE : NCS;
188 return v >= 0xC0 ? v & 0xE0 : v >= 0x80 ? 0x80 : 0;
189 };
190 if (const auto c = Qt::compareThreeWay(lhs: fastVariant(lhs), rhs: fastVariant(rhs)); !is_eq(o: c))
191 return c;
192#endif
193 return compareThreeWay_helper(lhs, rhs);
194 }
195
196public:
197/* To prevent a meta-type creation ambiguity on Windows, we put comparison
198 macros under NOT QT_CORE_REMOVED_SINCE(6, 8) part. */
199#if QT_CORE_REMOVED_SINCE(6, 8)
200 constexpr bool operator==(const QUuid &orig) const noexcept
201 {
202 return comparesEqual(*this, orig);
203 }
204
205 constexpr bool operator!=(const QUuid &orig) const noexcept
206 {
207 return !operator==(orig);
208 }
209
210 bool operator<(const QUuid &other) const noexcept;
211 bool operator>(const QUuid &other) const noexcept;
212#else
213 Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QUuid)
214#endif // QT_CORE_REMOVED_SINCE(6, 8)
215#if defined(Q_OS_WIN) || defined(Q_QDOC)
216 // On Windows we have a type GUID that is used by the platform API, so we
217 // provide convenience operators to cast from and to this type.
218 constexpr QUuid(const GUID &guid) noexcept
219 : data1(guid.Data1), data2(guid.Data2), data3(guid.Data3),
220 data4{guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
221 guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]} {}
222
223 constexpr QUuid &operator=(const GUID &guid) noexcept
224 {
225 *this = QUuid(guid);
226 return *this;
227 }
228
229 constexpr operator GUID() const noexcept
230 {
231 GUID guid = { data1, data2, data3, { data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7] } };
232 return guid;
233 }
234private:
235 friend constexpr bool comparesEqual(const QUuid &lhs, const GUID &rhs) noexcept
236 {
237 return comparesEqual(lhs, QUuid(rhs));
238 }
239public:
240/* To prevent a meta-type creation ambiguity on Windows, we put comparison
241 macros under NOT QT_CORE_REMOVED_SINCE(6, 8) part. */
242#if QT_CORE_REMOVED_SINCE(6, 8)
243 constexpr bool operator==(const GUID &guid) const noexcept
244 {
245 return comparesEqual(*this, QUuid(guid));
246 }
247
248 constexpr bool operator!=(const GUID &guid) const noexcept
249 {
250 return !operator==(guid);
251 }
252#else
253 Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QUuid, GUID)
254#endif // !QT_CORE_REMOVED_SINCE(6, 8)
255#endif
256public:
257 static QUuid createUuid();
258#if QT_CORE_REMOVED_SINCE(6, 8)
259 static QUuid createUuidV3(const QUuid &ns, const QByteArray &baseData) noexcept;
260 static QUuid createUuidV5(const QUuid &ns, const QByteArray &baseData) noexcept;
261#endif
262 static QUuid createUuidV5(QUuid ns, QByteArrayView baseData) noexcept;
263#ifndef QT_BOOTSTRAPPED
264 static QUuid createUuidV3(QUuid ns, QByteArrayView baseData) noexcept;
265#if !QT_CORE_REMOVED_SINCE(6, 8)
266 Q_WEAK_OVERLOAD
267#endif
268 static inline QUuid createUuidV3(const QUuid &ns, const QString &baseData)
269 {
270 return QUuid::createUuidV3(ns, baseData: qToByteArrayViewIgnoringNull(b: baseData.toUtf8()));
271 }
272#endif
273#if !QT_CORE_REMOVED_SINCE(6, 8)
274 Q_WEAK_OVERLOAD
275#endif
276 static inline QUuid createUuidV5(const QUuid &ns, const QString &baseData)
277 {
278 return QUuid::createUuidV5(ns, baseData: qToByteArrayViewIgnoringNull(b: baseData.toUtf8()));
279 }
280
281 static QUuid createUuidV7();
282
283private:
284 static constexpr bool isKnownVersion(Version v) noexcept
285 {
286 switch (v) {
287 case VerUnknown:
288 return false;
289 case Time:
290 case EmbeddedPOSIX:
291 case Md5:
292 case Random:
293 case Sha1:
294 case UnixEpoch:
295 return true;
296 }
297 return false;
298 }
299
300public:
301#if QT_CORE_REMOVED_SINCE(6, 9)
302 QUuid::Variant variant() const noexcept;
303 QUuid::Version version() const noexcept;
304#endif
305 constexpr Variant variant(QT6_DECL_NEW_OVERLOAD) const noexcept
306 {
307 // Check the 3 MSB of data4[0]
308 const quint8 var = data4[0] & 0xE0;
309 if (var < 0x80)
310 return isNull(QT6_CALL_NEW_OVERLOAD) ? VarUnknown : NCS;
311 if (var < 0xC0)
312 return DCE;
313 return Variant(var >> 5); // Microsoft or Reserved
314 }
315 constexpr Version version(QT6_DECL_NEW_OVERLOAD) const noexcept
316 {
317 // Check the 4 MSB of data3
318 const Version ver = Version(data3 >> 12);
319 // Check that variant() == DCE and version is in a valid range
320 if (isKnownVersion(v: ver) && (data4[0] & 0xC0) == 0x80)
321 return ver;
322 return VerUnknown;
323 }
324
325#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
326 static QUuid fromCFUUID(CFUUIDRef uuid);
327 CFUUIDRef toCFUUID() const Q_DECL_CF_RETURNS_RETAINED;
328 static QUuid fromNSUUID(const NSUUID *uuid);
329 NSUUID *toNSUUID() const Q_DECL_NS_RETURNS_AUTORELEASED;
330#endif
331
332 uint data1;
333 ushort data2;
334 ushort data3;
335 uchar data4[8];
336};
337
338Q_DECLARE_TYPEINFO(QUuid, Q_PRIMITIVE_TYPE);
339
340#ifndef QT_NO_DATASTREAM
341Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QUuid &);
342Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QUuid &);
343#endif
344
345#ifndef QT_NO_DEBUG_STREAM
346Q_CORE_EXPORT QDebug operator<<(QDebug, const QUuid &);
347#endif
348
349Q_CORE_EXPORT size_t qHash(const QUuid &uuid, size_t seed = 0) noexcept;
350
351QUuid::QUuid(Id128Bytes uuid, QSysInfo::Endian order) noexcept
352{
353 char bytes[sizeof uuid];
354 if (order == QSysInfo::LittleEndian)
355 qbswap(src: uuid, dest: bytes);
356 else
357 memcpy(dest: bytes, src: &uuid, n: sizeof bytes);
358 data1 = qFromBigEndian<quint32>(src: &bytes[0]);
359 data2 = qFromBigEndian<quint16>(src: &bytes[4]);
360 data3 = qFromBigEndian<quint16>(src: &bytes[6]);
361 memcpy(dest: data4, src: &bytes[8], n: sizeof(data4));
362}
363
364QUuid::Id128Bytes QUuid::toBytes(QSysInfo::Endian order) const noexcept
365{
366 Id128Bytes result = {};
367 qToBigEndian(src: data1, dest: &result.data[0]);
368 qToBigEndian(src: data2, dest: &result.data[4]);
369 qToBigEndian(src: data3, dest: &result.data[6]);
370 memcpy(dest: &result.data[8], src: data4, n: sizeof(data4));
371 if (order == QSysInfo::LittleEndian)
372 return qbswap(b: result);
373 return result;
374}
375
376QUuid QUuid::fromBytes(const void *bytes, QSysInfo::Endian order)
377{
378 Id128Bytes result = {};
379 memcpy(dest: result.data, src: bytes, n: sizeof(result));
380 return QUuid(result, order);
381}
382
383#ifdef QT_SUPPORTS_INT128
384constexpr QUuid QUuid::fromUInt128(quint128 uuid, QSysInfo::Endian order) noexcept
385{
386 QUuid result = {};
387 if (order == QSysInfo::BigEndian) {
388 result.data1 = qFromBigEndian<quint32>(source: int(uuid));
389 result.data2 = qFromBigEndian<quint16>(source: ushort(uuid >> 32));
390 result.data3 = qFromBigEndian<quint16>(source: ushort(uuid >> 48));
391 for (int i = 0; i < 8; ++i)
392 result.data4[i] = uchar(uuid >> (64 + i * 8));
393 } else {
394 result.data1 = qFromLittleEndian<quint32>(source: uint(uuid >> 96));
395 result.data2 = qFromLittleEndian<quint16>(source: ushort(uuid >> 80));
396 result.data3 = qFromLittleEndian<quint16>(source: ushort(uuid >> 64));
397 for (int i = 0; i < 8; ++i)
398 result.data4[i] = uchar(uuid >> (56 - i * 8));
399 }
400 return result;
401}
402
403constexpr quint128 QUuid::toUInt128(QSysInfo::Endian order) const noexcept
404{
405 quint128 result = {};
406 if (order == QSysInfo::BigEndian) {
407 for (int i = 0; i < 8; ++i)
408 result |= quint64(data4[i]) << (i * 8);
409 result = result << 64;
410 result |= quint64(qToBigEndian<quint16>(source: data3)) << 48;
411 result |= quint64(qToBigEndian<quint16>(source: data2)) << 32;
412 result |= qToBigEndian<quint32>(source: data1);
413 } else {
414 result = qToLittleEndian<quint32>(source: data1);
415 result = result << 32;
416 result |= quint64(qToLittleEndian<quint16>(source: data2)) << 16;
417 result |= quint64(qToLittleEndian<quint16>(source: data3));
418 result = result << 64;
419 for (int i = 0; i < 8; ++i)
420 result |= quint64(data4[i]) << (56 - i * 8);
421 }
422 return result;
423}
424#endif
425
426#if defined(Q_QDOC)
427// provide fake declarations of qXXXEndian() functions, so that qDoc could
428// distinguish them from the general template
429QUuid::Id128Bytes qFromBigEndian(QUuid::Id128Bytes src);
430QUuid::Id128Bytes qFromLittleEndian(QUuid::Id128Bytes src);
431QUuid::Id128Bytes qToBigEndian(QUuid::Id128Bytes src);
432QUuid::Id128Bytes qToLittleEndian(QUuid::Id128Bytes src);
433#endif
434
435QT_END_NAMESPACE
436
437#endif // QUUID_H
438

source code of qtbase/src/corelib/plugin/quuid.h