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 #ifndef QVERSIONNUMBER_H 43 #define QVERSIONNUMBER_H 44 45 #include <QtCore/qnamespace.h> 46 #include <QtCore/qstring.h> 47 #include <QtCore/qvector.h> 48 #include <QtCore/qmetatype.h> 49 #include <QtCore/qtypeinfo.h> 50 51 QT_BEGIN_NAMESPACE 52 53 class QVersionNumber; 54 Q_CORE_EXPORT uint qHash(const QVersionNumber &key, uint seed = 0); 55 56 #ifndef QT_NO_DATASTREAM 57 Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QVersionNumber &version); 58 Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version); 59 #endif 60 61 class QVersionNumber 62 { 63 /* 64 * QVersionNumber stores small values inline, without memory allocation. 65 * We do that by setting the LSB in the pointer that would otherwise hold 66 * the longer form of the segments. 67 * The constants below help us deal with the permutations for 32- and 64-bit, 68 * little- and big-endian architectures. 69 */ 70 enum { 71 // in little-endian, inline_segments[0] is shared with the pointer's LSB, while 72 // in big-endian, it's inline_segments[7] 73 InlineSegmentMarker = Q_BYTE_ORDER == Q_LITTLE_ENDIAN ? 0 : sizeof(void*) - 1, 74 InlineSegmentStartIdx = !InlineSegmentMarker, // 0 for BE, 1 for LE 75 InlineSegmentCount = sizeof(void*) - 1 76 }; 77 Q_STATIC_ASSERT(InlineSegmentCount >= 3); // at least major, minor, micro 78 79 struct SegmentStorage { 80 // Note: we alias the use of dummy and inline_segments in the use of the 81 // union below. This is undefined behavior in C++98, but most compilers implement 82 // the C++11 behavior. The one known exception is older versions of Sun Studio. 83 union { 84 quintptr dummy; 85 qint8 inline_segments[sizeof(void*)]; 86 QVector<int> *pointer_segments; 87 }; 88 89 // set the InlineSegmentMarker and set length to zero SegmentStorageSegmentStorage90 SegmentStorage() noexcept : dummy(1) {} 91 SegmentStorageSegmentStorage92 SegmentStorage(const QVector<int> &seg) 93 { 94 if (dataFitsInline(seg.begin(), seg.size())) 95 setInlineData(seg.begin(), seg.size()); 96 else 97 pointer_segments = new QVector<int>(seg); 98 } 99 SegmentStorageSegmentStorage100 SegmentStorage(const SegmentStorage &other) 101 { 102 if (other.isUsingPointer()) 103 pointer_segments = new QVector<int>(*other.pointer_segments); 104 else 105 dummy = other.dummy; 106 } 107 108 SegmentStorage &operator=(const SegmentStorage &other) 109 { 110 if (isUsingPointer() && other.isUsingPointer()) { 111 *pointer_segments = *other.pointer_segments; 112 } else if (other.isUsingPointer()) { 113 pointer_segments = new QVector<int>(*other.pointer_segments); 114 } else { 115 if (isUsingPointer()) 116 delete pointer_segments; 117 dummy = other.dummy; 118 } 119 return *this; 120 } 121 SegmentStorageSegmentStorage122 SegmentStorage(SegmentStorage &&other) noexcept 123 : dummy(other.dummy) 124 { 125 other.dummy = 1; 126 } 127 128 SegmentStorage &operator=(SegmentStorage &&other) noexcept 129 { 130 qSwap(dummy, other.dummy); 131 return *this; 132 } 133 SegmentStorageSegmentStorage134 explicit SegmentStorage(QVector<int> &&seg) 135 { 136 if (dataFitsInline(seg.begin(), seg.size())) 137 setInlineData(seg.begin(), seg.size()); 138 else 139 pointer_segments = new QVector<int>(std::move(seg)); 140 } SegmentStorageSegmentStorage141 SegmentStorage(std::initializer_list<int> args) 142 { 143 if (dataFitsInline(args.begin(), int(args.size()))) { 144 setInlineData(args.begin(), int(args.size())); 145 } else { 146 pointer_segments = new QVector<int>(args); 147 } 148 } 149 ~SegmentStorageSegmentStorage150 ~SegmentStorage() { if (isUsingPointer()) delete pointer_segments; } 151 isUsingPointerSegmentStorage152 bool isUsingPointer() const noexcept 153 { return (inline_segments[InlineSegmentMarker] & 1) == 0; } 154 sizeSegmentStorage155 int size() const noexcept 156 { return isUsingPointer() ? pointer_segments->size() : (inline_segments[InlineSegmentMarker] >> 1); } 157 setInlineSizeSegmentStorage158 void setInlineSize(int len) 159 { inline_segments[InlineSegmentMarker] = 1 + 2 * len; } 160 resizeSegmentStorage161 void resize(int len) 162 { 163 if (isUsingPointer()) 164 pointer_segments->resize(len); 165 else 166 setInlineSize(len); 167 } 168 atSegmentStorage169 int at(int index) const 170 { 171 return isUsingPointer() ? 172 pointer_segments->at(index) : 173 inline_segments[InlineSegmentStartIdx + index]; 174 } 175 176 void setSegments(int len, int maj, int min = 0, int mic = 0) 177 { 178 if (maj == qint8(maj) && min == qint8(min) && mic == qint8(mic)) { 179 int data[] = { maj, min, mic }; 180 setInlineData(data, len); 181 } else { 182 setVector(len, maj, min, mic); 183 } 184 } 185 186 private: dataFitsInlineSegmentStorage187 static bool dataFitsInline(const int *data, int len) 188 { 189 if (len > InlineSegmentCount) 190 return false; 191 for (int i = 0; i < len; ++i) 192 if (data[i] != qint8(data[i])) 193 return false; 194 return true; 195 } setInlineDataSegmentStorage196 void setInlineData(const int *data, int len) 197 { 198 dummy = 1 + len * 2; 199 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN 200 for (int i = 0; i < len; ++i) 201 dummy |= quintptr(data[i] & 0xFF) << (8 * (i + 1)); 202 #elif Q_BYTE_ORDER == Q_BIG_ENDIAN 203 for (int i = 0; i < len; ++i) 204 dummy |= quintptr(data[i] & 0xFF) << (8 * (sizeof(void *) - i - 1)); 205 #else 206 // the code above is equivalent to: 207 setInlineSize(len); 208 for (int i = 0; i < len; ++i) 209 inline_segments[InlineSegmentStartIdx + i] = data[i] & 0xFF; 210 #endif 211 } 212 213 Q_CORE_EXPORT void setVector(int len, int maj, int min, int mic); 214 } m_segments; 215 216 public: QVersionNumber()217 inline QVersionNumber() noexcept 218 : m_segments() 219 {} QVersionNumber(const QVector<int> & seg)220 inline explicit QVersionNumber(const QVector<int> &seg) 221 : m_segments(seg) 222 {} 223 224 // compiler-generated copy/move ctor/assignment operators and the destructor are ok 225 QVersionNumber(QVector<int> && seg)226 explicit QVersionNumber(QVector<int> &&seg) 227 : m_segments(std::move(seg)) 228 {} 229 QVersionNumber(std::initializer_list<int> args)230 inline QVersionNumber(std::initializer_list<int> args) 231 : m_segments(args) 232 {} 233 QVersionNumber(int maj)234 inline explicit QVersionNumber(int maj) 235 { m_segments.setSegments(1, maj); } 236 QVersionNumber(int maj,int min)237 inline explicit QVersionNumber(int maj, int min) 238 { m_segments.setSegments(2, maj, min); } 239 QVersionNumber(int maj,int min,int mic)240 inline explicit QVersionNumber(int maj, int min, int mic) 241 { m_segments.setSegments(3, maj, min, mic); } 242 isNull()243 Q_REQUIRED_RESULT inline bool isNull() const noexcept 244 { return segmentCount() == 0; } 245 isNormalized()246 Q_REQUIRED_RESULT inline bool isNormalized() const noexcept 247 { return isNull() || segmentAt(segmentCount() - 1) != 0; } 248 majorVersion()249 Q_REQUIRED_RESULT inline int majorVersion() const noexcept 250 { return segmentAt(0); } 251 minorVersion()252 Q_REQUIRED_RESULT inline int minorVersion() const noexcept 253 { return segmentAt(1); } 254 microVersion()255 Q_REQUIRED_RESULT inline int microVersion() const noexcept 256 { return segmentAt(2); } 257 258 Q_REQUIRED_RESULT Q_CORE_EXPORT QVersionNumber normalized() const; 259 260 Q_REQUIRED_RESULT Q_CORE_EXPORT QVector<int> segments() const; 261 segmentAt(int index)262 Q_REQUIRED_RESULT inline int segmentAt(int index) const noexcept 263 { return (m_segments.size() > index) ? m_segments.at(index) : 0; } 264 segmentCount()265 Q_REQUIRED_RESULT inline int segmentCount() const noexcept 266 { return m_segments.size(); } 267 268 Q_REQUIRED_RESULT Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const noexcept; 269 270 Q_REQUIRED_RESULT Q_CORE_EXPORT static int compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept; 271 272 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2); 273 274 Q_REQUIRED_RESULT Q_CORE_EXPORT QString toString() const; 275 #if QT_STRINGVIEW_LEVEL < 2 276 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr); 277 #endif 278 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QLatin1String string, int *suffixIndex = nullptr); 279 Q_REQUIRED_RESULT Q_CORE_EXPORT static Q_DECL_PURE_FUNCTION QVersionNumber fromString(QStringView string, int *suffixIndex = nullptr); 280 281 private: 282 #ifndef QT_NO_DATASTREAM 283 friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version); 284 #endif 285 friend Q_CORE_EXPORT uint qHash(const QVersionNumber &key, uint seed); 286 }; 287 288 Q_DECLARE_TYPEINFO(QVersionNumber, Q_MOVABLE_TYPE); 289 290 #ifndef QT_NO_DEBUG_STREAM 291 Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version); 292 #endif 293 294 Q_REQUIRED_RESULT inline bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept 295 { return QVersionNumber::compare(lhs, rhs) > 0; } 296 297 Q_REQUIRED_RESULT inline bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept 298 { return QVersionNumber::compare(lhs, rhs) >= 0; } 299 300 Q_REQUIRED_RESULT inline bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept 301 { return QVersionNumber::compare(lhs, rhs) < 0; } 302 303 Q_REQUIRED_RESULT inline bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept 304 { return QVersionNumber::compare(lhs, rhs) <= 0; } 305 306 Q_REQUIRED_RESULT inline bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept 307 { return QVersionNumber::compare(lhs, rhs) == 0; } 308 309 Q_REQUIRED_RESULT inline bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept 310 { return QVersionNumber::compare(lhs, rhs) != 0; } 311 312 QT_END_NAMESPACE 313 314 Q_DECLARE_METATYPE(QVersionNumber) 315 316 #endif //QVERSIONNUMBER_H 317