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#include <QtCore/qversionnumber.h>
43#include <QtCore/qhash.h>
44#include <QtCore/private/qlocale_tools_p.h>
45#include <QtCore/qcollator.h>
46
47#ifndef QT_NO_DATASTREAM
48# include <QtCore/qdatastream.h>
49#endif
50
51#ifndef QT_NO_DEBUG_STREAM
52# include <QtCore/qdebug.h>
53#endif
54
55#include <algorithm>
56#include <limits>
57
58QT_BEGIN_NAMESPACE
59
60/*!
61 \class QVersionNumber
62 \inmodule QtCore
63 \since 5.6
64 \brief The QVersionNumber class contains a version number with an arbitrary
65 number of segments.
66
67 \snippet qversionnumber/main.cpp 0
68*/
69
70/*!
71 \fn QVersionNumber::QVersionNumber()
72
73 Produces a null version.
74
75 \sa isNull()
76*/
77
78/*!
79 \fn QVersionNumber::QVersionNumber(int maj)
80
81 Constructs a QVersionNumber consisting of just the major version number \a maj.
82*/
83
84/*!
85 \fn QVersionNumber::QVersionNumber(int maj, int min)
86
87 Constructs a QVersionNumber consisting of the major and minor
88 version numbers \a maj and \a min, respectively.
89*/
90
91/*!
92 \fn QVersionNumber::QVersionNumber(int maj, int min, int mic)
93
94 Constructs a QVersionNumber consisting of the major, minor, and
95 micro version numbers \a maj, \a min and \a mic, respectively.
96*/
97
98/*!
99 \fn QVersionNumber::QVersionNumber(const QVector<int> &seg)
100
101 Constructs a version number from the list of numbers contained in \a seg.
102*/
103
104/*!
105 \fn QVersionNumber::QVersionNumber(QVector<int> &&seg)
106
107 Move-constructs a version number from the list of numbers contained in \a seg.
108
109 This constructor is only enabled if the compiler supports C++11 move semantics.
110*/
111
112/*!
113 \fn QVersionNumber::QVersionNumber(std::initializer_list<int> args)
114
115 Construct a version number from the std::initializer_list specified by
116 \a args.
117
118 This constructor is only enabled if the compiler supports C++11 initializer
119 lists.
120*/
121
122/*!
123 \fn bool QVersionNumber::isNull() const
124
125 Returns \c true if there are zero numerical segments, otherwise returns
126 \c false.
127
128 \sa segments()
129*/
130
131/*!
132 \fn bool QVersionNumber::isNormalized() const
133
134 Returns \c true if the version number does not contain any trailing zeros,
135 otherwise returns \c false.
136
137 \sa normalized()
138*/
139
140/*!
141 \fn int QVersionNumber::majorVersion() const
142
143 Returns the major version number, that is, the first segment.
144 This function is equivalent to segmentAt(0). If this QVersionNumber object
145 is null, this function returns 0.
146
147 \sa isNull(), segmentAt()
148*/
149
150/*!
151 \fn int QVersionNumber::minorVersion() const
152
153 Returns the minor version number, that is, the second segment.
154 This function is equivalent to segmentAt(1). If this QVersionNumber object
155 does not contain a minor number, this function returns 0.
156
157 \sa isNull(), segmentAt()
158*/
159
160/*!
161 \fn int QVersionNumber::microVersion() const
162
163 Returns the micro version number, that is, the third segment.
164 This function is equivalent to segmentAt(2). If this QVersionNumber object
165 does not contain a micro number, this function returns 0.
166
167 \sa isNull(), segmentAt()
168*/
169
170/*!
171 \fn const QVector<int>& QVersionNumber::segments() const
172
173 Returns all of the numerical segments.
174
175 \sa majorVersion(), minorVersion(), microVersion()
176*/
177QVector<int> QVersionNumber::segments() const
178{
179 if (m_segments.isUsingPointer())
180 return *m_segments.pointer_segments;
181
182 QVector<int> result;
183 result.resize(segmentCount());
184 for (int i = 0; i < segmentCount(); ++i)
185 result[i] = segmentAt(i);
186 return result;
187}
188
189/*!
190 \fn int QVersionNumber::segmentAt(int index) const
191
192 Returns the segement value at \a index. If the index does not exist,
193 returns 0.
194
195 \sa segments(), segmentCount()
196*/
197
198/*!
199 \fn int QVersionNumber::segmentCount() const
200
201 Returns the number of integers stored in segments().
202
203 \sa segments()
204*/
205
206/*!
207 \fn QVersionNumber QVersionNumber::normalized() const
208
209 Returns an equivalent version number but with all trailing zeros removed.
210
211 To check if two numbers are equivalent, use normalized() on both version
212 numbers before performing the compare.
213
214 \snippet qversionnumber/main.cpp 4
215 */
216QVersionNumber QVersionNumber::normalized() const
217{
218 int i;
219 for (i = m_segments.size(); i; --i)
220 if (m_segments.at(i - 1) != 0)
221 break;
222
223 QVersionNumber result(*this);
224 result.m_segments.resize(i);
225 return result;
226}
227
228/*!
229 \fn bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const
230
231 Returns \c true if the current version number is contained in the \a other
232 version number, otherwise returns \c false.
233
234 \snippet qversionnumber/main.cpp 2
235
236 \sa commonPrefix()
237*/
238bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const noexcept
239{
240 if (segmentCount() > other.segmentCount())
241 return false;
242 for (int i = 0; i < segmentCount(); ++i) {
243 if (segmentAt(i) != other.segmentAt(i))
244 return false;
245 }
246 return true;
247}
248
249/*!
250 \fn int QVersionNumber::compare(const QVersionNumber &v1,
251 const QVersionNumber &v2)
252
253 Compares \a v1 with \a v2 and returns an integer less than, equal to, or
254 greater than zero, depending on whether \a v1 is less than, equal to, or
255 greater than \a v2, respectively.
256
257 Comparisons are performed by comparing the segments of \a v1 and \a v2
258 starting at index 0 and working towards the end of the longer list.
259
260 \snippet qversionnumber/main.cpp 1
261*/
262int QVersionNumber::compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept
263{
264 int commonlen;
265
266 if (Q_LIKELY(!v1.m_segments.isUsingPointer() && !v2.m_segments.isUsingPointer())) {
267 // we can't use memcmp because it interprets the data as unsigned bytes
268 const qint8 *ptr1 = v1.m_segments.inline_segments + InlineSegmentStartIdx;
269 const qint8 *ptr2 = v2.m_segments.inline_segments + InlineSegmentStartIdx;
270 commonlen = qMin(v1.m_segments.size(),
271 v2.m_segments.size());
272 for (int i = 0; i < commonlen; ++i)
273 if (int x = ptr1[i] - ptr2[i])
274 return x;
275 } else {
276 commonlen = qMin(v1.segmentCount(), v2.segmentCount());
277 for (int i = 0; i < commonlen; ++i) {
278 if (v1.segmentAt(i) != v2.segmentAt(i))
279 return v1.segmentAt(i) - v2.segmentAt(i);
280 }
281 }
282
283 // ran out of segments in v1 and/or v2 and need to check the first trailing
284 // segment to finish the compare
285 if (v1.segmentCount() > commonlen) {
286 // v1 is longer
287 if (v1.segmentAt(commonlen) != 0)
288 return v1.segmentAt(commonlen);
289 else
290 return 1;
291 } else if (v2.segmentCount() > commonlen) {
292 // v2 is longer
293 if (v2.segmentAt(commonlen) != 0)
294 return -v2.segmentAt(commonlen);
295 else
296 return -1;
297 }
298
299 // the two version numbers are the same
300 return 0;
301}
302
303/*!
304 QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
305 const QVersionNumber &v2)
306
307 Returns a version number that is a parent version of both \a v1 and \a v2.
308
309 \sa isPrefixOf()
310*/
311QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
312 const QVersionNumber &v2)
313{
314 int commonlen = qMin(v1.segmentCount(), v2.segmentCount());
315 int i;
316 for (i = 0; i < commonlen; ++i) {
317 if (v1.segmentAt(i) != v2.segmentAt(i))
318 break;
319 }
320
321 if (i == 0)
322 return QVersionNumber();
323
324 // try to use the one with inline segments, if there's one
325 QVersionNumber result(!v1.m_segments.isUsingPointer() ? v1 : v2);
326 result.m_segments.resize(i);
327 return result;
328}
329
330/*!
331 \fn bool operator<(const QVersionNumber &lhs, const QVersionNumber &rhs)
332 \relates QVersionNumber
333
334 Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false.
335
336 \sa QVersionNumber::compare()
337*/
338
339/*!
340 \fn bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs)
341 \relates QVersionNumber
342
343 Returns \c true if \a lhs is less than or equal to \a rhs; otherwise
344 returns \c false.
345
346 \sa QVersionNumber::compare()
347*/
348
349/*!
350 \fn bool operator>(const QVersionNumber &lhs, const QVersionNumber &rhs)
351 \relates QVersionNumber
352
353 Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c
354 false.
355
356 \sa QVersionNumber::compare()
357*/
358
359/*!
360 \fn bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs)
361 \relates QVersionNumber
362
363 Returns \c true if \a lhs is greater than or equal to \a rhs; otherwise
364 returns \c false.
365
366 \sa QVersionNumber::compare()
367*/
368
369/*!
370 \fn bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs)
371 \relates QVersionNumber
372
373 Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
374
375 \sa QVersionNumber::compare()
376*/
377
378/*!
379 \fn bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs)
380 \relates QVersionNumber
381
382 Returns \c true if \a lhs is not equal to \a rhs; otherwise returns
383 \c false.
384
385 \sa QVersionNumber::compare()
386*/
387
388/*!
389 \fn QString QVersionNumber::toString() const
390
391 Returns a string with all of the segments delimited by a period (\c{.}).
392
393 \sa majorVersion(), minorVersion(), microVersion(), segments()
394*/
395QString QVersionNumber::toString() const
396{
397 QString version;
398 version.reserve(qMax(segmentCount() * 2 - 1, 0));
399 bool first = true;
400 for (int i = 0; i < segmentCount(); ++i) {
401 if (!first)
402 version += QLatin1Char('.');
403 version += QString::number(segmentAt(i));
404 first = false;
405 }
406 return version;
407}
408
409#if QT_STRINGVIEW_LEVEL < 2
410/*!
411 Constructs a QVersionNumber from a specially formatted \a string of
412 non-negative decimal numbers delimited by a period (\c{.}).
413
414 Once the numerical segments have been parsed, the remainder of the string
415 is considered to be the suffix string. The start index of that string will be
416 stored in \a suffixIndex if it is not null.
417
418 \snippet qversionnumber/main.cpp 3
419
420 \sa isNull()
421*/
422QVersionNumber QVersionNumber::fromString(const QString &string, int *suffixIndex)
423{
424 return fromString(QLatin1String(string.toLatin1()), suffixIndex);
425}
426#endif
427
428/*!
429 \since 5.10
430 \overload
431
432 Constructs a QVersionNumber from a specially formatted \a string of
433 non-negative decimal numbers delimited by '.'.
434
435 Once the numerical segments have been parsed, the remainder of the string
436 is considered to be the suffix string. The start index of that string will be
437 stored in \a suffixIndex if it is not null.
438
439 \snippet qversionnumber/main.cpp 3
440
441 \sa isNull()
442*/
443QVersionNumber QVersionNumber::fromString(QStringView string, int *suffixIndex)
444{
445 return fromString(QLatin1String(string.toLatin1()), suffixIndex);
446}
447
448/*!
449 \since 5.10
450 \overload
451
452 Constructs a QVersionNumber from a specially formatted \a string of
453 non-negative decimal numbers delimited by '.'.
454
455 Once the numerical segments have been parsed, the remainder of the string
456 is considered to be the suffix string. The start index of that string will be
457 stored in \a suffixIndex if it is not null.
458
459 \snippet qversionnumber/main.cpp 3-latin1-1
460
461 \sa isNull()
462*/
463QVersionNumber QVersionNumber::fromString(QLatin1String string, int *suffixIndex)
464{
465 QVector<int> seg;
466
467 const char *start = string.begin();
468 const char *end = start;
469 const char *lastGoodEnd = start;
470 const char *endOfString = string.end();
471
472 do {
473 bool ok = false;
474 const qulonglong value = qstrtoull(start, &end, 10, &ok);
475 if (!ok || value > qulonglong(std::numeric_limits<int>::max()))
476 break;
477 seg.append(int(value));
478 start = end + 1;
479 lastGoodEnd = end;
480 } while (start < endOfString && (end < endOfString && *end == '.'));
481
482 if (suffixIndex)
483 *suffixIndex = int(lastGoodEnd - string.begin());
484
485 return QVersionNumber(std::move(seg));
486}
487
488void QVersionNumber::SegmentStorage::setVector(int len, int maj, int min, int mic)
489{
490 pointer_segments = new QVector<int>;
491 pointer_segments->resize(len);
492 pointer_segments->data()[0] = maj;
493 if (len > 1) {
494 pointer_segments->data()[1] = min;
495 if (len > 2) {
496 pointer_segments->data()[2] = mic;
497 }
498 }
499}
500
501#ifndef QT_NO_DATASTREAM
502/*!
503 \fn QDataStream& operator<<(QDataStream &out,
504 const QVersionNumber &version)
505 \relates QVersionNumber
506
507 Writes the version number \a version to stream \a out.
508
509 Note that this has nothing to do with QDataStream::version().
510 */
511QDataStream& operator<<(QDataStream &out, const QVersionNumber &version)
512{
513 out << version.segments();
514 return out;
515}
516
517/*!
518 \fn QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
519 \relates QVersionNumber
520
521 Reads a version number from stream \a in and stores it in \a version.
522
523 Note that this has nothing to do with QDataStream::version().
524 */
525QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
526{
527 if (!version.m_segments.isUsingPointer())
528 version.m_segments.pointer_segments = new QVector<int>;
529 in >> *version.m_segments.pointer_segments;
530 return in;
531}
532#endif
533
534#ifndef QT_NO_DEBUG_STREAM
535QDebug operator<<(QDebug debug, const QVersionNumber &version)
536{
537 QDebugStateSaver saver(debug);
538 debug.noquote() << version.toString();
539 return debug;
540}
541#endif
542
543/*!
544 \fn uint qHash(const QVersionNumber &key, uint seed)
545 \relates QHash
546 \since 5.6
547
548 Returns the hash value for the \a key, using \a seed to seed the
549 calculation.
550*/
551uint qHash(const QVersionNumber &key, uint seed)
552{
553 QtPrivate::QHashCombine hash;
554 for (int i = 0; i < key.segmentCount(); ++i)
555 seed = hash(seed, key.segmentAt(i));
556 return seed;
557}
558
559QT_END_NAMESPACE
560
561