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