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 #include <QtCore/qversionnumber.h>
43 #include <QtCore/qhash.h>
44 #include <QtCore/private/qlocale_tools_p.h>
45 #include <QtCore/qcollator.h>
46 
47 #ifndef QT_NO_DATASTREAM
48 #  include <QtCore/qdatastream.h>
49 #endif
50 
51 #ifndef QT_NO_DEBUG_STREAM
52 #  include <QtCore/qdebug.h>
53 #endif
54 
55 #include <algorithm>
56 #include <limits>
57 
58 QT_BEGIN_NAMESPACE
59 
60 /*!
61     \class QVersionNumber
62     \inmodule QtCore
63     \since 5.6
64     \brief The QVersionNumber class contains a version number with an arbitrary
65            number of segments.
66 
67     \snippet qversionnumber/main.cpp 0
68 */
69 
70 /*!
71     \fn QVersionNumber::QVersionNumber()
72 
73     Produces a null version.
74 
75     \sa isNull()
76 */
77 
78 /*!
79     \fn QVersionNumber::QVersionNumber(int maj)
80 
81     Constructs a QVersionNumber consisting of just the major version number \a maj.
82 */
83 
84 /*!
85     \fn QVersionNumber::QVersionNumber(int maj, int min)
86 
87     Constructs a QVersionNumber consisting of the major and minor
88     version numbers \a maj and \a min, respectively.
89 */
90 
91 /*!
92     \fn QVersionNumber::QVersionNumber(int maj, int min, int mic)
93 
94     Constructs a QVersionNumber consisting of the major, minor, and
95     micro version numbers \a maj, \a min and \a mic, respectively.
96 */
97 
98 /*!
99     \fn QVersionNumber::QVersionNumber(const QVector<int> &seg)
100 
101     Constructs a version number from the list of numbers contained in \a seg.
102 */
103 
104 /*!
105     \fn QVersionNumber::QVersionNumber(QVector<int> &&seg)
106 
107     Move-constructs a version number from the list of numbers contained in \a seg.
108 
109     This constructor is only enabled if the compiler supports C++11 move semantics.
110 */
111 
112 /*!
113     \fn QVersionNumber::QVersionNumber(std::initializer_list<int> args)
114 
115     Construct a version number from the std::initializer_list specified by
116     \a args.
117 
118     This constructor is only enabled if the compiler supports C++11 initializer
119     lists.
120 */
121 
122 /*!
123     \fn bool QVersionNumber::isNull() const
124 
125     Returns \c true if there are zero numerical segments, otherwise returns
126     \c false.
127 
128     \sa segments()
129 */
130 
131 /*!
132   \fn bool QVersionNumber::isNormalized() const
133 
134   Returns \c true if the version number does not contain any trailing zeros,
135   otherwise returns \c false.
136 
137   \sa normalized()
138 */
139 
140 /*!
141     \fn int QVersionNumber::majorVersion() const
142 
143     Returns the major version number, that is, the first segment.
144     This function is equivalent to segmentAt(0). If this QVersionNumber object
145     is null, this function returns 0.
146 
147     \sa isNull(), segmentAt()
148 */
149 
150 /*!
151     \fn int QVersionNumber::minorVersion() const
152 
153     Returns the minor version number, that is, the second segment.
154     This function is equivalent to segmentAt(1). If this QVersionNumber object
155     does not contain a minor number, this function returns 0.
156 
157     \sa isNull(), segmentAt()
158 */
159 
160 /*!
161     \fn int QVersionNumber::microVersion() const
162 
163     Returns the micro version number, that is, the third segment.
164     This function is equivalent to segmentAt(2). If this QVersionNumber object
165     does not contain a micro number, this function returns 0.
166 
167     \sa isNull(), segmentAt()
168 */
169 
170 /*!
171     \fn const QVector<int>& QVersionNumber::segments() const
172 
173     Returns all of the numerical segments.
174 
175     \sa majorVersion(), minorVersion(), microVersion()
176 */
segments() const177 QVector<int> QVersionNumber::segments() const
178 {
179     if (m_segments.isUsingPointer())
180         return *m_segments.pointer_segments;
181 
182     QVector<int> result;
183     result.resize(segmentCount());
184     for (int i = 0; i < segmentCount(); ++i)
185         result[i] = segmentAt(i);
186     return result;
187 }
188 
189 /*!
190     \fn int QVersionNumber::segmentAt(int index) const
191 
192     Returns the segement value at \a index.  If the index does not exist,
193     returns 0.
194 
195     \sa segments(), segmentCount()
196 */
197 
198 /*!
199     \fn int QVersionNumber::segmentCount() const
200 
201     Returns the number of integers stored in segments().
202 
203     \sa segments()
204 */
205 
206 /*!
207     \fn QVersionNumber QVersionNumber::normalized() const
208 
209     Returns an equivalent version number but with all trailing zeros removed.
210 
211     To check if two numbers are equivalent, use normalized() on both version
212     numbers before performing the compare.
213 
214     \snippet qversionnumber/main.cpp 4
215  */
normalized() const216 QVersionNumber QVersionNumber::normalized() const
217 {
218     int i;
219     for (i = m_segments.size(); i; --i)
220         if (m_segments.at(i - 1) != 0)
221             break;
222 
223     QVersionNumber result(*this);
224     result.m_segments.resize(i);
225     return result;
226 }
227 
228 /*!
229     \fn bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const
230 
231     Returns \c true if the current version number is contained in the \a other
232     version number, otherwise returns \c false.
233 
234     \snippet qversionnumber/main.cpp 2
235 
236     \sa commonPrefix()
237 */
isPrefixOf(const QVersionNumber & other) const238 bool QVersionNumber::isPrefixOf(const QVersionNumber &other) const noexcept
239 {
240     if (segmentCount() > other.segmentCount())
241         return false;
242     for (int i = 0; i < segmentCount(); ++i) {
243         if (segmentAt(i) != other.segmentAt(i))
244             return false;
245     }
246     return true;
247 }
248 
249 /*!
250     \fn int QVersionNumber::compare(const QVersionNumber &v1,
251                                     const QVersionNumber &v2)
252 
253     Compares \a v1 with \a v2 and returns an integer less than, equal to, or
254     greater than zero, depending on whether \a v1 is less than, equal to, or
255     greater than \a v2, respectively.
256 
257     Comparisons are performed by comparing the segments of \a v1 and \a v2
258     starting at index 0 and working towards the end of the longer list.
259 
260     \snippet qversionnumber/main.cpp 1
261 */
compare(const QVersionNumber & v1,const QVersionNumber & v2)262 int QVersionNumber::compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept
263 {
264     int commonlen;
265 
266     if (Q_LIKELY(!v1.m_segments.isUsingPointer() && !v2.m_segments.isUsingPointer())) {
267         // we can't use memcmp because it interprets the data as unsigned bytes
268         const qint8 *ptr1 = v1.m_segments.inline_segments + InlineSegmentStartIdx;
269         const qint8 *ptr2 = v2.m_segments.inline_segments + InlineSegmentStartIdx;
270         commonlen = qMin(v1.m_segments.size(),
271                          v2.m_segments.size());
272         for (int i = 0; i < commonlen; ++i)
273             if (int x = ptr1[i] - ptr2[i])
274                 return x;
275     } else {
276         commonlen = qMin(v1.segmentCount(), v2.segmentCount());
277         for (int i = 0; i < commonlen; ++i) {
278             if (v1.segmentAt(i) != v2.segmentAt(i))
279                 return v1.segmentAt(i) - v2.segmentAt(i);
280         }
281     }
282 
283     // ran out of segments in v1 and/or v2 and need to check the first trailing
284     // segment to finish the compare
285     if (v1.segmentCount() > commonlen) {
286         // v1 is longer
287         if (v1.segmentAt(commonlen) != 0)
288             return v1.segmentAt(commonlen);
289         else
290             return 1;
291     } else if (v2.segmentCount() > commonlen) {
292         // v2 is longer
293         if (v2.segmentAt(commonlen) != 0)
294             return -v2.segmentAt(commonlen);
295         else
296             return -1;
297     }
298 
299     // the two version numbers are the same
300     return 0;
301 }
302 
303 /*!
304     QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
305                                                     const QVersionNumber &v2)
306 
307     Returns a version number that is a parent version of both \a v1 and \a v2.
308 
309     \sa isPrefixOf()
310 */
commonPrefix(const QVersionNumber & v1,const QVersionNumber & v2)311 QVersionNumber QVersionNumber::commonPrefix(const QVersionNumber &v1,
312                                             const QVersionNumber &v2)
313 {
314     int commonlen = qMin(v1.segmentCount(), v2.segmentCount());
315     int i;
316     for (i = 0; i < commonlen; ++i) {
317         if (v1.segmentAt(i) != v2.segmentAt(i))
318             break;
319     }
320 
321     if (i == 0)
322         return QVersionNumber();
323 
324     // try to use the one with inline segments, if there's one
325     QVersionNumber result(!v1.m_segments.isUsingPointer() ? v1 : v2);
326     result.m_segments.resize(i);
327     return result;
328 }
329 
330 /*!
331     \fn bool operator<(const QVersionNumber &lhs, const QVersionNumber &rhs)
332     \relates QVersionNumber
333 
334     Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false.
335 
336     \sa QVersionNumber::compare()
337 */
338 
339 /*!
340     \fn bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs)
341     \relates QVersionNumber
342 
343     Returns \c true if \a lhs is less than or equal to \a rhs; otherwise
344     returns \c false.
345 
346     \sa QVersionNumber::compare()
347 */
348 
349 /*!
350     \fn bool operator>(const QVersionNumber &lhs, const QVersionNumber &rhs)
351     \relates QVersionNumber
352 
353     Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c
354     false.
355 
356     \sa QVersionNumber::compare()
357 */
358 
359 /*!
360     \fn bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs)
361     \relates QVersionNumber
362 
363     Returns \c true if \a lhs is greater than or equal to \a rhs; otherwise
364     returns \c false.
365 
366     \sa QVersionNumber::compare()
367 */
368 
369 /*!
370     \fn bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs)
371     \relates QVersionNumber
372 
373     Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
374 
375     \sa QVersionNumber::compare()
376 */
377 
378 /*!
379     \fn bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs)
380     \relates QVersionNumber
381 
382     Returns \c true if \a lhs is not equal to \a rhs; otherwise returns
383     \c false.
384 
385     \sa QVersionNumber::compare()
386 */
387 
388 /*!
389     \fn QString QVersionNumber::toString() const
390 
391     Returns a string with all of the segments delimited by a period (\c{.}).
392 
393     \sa majorVersion(), minorVersion(), microVersion(), segments()
394 */
toString() const395 QString QVersionNumber::toString() const
396 {
397     QString version;
398     version.reserve(qMax(segmentCount() * 2 - 1, 0));
399     bool first = true;
400     for (int i = 0; i < segmentCount(); ++i) {
401         if (!first)
402             version += QLatin1Char('.');
403         version += QString::number(segmentAt(i));
404         first = false;
405     }
406     return version;
407 }
408 
409 #if QT_STRINGVIEW_LEVEL < 2
410 /*!
411     Constructs a QVersionNumber from a specially formatted \a string of
412     non-negative decimal numbers delimited by a period (\c{.}).
413 
414     Once the numerical segments have been parsed, the remainder of the string
415     is considered to be the suffix string.  The start index of that string will be
416     stored in \a suffixIndex if it is not null.
417 
418     \snippet qversionnumber/main.cpp 3
419 
420     \sa isNull()
421 */
fromString(const QString & string,int * suffixIndex)422 QVersionNumber QVersionNumber::fromString(const QString &string, int *suffixIndex)
423 {
424     return fromString(QLatin1String(string.toLatin1()), suffixIndex);
425 }
426 #endif
427 
428 /*!
429     \since 5.10
430     \overload
431 
432     Constructs a QVersionNumber from a specially formatted \a string of
433     non-negative decimal numbers delimited by '.'.
434 
435     Once the numerical segments have been parsed, the remainder of the string
436     is considered to be the suffix string.  The start index of that string will be
437     stored in \a suffixIndex if it is not null.
438 
439     \snippet qversionnumber/main.cpp 3
440 
441     \sa isNull()
442 */
fromString(QStringView string,int * suffixIndex)443 QVersionNumber QVersionNumber::fromString(QStringView string, int *suffixIndex)
444 {
445     return fromString(QLatin1String(string.toLatin1()), suffixIndex);
446 }
447 
448 /*!
449     \since 5.10
450     \overload
451 
452     Constructs a QVersionNumber from a specially formatted \a string of
453     non-negative decimal numbers delimited by '.'.
454 
455     Once the numerical segments have been parsed, the remainder of the string
456     is considered to be the suffix string.  The start index of that string will be
457     stored in \a suffixIndex if it is not null.
458 
459     \snippet qversionnumber/main.cpp 3-latin1-1
460 
461     \sa isNull()
462 */
fromString(QLatin1String string,int * suffixIndex)463 QVersionNumber QVersionNumber::fromString(QLatin1String string, int *suffixIndex)
464 {
465     QVector<int> seg;
466 
467     const char *start = string.begin();
468     const char *end = start;
469     const char *lastGoodEnd = start;
470     const char *endOfString = string.end();
471 
472     do {
473         bool ok = false;
474         const qulonglong value = qstrtoull(start, &end, 10, &ok);
475         if (!ok || value > qulonglong(std::numeric_limits<int>::max()))
476             break;
477         seg.append(int(value));
478         start = end + 1;
479         lastGoodEnd = end;
480     } while (start < endOfString && (end < endOfString && *end == '.'));
481 
482     if (suffixIndex)
483         *suffixIndex = int(lastGoodEnd - string.begin());
484 
485     return QVersionNumber(std::move(seg));
486 }
487 
setVector(int len,int maj,int min,int mic)488 void QVersionNumber::SegmentStorage::setVector(int len, int maj, int min, int mic)
489 {
490     pointer_segments = new QVector<int>;
491     pointer_segments->resize(len);
492     pointer_segments->data()[0] = maj;
493     if (len > 1) {
494         pointer_segments->data()[1] = min;
495         if (len > 2) {
496             pointer_segments->data()[2] = mic;
497         }
498     }
499 }
500 
501 #ifndef QT_NO_DATASTREAM
502 /*!
503    \fn  QDataStream& operator<<(QDataStream &out,
504                                 const QVersionNumber &version)
505    \relates QVersionNumber
506 
507    Writes the version number \a version to stream \a out.
508 
509    Note that this has nothing to do with QDataStream::version().
510  */
operator <<(QDataStream & out,const QVersionNumber & version)511 QDataStream& operator<<(QDataStream &out, const QVersionNumber &version)
512 {
513     out << version.segments();
514     return out;
515 }
516 
517 /*!
518    \fn QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
519    \relates QVersionNumber
520 
521    Reads a version number from stream \a in and stores it in \a version.
522 
523    Note that this has nothing to do with QDataStream::version().
524  */
operator >>(QDataStream & in,QVersionNumber & version)525 QDataStream& operator>>(QDataStream &in, QVersionNumber &version)
526 {
527     if (!version.m_segments.isUsingPointer())
528         version.m_segments.pointer_segments = new QVector<int>;
529     in >> *version.m_segments.pointer_segments;
530     return in;
531 }
532 #endif
533 
534 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug debug,const QVersionNumber & version)535 QDebug operator<<(QDebug debug, const QVersionNumber &version)
536 {
537     QDebugStateSaver saver(debug);
538     debug.noquote() << version.toString();
539     return debug;
540 }
541 #endif
542 
543 /*!
544     \fn uint qHash(const QVersionNumber &key, uint seed)
545     \relates QHash
546     \since 5.6
547 
548     Returns the hash value for the \a key, using \a seed to seed the
549     calculation.
550 */
qHash(const QVersionNumber & key,uint seed)551 uint qHash(const QVersionNumber &key, uint seed)
552 {
553     QtPrivate::QHashCombine hash;
554     for (int i = 0; i < key.segmentCount(); ++i)
555         seed = hash(seed, key.segmentAt(i));
556     return seed;
557 }
558 
559 QT_END_NAMESPACE
560 
561