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
16QT_BEGIN_NAMESPACE
17
18class QVersionNumber;
19Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed = 0);
20
21#ifndef QT_NO_DATASTREAM
22Q_CORE_EXPORT QDataStream &operator<<(QDataStream &out, const QVersionNumber &version);
23Q_CORE_EXPORT QDataStream &operator>>(QDataStream &in, QVersionNumber &version);
24#endif
25
26class 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
191public:
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
297private:
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
304Q_DECLARE_TYPEINFO(QVersionNumber, Q_RELOCATABLE_TYPE);
305
306#ifndef QT_NO_DEBUG_STREAM
307Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version);
308#endif
309
310class QTypeRevision;
311Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0);
312
313#ifndef QT_NO_DATASTREAM
314Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
315Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
316#endif
317
318class QTypeRevision
319{
320public:
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
436private:
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
454static_assert(sizeof(QTypeRevision) == 2);
455Q_DECLARE_TYPEINFO(QTypeRevision, Q_RELOCATABLE_TYPE);
456
457#ifndef QT_NO_DEBUG_STREAM
458Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
459#endif
460
461QT_END_NAMESPACE
462
463QT_DECL_METATYPE_EXTERN(QVersionNumber, Q_CORE_EXPORT)
464QT_DECL_METATYPE_EXTERN(QTypeRevision, Q_CORE_EXPORT)
465
466#endif // QVERSIONNUMBER_H
467

source code of qtbase/src/corelib/tools/qversionnumber.h