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