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
280private:
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
287Q_DECLARE_TYPEINFO(QVersionNumber, Q_MOVABLE_TYPE);
288
289#ifndef QT_NO_DEBUG_STREAM
290Q_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
311class QTypeRevision;
312Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0);
313
314#ifndef QT_NO_DATASTREAM
315Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
316Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
317#endif
318
319class QTypeRevision
320{
321public:
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
391private:
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
409inline constexpr bool operator==(QTypeRevision lhs, QTypeRevision rhs)
410{
411 return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>();
412}
413
414inline constexpr bool operator!=(QTypeRevision lhs, QTypeRevision rhs)
415{
416 return lhs.toEncodedVersion<quint16>() != rhs.toEncodedVersion<quint16>();
417}
418
419inline 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
440inline constexpr bool operator>(QTypeRevision lhs, QTypeRevision rhs)
441{
442 return lhs != rhs && !(lhs < rhs);
443}
444
445inline constexpr bool operator<=(QTypeRevision lhs, QTypeRevision rhs)
446{
447 return lhs == rhs || lhs < rhs;
448}
449
450inline constexpr bool operator>=(QTypeRevision lhs, QTypeRevision rhs)
451{
452 return lhs == rhs || !(lhs < rhs);
453}
454
455static_assert(sizeof(QTypeRevision) == 2);
456Q_DECLARE_TYPEINFO(QTypeRevision, Q_MOVABLE_TYPE);
457
458#ifndef QT_NO_DEBUG_STREAM
459Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
460#endif
461
462QT_END_NAMESPACE
463
464Q_DECLARE_METATYPE(QVersionNumber)
465Q_DECLARE_METATYPE(QTypeRevision)
466
467#endif // QVERSIONNUMBER_H
468