summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qtyperevision.h
blob: 8f255a77e89c7f2c07c1bb3e62b3b9ca386b18c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright (C) 2020 The Qt Company Ltd.
// Copyright (C) 2022 Intel Corporation.
// Copyright (C) 2015 Keith Gardner <kreios4004@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QTYPEREVISION_H
#define QTYPEREVISION_H

#include <QtCore/qassert.h>
#include <QtCore/qcompare.h>
#include <QtCore/qcontainertools_impl.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qtypeinfo.h>

#include <limits>

QT_BEGIN_NAMESPACE

class QDataStream;
class QDebug;

class QTypeRevision;
Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0);

#ifndef QT_NO_DATASTREAM
Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
#endif

class QTypeRevision
{
public:
    template<typename Integer>
    using if_valid_segment_type = typename std::enable_if<
            std::is_integral<Integer>::value, bool>::type;

    template<typename Integer>
    using if_valid_value_type = typename std::enable_if<
            std::is_integral<Integer>::value
            && (sizeof(Integer) > sizeof(quint16)
                || (sizeof(Integer) == sizeof(quint16)
                    && !std::is_signed<Integer>::value)), bool>::type;

    template<typename Integer, if_valid_segment_type<Integer> = true>
    static constexpr bool isValidSegment(Integer segment)
    {
        // using extra parentheses around max to avoid expanding it if it is a macro
        return segment >= Integer(0)
                && ((std::numeric_limits<Integer>::max)() < Integer(SegmentUnknown)
                    || segment < Integer(SegmentUnknown));
    }

    template<typename Major, typename Minor,
             if_valid_segment_type<Major> = true,
             if_valid_segment_type<Minor> = true>
    static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
    {
        return Q_ASSERT(isValidSegment(majorVersion)),
               Q_ASSERT(isValidSegment(minorVersion)),
               QTypeRevision(quint8(majorVersion), quint8(minorVersion));
    }

    template<typename Major, if_valid_segment_type<Major> = true>
    static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
    {
        return Q_ASSERT(isValidSegment(majorVersion)),
               QTypeRevision(quint8(majorVersion), SegmentUnknown);
    }

    template<typename Minor, if_valid_segment_type<Minor> = true>
    static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
    {
        return Q_ASSERT(isValidSegment(minorVersion)),
               QTypeRevision(SegmentUnknown, quint8(minorVersion));
    }

    template<typename Integer, if_valid_value_type<Integer> = true>
    static constexpr QTypeRevision fromEncodedVersion(Integer value)
    {
        return Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)),
               QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff));
    }

    static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); }

    constexpr QTypeRevision() = default;

    constexpr bool hasMajorVersion() const { return m_majorVersion != SegmentUnknown; }
    constexpr quint8 majorVersion() const { return m_majorVersion; }

    constexpr bool hasMinorVersion() const { return m_minorVersion != SegmentUnknown; }
    constexpr quint8 minorVersion() const { return m_minorVersion; }

    constexpr bool isValid() const { return hasMajorVersion() || hasMinorVersion(); }

    template<typename Integer, if_valid_value_type<Integer> = true>
    constexpr Integer toEncodedVersion() const
    {
        return Integer(m_majorVersion << 8) | Integer(m_minorVersion);
    }

private:
    friend constexpr bool
    comparesEqual(const QTypeRevision &lhs, const QTypeRevision &rhs) noexcept
    { return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>(); }
    friend constexpr Qt::strong_ordering
    compareThreeWay(const QTypeRevision &lhs, const QTypeRevision &rhs) noexcept
    {
        // For both major and minor the following rule applies:
        // non-0 ver > unspecified ver > 0 ver
        auto cmpUnspecified = [](quint8 leftVer, quint8 rightVer) {
            Q_ASSERT(leftVer != rightVer
                     && (leftVer == QTypeRevision::SegmentUnknown
                         || rightVer == QTypeRevision::SegmentUnknown));
            if (leftVer != QTypeRevision::SegmentUnknown)
                return leftVer > 0 ? Qt::strong_ordering::greater : Qt::strong_ordering::less;
            return rightVer > 0 ? Qt::strong_ordering::less : Qt::strong_ordering::greater;
        };

        if (lhs.hasMajorVersion() != rhs.hasMajorVersion()) {
            return cmpUnspecified(lhs.majorVersion(), rhs.majorVersion());
        } else {
            const auto majorRes = Qt::compareThreeWay(lhs.majorVersion(), rhs.majorVersion());
            if (is_eq(majorRes)) {
                if (lhs.hasMinorVersion() != rhs.hasMinorVersion())
                    return cmpUnspecified(lhs.minorVersion(), rhs.minorVersion());
                return Qt::compareThreeWay(lhs.minorVersion(), rhs.minorVersion());
            }
            return majorRes;
        }
    }
    Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QTypeRevision)

    enum { SegmentUnknown = 0xff };

#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
    constexpr QTypeRevision(quint8 major, quint8 minor)
        : m_minorVersion(minor), m_majorVersion(major) {}

    quint8 m_minorVersion = SegmentUnknown;
    quint8 m_majorVersion = SegmentUnknown;
#else
    constexpr QTypeRevision(quint8 major, quint8 minor)
        : m_majorVersion(major), m_minorVersion(minor) {}

    quint8 m_majorVersion = SegmentUnknown;
    quint8 m_minorVersion = SegmentUnknown;
#endif
};

static_assert(sizeof(QTypeRevision) == 2);
Q_DECLARE_TYPEINFO(QTypeRevision, Q_RELOCATABLE_TYPE);

#ifndef QT_NO_DEBUG_STREAM
Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
#endif

QT_END_NAMESPACE

QT_DECL_METATYPE_EXTERN(QTypeRevision, Q_CORE_EXPORT)

#endif // QTYPEREVISION_H

#if !defined(QT_LEAN_HEADERS) || QT_LEAN_HEADERS < 2
// make QVersionNumber available from <QTypeRevision>
#include <QtCore/qversionnumber.h>
#endif