/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Copyright (C) 2014 Keith Gardner ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSIONNUMBER_H #define QVERSIONNUMBER_H #include #include #include #include #include QT_BEGIN_NAMESPACE class QVersionNumber; Q_CORE_EXPORT uint qHash(const QVersionNumber &key, uint seed = 0); #ifndef QT_NO_DATASTREAM Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QVersionNumber &version); Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version); #endif class QVersionNumber { /* * QVersionNumber stores small values inline, without memory allocation. * We do that by setting the LSB in the pointer that would otherwise hold * the longer form of the segments. * The constants below help us deal with the permutations for 32- and 64-bit, * little- and big-endian architectures. */ enum { // in little-endian, inline_segments[0] is shared with the pointer's LSB, while // in big-endian, it's inline_segments[7] InlineSegmentMarker = Q_BYTE_ORDER == Q_LITTLE_ENDIAN ? 0 : sizeof(void*) - 1, InlineSegmentStartIdx = !InlineSegmentMarker, // 0 for BE, 1 for LE InlineSegmentCount = sizeof(void*) - 1 }; Q_STATIC_ASSERT(InlineSegmentCount >= 3); // at least major, minor, micro struct SegmentStorage { // Note: we alias the use of dummy and inline_segments in the use of the // union below. This is undefined behavior in C++98, but most compilers implement // the C++11 behavior. The one known exception is older versions of Sun Studio. union { quintptr dummy; qint8 inline_segments[sizeof(void*)]; QVector *pointer_segments; }; // set the InlineSegmentMarker and set length to zero SegmentStorage() noexcept : dummy(1) {} SegmentStorage(const QVector &seg) { if (dataFitsInline(seg.begin(), seg.size())) setInlineData(seg.begin(), seg.size()); else pointer_segments = new QVector(seg); } SegmentStorage(const SegmentStorage &other) { if (other.isUsingPointer()) pointer_segments = new QVector(*other.pointer_segments); else dummy = other.dummy; } SegmentStorage &operator=(const SegmentStorage &other) { if (isUsingPointer() && other.isUsingPointer()) { *pointer_segments = *other.pointer_segments; } else if (other.isUsingPointer()) { pointer_segments = new QVector(*other.pointer_segments); } else { if (isUsingPointer()) delete pointer_segments; dummy = other.dummy; } return *this; } SegmentStorage(SegmentStorage &&other) noexcept : dummy(other.dummy) { other.dummy = 1; } SegmentStorage &operator=(SegmentStorage &&other) noexcept { qSwap(dummy, other.dummy); return *this; } explicit SegmentStorage(QVector &&seg) { if (dataFitsInline(seg.begin(), seg.size())) setInlineData(seg.begin(), seg.size()); else pointer_segments = new QVector(std::move(seg)); } SegmentStorage(std::initializer_list args) { if (dataFitsInline(args.begin(), int(args.size()))) { setInlineData(args.begin(), int(args.size())); } else { pointer_segments = new QVector(args); } } ~SegmentStorage() { if (isUsingPointer()) delete pointer_segments; } bool isUsingPointer() const noexcept { return (inline_segments[InlineSegmentMarker] & 1) == 0; } int size() const noexcept { return isUsingPointer() ? pointer_segments->size() : (inline_segments[InlineSegmentMarker] >> 1); } void setInlineSize(int len) { inline_segments[InlineSegmentMarker] = 1 + 2 * len; } void resize(int len) { if (isUsingPointer()) pointer_segments->resize(len); else setInlineSize(len); } int at(int index) const { return isUsingPointer() ? pointer_segments->at(index) : inline_segments[InlineSegmentStartIdx + index]; } void setSegments(int len, int maj, int min = 0, int mic = 0) { if (maj == qint8(maj) && min == qint8(min) && mic == qint8(mic)) { int data[] = { maj, min, mic }; setInlineData(data, len); } else { setVector(len, maj, min, mic); } } private: static bool dataFitsInline(const int *data, int len) { if (len > InlineSegmentCount) return false; for (int i = 0; i < len; ++i) if (data[i] != qint8(data[i])) return false; return true; } void setInlineData(const int *data, int len) { dummy = 1 + len * 2; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN for (int i = 0; i < len; ++i) dummy |= quintptr(data[i] & 0xFF) << (8 * (i + 1)); #elif Q_BYTE_ORDER == Q_BIG_ENDIAN for (int i = 0; i < len; ++i) dummy |= quintptr(data[i] & 0xFF) << (8 * (sizeof(void *) - i - 1)); #else // the code above is equivalent to: setInlineSize(len); for (int i = 0; i < len; ++i) inline_segments[InlineSegmentStartIdx + i] = data[i] & 0xFF; #endif } Q_CORE_EXPORT void setVector(int len, int maj, int min, int mic); } m_segments; public: inline QVersionNumber() noexcept : m_segments() {} inline explicit QVersionNumber(const QVector &seg) : m_segments(seg) {} // compiler-generated copy/move ctor/assignment operators and the destructor are ok explicit QVersionNumber(QVector &&seg) : m_segments(std::move(seg)) {} inline QVersionNumber(std::initializer_list args) : m_segments(args) {} inline explicit QVersionNumber(int maj) { m_segments.setSegments(1, maj); } inline explicit QVersionNumber(int maj, int min) { m_segments.setSegments(2, maj, min); } inline explicit QVersionNumber(int maj, int min, int mic) { m_segments.setSegments(3, maj, min, mic); } Q_REQUIRED_RESULT inline bool isNull() const noexcept { return segmentCount() == 0; } Q_REQUIRED_RESULT inline bool isNormalized() const noexcept { return isNull() || segmentAt(segmentCount() - 1) != 0; } Q_REQUIRED_RESULT inline int majorVersion() const noexcept { return segmentAt(0); } Q_REQUIRED_RESULT inline int minorVersion() const noexcept { return segmentAt(1); } Q_REQUIRED_RESULT inline int microVersion() const noexcept { return segmentAt(2); } Q_REQUIRED_RESULT Q_CORE_EXPORT QVersionNumber normalized() const; Q_REQUIRED_RESULT Q_CORE_EXPORT QVector segments() const; Q_REQUIRED_RESULT inline int segmentAt(int index) const noexcept { return (m_segments.size() > index) ? m_segments.at(index) : 0; } Q_REQUIRED_RESULT inline int segmentCount() const noexcept { return m_segments.size(); } Q_REQUIRED_RESULT Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const noexcept; Q_REQUIRED_RESULT Q_CORE_EXPORT static int compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept; Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2); Q_REQUIRED_RESULT Q_CORE_EXPORT QString toString() const; #if QT_STRINGVIEW_LEVEL < 2 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr); #endif Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QLatin1String string, int *suffixIndex = nullptr); Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QStringView string, int *suffixIndex = nullptr); private: #ifndef QT_NO_DATASTREAM friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version); #endif friend Q_CORE_EXPORT uint qHash(const QVersionNumber &key, uint seed); }; Q_DECLARE_TYPEINFO(QVersionNumber, Q_MOVABLE_TYPE); #ifndef QT_NO_DEBUG_STREAM Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version); #endif Q_REQUIRED_RESULT inline bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept { return QVersionNumber::compare(lhs, rhs) > 0; } Q_REQUIRED_RESULT inline bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept { return QVersionNumber::compare(lhs, rhs) >= 0; } Q_REQUIRED_RESULT inline bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept { return QVersionNumber::compare(lhs, rhs) < 0; } Q_REQUIRED_RESULT inline bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept { return QVersionNumber::compare(lhs, rhs) <= 0; } Q_REQUIRED_RESULT inline bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept { return QVersionNumber::compare(lhs, rhs) == 0; } Q_REQUIRED_RESULT inline bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept { return QVersionNumber::compare(lhs, rhs) != 0; } QT_END_NAMESPACE Q_DECLARE_METATYPE(QVersionNumber) #endif //QVERSIONNUMBER_H