123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 | // 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#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>{staticinline constexpr Int MinInt = (std::numeric_limits<Int>::min)();staticinline constexpr Int MaxInt = (std::numeric_limits<Int>::max)();[[nodiscard]]staticconstexprbooladd(Int a, Int b, Int *result) noexcept {return!qAddOverflow(a, b, result);}[[nodiscard]]staticconstexprboolsub(Int a, Int b, Int *result) noexcept {return!qSubOverflow(a, b, result);}[[nodiscard]]staticconstexprboolmul(Int a, Int b, Int *result) noexcept {return!qMulOverflow(a, b, result);}[[nodiscard]]staticconstexprbooldiv(Int a, Int b, Int *result) noexcept {if(Q_UNLIKELY(b ==0))return false;*result = a / b;return true;}};// Reports failed checks through Q_ASSERT.struct AssertReportPolicy {staticconstexprvoidcheck(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 constexprQCheckedInt(Int i) noexcept :m_i(i){}// Conversionsexplicit constexpr operatorInt()const noexcept {return m_i;}// Accessorsconstexpr Int value()const noexcept {return m_i; }template<typename AInt, if_is_same_int<AInt> =true>constexprvoidsetValue(AInt i) noexcept { m_i = i; }constexpr Int &as_underlying() & noexcept {return m_i; }constexprconst Int &as_underlying()const& noexcept {return m_i; }constexpr Int &&as_underlying() && noexcept {returnstd::move(m_i); }constexprconst Int &&as_underlying()const&& noexcept {returnstd::move(m_i); }// Unary opsconstexpr 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;}// Additionfriend 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){returnQCheckedInt(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);}// Subtractionfriend 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){returnQCheckedInt(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);}// Multiplicationfriend 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){returnQCheckedInt(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);}// Divisionfriend 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){returnQCheckedInt(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// Comparisonsfriend constexprboolcomparesEqual(QCheckedInt lhs, QCheckedInt rhs) noexcept {return lhs.m_i == rhs.m_i;}template<typename AInt, if_is_same_int<AInt> =true>friend constexprboolcomparesEqual(QCheckedInt lhs, AInt rhs) noexcept {return lhs.m_i == rhs;}friend constexpr Qt::strong_ordering compareThreeWay(QCheckedInt lhs, QCheckedInt rhs) noexcept {returnQt::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 {returnQt::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>)};template<typename Int, typename I, typename P> Q_DECL_CONST_FUNCTION size_tconstexpr inlineqHash(QCheckedInt<Int, I, P> key,size_t seed =0) noexcept {usingQT_PREPEND_NAMESPACE(qHash);returnqHash(key.value(), seed);}}// namespace QCheckedIntegers}// namespace QtPrivate QT_END_NAMESPACE #endif// QCHECKEDINT_H
|