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 
51 QT_BEGIN_NAMESPACE
52 
53 class QVersionNumber;
54 Q_CORE_EXPORT uint qHash(const QVersionNumber &key, uint seed = 0);
55 
56 #ifndef QT_NO_DATASTREAM
57 Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QVersionNumber &version);
58 Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
59 #endif
60 
61 class 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
SegmentStorageSegmentStorage90         SegmentStorage() noexcept : dummy(1) {}
91 
SegmentStorageSegmentStorage92         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 
SegmentStorageSegmentStorage100         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 
SegmentStorageSegmentStorage122         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 
SegmentStorageSegmentStorage134         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         }
SegmentStorageSegmentStorage141         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 
~SegmentStorageSegmentStorage150         ~SegmentStorage() { if (isUsingPointer()) delete pointer_segments; }
151 
isUsingPointerSegmentStorage152         bool isUsingPointer() const noexcept
153         { return (inline_segments[InlineSegmentMarker] & 1) == 0; }
154 
sizeSegmentStorage155         int size() const noexcept
156         { return isUsingPointer() ? pointer_segments->size() : (inline_segments[InlineSegmentMarker] >> 1); }
157 
setInlineSizeSegmentStorage158         void setInlineSize(int len)
159         { inline_segments[InlineSegmentMarker] = 1 + 2 * len; }
160 
resizeSegmentStorage161         void resize(int len)
162         {
163             if (isUsingPointer())
164                 pointer_segments->resize(len);
165             else
166                 setInlineSize(len);
167         }
168 
atSegmentStorage169         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:
dataFitsInlineSegmentStorage187         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         }
setInlineDataSegmentStorage196         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 
216 public:
QVersionNumber()217     inline QVersionNumber() noexcept
218         : m_segments()
219     {}
QVersionNumber(const QVector<int> & seg)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 
QVersionNumber(QVector<int> && seg)226     explicit QVersionNumber(QVector<int> &&seg)
227         : m_segments(std::move(seg))
228     {}
229 
QVersionNumber(std::initializer_list<int> args)230     inline QVersionNumber(std::initializer_list<int> args)
231         : m_segments(args)
232     {}
233 
QVersionNumber(int maj)234     inline explicit QVersionNumber(int maj)
235     { m_segments.setSegments(1, maj); }
236 
QVersionNumber(int maj,int min)237     inline explicit QVersionNumber(int maj, int min)
238     { m_segments.setSegments(2, maj, min); }
239 
QVersionNumber(int maj,int min,int mic)240     inline explicit QVersionNumber(int maj, int min, int mic)
241     { m_segments.setSegments(3, maj, min, mic); }
242 
isNull()243     Q_REQUIRED_RESULT inline bool isNull() const noexcept
244     { return segmentCount() == 0; }
245 
isNormalized()246     Q_REQUIRED_RESULT inline bool isNormalized() const noexcept
247     { return isNull() || segmentAt(segmentCount() - 1) != 0; }
248 
majorVersion()249     Q_REQUIRED_RESULT inline int majorVersion() const noexcept
250     { return segmentAt(0); }
251 
minorVersion()252     Q_REQUIRED_RESULT inline int minorVersion() const noexcept
253     { return segmentAt(1); }
254 
microVersion()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 
segmentAt(int index)262     Q_REQUIRED_RESULT inline int segmentAt(int index) const noexcept
263     { return (m_segments.size() > index) ? m_segments.at(index) : 0; }
264 
segmentCount()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 
281 private:
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 
288 Q_DECLARE_TYPEINFO(QVersionNumber, Q_MOVABLE_TYPE);
289 
290 #ifndef QT_NO_DEBUG_STREAM
291 Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version);
292 #endif
293 
294 Q_REQUIRED_RESULT inline bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
295 { return QVersionNumber::compare(lhs, rhs) > 0; }
296 
297 Q_REQUIRED_RESULT inline bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
298 { return QVersionNumber::compare(lhs, rhs) >= 0; }
299 
300 Q_REQUIRED_RESULT inline bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
301 { return QVersionNumber::compare(lhs, rhs) < 0; }
302 
303 Q_REQUIRED_RESULT inline bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
304 { return QVersionNumber::compare(lhs, rhs) <= 0; }
305 
306 Q_REQUIRED_RESULT inline bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
307 { return QVersionNumber::compare(lhs, rhs) == 0; }
308 
309 Q_REQUIRED_RESULT inline bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
310 { return QVersionNumber::compare(lhs, rhs) != 0; }
311 
312 QT_END_NAMESPACE
313 
314 Q_DECLARE_METATYPE(QVersionNumber)
315 
316 #endif //QVERSIONNUMBER_H
317