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
52QT_BEGIN_NAMESPACE
53
54class QVersionNumber;
55Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed = 0);
56
57#ifndef QT_NO_DATASTREAM
58Q_CORE_EXPORT QDataStream &operator<<(QDataStream &out, const QVersionNumber &version);
59Q_CORE_EXPORT QDataStream &operator>>(QDataStream &in, QVersionNumber &version);
60#endif
61
62class 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
219public:
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 [[nodiscard]] friend bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
281 { return compare(lhs, rhs) > 0; }
282
283 [[nodiscard]] friend bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
284 { return compare(lhs, rhs) >= 0; }
285
286 [[nodiscard]] friend bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
287 { return compare(lhs, rhs) < 0; }
288
289 [[nodiscard]] friend bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
290 { return compare(lhs, rhs) <= 0; }
291
292 [[nodiscard]] friend bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
293 { return compare(lhs, rhs) == 0; }
294
295 [[nodiscard]] friend bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
296 { return compare(lhs, rhs) != 0; }
297
298
299private:
300#ifndef QT_NO_DATASTREAM
301 friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
302#endif
303 friend Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed);
304};
305
306Q_DECLARE_TYPEINFO(QVersionNumber, Q_RELOCATABLE_TYPE);
307
308#ifndef QT_NO_DEBUG_STREAM
309Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version);
310#endif
311
312class QTypeRevision;
313Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0);
314
315#ifndef QT_NO_DATASTREAM
316Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
317Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
318#endif
319
320class QTypeRevision
321{
322public:
323 template<typename Integer>
324 using if_valid_segment_type = typename std::enable_if<
325 std::is_integral<Integer>::value, bool>::type;
326
327 template<typename Integer>
328 using if_valid_value_type = typename std::enable_if<
329 std::is_integral<Integer>::value
330 && (sizeof(Integer) > sizeof(quint16)
331 || (sizeof(Integer) == sizeof(quint16)
332 && !std::is_signed<Integer>::value)), bool>::type;
333
334 template<typename Integer, if_valid_segment_type<Integer> = true>
335 static constexpr bool isValidSegment(Integer segment)
336 {
337 // using extra parentheses around max to avoid expanding it if it is a macro
338 return segment >= Integer(0)
339 && ((std::numeric_limits<Integer>::max)() < Integer(SegmentUnknown)
340 || segment < Integer(SegmentUnknown));
341 }
342
343 template<typename Major, typename Minor,
344 if_valid_segment_type<Major> = true,
345 if_valid_segment_type<Minor> = true>
346 static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
347 {
348 return Q_ASSERT(isValidSegment(majorVersion)),
349 Q_ASSERT(isValidSegment(minorVersion)),
350 QTypeRevision(quint8(majorVersion), quint8(minorVersion));
351 }
352
353 template<typename Major, if_valid_segment_type<Major> = true>
354 static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
355 {
356 return Q_ASSERT(isValidSegment(majorVersion)),
357 QTypeRevision(quint8(majorVersion), SegmentUnknown);
358 }
359
360 template<typename Minor, if_valid_segment_type<Minor> = true>
361 static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
362 {
363 return Q_ASSERT(isValidSegment(minorVersion)),
364 QTypeRevision(SegmentUnknown, quint8(minorVersion));
365 }
366
367 template<typename Integer, if_valid_value_type<Integer> = true>
368 static constexpr QTypeRevision fromEncodedVersion(Integer value)
369 {
370 return Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)),
371 QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff));
372 }
373
374 static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); }
375
376 constexpr QTypeRevision() = default;
377
378 constexpr bool hasMajorVersion() const { return m_majorVersion != SegmentUnknown; }
379 constexpr quint8 majorVersion() const { return m_majorVersion; }
380
381 constexpr bool hasMinorVersion() const { return m_minorVersion != SegmentUnknown; }
382 constexpr quint8 minorVersion() const { return m_minorVersion; }
383
384 constexpr bool isValid() const { return hasMajorVersion() || hasMinorVersion(); }
385
386 template<typename Integer, if_valid_value_type<Integer> = true>
387 constexpr Integer toEncodedVersion() const
388 {
389 return Integer(m_majorVersion << 8) | Integer(m_minorVersion);
390 }
391
392 [[nodiscard]] friend constexpr bool operator==(QTypeRevision lhs, QTypeRevision rhs)
393 {
394 return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>();
395 }
396
397 [[nodiscard]] friend constexpr bool operator!=(QTypeRevision lhs, QTypeRevision rhs)
398 {
399 return lhs.toEncodedVersion<quint16>() != rhs.toEncodedVersion<quint16>();
400 }
401
402 [[nodiscard]] friend constexpr bool operator<(QTypeRevision lhs, QTypeRevision rhs)
403 {
404 return (!lhs.hasMajorVersion() && rhs.hasMajorVersion())
405 // non-0 major > unspecified major > major 0
406 ? rhs.majorVersion() != 0
407 : ((lhs.hasMajorVersion() && !rhs.hasMajorVersion())
408 // major 0 < unspecified major < non-0 major
409 ? lhs.majorVersion() == 0
410 : (lhs.majorVersion() != rhs.majorVersion()
411 // both majors specified and non-0
412 ? lhs.majorVersion() < rhs.majorVersion()
413 : ((!lhs.hasMinorVersion() && rhs.hasMinorVersion())
414 // non-0 minor > unspecified minor > minor 0
415 ? rhs.minorVersion() != 0
416 : ((lhs.hasMinorVersion() && !rhs.hasMinorVersion())
417 // minor 0 < unspecified minor < non-0 minor
418 ? lhs.minorVersion() == 0
419 // both minors specified and non-0
420 : lhs.minorVersion() < rhs.minorVersion()))));
421 }
422
423 [[nodiscard]] friend constexpr bool operator>(QTypeRevision lhs, QTypeRevision rhs)
424 {
425 return lhs != rhs && !(lhs < rhs);
426 }
427
428 [[nodiscard]] friend constexpr bool operator<=(QTypeRevision lhs, QTypeRevision rhs)
429 {
430 return lhs == rhs || lhs < rhs;
431 }
432
433 [[nodiscard]] friend constexpr bool operator>=(QTypeRevision lhs, QTypeRevision rhs)
434 {
435 return lhs == rhs || !(lhs < rhs);
436 }
437
438private:
439 enum { SegmentUnknown = 0xff };
440
441#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
442 constexpr QTypeRevision(quint8 major, quint8 minor)
443 : m_minorVersion(minor), m_majorVersion(major) {}
444
445 quint8 m_minorVersion = SegmentUnknown;
446 quint8 m_majorVersion = SegmentUnknown;
447#else
448 constexpr QTypeRevision(quint8 major, quint8 minor)
449 : m_majorVersion(major), m_minorVersion(minor) {}
450
451 quint8 m_majorVersion = SegmentUnknown;
452 quint8 m_minorVersion = SegmentUnknown;
453#endif
454};
455
456static_assert(sizeof(QTypeRevision) == 2);
457Q_DECLARE_TYPEINFO(QTypeRevision, Q_RELOCATABLE_TYPE);
458
459#ifndef QT_NO_DEBUG_STREAM
460Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
461#endif
462
463QT_END_NAMESPACE
464
465Q_DECLARE_METATYPE(QVersionNumber)
466Q_DECLARE_METATYPE(QTypeRevision)
467
468#endif // QVERSIONNUMBER_H
469

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