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 QList<int> &seg)
100
101 Constructs a version number from the list of numbers contained in \a seg.
102*/
103
104/*!
105 \fn QVersionNumber::QVersionNumber(QList<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 QList<int>& QVersionNumber::segments() const
172
173 Returns all of the numerical segments.
174
175 \sa majorVersion(), minorVersion(), microVersion()
176*/
177QList<int> QVersionNumber::segments() const
178{
179 if (m_segments.isUsingPointer())
180 return *m_segments.pointer_segments;
181
182 QList<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 QList<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 QList<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 QList<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 size_t qHash(const QVersionNumber &key, size_t 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*/
551size_t qHash(const QVersionNumber &key, size_t 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
559/*!
560 \class QTypeRevision
561 \inmodule QtCore
562 \since 6.0
563 \brief The QTypeRevision class contains a lightweight representation of
564 a version number with two 8-bit segments, major and minor, either
565 of which can be unknown.
566
567 Use this class to describe revisions of a type. Compatible revisions can be
568 expressed as increments of the minor version. Breaking changes can be
569 expressed as increments of the major version. The return values of
570 \l QMetaMethod::revision() and \l QMetaProperty::revision() can be passed to
571 \l QTypeRevision::fromEncodedVersion(). The resulting major and minor versions
572 specify in which Qt versions the properties and methods were added.
573
574 \sa QMetaMethod::revision(), QMetaProperty::revision()
575*/
576
577/*!
578 \fn template<typename Integer> static bool QTypeRevision::isValidSegment(Integer segment)
579
580 Returns true if the given number can be used as either major or minor
581 version in a QTypeRevision. The valid range for \a segment is \c {>= 0} and \c {< 255}.
582*/
583
584/*!
585 \fn QTypeRevision::QTypeRevision()
586
587 Produces an invalid revision.
588
589 \sa isValid()
590*/
591
592/*!
593 \fn template <typename Major, typename Minor> static QTypeRevision QTypeRevision::fromVersion(Major majorVersion, Minor minorVersion)
594
595 Produces a QTypeRevision from the given \a majorVersion and \a minorVersion,
596 both of which need to be a valid segments.
597
598 \sa isValidSegment()
599*/
600
601/*!
602 \fn template <typename Major> static QTypeRevision QTypeRevision::fromMajorVersion(Major majorVersion)
603
604 Produces a QTypeRevision from the given \a majorVersion with an invalid minor
605 version. \a majorVersion needs to be a valid segment.
606
607 \sa isValidSegment()
608*/
609
610/*!
611 \fn template <typename Minor> static QTypeRevision QTypeRevision::fromMinorVersion(Minor minorVersion)
612
613 Produces a QTypeRevision from the given \a minorVersion with an invalid major
614 version. \a minorVersion needs to be a valid segment.
615
616 \sa isValidSegment()
617*/
618
619/*!
620 \fn template <typename Integer> static QTypeRevision QTypeRevision::fromEncodedVersion(Integer value)
621
622 Produces a QTypeRevision from the given \a value. \a value encodes both the
623 minor and major versions in the least significant and second least
624 significant byte, respectively.
625
626 \a value must not have any bits outside the least significant two bytes set.
627 \c Integer needs to be at least 16 bits wide, and must not have a sign bit
628 in the least significant 16 bits.
629
630 \sa toEncodedVersion()
631*/
632
633/*!
634 \fn static QTypeRevision QTypeRevision::zero()
635
636 Produces a QTypeRevision with major and minor version \c{0}.
637*/
638
639/*!
640 \fn bool QTypeRevision::hasMajorVersion() const
641
642 Returns true if the major version is known, otherwise false.
643
644 \sa majorVersion(), hasMinorVersion()
645*/
646
647/*!
648 \fn quint8 QTypeRevision::majorVersion() const
649
650 Returns the major version encoded in the revision.
651
652 \sa hasMajorVersion(), minorVersion()
653*/
654
655/*!
656 \fn bool QTypeRevision::hasMinorVersion() const
657
658 Returns true if the minor version is known, otherwise false.
659
660 \sa minorVersion(), hasMajorVersion()
661*/
662
663/*!
664 \fn quint8 QTypeRevision::minorVersion() const
665
666 Returns the minor version encoded in the revision.
667
668 \sa hasMinorVersion(), majorVersion()
669*/
670
671/*!
672 \fn bool QTypeRevision::isValid() const
673
674 Returns true if the major version or the minor version is known,
675 otherwise false.
676
677 \sa hasMajorVersion(), hasMinorVersion()
678*/
679
680/*!
681 \fn template<typename Integer> Integer QTypeRevision::toEncodedVersion() const
682
683 Transforms the revision into an integer value, encoding the minor
684 version into the least significant byte, and the major version into
685 the second least significant byte.
686
687 \c Integer needs to be at least 16 bits wide, and must not have a sign bit
688 in the least significant 16 bits.
689
690 \sa fromEncodedVersion()
691*/
692
693#ifndef QT_NO_DATASTREAM
694/*!
695 \fn QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision)
696 \relates QTypeRevision
697 \since 6.0
698
699 Writes the revision \a revision to stream \a out.
700 */
701QDataStream &operator<<(QDataStream &out, const QTypeRevision &revision)
702{
703 return out << revision.toEncodedVersion<quint16>();
704}
705
706/*!
707 \fn QDataStream& operator>>(QDataStream &in, QTypeRevision &revision)
708 \relates QTypeRevision
709 \since 6.0
710
711 Reads a revision from stream \a in and stores it in \a revision.
712 */
713QDataStream &operator>>(QDataStream &in, QTypeRevision &revision)
714{
715 quint16 value;
716 in >> value;
717 revision = QTypeRevision::fromEncodedVersion(value);
718 return in;
719}
720#endif
721
722#ifndef QT_NO_DEBUG_STREAM
723QDebug operator<<(QDebug debug, const QTypeRevision &revision)
724{
725 QDebugStateSaver saver(debug);
726 if (revision.hasMajorVersion()) {
727 if (revision.hasMinorVersion())
728 debug.nospace() << revision.majorVersion() << '.' << revision.minorVersion();
729 else
730 debug.nospace().noquote() << revision.majorVersion() << ".x";
731 } else {
732 if (revision.hasMinorVersion())
733 debug << revision.minorVersion();
734 else
735 debug.noquote() << "invalid";
736 }
737 return debug;
738}
739#endif
740
741/*!
742 \fn size_t qHash(const QTypeRevision &key, size_t seed)
743 \relates QHash
744 \since 6.0
745
746 Returns the hash value for the \a key, using \a seed to seed the
747 calculation.
748*/
749size_t qHash(const QTypeRevision &key, size_t seed)
750{
751 return qHash(key.toEncodedVersion<quint16>(), seed);
752}
753
754QT_END_NAMESPACE
755