Revision control

++ // Copyright (C) 2025 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QCHECKEDINT_H
++ #define QCHECKEDINT_H
++
++ #include <QtCore/qassert.h>
++ #include <QtCore/qcompare.h>
++ #include <QtCore/qhashfunctions.h>
++ #include <QtCore/qnumeric.h>
++
++ #include <type_traits>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QtPrivate {
++ namespace QCheckedIntegers {
++
++ template <typename Int>
++ struct CheckIntTypeHelper
++ {
++ static_assert(std::is_integral_v<Int>, "Only integer types are supported");
++ static_assert(std::is_signed_v<Int>, "Only signed types are supported");
++ static_assert(std::is_same_v<Int, decltype(+Int{})>, "Only fully promoted types are supported");
++ };
++
++ // Implements wraparound semantics, and checks for overflow.
++ // Never causes UB on any operation.
++ template <typename Int>
++ struct SafeCheckImpl : private CheckIntTypeHelper<Int>
++ {
++ static inline constexpr Int MinInt = (std::numeric_limits<Int>::min)();
++ static inline constexpr Int MaxInt = (std::numeric_limits<Int>::max)();
++
++ [[nodiscard]]
++ static constexpr bool add(Int a, Int b, Int *result) noexcept
++ {
++ return !qAddOverflow(a, b, result);
++ }
++
++ [[nodiscard]]
++ static constexpr bool sub(Int a, Int b, Int *result) noexcept
++ {
++ return !qSubOverflow(a, b, result);
++ }
++
++ [[nodiscard]]
++ static constexpr bool mul(Int a, Int b, Int *result) noexcept
++ {
++ return !qMulOverflow(a, b, result);
++ }
++
++ [[nodiscard]]
++ static constexpr bool div(Int a, Int b, Int *result) noexcept
++ {
++ if (Q_UNLIKELY(b == 0))
++ return false;
++ if (b == -1 && a == MinInt)
++ return false;
++
++ *result = a / b;
++ return true;
++ }
++ };
++
++ // Reports failed checks through Q_ASSERT.
++ struct AssertReportPolicy
++ {
++ static constexpr void check(bool ok, const char *where, const char *description)
++ {
++ Q_ASSERT_X(ok, where, description);
++ }
++ };
++
++ template <typename Int,
++ typename Impl = SafeCheckImpl<Int>,
++ typename FailureReportPolicy = AssertReportPolicy>
++ class QCheckedInt : private CheckIntTypeHelper<Int>
++ {
++ Int m_i;
++
++ #define Q_CHECKEDINT_POLICY_CHECK(cond, what) \
++ FailureReportPolicy::check(cond, Q_FUNC_INFO, what)
++
++ public:
++ template <typename AInt>
++ using if_is_same_int = std::enable_if_t<std::is_same_v<Int, AInt>, bool>;
++
++ QCheckedInt() = default;
++
++ explicit constexpr QCheckedInt(Int i) noexcept
++ : m_i(i)
++ {}
++
++ // Conversions
++ explicit constexpr operator Int() const noexcept
++ {
++ return m_i;
++ }
++
++ // Accessors
++ constexpr Int value() const noexcept { return m_i; }
++ template <typename AInt, if_is_same_int<AInt> = true>
++ constexpr void setValue(AInt i) noexcept { m_i = i; }
++
++ constexpr Int &as_underlying() & noexcept { return m_i; }
++ constexpr const Int &as_underlying() const & noexcept { return m_i; }
++ constexpr Int &&as_underlying() && noexcept { return std::move(m_i); }
++ constexpr const Int &&as_underlying() const && noexcept { return std::move(m_i); }
++
++ // Unary ops
++ constexpr QCheckedInt operator+() const noexcept { return *this; }
++ constexpr QCheckedInt operator-() const
++ {
++ QCheckedInt result{};
++ const bool ok = Impl::sub(Int(0), m_i, &result.m_i);
++ Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in unary negation");
++ return result;
++ }
++
++ constexpr QCheckedInt &operator++()
++ {
++ const bool ok = Impl::add(m_i, Int(1), &m_i);
++ Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator++");
++ return *this;
++ }
++
++ constexpr QCheckedInt operator++(int)
++ {
++ QCheckedInt result = *this;
++ ++*this;
++ return result;
++ }
++
++ constexpr QCheckedInt &operator--()
++ {
++ const bool ok = Impl::sub(m_i, Int(1), &m_i);
++ Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator--");
++ return *this;
++ }
++
++ constexpr QCheckedInt operator--(int)
++ {
++ QCheckedInt result = *this;
++ --*this;
++ return result;
++ }
++
++ // Addition
++ friend constexpr QCheckedInt operator+(QCheckedInt lhs, QCheckedInt rhs)
++ {
++ QCheckedInt result{};
++ const bool ok = Impl::add(lhs.m_i, rhs.m_i, &result.m_i);
++ Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator+");
++ return result;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator+(QCheckedInt lhs, AInt rhs)
++ {
++ return lhs + QCheckedInt(rhs);
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator+(AInt lhs, QCheckedInt rhs)
++ {
++ return QCheckedInt(lhs) + rhs;
++ }
++
++ constexpr QCheckedInt &operator+=(QCheckedInt other)
++ {
++ return *this = *this + other;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ constexpr QCheckedInt &operator+=(AInt other)
++ {
++ return *this = *this + QCheckedInt(other);
++ }
++
++ // Subtraction
++ friend constexpr QCheckedInt operator-(QCheckedInt lhs, QCheckedInt rhs)
++ {
++ QCheckedInt result{};
++ const bool ok = Impl::sub(lhs.m_i, rhs.m_i, &result.m_i);
++ Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator-");
++ return result;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator-(QCheckedInt lhs, AInt rhs)
++ {
++ return lhs - QCheckedInt(rhs);
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator-(AInt lhs, QCheckedInt rhs)
++ {
++ return QCheckedInt(lhs) - rhs;
++ }
++
++ constexpr QCheckedInt &operator-=(QCheckedInt other)
++ {
++ return *this = *this - other;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ constexpr QCheckedInt &operator-=(AInt other)
++ {
++ return *this = *this - QCheckedInt(other);
++ }
++
++ // Multiplication
++ friend constexpr QCheckedInt operator*(QCheckedInt lhs, QCheckedInt rhs)
++ {
++ QCheckedInt result{};
++ const bool ok = Impl::mul(lhs.m_i, rhs.m_i, &result.m_i);
++ Q_CHECKEDINT_POLICY_CHECK(ok, "Overflow in operator*");
++ return result;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator*(QCheckedInt lhs, AInt rhs)
++ {
++ return lhs * QCheckedInt(rhs);
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator*(AInt lhs, QCheckedInt rhs)
++ {
++ return QCheckedInt(lhs) * rhs;
++ }
++
++ constexpr QCheckedInt &operator*=(QCheckedInt other)
++ {
++ return *this = *this * other;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ constexpr QCheckedInt &operator*=(AInt other)
++ {
++ return *this = *this * QCheckedInt(other);
++ }
++
++ // Division
++ friend constexpr QCheckedInt operator/(QCheckedInt lhs, QCheckedInt rhs)
++ {
++ QCheckedInt result{};
++ const bool ok = Impl::div(lhs.m_i, rhs.m_i, &result.m_i);
++ Q_CHECKEDINT_POLICY_CHECK(ok, "Division by zero");
++ return result;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator/(QCheckedInt lhs, AInt rhs)
++ {
++ return lhs / QCheckedInt(rhs);
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr QCheckedInt operator/(AInt lhs, QCheckedInt rhs)
++ {
++ return QCheckedInt(lhs) / rhs;
++ }
++
++ constexpr QCheckedInt &operator/=(QCheckedInt other)
++ {
++ return *this = *this / other;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ constexpr QCheckedInt &operator/=(AInt other)
++ {
++ return *this = *this / QCheckedInt(other);
++ }
++
++ #undef Q_CHECKEDINT_POLICY_CHECK
++
++ // Comparisons
++ friend constexpr bool comparesEqual(QCheckedInt lhs, QCheckedInt rhs) noexcept
++ {
++ return lhs.m_i == rhs.m_i;
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr bool comparesEqual(QCheckedInt lhs, AInt rhs) noexcept
++ {
++ return lhs.m_i == rhs;
++ }
++
++ friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, QCheckedInt rhs) noexcept
++ {
++ return Qt::compareThreeWay(lhs.m_i, rhs.m_i);
++ }
++
++ template <typename AInt, if_is_same_int<AInt> = true>
++ friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, AInt rhs) noexcept
++ {
++ return Qt::compareThreeWay(lhs.m_i, rhs);
++ }
++
++ Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QCheckedInt)
++ Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(
++ QCheckedInt,
++ AInt,
++ template <typename AInt, if_is_same_int<AInt> = true>)
++
++ private:
++ friend size_t constexpr qHash(QCheckedInt key, size_t seed = 0) noexcept
++ {
++ using QT_PREPEND_NAMESPACE(qHash); // ### needed?
++ return qHash(key.value(), seed);
++ }
++ };
++
++ } // namespace QCheckedIntegers
++ } // namespace QtPrivate
++
++ QT_END_NAMESPACE
++
++ #endif // QCHECKEDINT_H