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