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/qnamespace.h>
46#include <QtCore/qstring.h>
47#include <QtCore/qvector.h>
48#include <QtCore/qmetatype.h>
49#include <QtCore/qtypeinfo.h>
50
51QT_BEGIN_NAMESPACE
52
53class QVersionNumber;
54Q_CORE_EXPORT uint qHash(const QVersionNumber &key, uint seed = 0);
55
56#ifndef QT_NO_DATASTREAM
57Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QVersionNumber &version);
58Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
59#endif
60
61class QVersionNumber
62{
63 /*
64 * QVersionNumber stores small values inline, without memory allocation.
65 * We do that by setting the LSB in the pointer that would otherwise hold
66 * the longer form of the segments.
67 * The constants below help us deal with the permutations for 32- and 64-bit,
68 * little- and big-endian architectures.
69 */
70 enum {
71 // in little-endian, inline_segments[0] is shared with the pointer's LSB, while
72 // in big-endian, it's inline_segments[7]
73 InlineSegmentMarker = Q_BYTE_ORDER == Q_LITTLE_ENDIAN ? 0 : sizeof(void*) - 1,
74 InlineSegmentStartIdx = !InlineSegmentMarker, // 0 for BE, 1 for LE
75 InlineSegmentCount = sizeof(void*) - 1
76 };
77 Q_STATIC_ASSERT(InlineSegmentCount >= 3); // at least major, minor, micro
78
79 struct SegmentStorage {
80 // Note: we alias the use of dummy and inline_segments in the use of the
81 // union below. This is undefined behavior in C++98, but most compilers implement
82 // the C++11 behavior. The one known exception is older versions of Sun Studio.
83 union {
84 quintptr dummy;
85 qint8 inline_segments[sizeof(void*)];
86 QVector<int> *pointer_segments;
87 };
88
89 // set the InlineSegmentMarker and set length to zero
90 SegmentStorage() noexcept : dummy(1) {}
91
92 SegmentStorage(const QVector<int> &seg)
93 {
94 if (dataFitsInline(seg.begin(), seg.size()))
95 setInlineData(seg.begin(), seg.size());
96 else
97 pointer_segments = new QVector<int>(seg);
98 }
99
100 SegmentStorage(const SegmentStorage &other)
101 {
102 if (other.isUsingPointer())
103 pointer_segments = new QVector<int>(*other.pointer_segments);
104 else
105 dummy = other.dummy;
106 }
107
108 SegmentStorage &operator=(const SegmentStorage &other)
109 {
110 if (isUsingPointer() && other.isUsingPointer()) {
111 *pointer_segments = *other.pointer_segments;
112 } else if (other.isUsingPointer()) {
113 pointer_segments = new QVector<int>(*other.pointer_segments);
114 } else {
115 if (isUsingPointer())
116 delete pointer_segments;
117 dummy = other.dummy;
118 }
119 return *this;
120 }
121
122 SegmentStorage(SegmentStorage &&other) noexcept
123 : dummy(other.dummy)
124 {
125 other.dummy = 1;
126 }
127
128 SegmentStorage &operator=(SegmentStorage &&other) noexcept
129 {
130 qSwap(dummy, other.dummy);
131 return *this;
132 }
133
134 explicit SegmentStorage(QVector<int> &&seg)
135 {
136 if (dataFitsInline(seg.begin(), seg.size()))
137 setInlineData(seg.begin(), seg.size());
138 else
139 pointer_segments = new QVector<int>(std::move(seg));
140 }
141 SegmentStorage(std::initializer_list<int> args)
142 {
143 if (dataFitsInline(args.begin(), int(args.size()))) {
144 setInlineData(args.begin(), int(args.size()));
145 } else {
146 pointer_segments = new QVector<int>(args);
147 }
148 }
149
150 ~SegmentStorage() { if (isUsingPointer()) delete pointer_segments; }
151
152 bool isUsingPointer() const noexcept
153 { return (inline_segments[InlineSegmentMarker] & 1) == 0; }
154
155 int size() const noexcept
156 { return isUsingPointer() ? pointer_segments->size() : (inline_segments[InlineSegmentMarker] >> 1); }
157
158 void setInlineSize(int len)
159 { inline_segments[InlineSegmentMarker] = 1 + 2 * len; }
160
161 void resize(int len)
162 {
163 if (isUsingPointer())
164 pointer_segments->resize(len);
165 else
166 setInlineSize(len);
167 }
168
169 int at(int index) const
170 {
171 return isUsingPointer() ?
172 pointer_segments->at(index) :
173 inline_segments[InlineSegmentStartIdx + index];
174 }
175
176 void setSegments(int len, int maj, int min = 0, int mic = 0)
177 {
178 if (maj == qint8(maj) && min == qint8(min) && mic == qint8(mic)) {
179 int data[] = { maj, min, mic };
180 setInlineData(data, len);
181 } else {
182 setVector(len, maj, min, mic);
183 }
184 }
185
186 private:
187 static bool dataFitsInline(const int *data, int len)
188 {
189 if (len > InlineSegmentCount)
190 return false;
191 for (int i = 0; i < len; ++i)
192 if (data[i] != qint8(data[i]))
193 return false;
194 return true;
195 }
196 void setInlineData(const int *data, int len)
197 {
198 dummy = 1 + len * 2;
199#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
200 for (int i = 0; i < len; ++i)
201 dummy |= quintptr(data[i] & 0xFF) << (8 * (i + 1));
202#elif Q_BYTE_ORDER == Q_BIG_ENDIAN
203 for (int i = 0; i < len; ++i)
204 dummy |= quintptr(data[i] & 0xFF) << (8 * (sizeof(void *) - i - 1));
205#else
206 // the code above is equivalent to:
207 setInlineSize(len);
208 for (int i = 0; i < len; ++i)
209 inline_segments[InlineSegmentStartIdx + i] = data[i] & 0xFF;
210#endif
211 }
212
213 Q_CORE_EXPORT void setVector(int len, int maj, int min, int mic);
214 } m_segments;
215
216public:
217 inline QVersionNumber() noexcept
218 : m_segments()
219 {}
220 inline explicit QVersionNumber(const QVector<int> &seg)
221 : m_segments(seg)
222 {}
223
224 // compiler-generated copy/move ctor/assignment operators and the destructor are ok
225
226 explicit QVersionNumber(QVector<int> &&seg)
227 : m_segments(std::move(seg))
228 {}
229
230 inline QVersionNumber(std::initializer_list<int> args)
231 : m_segments(args)
232 {}
233
234 inline explicit QVersionNumber(int maj)
235 { m_segments.setSegments(1, maj); }
236
237 inline explicit QVersionNumber(int maj, int min)
238 { m_segments.setSegments(2, maj, min); }
239
240 inline explicit QVersionNumber(int maj, int min, int mic)
241 { m_segments.setSegments(3, maj, min, mic); }
242
243 Q_REQUIRED_RESULT inline bool isNull() const noexcept
244 { return segmentCount() == 0; }
245
246 Q_REQUIRED_RESULT inline bool isNormalized() const noexcept
247 { return isNull() || segmentAt(segmentCount() - 1) != 0; }
248
249 Q_REQUIRED_RESULT inline int majorVersion() const noexcept
250 { return segmentAt(0); }
251
252 Q_REQUIRED_RESULT inline int minorVersion() const noexcept
253 { return segmentAt(1); }
254
255 Q_REQUIRED_RESULT inline int microVersion() const noexcept
256 { return segmentAt(2); }
257
258 Q_REQUIRED_RESULT Q_CORE_EXPORT QVersionNumber normalized() const;
259
260 Q_REQUIRED_RESULT Q_CORE_EXPORT QVector<int> segments() const;
261
262 Q_REQUIRED_RESULT inline int segmentAt(int index) const noexcept
263 { return (m_segments.size() > index) ? m_segments.at(index) : 0; }
264
265 Q_REQUIRED_RESULT inline int segmentCount() const noexcept
266 { return m_segments.size(); }
267
268 Q_REQUIRED_RESULT Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const noexcept;
269
270 Q_REQUIRED_RESULT Q_CORE_EXPORT static int compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept;
271
272 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2);
273
274 Q_REQUIRED_RESULT Q_CORE_EXPORT QString toString() const;
275#if QT_STRINGVIEW_LEVEL < 2
276 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr);
277#endif
278 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QLatin1String string, int *suffixIndex = nullptr);
279 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QStringView string, int *suffixIndex = nullptr);
280
281private:
282#ifndef QT_NO_DATASTREAM
283 friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
284#endif
285 friend Q_CORE_EXPORT uint qHash(const QVersionNumber &key, uint seed);
286};
287
288Q_DECLARE_TYPEINFO(QVersionNumber, Q_MOVABLE_TYPE);
289
290#ifndef QT_NO_DEBUG_STREAM
291Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version);
292#endif
293
294Q_REQUIRED_RESULT inline bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
295{ return QVersionNumber::compare(lhs, rhs) > 0; }
296
297Q_REQUIRED_RESULT inline bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
298{ return QVersionNumber::compare(lhs, rhs) >= 0; }
299
300Q_REQUIRED_RESULT inline bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
301{ return QVersionNumber::compare(lhs, rhs) < 0; }
302
303Q_REQUIRED_RESULT inline bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
304{ return QVersionNumber::compare(lhs, rhs) <= 0; }
305
306Q_REQUIRED_RESULT inline bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
307{ return QVersionNumber::compare(lhs, rhs) == 0; }
308
309Q_REQUIRED_RESULT inline bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
310{ return QVersionNumber::compare(lhs, rhs) != 0; }
311
312QT_END_NAMESPACE
313
314Q_DECLARE_METATYPE(QVersionNumber)
315
316#endif //QVERSIONNUMBER_H
317