Use "Constructs" instead of "Construct" in the initializer_list ctor docs, because it's what all other ctor docs also use. Pick-to: 6.6 6.5 6.2 5.15 Change-Id: Ie84d208b81a062851d95fb336553e8e7c57d4b73 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 6147b3f5b2da44b26ff736e3ec850f7cd4cd2ccf) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
536 lines
15 KiB
C++
536 lines
15 KiB
C++
// Copyright (C) 2021 The Qt Company Ltd.
|
|
// Copyright (C) 2016 Intel Corporation.
|
|
// Copyright (C) 2014 Keith Gardner <kreios4004@gmail.com>
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#include <QtCore/qversionnumber.h>
|
|
#include <QtCore/qhash.h>
|
|
#include <QtCore/private/qlocale_tools_p.h>
|
|
#include <QtCore/qcollator.h>
|
|
|
|
#ifndef QT_NO_DATASTREAM
|
|
# include <QtCore/qdatastream.h>
|
|
#endif
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
# include <QtCore/qdebug.h>
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
QT_IMPL_METATYPE_EXTERN(QVersionNumber)
|
|
|
|
/*!
|
|
\class QVersionNumber
|
|
\inmodule QtCore
|
|
\since 5.6
|
|
\brief The QVersionNumber class contains a version number with an arbitrary
|
|
number of segments.
|
|
|
|
\snippet qversionnumber/main.cpp 0
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber::QVersionNumber()
|
|
|
|
Produces a null version.
|
|
|
|
\sa isNull()
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber::QVersionNumber(int maj)
|
|
|
|
Constructs a QVersionNumber consisting of just the major version number \a maj.
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber::QVersionNumber(int maj, int min)
|
|
|
|
Constructs a QVersionNumber consisting of the major and minor
|
|
version numbers \a maj and \a min, respectively.
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber::QVersionNumber(int maj, int min, int mic)
|
|
|
|
Constructs a QVersionNumber consisting of the major, minor, and
|
|
micro version numbers \a maj, \a min and \a mic, respectively.
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber::QVersionNumber(const QList<int> &seg)
|
|
|
|
Constructs a version number from the list of numbers contained in \a seg.
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber::QVersionNumber(QList<int> &&seg)
|
|
|
|
Move-constructs a version number from the list of numbers contained in \a seg.
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber::QVersionNumber(std::initializer_list<int> args)
|
|
|
|
Constructs a version number from the std::initializer_list specified by
|
|
\a args.
|
|
*/
|
|
|
|
/*!
|
|
\fn template <qsizetype N> QVersionNumber::QVersionNumber(const QVarLengthArray<int, N> &seg)
|
|
\since 6.4
|
|
|
|
Constructs a version number from the list of numbers contained in \a seg.
|
|
*/
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::isNull() const
|
|
|
|
Returns \c true if there are zero numerical segments, otherwise returns
|
|
\c false.
|
|
|
|
\sa segments()
|
|
*/
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::isNormalized() const
|
|
|
|
Returns \c true if the version number does not contain any trailing zeros,
|
|
otherwise returns \c false.
|
|
|
|
\sa normalized()
|
|
*/
|
|
|
|
/*!
|
|
\fn int QVersionNumber::majorVersion() const
|
|
|
|
Returns the major version number, that is, the first segment.
|
|
This function is equivalent to segmentAt(0). If this QVersionNumber object
|
|
is null, this function returns 0.
|
|
|
|
\sa isNull(), segmentAt()
|
|
*/
|
|
|
|
/*!
|
|
\fn int QVersionNumber::minorVersion() const
|
|
|
|
Returns the minor version number, that is, the second segment.
|
|
This function is equivalent to segmentAt(1). If this QVersionNumber object
|
|
does not contain a minor number, this function returns 0.
|
|
|
|
\sa isNull(), segmentAt()
|
|
*/
|
|
|
|
/*!
|
|
\fn int QVersionNumber::microVersion() const
|
|
|
|
Returns the micro version number, that is, the third segment.
|
|
This function is equivalent to segmentAt(2). If this QVersionNumber object
|
|
does not contain a micro number, this function returns 0.
|
|
|
|
\sa isNull(), segmentAt()
|
|
*/
|
|
|
|
/*!
|
|
\fn QList<int> QVersionNumber::segments() const
|
|
|
|
Returns all of the numerical segments.
|
|
|
|
\sa majorVersion(), minorVersion(), microVersion()
|
|
*/
|
|
QList<int> QVersionNumber::segments() const
|
|
{
|
|
if (m_segments.isUsingPointer())
|
|
return *m_segments.pointer_segments;
|
|
|
|
QList<int> result;
|
|
result.resize(segmentCount());
|
|
for (qsizetype i = 0; i < segmentCount(); ++i)
|
|
result[i] = segmentAt(i);
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
\fn int QVersionNumber::segmentAt(qsizetype index) const
|
|
|
|
Returns the segment value at \a index. If the index does not exist,
|
|
returns 0.
|
|
|
|
\sa segments(), segmentCount()
|
|
*/
|
|
|
|
/*!
|
|
\fn qsizetype QVersionNumber::segmentCount() const
|
|
|
|
Returns the number of integers stored in segments().
|
|
|
|
\sa segments()
|
|
*/
|
|
|
|
/*!
|
|
\fn QVersionNumber QVersionNumber::normalized() const
|
|
|
|
Returns an equivalent version number but with all trailing zeros removed.
|
|
|
|
To check if two numbers are equivalent, use normalized() on both version
|
|
numbers before performing the compare.
|
|
|
|
\snippet qversionnumber/main.cpp 4
|
|
*/
|
|
QVersionNumber QVersionNumber::normalized() const
|
|
{
|
|
qsizetype i;
|
|
for (i = m_segments.size(); i; --i)
|
|
if (m_segments.at(i - 1) != 0)
|
|
break;
|
|
|
|
QVersionNumber result(*this);
|
|
result.m_segments.resize(i);
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const
|
|
|
|
Returns \c true if the current version number is contained in the \a other
|
|
version number, otherwise returns \c false.
|
|
|
|
\snippet qversionnumber/main.cpp 2
|
|
|
|
\sa commonPrefix()
|
|
*/
|
|
bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const noexcept
|
|
{
|
|
if (segmentCount() > other.segmentCount())
|
|
return false;
|
|
for (qsizetype i = 0; i < segmentCount(); ++i) {
|
|
if (segmentAt(i) != other.segmentAt(i))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
\fn int QVersionNumber::compare(const QVersionNumber &v1,
|
|
const QVersionNumber &v2)
|
|
|
|
Compares \a v1 with \a v2 and returns an integer less than, equal to, or
|
|
greater than zero, depending on whether \a v1 is less than, equal to, or
|
|
greater than \a v2, respectively.
|
|
|
|
Comparisons are performed by comparing the segments of \a v1 and \a v2
|
|
starting at index 0 and working towards the end of the longer list.
|
|
|
|
\snippet qversionnumber/main.cpp 1
|
|
*/
|
|
int QVersionNumber::compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept
|
|
{
|
|
qsizetype commonlen;
|
|
|
|
if (Q_LIKELY(!v1.m_segments.isUsingPointer() && !v2.m_segments.isUsingPointer())) {
|
|
// we can't use memcmp because it interprets the data as unsigned bytes
|
|
const qint8 *ptr1 = v1.m_segments.inline_segments + InlineSegmentStartIdx;
|
|
const qint8 *ptr2 = v2.m_segments.inline_segments + InlineSegmentStartIdx;
|
|
commonlen = qMin(v1.m_segments.size(),
|
|
v2.m_segments.size());
|
|
for (qsizetype i = 0; i < commonlen; ++i)
|
|
if (int x = ptr1[i] - ptr2[i])
|
|
return x;
|
|
} else {
|
|
commonlen = qMin(v1.segmentCount(), v2.segmentCount());
|
|
for (qsizetype i = 0; i < commonlen; ++i) {
|
|
if (v1.segmentAt(i) != v2.segmentAt(i))
|
|
return v1.segmentAt(i) - v2.segmentAt(i);
|
|
}
|
|
}
|
|
|
|
// ran out of segments in v1 and/or v2 and need to check the first trailing
|
|
// segment to finish the compare
|
|
if (v1.segmentCount() > commonlen) {
|
|
// v1 is longer
|
|
if (v1.segmentAt(commonlen) != 0)
|
|
return v1.segmentAt(commonlen);
|
|
else
|
|
return 1;
|
|
} else if (v2.segmentCount() > commonlen) {
|
|
// v2 is longer
|
|
if (v2.segmentAt(commonlen) != 0)
|
|
return -v2.segmentAt(commonlen);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
// the two version numbers are the same
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
|
|
const QVersionNumber &v2)
|
|
|
|
Returns a version number that is a parent version of both \a v1 and \a v2.
|
|
|
|
\sa isPrefixOf()
|
|
*/
|
|
QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
|
|
const QVersionNumber &v2)
|
|
{
|
|
qsizetype commonlen = qMin(v1.segmentCount(), v2.segmentCount());
|
|
qsizetype i;
|
|
for (i = 0; i < commonlen; ++i) {
|
|
if (v1.segmentAt(i) != v2.segmentAt(i))
|
|
break;
|
|
}
|
|
|
|
if (i == 0)
|
|
return QVersionNumber();
|
|
|
|
// try to use the one with inline segments, if there's one
|
|
QVersionNumber result(!v1.m_segments.isUsingPointer() ? v1 : v2);
|
|
result.m_segments.resize(i);
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::operator<(const QVersionNumber &lhs, const QVersionNumber &rhs)
|
|
|
|
Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false.
|
|
|
|
\sa QVersionNumber::compare()
|
|
*/
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs)
|
|
|
|
Returns \c true if \a lhs is less than or equal to \a rhs; otherwise
|
|
returns \c false.
|
|
|
|
\sa QVersionNumber::compare()
|
|
*/
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::operator>(const QVersionNumber &lhs, const QVersionNumber &rhs)
|
|
|
|
Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c
|
|
false.
|
|
|
|
\sa QVersionNumber::compare()
|
|
*/
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs)
|
|
|
|
Returns \c true if \a lhs is greater than or equal to \a rhs; otherwise
|
|
returns \c false.
|
|
|
|
\sa QVersionNumber::compare()
|
|
*/
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::operator==(const QVersionNumber &lhs, const QVersionNumber &rhs)
|
|
|
|
Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
|
|
|
|
\sa QVersionNumber::compare()
|
|
*/
|
|
|
|
/*!
|
|
\fn bool QVersionNumber::operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs)
|
|
|
|
Returns \c true if \a lhs is not equal to \a rhs; otherwise returns
|
|
\c false.
|
|
|
|
\sa QVersionNumber::compare()
|
|
*/
|
|
|
|
/*!
|
|
\fn QString QVersionNumber::toString() const
|
|
|
|
Returns a string with all of the segments delimited by a period (\c{.}).
|
|
|
|
\sa majorVersion(), minorVersion(), microVersion(), segments()
|
|
*/
|
|
QString QVersionNumber::toString() const
|
|
{
|
|
QString version;
|
|
version.reserve(qMax(segmentCount() * 2 - 1, 0));
|
|
bool first = true;
|
|
for (qsizetype i = 0; i < segmentCount(); ++i) {
|
|
if (!first)
|
|
version += u'.';
|
|
version += QString::number(segmentAt(i));
|
|
first = false;
|
|
}
|
|
return version;
|
|
}
|
|
|
|
/*!
|
|
\fn QVersionNumber QVersionNumber::fromString(QAnyStringView string, qsizetype *suffixIndex)
|
|
\since 6.4
|
|
|
|
Constructs a QVersionNumber from a specially formatted \a string of
|
|
non-negative decimal numbers delimited by a period (\c{.}).
|
|
|
|
Once the numerical segments have been parsed, the remainder of the string
|
|
is considered to be the suffix string. The start index of that string will be
|
|
stored in \a suffixIndex if it is not null.
|
|
|
|
\snippet qversionnumber/main.cpp 3-latin1-1
|
|
|
|
\note In versions prior to Qt 6.4, this function was overloaded for QString,
|
|
QLatin1StringView and QStringView instead, and \a suffixIndex was an \c{int*}.
|
|
|
|
\sa isNull()
|
|
*/
|
|
|
|
static QVersionNumber from_string(QLatin1StringView string, qsizetype *suffixIndex)
|
|
{
|
|
// 32 should be more than enough, and, crucially, it means we're allocating
|
|
// not more (and often less) often when compared with direct QList usage
|
|
// for all possible segment counts (under the constraint that we don't want
|
|
// to keep more capacity around for the lifetime of the resulting
|
|
// QVersionNumber than required), esp. in the common case where the inline
|
|
// storage can be used.
|
|
QVarLengthArray<int, 32> seg;
|
|
|
|
const char *start = string.begin();
|
|
const char *lastGoodEnd = start;
|
|
const char *endOfString = string.end();
|
|
|
|
do {
|
|
// parsing as unsigned so a minus sign is rejected
|
|
auto [value, used] = qstrntoull(start, endOfString - start, 10);
|
|
if (used <= 0 || value > qulonglong(std::numeric_limits<int>::max()))
|
|
break;
|
|
seg.append(int(value));
|
|
start += used + 1;
|
|
lastGoodEnd = start - 1;
|
|
} while (start < endOfString && *lastGoodEnd == '.');
|
|
|
|
if (suffixIndex)
|
|
*suffixIndex = lastGoodEnd - string.begin();
|
|
|
|
return QVersionNumber(seg);
|
|
}
|
|
|
|
static QVersionNumber from_string(q_no_char8_t::QUtf8StringView string, qsizetype *suffixIndex)
|
|
{
|
|
return from_string(QLatin1StringView(string.data(), string.size()), suffixIndex);
|
|
}
|
|
|
|
// in qstring.cpp
|
|
extern void qt_to_latin1(uchar *dst, const char16_t *uc, qsizetype len);
|
|
|
|
static QVersionNumber from_string(QStringView string, qsizetype *suffixIndex)
|
|
{
|
|
QVarLengthArray<char> copy;
|
|
copy.resize(string.size());
|
|
qt_to_latin1(reinterpret_cast<uchar*>(copy.data()), string.utf16(), string.size());
|
|
return from_string(QLatin1StringView(copy.data(), copy.size()), suffixIndex);
|
|
}
|
|
|
|
QVersionNumber QVersionNumber::fromString(QAnyStringView string, qsizetype *suffixIndex)
|
|
{
|
|
return string.visit([=] (auto string) { return from_string(string, suffixIndex); });
|
|
}
|
|
|
|
void QVersionNumber::SegmentStorage::setListData(const QList<int> &seg)
|
|
{
|
|
pointer_segments = new QList<int>(seg);
|
|
}
|
|
|
|
void QVersionNumber::SegmentStorage::setListData(QList<int> &&seg)
|
|
{
|
|
pointer_segments = new QList<int>(std::move(seg));
|
|
}
|
|
|
|
void QVersionNumber::SegmentStorage::setListData(const int *first, const int *last)
|
|
{
|
|
pointer_segments = new QList<int>(first, last);
|
|
}
|
|
|
|
void QVersionNumber::SegmentStorage::resize(qsizetype len)
|
|
{
|
|
if (isUsingPointer())
|
|
pointer_segments->resize(len);
|
|
else
|
|
setInlineSize(len);
|
|
}
|
|
|
|
void QVersionNumber::SegmentStorage::setVector(int len, int maj, int min, int mic)
|
|
{
|
|
pointer_segments = new QList<int>;
|
|
pointer_segments->resize(len);
|
|
pointer_segments->data()[0] = maj;
|
|
if (len > 1) {
|
|
pointer_segments->data()[1] = min;
|
|
if (len > 2) {
|
|
pointer_segments->data()[2] = mic;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef QT_NO_DATASTREAM
|
|
/*!
|
|
\fn QDataStream& operator<<(QDataStream &out,
|
|
const QVersionNumber &version)
|
|
\relates QVersionNumber
|
|
|
|
Writes the version number \a version to stream \a out.
|
|
|
|
Note that this has nothing to do with QDataStream::version().
|
|
*/
|
|
QDataStream& operator<<(QDataStream &out, const QVersionNumber &version)
|
|
{
|
|
out << version.segments();
|
|
return out;
|
|
}
|
|
|
|
/*!
|
|
\fn QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
|
|
\relates QVersionNumber
|
|
|
|
Reads a version number from stream \a in and stores it in \a version.
|
|
|
|
Note that this has nothing to do with QDataStream::version().
|
|
*/
|
|
QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
|
|
{
|
|
if (!version.m_segments.isUsingPointer())
|
|
version.m_segments.pointer_segments = new QList<int>;
|
|
in >> *version.m_segments.pointer_segments;
|
|
return in;
|
|
}
|
|
#endif
|
|
|
|
#ifndef QT_NO_DEBUG_STREAM
|
|
QDebug operator<<(QDebug debug, const QVersionNumber &version)
|
|
{
|
|
QDebugStateSaver saver(debug);
|
|
debug.nospace().noquote();
|
|
debug << "QVersionNumber(" << version.toString() << ")";
|
|
return debug;
|
|
}
|
|
#endif
|
|
|
|
/*!
|
|
\relates QHash
|
|
\since 5.6
|
|
|
|
Returns the hash value for the \a key, using \a seed to seed the
|
|
calculation.
|
|
*/
|
|
size_t qHash(const QVersionNumber &key, size_t seed)
|
|
{
|
|
QtPrivate::QHashCombine hash;
|
|
for (int i = 0; i < key.segmentCount(); ++i)
|
|
seed = hash(seed, key.segmentAt(i));
|
|
return seed;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|