Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qversionnumber.cpp
Go to the documentation of this file.
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// Qt-Security score:critical reason:data-parser
6
7#include <QtCore/qversionnumber.h>
8#include <QtCore/qhash.h>
9#include <QtCore/private/qlocale_tools_p.h>
10#include <QtCore/qcollator.h>
11
12#ifndef QT_NO_DATASTREAM
13# include <QtCore/qdatastream.h>
14#endif
15
16#ifndef QT_NO_DEBUG_STREAM
17# include <QtCore/qdebug.h>
18#endif
19
20#include <algorithm>
21#include <limits>
22
23QT_BEGIN_NAMESPACE
24
25QT_IMPL_METATYPE_EXTERN(QVersionNumber)
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 \compares strong
35 \snippet qversionnumber/main.cpp 0
36*/
37
38/*!
39 \fn QVersionNumber::QVersionNumber()
40
41 Produces a null version.
42
43 \sa isNull()
44*/
45
46/*!
47 \fn QVersionNumber::QVersionNumber(int maj)
48
49 Constructs a QVersionNumber consisting of just the major version number \a maj.
50*/
51
52/*!
53 \fn QVersionNumber::QVersionNumber(int maj, int min)
54
55 Constructs a QVersionNumber consisting of the major and minor
56 version numbers \a maj and \a min, respectively.
57*/
58
59/*!
60 \fn QVersionNumber::QVersionNumber(int maj, int min, int mic)
61
62 Constructs a QVersionNumber consisting of the major, minor, and
63 micro version numbers \a maj, \a min and \a mic, respectively.
64*/
65
66/*!
67 \fn QVersionNumber::QVersionNumber(const QList<int> &seg)
68
69 Constructs a version number from the list of numbers contained in \a seg.
70*/
71
72/*!
73 \fn QVersionNumber::QVersionNumber(QList<int> &&seg)
74
75 Move-constructs a version number from the list of numbers contained in \a seg.
76*/
77
78/*!
79 \fn QVersionNumber::QVersionNumber(std::initializer_list<int> args)
80
81 Constructs a version number from the std::initializer_list specified by
82 \a args.
83*/
84
85/*!
86 \fn QVersionNumber::QVersionNumber(QSpan<const int> args)
87 \since 6.8
88
89 Constructs a version number from the span specified by \a args.
90
91 \note In Qt versions prior to 6.8, QVersionNumber could only be constructed
92 from QList, QVarLenthArray or std::initializer_list.
93*/
94
95/*!
96 \fn bool QVersionNumber::isNull() const
97
98 Returns \c true if there are zero numerical segments, otherwise returns
99 \c false.
100
101 \sa segments()
102*/
103
104/*!
105 \fn bool QVersionNumber::isNormalized() const
106
107 Returns \c true if the version number does not contain any trailing zeros,
108 otherwise returns \c false.
109
110 \sa normalized()
111*/
112
113/*!
114 \fn int QVersionNumber::majorVersion() const
115
116 Returns the major version number, that is, the first segment.
117 This function is equivalent to segmentAt(0). If this QVersionNumber object
118 is null, this function returns 0.
119
120 \sa isNull(), segmentAt()
121*/
122
123/*!
124 \fn int QVersionNumber::minorVersion() const
125
126 Returns the minor version number, that is, the second segment.
127 This function is equivalent to segmentAt(1). If this QVersionNumber object
128 does not contain a minor number, this function returns 0.
129
130 \sa isNull(), segmentAt()
131*/
132
133/*!
134 \fn int QVersionNumber::microVersion() const
135
136 Returns the micro version number, that is, the third segment.
137 This function is equivalent to segmentAt(2). If this QVersionNumber object
138 does not contain a micro number, this function returns 0.
139
140 \sa isNull(), segmentAt()
141*/
142
143/*!
144 \fn QList<int> QVersionNumber::segments() const
145
146 Returns all of the numerical segments.
147
148 \sa majorVersion(), minorVersion(), microVersion()
149*/
150QList<int> QVersionNumber::segments() const
151{
152 if (m_segments.isUsingPointer())
153 return *m_segments.pointer_segments;
154
155 QList<int> result;
156 result.resize(segmentCount());
157 for (qsizetype i = 0; i < segmentCount(); ++i)
158 result[i] = segmentAt(i);
159 return result;
160}
161
162/*!
163 \fn int QVersionNumber::segmentAt(qsizetype index) const
164
165 Returns the segment value at \a index. If the index does not exist,
166 returns 0.
167
168 \sa segments(), segmentCount()
169*/
170
171/*!
172 \fn qsizetype QVersionNumber::segmentCount() const
173
174 Returns the number of integers stored in segments().
175
176 \sa segments()
177*/
178
179/*!
180 \typedef QVersionNumber::const_iterator
181 \typedef QVersionNumber::const_reverse_iterator
182 \since 6.8
183
184 Typedefs for an opaque class that implements a (reverse) random-access
185 iterator over QVersionNumber segments.
186
187 \note QVersionNumber does not support modifying segments in-place, so
188 there is no mutable iterator.
189*/
190
191/*!
192 \typedef QVersionNumber::value_type
193 \typedef QVersionNumber::difference_type
194 \typedef QVersionNumber::size_type
195 \typedef QVersionNumber::reference
196 \typedef QVersionNumber::const_reference
197 \typedef QVersionNumber::pointer
198 \typedef QVersionNumber::const_pointer
199 \since 6.8
200
201 Provided for STL-compatibility.
202
203 \note QVersionNumber does not support modifying segments in-place, so
204 reference and const_reference, as well as pointer and const_pointer are
205 pairwise the same types.
206*/
207
208/*!
209 \fn QVersionNumber::begin() const
210 \fn QVersionNumber::end() const;
211 \fn QVersionNumber::rbegin() const
212 \fn QVersionNumber::rend() const;
213 \fn QVersionNumber::cbegin() const
214 \fn QVersionNumber::cend() const;
215 \fn QVersionNumber::crbegin() const
216 \fn QVersionNumber::crend() const;
217 \fn QVersionNumber::constBegin() const;
218 \fn QVersionNumber::constEnd() const;
219 \since 6.8
220
221 Returns a const_iterator or const_reverse_iterator, respectively, pointing
222 to the first or one past the last segment of this version number.
223
224 \note QVersionNumber does not support modifying segments in-place, so
225 there is no mutable iterator.
226*/
227
228/*!
229 \fn QVersionNumber QVersionNumber::normalized() const
230
231 Returns an equivalent version number but with all trailing zeros removed.
232
233 To check if two numbers are equivalent, use normalized() on both version
234 numbers before performing the compare.
235
236 \snippet qversionnumber/main.cpp 4
237 */
238QVersionNumber QVersionNumber::normalized() const
239{
240 qsizetype i;
241 for (i = m_segments.size(); i; --i)
242 if (m_segments.at(i - 1) != 0)
243 break;
244
245 QVersionNumber result(*this);
246 result.m_segments.resize(i);
247 return result;
248}
249
250/*!
251 \fn bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const
252
253 Returns \c true if the current version number is contained in the \a other
254 version number, otherwise returns \c false.
255
256 \snippet qversionnumber/main.cpp 2
257
258 \sa commonPrefix()
259*/
260bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const noexcept
261{
262 if (segmentCount() > other.segmentCount())
263 return false;
264 for (qsizetype i = 0; i < segmentCount(); ++i) {
265 if (segmentAt(i) != other.segmentAt(i))
266 return false;
267 }
268 return true;
269}
270
271/*!
272 \fn int QVersionNumber::compare(const QVersionNumber &v1,
273 const QVersionNumber &v2)
274
275 Compares \a v1 with \a v2 and returns an integer less than, equal to, or
276 greater than zero, depending on whether \a v1 is less than, equal to, or
277 greater than \a v2, respectively.
278
279 Comparisons are performed by comparing the segments of \a v1 and \a v2
280 starting at index 0 and working towards the end of the longer list.
281
282 \snippet qversionnumber/main.cpp 1
283*/
284int QVersionNumber::compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept
285{
286 qsizetype commonlen;
287
288 if (Q_LIKELY(!v1.m_segments.isUsingPointer() && !v2.m_segments.isUsingPointer())) {
289 // we can't use memcmp because it interprets the data as unsigned bytes
290 const qint8 *ptr1 = v1.m_segments.inline_segments + InlineSegmentStartIdx;
291 const qint8 *ptr2 = v2.m_segments.inline_segments + InlineSegmentStartIdx;
292 commonlen = qMin(v1.m_segments.size(),
293 v2.m_segments.size());
294 for (qsizetype i = 0; i < commonlen; ++i)
295 if (int x = ptr1[i] - ptr2[i])
296 return x;
297 } else {
298 commonlen = qMin(v1.segmentCount(), v2.segmentCount());
299 for (qsizetype i = 0; i < commonlen; ++i) {
300 if (v1.segmentAt(i) != v2.segmentAt(i))
301 return v1.segmentAt(i) - v2.segmentAt(i);
302 }
303 }
304
305 // ran out of segments in v1 and/or v2 and need to check the first trailing
306 // segment to finish the compare
307 if (v1.segmentCount() > commonlen) {
308 // v1 is longer
309 if (v1.segmentAt(commonlen) != 0)
310 return v1.segmentAt(commonlen);
311 else
312 return 1;
313 } else if (v2.segmentCount() > commonlen) {
314 // v2 is longer
315 if (v2.segmentAt(commonlen) != 0)
316 return -v2.segmentAt(commonlen);
317 else
318 return -1;
319 }
320
321 // the two version numbers are the same
322 return 0;
323}
324
325/*!
326 QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
327 const QVersionNumber &v2)
328
329 Returns a version number that is a parent version of both \a v1 and \a v2.
330
331 \sa isPrefixOf()
332*/
333QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
334 const QVersionNumber &v2)
335{
336 qsizetype commonlen = qMin(v1.segmentCount(), v2.segmentCount());
337 qsizetype i;
338 for (i = 0; i < commonlen; ++i) {
339 if (v1.segmentAt(i) != v2.segmentAt(i))
340 break;
341 }
342
343 if (i == 0)
344 return QVersionNumber();
345
346 // try to use the one with inline segments, if there's one
347 QVersionNumber result(!v1.m_segments.isUsingPointer() ? v1 : v2);
348 result.m_segments.resize(i);
349 return result;
350}
351
352/*!
353 \fn bool QVersionNumber::operator<(const QVersionNumber &lhs, const QVersionNumber &rhs)
354
355 Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false.
356
357 \sa QVersionNumber::compare()
358*/
359
360/*!
361 \fn bool QVersionNumber::operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs)
362
363 Returns \c true if \a lhs is less than or equal to \a rhs; otherwise
364 returns \c false.
365
366 \sa QVersionNumber::compare()
367*/
368
369/*!
370 \fn bool QVersionNumber::operator>(const QVersionNumber &lhs, const QVersionNumber &rhs)
371
372 Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c
373 false.
374
375 \sa QVersionNumber::compare()
376*/
377
378/*!
379 \fn bool QVersionNumber::operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs)
380
381 Returns \c true if \a lhs is greater than or equal to \a rhs; otherwise
382 returns \c false.
383
384 \sa QVersionNumber::compare()
385*/
386
387/*!
388 \fn bool QVersionNumber::operator==(const QVersionNumber &lhs, const QVersionNumber &rhs)
389
390 Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
391
392 \sa QVersionNumber::compare()
393*/
394
395/*!
396 \fn bool QVersionNumber::operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs)
397
398 Returns \c true if \a lhs is not equal to \a rhs; otherwise returns
399 \c false.
400
401 \sa QVersionNumber::compare()
402*/
403
404/*!
405 \fn QString QVersionNumber::toString() const
406
407 Returns a string with all of the segments delimited by a period (\c{.}).
408
409 \sa majorVersion(), minorVersion(), microVersion(), segments()
410*/
411QString QVersionNumber::toString() const
412{
413 QString version;
414 version.reserve(qMax(segmentCount() * 2 - 1, 0));
415 bool first = true;
416 for (qsizetype i = 0; i < segmentCount(); ++i) {
417 if (!first)
418 version += u'.';
419 version += QString::number(segmentAt(i));
420 first = false;
421 }
422 return version;
423}
424
425/*!
426 \fn QVersionNumber QVersionNumber::fromString(QAnyStringView string, qsizetype *suffixIndex)
427 \since 6.4
428
429 Constructs a QVersionNumber from a specially formatted \a string of
430 non-negative decimal numbers delimited by a period (\c{.}).
431
432 Once the numerical segments have been parsed, the remainder of the string
433 is considered to be the suffix string. The start index of that string will be
434 stored in \a suffixIndex if it is not null.
435
436 \snippet qversionnumber/main.cpp 3-latin1-1
437
438 \note In versions prior to Qt 6.4, this function was overloaded for QString,
439 QLatin1StringView and QStringView instead, and \a suffixIndex was an \c{int*}.
440
441 \sa isNull()
442*/
443
444static QVersionNumber from_string(QLatin1StringView string, qsizetype *suffixIndex)
445{
446 // 32 should be more than enough, and, crucially, it means we're allocating
447 // not more (and often less) often when compared with direct QList usage
448 // for all possible segment counts (under the constraint that we don't want
449 // to keep more capacity around for the lifetime of the resulting
450 // QVersionNumber than required), esp. in the common case where the inline
451 // storage can be used.
452 QVarLengthArray<int, 32> seg;
453
454 const char *start = string.begin();
455 const char *lastGoodEnd = start;
456 const char *endOfString = string.end();
457
458 do {
459 // parsing as unsigned so a minus sign is rejected
460 auto [value, used] = qstrntoull(start, endOfString - start, 10);
461 if (used <= 0 || value > qulonglong(std::numeric_limits<int>::max()))
462 break;
463 seg.append(int(value));
464 start += used + 1;
465 lastGoodEnd = start - 1;
466 } while (start < endOfString && *lastGoodEnd == '.');
467
468 if (suffixIndex)
469 *suffixIndex = lastGoodEnd - string.begin();
470
471 return QVersionNumber(seg);
472}
473
474static QVersionNumber from_string(q_no_char8_t::QUtf8StringView string, qsizetype *suffixIndex)
475{
476 return from_string(QLatin1StringView(string.data(), string.size()), suffixIndex);
477}
478
479// in qstring.cpp
480extern void qt_to_latin1(uchar *dst, const char16_t *uc, qsizetype len);
481
482static QVersionNumber from_string(QStringView string, qsizetype *suffixIndex)
483{
484 QVarLengthArray<char> copy;
485 copy.resize(string.size());
486 qt_to_latin1(reinterpret_cast<uchar*>(copy.data()), string.utf16(), string.size());
487 return from_string(QLatin1StringView(copy.data(), copy.size()), suffixIndex);
488}
489
490QVersionNumber QVersionNumber::fromString(QAnyStringView string, qsizetype *suffixIndex)
491{
492 return string.visit([=] (auto string) { return from_string(string, suffixIndex); });
493}
494
495void QVersionNumber::SegmentStorage::setListData(const QList<int> &seg)
496{
497 pointer_segments = new QList<int>(seg);
498}
499
500void QVersionNumber::SegmentStorage::setListData(QList<int> &&seg)
501{
502 pointer_segments = new QList<int>(std::move(seg));
503}
504
505void QVersionNumber::SegmentStorage::setListData(const int *first, const int *last)
506{
507 pointer_segments = new QList<int>(first, last);
508}
509
510void QVersionNumber::SegmentStorage::resize(qsizetype len)
511{
512 if (isUsingPointer())
513 pointer_segments->resize(len);
514 else
515 setInlineSize(len);
516}
517
518void QVersionNumber::SegmentStorage::setVector(int len, int maj, int min, int mic)
519{
520 pointer_segments = new QList<int>;
521 pointer_segments->resize(len);
522 pointer_segments->data()[0] = maj;
523 if (len > 1) {
524 pointer_segments->data()[1] = min;
525 if (len > 2) {
526 pointer_segments->data()[2] = mic;
527 }
528 }
529}
530
531#ifndef QT_NO_DATASTREAM
532/*!
533 \fn QDataStream& operator<<(QDataStream &out,
534 const QVersionNumber &version)
535 \relates QVersionNumber
536
537 Writes the version number \a version to stream \a out.
538
539 Note that this has nothing to do with QDataStream::version().
540 */
541QDataStream& operator<<(QDataStream &out, const QVersionNumber &version)
542{
543 out << version.segments();
544 return out;
545}
546
547/*!
548 \fn QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
549 \relates QVersionNumber
550
551 Reads a version number from stream \a in and stores it in \a version.
552
553 Note that this has nothing to do with QDataStream::version().
554 */
555QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
556{
557 if (!version.m_segments.isUsingPointer())
558 version.m_segments.pointer_segments = new QList<int>;
559 in >> *version.m_segments.pointer_segments;
560 return in;
561}
562#endif
563
564#ifndef QT_NO_DEBUG_STREAM
565QDebug operator<<(QDebug debug, const QVersionNumber &version)
566{
567 QDebugStateSaver saver(debug);
568 debug.nospace().noquote();
569 debug << "QVersionNumber(" << version.toString() << ")";
570 return debug;
571}
572#endif
573
574/*!
575 \qhashold{QHash}
576 \since 5.6
577*/
578size_t qHash(const QVersionNumber &key, size_t seed)
579{
580 return qHashRange(key.begin(), key.end(), seed);
581}
582
583QT_END_NAMESPACE
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
constexpr size_t qHash(const QSize &s, size_t seed=0) noexcept
Definition qsize.h:192
static QVersionNumber from_string(QStringView string, qsizetype *suffixIndex)
QDataStream & operator<<(QDataStream &stream, const QImage &image)
[0]
Definition qimage.cpp:4009
QDataStream & operator>>(QDataStream &stream, QImage &image)
Definition qimage.cpp:4035