1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // Copyright (C) 2022 Intel Corporation. |
3 | // Copyright (C) 2015 Keith Gardner <kreios4004@gmail.com> |
4 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
5 | |
6 | #ifndef QVERSIONNUMBER_H |
7 | #define QVERSIONNUMBER_H |
8 | |
9 | #include <QtCore/qlist.h> |
10 | #include <QtCore/qmetatype.h> |
11 | #include <QtCore/qnamespace.h> |
12 | #include <QtCore/qstring.h> |
13 | #include <QtCore/qtypeinfo.h> |
14 | #include <limits> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | class QVersionNumber; |
19 | Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed = 0); |
20 | |
21 | #ifndef QT_NO_DATASTREAM |
22 | Q_CORE_EXPORT QDataStream &operator<<(QDataStream &out, const QVersionNumber &version); |
23 | Q_CORE_EXPORT QDataStream &operator>>(QDataStream &in, QVersionNumber &version); |
24 | #endif |
25 | |
26 | class QVersionNumber |
27 | { |
28 | /* |
29 | * QVersionNumber stores small values inline, without memory allocation. |
30 | * We do that by setting the LSB in the pointer that would otherwise hold |
31 | * the longer form of the segments. |
32 | * The constants below help us deal with the permutations for 32- and 64-bit, |
33 | * little- and big-endian architectures. |
34 | */ |
35 | enum { |
36 | // in little-endian, inline_segments[0] is shared with the pointer's LSB, while |
37 | // in big-endian, it's inline_segments[7] |
38 | InlineSegmentMarker = Q_BYTE_ORDER == Q_LITTLE_ENDIAN ? 0 : sizeof(void *) - 1, |
39 | InlineSegmentStartIdx = !InlineSegmentMarker, // 0 for BE, 1 for LE |
40 | InlineSegmentCount = sizeof(void *) - 1 |
41 | }; |
42 | static_assert(InlineSegmentCount >= 3); // at least major, minor, micro |
43 | |
44 | struct SegmentStorage |
45 | { |
46 | // Note: we alias the use of dummy and inline_segments in the use of the |
47 | // union below. This is undefined behavior in C++98, but most compilers implement |
48 | // the C++11 behavior. The one known exception is older versions of Sun Studio. |
49 | union { |
50 | quintptr dummy; |
51 | qint8 inline_segments[sizeof(void *)]; |
52 | QList<int> *pointer_segments; |
53 | }; |
54 | |
55 | // set the InlineSegmentMarker and set length to zero |
56 | SegmentStorage() noexcept : dummy(1) {} |
57 | |
58 | SegmentStorage(const QList<int> &seg) |
59 | { |
60 | if (dataFitsInline(data: seg.data(), len: seg.size())) |
61 | setInlineData(data: seg.data(), len: seg.size()); |
62 | else |
63 | setListData(seg); |
64 | } |
65 | |
66 | Q_CORE_EXPORT void setListData(const QList<int> &seg); |
67 | |
68 | SegmentStorage(const SegmentStorage &other) |
69 | { |
70 | if (other.isUsingPointer()) |
71 | setListData(*other.pointer_segments); |
72 | else |
73 | dummy = other.dummy; |
74 | } |
75 | |
76 | SegmentStorage &operator=(const SegmentStorage &other) |
77 | { |
78 | if (isUsingPointer() && other.isUsingPointer()) { |
79 | *pointer_segments = *other.pointer_segments; |
80 | } else if (other.isUsingPointer()) { |
81 | setListData(*other.pointer_segments); |
82 | } else { |
83 | if (isUsingPointer()) |
84 | delete pointer_segments; |
85 | dummy = other.dummy; |
86 | } |
87 | return *this; |
88 | } |
89 | |
90 | SegmentStorage(SegmentStorage &&other) noexcept |
91 | : dummy(other.dummy) |
92 | { |
93 | other.dummy = 1; |
94 | } |
95 | |
96 | QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(SegmentStorage) |
97 | |
98 | void swap(SegmentStorage &other) noexcept |
99 | { |
100 | std::swap(a&: dummy, b&: other.dummy); |
101 | } |
102 | |
103 | explicit SegmentStorage(QList<int> &&seg) |
104 | { |
105 | if (dataFitsInline(data: std::as_const(t&: seg).data(), len: seg.size())) |
106 | setInlineData(data: std::as_const(t&: seg).data(), len: seg.size()); |
107 | else |
108 | setListData(std::move(seg)); |
109 | } |
110 | |
111 | Q_CORE_EXPORT void setListData(QList<int> &&seg); |
112 | |
113 | explicit SegmentStorage(std::initializer_list<int> args) |
114 | : SegmentStorage(args.begin(), args.end()) {} |
115 | |
116 | explicit SegmentStorage(const int *first, const int *last) |
117 | { |
118 | if (dataFitsInline(data: first, len: last - first)) { |
119 | setInlineData(data: first, len: last - first); |
120 | } else { |
121 | setListData(first, last); |
122 | } |
123 | } |
124 | |
125 | Q_CORE_EXPORT void setListData(const int *first, const int *last); |
126 | |
127 | ~SegmentStorage() { if (isUsingPointer()) delete pointer_segments; } |
128 | |
129 | bool isUsingPointer() const noexcept |
130 | { return (inline_segments[InlineSegmentMarker] & 1) == 0; } |
131 | |
132 | qsizetype size() const noexcept |
133 | { return isUsingPointer() ? pointer_segments->size() : (inline_segments[InlineSegmentMarker] >> 1); } |
134 | |
135 | void setInlineSize(qsizetype len) |
136 | { |
137 | Q_ASSERT(len <= InlineSegmentCount); |
138 | inline_segments[InlineSegmentMarker] = qint8(1 + 2 * len); |
139 | } |
140 | |
141 | Q_CORE_EXPORT void resize(qsizetype len); |
142 | |
143 | int at(qsizetype index) const |
144 | { |
145 | return isUsingPointer() ? |
146 | pointer_segments->at(i: index) : |
147 | inline_segments[InlineSegmentStartIdx + index]; |
148 | } |
149 | |
150 | void setSegments(int len, int maj, int min = 0, int mic = 0) |
151 | { |
152 | if (maj == qint8(maj) && min == qint8(min) && mic == qint8(mic)) { |
153 | int data[] = { maj, min, mic }; |
154 | setInlineData(data, len); |
155 | } else { |
156 | setVector(len, maj, min, mic); |
157 | } |
158 | } |
159 | |
160 | private: |
161 | static bool dataFitsInline(const int *data, qsizetype len) |
162 | { |
163 | if (len > InlineSegmentCount) |
164 | return false; |
165 | for (qsizetype i = 0; i < len; ++i) |
166 | if (data[i] != qint8(data[i])) |
167 | return false; |
168 | return true; |
169 | } |
170 | void setInlineData(const int *data, qsizetype len) |
171 | { |
172 | Q_ASSERT(len <= InlineSegmentCount); |
173 | dummy = 1 + len * 2; |
174 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
175 | for (qsizetype i = 0; i < len; ++i) |
176 | dummy |= quintptr(data[i] & 0xFF) << (8 * (i + 1)); |
177 | #elif Q_BYTE_ORDER == Q_BIG_ENDIAN |
178 | for (qsizetype i = 0; i < len; ++i) |
179 | dummy |= quintptr(data[i] & 0xFF) << (8 * (sizeof(void *) - i - 1)); |
180 | #else |
181 | // the code above is equivalent to: |
182 | setInlineSize(len); |
183 | for (qsizetype i = 0; i < len; ++i) |
184 | inline_segments[InlineSegmentStartIdx + i] = data[i] & 0xFF; |
185 | #endif |
186 | } |
187 | |
188 | Q_CORE_EXPORT void setVector(int len, int maj, int min, int mic); |
189 | } m_segments; |
190 | |
191 | public: |
192 | inline QVersionNumber() noexcept |
193 | : m_segments() |
194 | {} |
195 | inline explicit QVersionNumber(const QList<int> &seg) : m_segments(seg) { } |
196 | |
197 | // compiler-generated copy/move ctor/assignment operators and the destructor are ok |
198 | |
199 | explicit QVersionNumber(QList<int> &&seg) : m_segments(std::move(seg)) { } |
200 | |
201 | inline QVersionNumber(std::initializer_list<int> args) |
202 | : m_segments(args) |
203 | {} |
204 | |
205 | template <qsizetype N> |
206 | explicit QVersionNumber(const QVarLengthArray<int, N> &sec) |
207 | : m_segments(sec.begin(), sec.end()) |
208 | {} |
209 | |
210 | inline explicit QVersionNumber(int maj) |
211 | { m_segments.setSegments(len: 1, maj); } |
212 | |
213 | inline explicit QVersionNumber(int maj, int min) |
214 | { m_segments.setSegments(len: 2, maj, min); } |
215 | |
216 | inline explicit QVersionNumber(int maj, int min, int mic) |
217 | { m_segments.setSegments(len: 3, maj, min, mic); } |
218 | |
219 | [[nodiscard]] inline bool isNull() const noexcept |
220 | { return segmentCount() == 0; } |
221 | |
222 | [[nodiscard]] inline bool isNormalized() const noexcept |
223 | { return isNull() || segmentAt(index: segmentCount() - 1) != 0; } |
224 | |
225 | [[nodiscard]] inline int majorVersion() const noexcept |
226 | { return segmentAt(index: 0); } |
227 | |
228 | [[nodiscard]] inline int minorVersion() const noexcept |
229 | { return segmentAt(index: 1); } |
230 | |
231 | [[nodiscard]] inline int microVersion() const noexcept |
232 | { return segmentAt(index: 2); } |
233 | |
234 | [[nodiscard]] Q_CORE_EXPORT QVersionNumber normalized() const; |
235 | |
236 | [[nodiscard]] Q_CORE_EXPORT QList<int> segments() const; |
237 | |
238 | [[nodiscard]] inline int segmentAt(qsizetype index) const noexcept |
239 | { return (m_segments.size() > index) ? m_segments.at(index) : 0; } |
240 | |
241 | [[nodiscard]] inline qsizetype segmentCount() const noexcept |
242 | { return m_segments.size(); } |
243 | |
244 | [[nodiscard]] Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const noexcept; |
245 | |
246 | [[nodiscard]] Q_CORE_EXPORT static int compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept; |
247 | |
248 | [[nodiscard]] Q_CORE_EXPORT static QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2); |
249 | |
250 | [[nodiscard]] Q_CORE_EXPORT QString toString() const; |
251 | [[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(QAnyStringView string, qsizetype *suffixIndex = nullptr); |
252 | |
253 | #if QT_DEPRECATED_SINCE(6, 4) && QT_POINTER_SIZE != 4 |
254 | Q_WEAK_OVERLOAD |
255 | QT_DEPRECATED_VERSION_X_6_4("Use the 'qsizetype *suffixIndex' overload." ) |
256 | [[nodiscard]] static QVersionNumber fromString(QAnyStringView string, int *suffixIndex) |
257 | { |
258 | QT_WARNING_PUSH |
259 | // fromString() writes to *n unconditionally, but GCC can't know that |
260 | QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized" ) |
261 | qsizetype n; |
262 | auto r = fromString(string, suffixIndex: &n); |
263 | if (suffixIndex) { |
264 | Q_ASSERT(int(n) == n); |
265 | *suffixIndex = int(n); |
266 | } |
267 | return r; |
268 | QT_WARNING_POP |
269 | } |
270 | #endif |
271 | |
272 | |
273 | #if QT_CORE_REMOVED_SINCE(6, 4) |
274 | [[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(const QString &string, int *suffixIndex); |
275 | [[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(QLatin1StringView string, int *suffixIndex); |
276 | [[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(QStringView string, int *suffixIndex); |
277 | #endif |
278 | |
279 | [[nodiscard]] friend bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept |
280 | { return compare(v1: lhs, v2: rhs) > 0; } |
281 | |
282 | [[nodiscard]] friend bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept |
283 | { return compare(v1: lhs, v2: rhs) >= 0; } |
284 | |
285 | [[nodiscard]] friend bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept |
286 | { return compare(v1: lhs, v2: rhs) < 0; } |
287 | |
288 | [[nodiscard]] friend bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept |
289 | { return compare(v1: lhs, v2: rhs) <= 0; } |
290 | |
291 | [[nodiscard]] friend bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept |
292 | { return compare(v1: lhs, v2: rhs) == 0; } |
293 | |
294 | [[nodiscard]] friend bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept |
295 | { return compare(v1: lhs, v2: rhs) != 0; } |
296 | |
297 | private: |
298 | #ifndef QT_NO_DATASTREAM |
299 | friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version); |
300 | #endif |
301 | friend Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed); |
302 | }; |
303 | |
304 | Q_DECLARE_TYPEINFO(QVersionNumber, Q_RELOCATABLE_TYPE); |
305 | |
306 | #ifndef QT_NO_DEBUG_STREAM |
307 | Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version); |
308 | #endif |
309 | |
310 | class QTypeRevision; |
311 | Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0); |
312 | |
313 | #ifndef QT_NO_DATASTREAM |
314 | Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision); |
315 | Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision); |
316 | #endif |
317 | |
318 | class QTypeRevision |
319 | { |
320 | public: |
321 | template<typename Integer> |
322 | using if_valid_segment_type = typename std::enable_if< |
323 | std::is_integral<Integer>::value, bool>::type; |
324 | |
325 | template<typename Integer> |
326 | using if_valid_value_type = typename std::enable_if< |
327 | std::is_integral<Integer>::value |
328 | && (sizeof(Integer) > sizeof(quint16) |
329 | || (sizeof(Integer) == sizeof(quint16) |
330 | && !std::is_signed<Integer>::value)), bool>::type; |
331 | |
332 | template<typename Integer, if_valid_segment_type<Integer> = true> |
333 | static constexpr bool isValidSegment(Integer segment) |
334 | { |
335 | // using extra parentheses around max to avoid expanding it if it is a macro |
336 | return segment >= Integer(0) |
337 | && ((std::numeric_limits<Integer>::max)() < Integer(SegmentUnknown) |
338 | || segment < Integer(SegmentUnknown)); |
339 | } |
340 | |
341 | template<typename Major, typename Minor, |
342 | if_valid_segment_type<Major> = true, |
343 | if_valid_segment_type<Minor> = true> |
344 | static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion) |
345 | { |
346 | return Q_ASSERT(isValidSegment(majorVersion)), |
347 | Q_ASSERT(isValidSegment(minorVersion)), |
348 | QTypeRevision(quint8(majorVersion), quint8(minorVersion)); |
349 | } |
350 | |
351 | template<typename Major, if_valid_segment_type<Major> = true> |
352 | static constexpr QTypeRevision fromMajorVersion(Major majorVersion) |
353 | { |
354 | return Q_ASSERT(isValidSegment(majorVersion)), |
355 | QTypeRevision(quint8(majorVersion), SegmentUnknown); |
356 | } |
357 | |
358 | template<typename Minor, if_valid_segment_type<Minor> = true> |
359 | static constexpr QTypeRevision fromMinorVersion(Minor minorVersion) |
360 | { |
361 | return Q_ASSERT(isValidSegment(minorVersion)), |
362 | QTypeRevision(SegmentUnknown, quint8(minorVersion)); |
363 | } |
364 | |
365 | template<typename Integer, if_valid_value_type<Integer> = true> |
366 | static constexpr QTypeRevision fromEncodedVersion(Integer value) |
367 | { |
368 | return Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)), |
369 | QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff)); |
370 | } |
371 | |
372 | static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); } |
373 | |
374 | constexpr QTypeRevision() = default; |
375 | |
376 | constexpr bool hasMajorVersion() const { return m_majorVersion != SegmentUnknown; } |
377 | constexpr quint8 majorVersion() const { return m_majorVersion; } |
378 | |
379 | constexpr bool hasMinorVersion() const { return m_minorVersion != SegmentUnknown; } |
380 | constexpr quint8 minorVersion() const { return m_minorVersion; } |
381 | |
382 | constexpr bool isValid() const { return hasMajorVersion() || hasMinorVersion(); } |
383 | |
384 | template<typename Integer, if_valid_value_type<Integer> = true> |
385 | constexpr Integer toEncodedVersion() const |
386 | { |
387 | return Integer(m_majorVersion << 8) | Integer(m_minorVersion); |
388 | } |
389 | |
390 | [[nodiscard]] friend constexpr bool operator==(QTypeRevision lhs, QTypeRevision rhs) |
391 | { |
392 | return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>(); |
393 | } |
394 | |
395 | [[nodiscard]] friend constexpr bool operator!=(QTypeRevision lhs, QTypeRevision rhs) |
396 | { |
397 | return lhs.toEncodedVersion<quint16>() != rhs.toEncodedVersion<quint16>(); |
398 | } |
399 | |
400 | [[nodiscard]] friend constexpr bool operator<(QTypeRevision lhs, QTypeRevision rhs) |
401 | { |
402 | return (!lhs.hasMajorVersion() && rhs.hasMajorVersion()) |
403 | // non-0 major > unspecified major > major 0 |
404 | ? rhs.majorVersion() != 0 |
405 | : ((lhs.hasMajorVersion() && !rhs.hasMajorVersion()) |
406 | // major 0 < unspecified major < non-0 major |
407 | ? lhs.majorVersion() == 0 |
408 | : (lhs.majorVersion() != rhs.majorVersion() |
409 | // both majors specified and non-0 |
410 | ? lhs.majorVersion() < rhs.majorVersion() |
411 | : ((!lhs.hasMinorVersion() && rhs.hasMinorVersion()) |
412 | // non-0 minor > unspecified minor > minor 0 |
413 | ? rhs.minorVersion() != 0 |
414 | : ((lhs.hasMinorVersion() && !rhs.hasMinorVersion()) |
415 | // minor 0 < unspecified minor < non-0 minor |
416 | ? lhs.minorVersion() == 0 |
417 | // both minors specified and non-0 |
418 | : lhs.minorVersion() < rhs.minorVersion())))); |
419 | } |
420 | |
421 | [[nodiscard]] friend constexpr bool operator>(QTypeRevision lhs, QTypeRevision rhs) |
422 | { |
423 | return lhs != rhs && !(lhs < rhs); |
424 | } |
425 | |
426 | [[nodiscard]] friend constexpr bool operator<=(QTypeRevision lhs, QTypeRevision rhs) |
427 | { |
428 | return lhs == rhs || lhs < rhs; |
429 | } |
430 | |
431 | [[nodiscard]] friend constexpr bool operator>=(QTypeRevision lhs, QTypeRevision rhs) |
432 | { |
433 | return lhs == rhs || !(lhs < rhs); |
434 | } |
435 | |
436 | private: |
437 | enum { SegmentUnknown = 0xff }; |
438 | |
439 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
440 | constexpr QTypeRevision(quint8 major, quint8 minor) |
441 | : m_minorVersion(minor), m_majorVersion(major) {} |
442 | |
443 | quint8 m_minorVersion = SegmentUnknown; |
444 | quint8 m_majorVersion = SegmentUnknown; |
445 | #else |
446 | constexpr QTypeRevision(quint8 major, quint8 minor) |
447 | : m_majorVersion(major), m_minorVersion(minor) {} |
448 | |
449 | quint8 m_majorVersion = SegmentUnknown; |
450 | quint8 m_minorVersion = SegmentUnknown; |
451 | #endif |
452 | }; |
453 | |
454 | static_assert(sizeof(QTypeRevision) == 2); |
455 | Q_DECLARE_TYPEINFO(QTypeRevision, Q_RELOCATABLE_TYPE); |
456 | |
457 | #ifndef QT_NO_DEBUG_STREAM |
458 | Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision); |
459 | #endif |
460 | |
461 | QT_END_NAMESPACE |
462 | |
463 | QT_DECL_METATYPE_EXTERN(QVersionNumber, Q_CORE_EXPORT) |
464 | QT_DECL_METATYPE_EXTERN(QTypeRevision, Q_CORE_EXPORT) |
465 | |
466 | #endif // QVERSIONNUMBER_H |
467 | |