Revision control
++ // 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
++ // Qt-Security score:critical reason:data-parser
++
++ #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.
++
++ \compares strong
++ \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 QVersionNumber::QVersionNumber(QSpan<const int> args)
++ \since 6.8
++
++ Constructs a version number from the span specified by \a args.
++
++ \note In Qt versions prior to 6.8, QVersionNumber could only be constructed
++ from QList, QVarLenthArray or std::initializer_list.
++ */
++
++ /*!
++ \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()
++ */
++
++ /*!
++ \typedef QVersionNumber::const_iterator
++ \typedef QVersionNumber::const_reverse_iterator
++ \since 6.8
++
++ Typedefs for an opaque class that implements a (reverse) random-access
++ iterator over QVersionNumber segments.
++
++ \note QVersionNumber does not support modifying segments in-place, so
++ there is no mutable iterator.
++ */
++
++ /*!
++ \typedef QVersionNumber::value_type
++ \typedef QVersionNumber::difference_type
++ \typedef QVersionNumber::size_type
++ \typedef QVersionNumber::reference
++ \typedef QVersionNumber::const_reference
++ \typedef QVersionNumber::pointer
++ \typedef QVersionNumber::const_pointer
++ \since 6.8
++
++ Provided for STL-compatibility.
++
++ \note QVersionNumber does not support modifying segments in-place, so
++ reference and const_reference, as well as pointer and const_pointer are
++ pairwise the same types.
++ */
++
++ /*!
++ \fn QVersionNumber::begin() const
++ \fn QVersionNumber::end() const;
++ \fn QVersionNumber::rbegin() const
++ \fn QVersionNumber::rend() const;
++ \fn QVersionNumber::cbegin() const
++ \fn QVersionNumber::cend() const;
++ \fn QVersionNumber::crbegin() const
++ \fn QVersionNumber::crend() const;
++ \fn QVersionNumber::constBegin() const;
++ \fn QVersionNumber::constEnd() const;
++ \since 6.8
++
++ Returns a const_iterator or const_reverse_iterator, respectively, pointing
++ to the first or one past the last segment of this version number.
++
++ \note QVersionNumber does not support modifying segments in-place, so
++ there is no mutable iterator.
++ */
++
++ /*!
++ \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
++
++ /*!
++ \qhashold{QHash}
++ \since 5.6
++ */
++ size_t qHash(const QVersionNumber &key, size_t seed)
++ {
++ return qHashRange(key.begin(), key.end(), seed);
++ }
++
++ QT_END_NAMESPACE