Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-07 10:16:09

0001 // Copyright (C) 2025 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
0002 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0003 
0004 #ifndef QCHECKEDINT_H
0005 #define QCHECKEDINT_H
0006 
0007 #include <QtCore/qassert.h>
0008 #include <QtCore/qcompare.h>
0009 #include <QtCore/qhashfunctions.h>
0010 #include <QtCore/qnumeric.h>
0011 
0012 #include <type_traits>
0013 
0014 QT_BEGIN_NAMESPACE
0015 
0016 namespace QtPrivate {
0017 namespace QCheckedIntegers {
0018 
0019 template <typename Int>
0020 struct CheckIntTypeHelper
0021 {
0022     static_assert(std::is_integral_v<Int>, "Only integer types are supported");
0023     static_assert(std::is_signed_v<Int>, "Only signed types are supported");
0024     static_assert(std::is_same_v<Int, decltype(+Int{})>, "Only fully promoted types are supported");
0025 };
0026 
0027 // Implements wraparound semantics, and checks for overflow.
0028 // Never causes UB on any operation.
0029 template <typename Int>
0030 struct SafeCheckImpl : private CheckIntTypeHelper<Int>
0031 {
0032     static inline constexpr Int MinInt = (std::numeric_limits<Int>::min)();
0033     static inline constexpr Int MaxInt = (std::numeric_limits<Int>::max)();
0034 
0035     [[nodiscard]]
0036     static constexpr bool add(Int a, Int b, Int *result) noexcept
0037     {
0038         return !qAddOverflow(a, b, result);
0039     }
0040 
0041     [[nodiscard]]
0042     static constexpr bool sub(Int a, Int b, Int *result) noexcept
0043     {
0044         return !qSubOverflow(a, b, result);
0045     }
0046 
0047     [[nodiscard]]
0048     static constexpr bool mul(Int a, Int b, Int *result) noexcept
0049     {
0050         return !qMulOverflow(a, b, result);
0051     }
0052 
0053     [[nodiscard]]
0054     static constexpr bool div(Int a, Int b, Int *result) noexcept
0055     {
0056         if (Q_UNLIKELY(b == 0))
0057             return false;
0058 
0059         *result = a / b;
0060         return true;
0061     }
0062 };
0063 
0064 // Reports failed checks through Q_ASSERT.
0065 struct AssertReportPolicy
0066 {
0067     static constexpr void check(bool ok, const char *where, const char *description)
0068     {
0069         Q_ASSERT_X(ok, where, description);
0070     }
0071 };
0072 
0073 template <typename Int,
0074           typename Impl = SafeCheckImpl<Int>,
0075           typename FailureReportPolicy = AssertReportPolicy>
0076 class QCheckedInt : private CheckIntTypeHelper<Int>
0077 {
0078     Int m_i;
0079 
0080 #define Q_CHECKEDINT_POLICY_CHECK(cond, what) \
0081     FailureReportPolicy::check(cond, Q_FUNC_INFO, what)
0082 
0083 public:
0084     template <typename AInt>
0085     using if_is_same_int = std::enable_if_t<std::is_same_v<Int, AInt>, bool>;
0086 
0087     QCheckedInt() = default;
0088 
0089     explicit constexpr QCheckedInt(Int i) noexcept
0090         : m_i(i)
0091     {}
0092 
0093     // Conversions
0094     explicit constexpr operator Int() const noexcept
0095     {
0096         return m_i;
0097     }
0098 
0099     // Accessors
0100     constexpr Int value() const noexcept { return m_i; }
0101     template <typename AInt, if_is_same_int<AInt> = true>
0102     constexpr void setValue(AInt i) noexcept { m_i = i; }
0103 
0104     constexpr Int &as_underlying() & noexcept { return m_i; }
0105     constexpr const Int &as_underlying() const & noexcept { return m_i; }
0106     constexpr Int &&as_underlying() && noexcept { return std::move(m_i); }
0107     constexpr const Int &&as_underlying() const && noexcept { return std::move(m_i); }
0108 
0109     // Unary ops
0110     constexpr QCheckedInt operator+() const noexcept { return *this; }
0111     constexpr QCheckedInt operator-() const
0112     {
0113         QCheckedInt result{};
0114         const bool ok = Impl::sub(Int(0), m_i, &result.m_i);
0115         Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in unary negation");
0116         return result;
0117     }
0118 
0119     constexpr QCheckedInt &operator++()
0120     {
0121         const bool ok = Impl::add(m_i, Int(1), &m_i);
0122         Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator++");
0123         return *this;
0124     }
0125 
0126     constexpr QCheckedInt operator++(int)
0127     {
0128         QCheckedInt result = *this;
0129         ++*this;
0130         return result;
0131     }
0132 
0133     constexpr QCheckedInt &operator--()
0134     {
0135         const bool ok = Impl::sub(m_i, Int(1), &m_i);
0136         Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator--");
0137         return *this;
0138     }
0139 
0140     constexpr QCheckedInt operator--(int)
0141     {
0142         QCheckedInt result = *this;
0143         --*this;
0144         return result;
0145     }
0146 
0147     // Addition
0148     friend constexpr QCheckedInt operator+(QCheckedInt lhs, QCheckedInt rhs)
0149     {
0150         QCheckedInt result{};
0151         const bool ok = Impl::add(lhs.m_i, rhs.m_i, &result.m_i);
0152         Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator+");
0153         return result;
0154     }
0155 
0156     template <typename AInt, if_is_same_int<AInt> = true>
0157     friend constexpr QCheckedInt operator+(QCheckedInt lhs, AInt rhs)
0158     {
0159         return lhs + QCheckedInt(rhs);
0160     }
0161 
0162     template <typename AInt, if_is_same_int<AInt> = true>
0163     friend constexpr QCheckedInt operator+(AInt lhs, QCheckedInt rhs)
0164     {
0165         return QCheckedInt(lhs) + rhs;
0166     }
0167 
0168     constexpr QCheckedInt &operator+=(QCheckedInt other)
0169     {
0170         return *this = *this + other;
0171     }
0172 
0173     template <typename AInt, if_is_same_int<AInt> = true>
0174     constexpr QCheckedInt &operator+=(AInt other)
0175     {
0176         return *this = *this + QCheckedInt(other);
0177     }
0178 
0179     // Subtraction
0180     friend constexpr QCheckedInt operator-(QCheckedInt lhs, QCheckedInt rhs)
0181     {
0182         QCheckedInt result{};
0183         const bool ok = Impl::sub(lhs.m_i, rhs.m_i, &result.m_i);
0184         Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator-");
0185         return result;
0186     }
0187 
0188     template <typename AInt, if_is_same_int<AInt> = true>
0189     friend constexpr QCheckedInt operator-(QCheckedInt lhs, AInt rhs)
0190     {
0191         return lhs - QCheckedInt(rhs);
0192     }
0193 
0194     template <typename AInt, if_is_same_int<AInt> = true>
0195     friend constexpr QCheckedInt operator-(AInt lhs, QCheckedInt rhs)
0196     {
0197         return QCheckedInt(lhs) - rhs;
0198     }
0199 
0200     constexpr QCheckedInt &operator-=(QCheckedInt other)
0201     {
0202         return *this = *this - other;
0203     }
0204 
0205     template <typename AInt, if_is_same_int<AInt> = true>
0206     constexpr QCheckedInt &operator-=(AInt other)
0207     {
0208         return *this = *this - QCheckedInt(other);
0209     }
0210 
0211     // Multiplication
0212     friend constexpr QCheckedInt operator*(QCheckedInt lhs, QCheckedInt rhs)
0213     {
0214         QCheckedInt result{};
0215         const bool ok = Impl::mul(lhs.m_i, rhs.m_i, &result.m_i);
0216         Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator*");
0217         return result;
0218     }
0219 
0220     template <typename AInt, if_is_same_int<AInt> = true>
0221     friend constexpr QCheckedInt operator*(QCheckedInt lhs, AInt rhs)
0222     {
0223         return lhs * QCheckedInt(rhs);
0224     }
0225 
0226     template <typename AInt, if_is_same_int<AInt> = true>
0227     friend constexpr QCheckedInt operator*(AInt lhs, QCheckedInt rhs)
0228     {
0229         return QCheckedInt(lhs) * rhs;
0230     }
0231 
0232     constexpr QCheckedInt &operator*=(QCheckedInt other)
0233     {
0234         return *this = *this * other;
0235     }
0236 
0237     template <typename AInt, if_is_same_int<AInt> = true>
0238     constexpr QCheckedInt &operator*=(AInt other)
0239     {
0240         return *this = *this * QCheckedInt(other);
0241     }
0242 
0243     // Division
0244     friend constexpr QCheckedInt operator/(QCheckedInt lhs, QCheckedInt rhs)
0245     {
0246         QCheckedInt result{};
0247         const bool ok = Impl::div(lhs.m_i, rhs.m_i, &result.m_i);
0248         Q_CHECKEDINT_POLICY_CHECK(ok, "Division by zero");
0249         return result;
0250     }
0251 
0252     template <typename AInt, if_is_same_int<AInt> = true>
0253     friend constexpr QCheckedInt operator/(QCheckedInt lhs, AInt rhs)
0254     {
0255         return lhs / QCheckedInt(rhs);
0256     }
0257 
0258     template <typename AInt, if_is_same_int<AInt> = true>
0259     friend constexpr QCheckedInt operator/(AInt lhs, QCheckedInt rhs)
0260     {
0261         return QCheckedInt(lhs) / rhs;
0262     }
0263 
0264     constexpr QCheckedInt &operator/=(QCheckedInt other)
0265     {
0266         return *this = *this / other;
0267     }
0268 
0269     template <typename AInt, if_is_same_int<AInt> = true>
0270     constexpr QCheckedInt &operator/=(AInt other)
0271     {
0272         return *this = *this / QCheckedInt(other);
0273     }
0274 
0275 #undef Q_CHECKEDINT_POLICY_CHECK
0276 
0277     // Comparisons
0278     friend constexpr bool comparesEqual(QCheckedInt lhs, QCheckedInt rhs) noexcept
0279     {
0280         return lhs.m_i == rhs.m_i;
0281     }
0282 
0283     template <typename AInt, if_is_same_int<AInt> = true>
0284     friend constexpr bool comparesEqual(QCheckedInt lhs, AInt rhs) noexcept
0285     {
0286         return lhs.m_i == rhs;
0287     }
0288 
0289     friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, QCheckedInt rhs) noexcept
0290     {
0291         return Qt::compareThreeWay(lhs.m_i, rhs.m_i);
0292     }
0293 
0294     template <typename AInt, if_is_same_int<AInt> = true>
0295     friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, AInt rhs) noexcept
0296     {
0297         return Qt::compareThreeWay(lhs.m_i, rhs);
0298     }
0299 
0300     Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QCheckedInt)
0301     Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(
0302         QCheckedInt,
0303         AInt,
0304         template <typename AInt, if_is_same_int<AInt> = true>)
0305 
0306 private:
0307     friend size_t constexpr qHash(QCheckedInt key, size_t seed = 0) noexcept
0308     {
0309         using QT_PREPEND_NAMESPACE(qHash); // ### needed?
0310         return qHash(key.value(), seed);
0311     }
0312 };
0313 
0314 } // namespace QCheckedIntegers
0315 } // namespace QtPrivate
0316 
0317 QT_END_NAMESPACE
0318 
0319 #endif // QCHECKEDINT_H