1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// Copyright (C) 2014 Keith Gardner <kreios4004@gmail.com>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include <QtCore/qversionnumber.h>
7#include <QtCore/qhash.h>
8#include <QtCore/private/qlocale_tools_p.h>
9#include <QtCore/qcollator.h>
10
11#ifndef QT_NO_DATASTREAM
12# include <QtCore/qdatastream.h>
13#endif
14
15#ifndef QT_NO_DEBUG_STREAM
16# include <QtCore/qdebug.h>
17#endif
18
19#include <algorithm>
20#include <limits>
21
22QT_BEGIN_NAMESPACE
23
24QT_IMPL_METATYPE_EXTERN(QVersionNumber)
25QT_IMPL_METATYPE_EXTERN(QTypeRevision)
26
27/*!
28 \class QVersionNumber
29 \inmodule QtCore
30 \since 5.6
31 \brief The QVersionNumber class contains a version number with an arbitrary
32 number of segments.
33
34 \snippet qversionnumber/main.cpp 0
35*/
36
37/*!
38 \fn QVersionNumber::QVersionNumber()
39
40 Produces a null version.
41
42 \sa isNull()
43*/
44
45/*!
46 \fn QVersionNumber::QVersionNumber(int maj)
47
48 Constructs a QVersionNumber consisting of just the major version number \a maj.
49*/
50
51/*!
52 \fn QVersionNumber::QVersionNumber(int maj, int min)
53
54 Constructs a QVersionNumber consisting of the major and minor
55 version numbers \a maj and \a min, respectively.
56*/
57
58/*!
59 \fn QVersionNumber::QVersionNumber(int maj, int min, int mic)
60
61 Constructs a QVersionNumber consisting of the major, minor, and
62 micro version numbers \a maj, \a min and \a mic, respectively.
63*/
64
65/*!
66 \fn QVersionNumber::QVersionNumber(const QList<int> &seg)
67
68 Constructs a version number from the list of numbers contained in \a seg.
69*/
70
71/*!
72 \fn QVersionNumber::QVersionNumber(QList<int> &&seg)
73
74 Move-constructs a version number from the list of numbers contained in \a seg.
75
76 This constructor is only enabled if the compiler supports C++11 move semantics.
77*/
78
79/*!
80 \fn QVersionNumber::QVersionNumber(std::initializer_list<int> args)
81
82 Construct a version number from the std::initializer_list specified by
83 \a args.
84
85 This constructor is only enabled if the compiler supports C++11 initializer
86 lists.
87*/
88
89/*!
90 \fn template <qsizetype N> QVersionNumber::QVersionNumber(const QVarLengthArray<int, N> &seg)
91 \since 6.4
92
93 Constructs a version number from the list of numbers contained in \a seg.
94*/
95
96/*!
97 \fn bool QVersionNumber::isNull() const
98
99 Returns \c true if there are zero numerical segments, otherwise returns
100 \c false.
101
102 \sa segments()
103*/
104
105/*!
106 \fn bool QVersionNumber::isNormalized() const
107
108 Returns \c true if the version number does not contain any trailing zeros,
109 otherwise returns \c false.
110
111 \sa normalized()
112*/
113
114/*!
115 \fn int QVersionNumber::majorVersion() const
116
117 Returns the major version number, that is, the first segment.
118 This function is equivalent to segmentAt(0). If this QVersionNumber object
119 is null, this function returns 0.
120
121 \sa isNull(), segmentAt()
122*/
123
124/*!
125 \fn int QVersionNumber::minorVersion() const
126
127 Returns the minor version number, that is, the second segment.
128 This function is equivalent to segmentAt(1). If this QVersionNumber object
129 does not contain a minor number, this function returns 0.
130
131 \sa isNull(), segmentAt()
132*/
133
134/*!
135 \fn int QVersionNumber::microVersion() const
136
137 Returns the micro version number, that is, the third segment.
138 This function is equivalent to segmentAt(2). If this QVersionNumber object
139 does not contain a micro number, this function returns 0.
140
141 \sa isNull(), segmentAt()
142*/
143
144/*!
145 \fn QList<int> QVersionNumber::segments() const
146
147 Returns all of the numerical segments.
148
149 \sa majorVersion(), minorVersion(), microVersion()
150*/
151QList<int> QVersionNumber::segments() const
152{
153 if (m_segments.isUsingPointer())
154 return *m_segments.pointer_segments;
155
156 QList<int> result;
157 result.resize(size: segmentCount());
158 for (qsizetype i = 0; i < segmentCount(); ++i)
159 result[i] = segmentAt(index: i);
160 return result;
161}
162
163/*!
164 \fn int QVersionNumber::segmentAt(qsizetype index) const
165
166 Returns the segment value at \a index. If the index does not exist,
167 returns 0.
168
169 \sa segments(), segmentCount()
170*/
171
172/*!
173 \fn qsizetype QVersionNumber::segmentCount() const
174
175 Returns the number of integers stored in segments().
176
177 \sa segments()
178*/
179
180/*!
181 \fn QVersionNumber QVersionNumber::normalized() const
182
183 Returns an equivalent version number but with all trailing zeros removed.
184
185 To check if two numbers are equivalent, use normalized() on both version
186 numbers before performing the compare.
187
188 \snippet qversionnumber/main.cpp 4
189 */
190QVersionNumber QVersionNumber::normalized() const
191{
192 qsizetype i;
193 for (i = m_segments.size(); i; --i)
194 if (m_segments.at(index: i - 1) != 0)
195 break;
196
197 QVersionNumber result(*this);
198 result.m_segments.resize(len: i);
199 return result;
200}
201
202/*!
203 \fn bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const
204
205 Returns \c true if the current version number is contained in the \a other
206 version number, otherwise returns \c false.
207
208 \snippet qversionnumber/main.cpp 2
209
210 \sa commonPrefix()
211*/
212bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const noexcept
213{
214 if (segmentCount() > other.segmentCount())
215 return false;
216 for (qsizetype i = 0; i < segmentCount(); ++i) {
217 if (segmentAt(index: i) != other.segmentAt(index: i))
218 return false;
219 }
220 return true;
221}
222
223/*!
224 \fn int QVersionNumber::compare(const QVersionNumber &v1,
225 const QVersionNumber &v2)
226
227 Compares \a v1 with \a v2 and returns an integer less than, equal to, or
228 greater than zero, depending on whether \a v1 is less than, equal to, or
229 greater than \a v2, respectively.
230
231 Comparisons are performed by comparing the segments of \a v1 and \a v2
232 starting at index 0 and working towards the end of the longer list.
233
234 \snippet qversionnumber/main.cpp 1
235*/
236int QVersionNumber::compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept
237{
238 qsizetype commonlen;
239
240 if (Q_LIKELY(!v1.m_segments.isUsingPointer() && !v2.m_segments.isUsingPointer())) {
241 // we can't use memcmp because it interprets the data as unsigned bytes
242 const qint8 *ptr1 = v1.m_segments.inline_segments + InlineSegmentStartIdx;
243 const qint8 *ptr2 = v2.m_segments.inline_segments + InlineSegmentStartIdx;
244 commonlen = qMin(a: v1.m_segments.size(),
245 b: v2.m_segments.size());
246 for (qsizetype i = 0; i < commonlen; ++i)
247 if (int x = ptr1[i] - ptr2[i])
248 return x;
249 } else {
250 commonlen = qMin(a: v1.segmentCount(), b: v2.segmentCount());
251 for (qsizetype i = 0; i < commonlen; ++i) {
252 if (v1.segmentAt(index: i) != v2.segmentAt(index: i))
253 return v1.segmentAt(index: i) - v2.segmentAt(index: i);
254 }
255 }
256
257 // ran out of segments in v1 and/or v2 and need to check the first trailing
258 // segment to finish the compare
259 if (v1.segmentCount() > commonlen) {
260 // v1 is longer
261 if (v1.segmentAt(index: commonlen) != 0)
262 return v1.segmentAt(index: commonlen);
263 else
264 return 1;
265 } else if (v2.segmentCount() > commonlen) {
266 // v2 is longer
267 if (v2.segmentAt(index: commonlen) != 0)
268 return -v2.segmentAt(index: commonlen);
269 else
270 return -1;
271 }
272
273 // the two version numbers are the same
274 return 0;
275}
276
277/*!
278 QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
279 const QVersionNumber &v2)
280
281 Returns a version number that is a parent version of both \a v1 and \a v2.
282
283 \sa isPrefixOf()
284*/
285QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
286 const QVersionNumber &v2)
287{
288 qsizetype commonlen = qMin(a: v1.segmentCount(), b: v2.segmentCount());
289 qsizetype i;
290 for (i = 0; i < commonlen; ++i) {
291 if (v1.segmentAt(index: i) != v2.segmentAt(index: i))
292 break;
293 }
294
295 if (i == 0)
296 return QVersionNumber();
297
298 // try to use the one with inline segments, if there's one
299 QVersionNumber result(!v1.m_segments.isUsingPointer() ? v1 : v2);
300 result.m_segments.resize(len: i);
301 return result;
302}
303
304/*!
305 \fn bool QVersionNumber::operator<(const QVersionNumber &lhs, const QVersionNumber &rhs)
306
307 Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false.
308
309 \sa QVersionNumber::compare()
310*/
311
312/*!
313 \fn bool QVersionNumber::operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs)
314
315 Returns \c true if \a lhs is less than or equal to \a rhs; otherwise
316 returns \c false.
317
318 \sa QVersionNumber::compare()
319*/
320
321/*!
322 \fn bool QVersionNumber::operator>(const QVersionNumber &lhs, const QVersionNumber &rhs)
323
324 Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c
325 false.
326
327 \sa QVersionNumber::compare()
328*/
329
330/*!
331 \fn bool QVersionNumber::operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs)
332
333 Returns \c true if \a lhs is greater than or equal to \a rhs; otherwise
334 returns \c false.
335
336 \sa QVersionNumber::compare()
337*/
338
339/*!
340 \fn bool QVersionNumber::operator==(const QVersionNumber &lhs, const QVersionNumber &rhs)
341
342 Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
343
344 \sa QVersionNumber::compare()
345*/
346
347/*!
348 \fn bool QVersionNumber::operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs)
349
350 Returns \c true if \a lhs is not equal to \a rhs; otherwise returns
351 \c false.
352
353 \sa QVersionNumber::compare()
354*/
355
356/*!
357 \fn QString QVersionNumber::toString() const
358
359 Returns a string with all of the segments delimited by a period (\c{.}).
360
361 \sa majorVersion(), minorVersion(), microVersion(), segments()
362*/
363QString QVersionNumber::toString() const
364{
365 QString version;
366 version.reserve(asize: qMax(a: segmentCount() * 2 - 1, b: 0));
367 bool first = true;
368 for (qsizetype i = 0; i < segmentCount(); ++i) {
369 if (!first)
370 version += u'.';
371 version += QString::number(segmentAt(index: i));
372 first = false;
373 }
374 return version;
375}
376
377/*!
378 \fn QVersionNumber QVersionNumber::fromString(QAnyStringView string, qsizetype *suffixIndex)
379 \since 6.4
380
381 Constructs a QVersionNumber from a specially formatted \a string of
382 non-negative decimal numbers delimited by a period (\c{.}).
383
384 Once the numerical segments have been parsed, the remainder of the string
385 is considered to be the suffix string. The start index of that string will be
386 stored in \a suffixIndex if it is not null.
387
388 \snippet qversionnumber/main.cpp 3-latin1-1
389
390 \note In versions prior to Qt 6.4, this function was overloaded for QString,
391 QLatin1StringView and QStringView instead, and \a suffixIndex was an \c{int*}.
392
393 \sa isNull()
394*/
395
396static QVersionNumber from_string(QLatin1StringView string, qsizetype *suffixIndex)
397{
398 // 32 should be more than enough, and, crucially, it means we're allocating
399 // not more (and often less) often when compared with direct QList usage
400 // for all possible segment counts (under the constraint that we don't want
401 // to keep more capacity around for the lifetime of the resulting
402 // QVersionNumber than required), esp. in the common case where the inline
403 // storage can be used.
404 QVarLengthArray<int, 32> seg;
405
406 const char *start = string.begin();
407 const char *lastGoodEnd = start;
408 const char *endOfString = string.end();
409
410 do {
411 // parsing as unsigned so a minus sign is rejected
412 auto [value, used] = qstrntoull(nptr: start, size: endOfString - start, base: 10);
413 if (used <= 0 || value > qulonglong(std::numeric_limits<int>::max()))
414 break;
415 seg.append(t: int(value));
416 start += used + 1;
417 lastGoodEnd = start - 1;
418 } while (start < endOfString && *lastGoodEnd == '.');
419
420 if (suffixIndex)
421 *suffixIndex = lastGoodEnd - string.begin();
422
423 return QVersionNumber(seg);
424}
425
426static QVersionNumber from_string(q_no_char8_t::QUtf8StringView string, qsizetype *suffixIndex)
427{
428 return from_string(string: QLatin1StringView(string.data(), string.size()), suffixIndex);
429}
430
431// in qstring.cpp
432extern void qt_to_latin1(uchar *dst, const char16_t *uc, qsizetype len);
433
434static QVersionNumber from_string(QStringView string, qsizetype *suffixIndex)
435{
436 QVarLengthArray<char> copy;
437 copy.resize(sz: string.size());
438 qt_to_latin1(dst: reinterpret_cast<uchar*>(copy.data()), uc: string.utf16(), len: string.size());
439 return from_string(string: QLatin1StringView(copy.data(), copy.size()), suffixIndex);
440}
441
442QVersionNumber QVersionNumber::fromString(QAnyStringView string, qsizetype *suffixIndex)
443{
444 return string.visit(v: [=] (auto string) { return from_string(string, suffixIndex); });
445}
446
447void QVersionNumber::SegmentStorage::setListData(const QList<int> &seg)
448{
449 pointer_segments = new QList<int>(seg);
450}
451
452void QVersionNumber::SegmentStorage::setListData(QList<int> &&seg)
453{
454 pointer_segments = new QList<int>(std::move(seg));
455}
456
457void QVersionNumber::SegmentStorage::setListData(const int *first, const int *last)
458{
459 pointer_segments = new QList<int>(first, last);
460}
461
462void QVersionNumber::SegmentStorage::resize(qsizetype len)
463{
464 if (isUsingPointer())
465 pointer_segments->resize(size: len);
466 else
467 setInlineSize(len);
468}
469
470void QVersionNumber::SegmentStorage::setVector(int len, int maj, int min, int mic)
471{
472 pointer_segments = new QList<int>;
473 pointer_segments->resize(size: len);
474 pointer_segments->data()[0] = maj;
475 if (len > 1) {
476 pointer_segments->data()[1] = min;
477 if (len > 2) {
478 pointer_segments->data()[2] = mic;
479 }
480 }
481}
482
483#ifndef QT_NO_DATASTREAM
484/*!
485 \fn QDataStream& operator<<(QDataStream &out,
486 const QVersionNumber &version)
487 \relates QVersionNumber
488
489 Writes the version number \a version to stream \a out.
490
491 Note that this has nothing to do with QDataStream::version().
492 */
493QDataStream& operator<<(QDataStream &out, const QVersionNumber &version)
494{
495 out << version.segments();
496 return out;
497}
498
499/*!
500 \fn QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
501 \relates QVersionNumber
502
503 Reads a version number from stream \a in and stores it in \a version.
504
505 Note that this has nothing to do with QDataStream::version().
506 */
507QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
508{
509 if (!version.m_segments.isUsingPointer())
510 version.m_segments.pointer_segments = new QList<int>;
511 in >> *version.m_segments.pointer_segments;
512 return in;
513}
514#endif
515
516#ifndef QT_NO_DEBUG_STREAM
517QDebug operator<<(QDebug debug, const QVersionNumber &version)
518{
519 QDebugStateSaver saver(debug);
520 debug.nospace().noquote();
521 debug << "QVersionNumber(" << version.toString() << ")";
522 return debug;
523}
524#endif
525
526/*!
527 \relates QHash
528 \since 5.6
529
530 Returns the hash value for the \a key, using \a seed to seed the
531 calculation.
532*/
533size_t qHash(const QVersionNumber &key, size_t seed)
534{
535 QtPrivate::QHashCombine hash;
536 for (int i = 0; i < key.segmentCount(); ++i)
537 seed = hash(seed, key.segmentAt(index: i));
538 return seed;
539}
540
541/*!
542 \class QTypeRevision
543 \inmodule QtCore
544 \since 6.0
545 \brief The QTypeRevision class contains a lightweight representation of
546 a version number with two 8-bit segments, major and minor, either
547 of which can be unknown.
548
549 Use this class to describe revisions of a type. Compatible revisions can be
550 expressed as increments of the minor version. Breaking changes can be
551 expressed as increments of the major version. The return values of
552 \l QMetaMethod::revision() and \l QMetaProperty::revision() can be passed to
553 \l QTypeRevision::fromEncodedVersion(). The resulting major and minor versions
554 specify in which Qt versions the properties and methods were added.
555
556 \sa QMetaMethod::revision(), QMetaProperty::revision()
557*/
558
559/*!
560 \fn template<typename Integer> static bool QTypeRevision::isValidSegment(Integer segment)
561
562 Returns true if the given number can be used as either major or minor
563 version in a QTypeRevision. The valid range for \a segment is \c {>= 0} and \c {< 255}.
564*/
565
566/*!
567 \fn QTypeRevision::QTypeRevision()
568
569 Produces an invalid revision.
570
571 \sa isValid()
572*/
573
574/*!
575 \fn template <typename Major, typename Minor> static QTypeRevision QTypeRevision::fromVersion(Major majorVersion, Minor minorVersion)
576
577 Produces a QTypeRevision from the given \a majorVersion and \a minorVersion,
578 both of which need to be a valid segments.
579
580 \sa isValidSegment()
581*/
582
583/*!
584 \fn template <typename Major> static QTypeRevision QTypeRevision::fromMajorVersion(Major majorVersion)
585
586 Produces a QTypeRevision from the given \a majorVersion with an invalid minor
587 version. \a majorVersion needs to be a valid segment.
588
589 \sa isValidSegment()
590*/
591
592/*!
593 \fn template <typename Minor> static QTypeRevision QTypeRevision::fromMinorVersion(Minor minorVersion)
594
595 Produces a QTypeRevision from the given \a minorVersion with an invalid major
596 version. \a minorVersion needs to be a valid segment.
597
598 \sa isValidSegment()
599*/
600
601/*!
602 \fn template <typename Integer> static QTypeRevision QTypeRevision::fromEncodedVersion(Integer value)
603
604 Produces a QTypeRevision from the given \a value. \a value encodes both the
605 minor and major versions in the least significant and second least
606 significant byte, respectively.
607
608 \a value must not have any bits outside the least significant two bytes set.
609 \c Integer needs to be at least 16 bits wide, and must not have a sign bit
610 in the least significant 16 bits.
611
612 \sa toEncodedVersion()
613*/
614
615/*!
616 \fn static QTypeRevision QTypeRevision::zero()
617
618 Produces a QTypeRevision with major and minor version \c{0}.
619*/
620
621/*!
622 \fn bool QTypeRevision::hasMajorVersion() const
623
624 Returns true if the major version is known, otherwise false.
625
626 \sa majorVersion(), hasMinorVersion()
627*/
628
629/*!
630 \fn quint8 QTypeRevision::majorVersion() const
631
632 Returns the major version encoded in the revision.
633
634 \sa hasMajorVersion(), minorVersion()
635*/
636
637/*!
638 \fn bool QTypeRevision::hasMinorVersion() const
639
640 Returns true if the minor version is known, otherwise false.
641
642 \sa minorVersion(), hasMajorVersion()
643*/
644
645/*!
646 \fn quint8 QTypeRevision::minorVersion() const
647
648 Returns the minor version encoded in the revision.
649
650 \sa hasMinorVersion(), majorVersion()
651*/
652
653/*!
654 \fn bool QTypeRevision::isValid() const
655
656 Returns true if the major version or the minor version is known,
657 otherwise false.
658
659 \sa hasMajorVersion(), hasMinorVersion()
660*/
661
662/*!
663 \fn template<typename Integer> Integer QTypeRevision::toEncodedVersion() const
664
665 Transforms the revision into an integer value, encoding the minor
666 version into the least significant byte, and the major version into
667 the second least significant byte.
668
669 \c Integer needs to be at least 16 bits wide, and must not have a sign bit
670 in the least significant 16 bits.
671
672 \sa fromEncodedVersion()
673*/
674
675#ifndef QT_NO_DATASTREAM
676/*!
677 \fn QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision)
678 \relates QTypeRevision
679 \since 6.0
680
681 Writes the revision \a revision to stream \a out.
682 */
683QDataStream &operator<<(QDataStream &out, const QTypeRevision &revision)
684{
685 return out << revision.toEncodedVersion<quint16>();
686}
687
688/*!
689 \fn QDataStream& operator>>(QDataStream &in, QTypeRevision &revision)
690 \relates QTypeRevision
691 \since 6.0
692
693 Reads a revision from stream \a in and stores it in \a revision.
694 */
695QDataStream &operator>>(QDataStream &in, QTypeRevision &revision)
696{
697 quint16 value;
698 in >> value;
699 revision = QTypeRevision::fromEncodedVersion(value);
700 return in;
701}
702#endif
703
704#ifndef QT_NO_DEBUG_STREAM
705QDebug operator<<(QDebug debug, const QTypeRevision &revision)
706{
707 QDebugStateSaver saver(debug);
708 if (revision.hasMajorVersion()) {
709 if (revision.hasMinorVersion())
710 debug.nospace() << revision.majorVersion() << '.' << revision.minorVersion();
711 else
712 debug.nospace().noquote() << revision.majorVersion() << ".x";
713 } else {
714 if (revision.hasMinorVersion())
715 debug << revision.minorVersion();
716 else
717 debug.noquote() << "invalid";
718 }
719 return debug;
720}
721#endif
722
723/*!
724 \relates QHash
725 \since 6.0
726
727 Returns the hash value for the \a key, using \a seed to seed the
728 calculation.
729*/
730size_t qHash(const QTypeRevision &key, size_t seed)
731{
732 return qHash(key: key.toEncodedVersion<quint16>(), seed);
733}
734
735QT_END_NAMESPACE
736

source code of qtbase/src/corelib/tools/qversionnumber.cpp