diff --git a/sus/CMakeLists.txt b/sus/CMakeLists.txt index d30c615e8..8bd66dfb7 100644 --- a/sus/CMakeLists.txt +++ b/sus/CMakeLists.txt @@ -306,6 +306,7 @@ if(${SUBSPACE_BUILD_TESTS}) "num/overflow_integer_unittest.cc" "num/u8_unittest.cc" "num/u16_unittest.cc" + "num/u32_overflow_unittest.cc" "num/u32_unittest.cc" "num/u64_unittest.cc" "num/uptr_unittest.cc" diff --git a/sus/num/__private/intrinsics.h b/sus/num/__private/intrinsics.h index e4cb54b97..46020fa25 100644 --- a/sus/num/__private/intrinsics.h +++ b/sus/num/__private/intrinsics.h @@ -705,8 +705,8 @@ sus_pure_const sus_always_inline constexpr T reverse_bits(T value) noexcept { template requires(std::is_integral_v && std::is_unsigned_v) -sus_pure_const inline constexpr T rotate_left(T value, uint32_t n) noexcept { - const uint32_t num_bits = unchecked_mul(unchecked_sizeof(), uint32_t{8}); +sus_pure_const inline constexpr T rotate_left(T value, uint64_t n) noexcept { + const uint64_t num_bits = unchecked_mul(unchecked_sizeof(), uint32_t{8}); // Try avoid slow % operation if we can. Comparisons are much faster than %. if (n >= num_bits) n %= num_bits; if (n == 0) return value; @@ -716,8 +716,8 @@ sus_pure_const inline constexpr T rotate_left(T value, uint32_t n) noexcept { template requires(std::is_integral_v && std::is_unsigned_v) -sus_pure_const inline constexpr T rotate_right(T value, uint32_t n) noexcept { - const uint32_t num_bits = unchecked_mul(unchecked_sizeof(), uint32_t{8}); +sus_pure_const inline constexpr T rotate_right(T value, uint64_t n) noexcept { + const uint64_t num_bits = unchecked_mul(unchecked_sizeof(), uint32_t{8}); // Try avoid slow % operation if we can. Comparisons are much faster than %. if (n >= num_bits) n %= num_bits; if (n == 0) return value; diff --git a/sus/num/__private/signed_integer_methods.inc b/sus/num/__private/signed_integer_methods.inc index 736e82025..1f6faf34e 100644 --- a/sus/num/__private/signed_integer_methods.inc +++ b/sus/num/__private/signed_integer_methods.inc @@ -998,7 +998,7 @@ constexpr inline void operator^=(_self r) & noexcept { /// This operator will panic if the shift amount is not less than /// [`@doc.self::BITS`]($sus::num::@doc.self::BITS) and [overflow checks]( /// $sus::num#overflow-behaviour) are enabled. -constexpr inline void operator<<=(u32 r) & noexcept { +constexpr inline void operator<<=(u64 r) & noexcept { if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { const auto out = __private::shl_with_overflow(primitive_value, r.primitive_value); @@ -1016,7 +1016,7 @@ constexpr inline void operator<<=(u32 r) & noexcept { /// This operator will panic if the shift amount is not less than /// [`@doc.self::BITS`]($sus::num::@doc.self::BITS) and [overflow checks]( /// $sus::num#overflow-behaviour) are enabled. -constexpr inline void operator>>=(u32 r) & noexcept { +constexpr inline void operator>>=(u64 r) & noexcept { if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { const auto out = __private::shr_with_overflow(primitive_value, r.primitive_value); @@ -1798,7 +1798,7 @@ sus_pure constexpr _self reverse_bits() const& noexcept { /// truncated bits to the end of the resulting integer. /// /// Please note this isn't the same operation as the `<<` shifting operator! -sus_pure constexpr _self rotate_left(u32 n) const& noexcept { +sus_pure constexpr _self rotate_left(u64 n) const& noexcept { return __private::into_signed(__private::rotate_left( __private::into_unsigned(primitive_value), n.primitive_value)); } @@ -1807,7 +1807,7 @@ sus_pure constexpr _self rotate_left(u32 n) const& noexcept { /// truncated bits to the beginning of the resulting integer. /// /// Please note this isn't the same operation as the >> shifting operator! -sus_pure constexpr _self rotate_right(u32 n) const& noexcept { +sus_pure constexpr _self rotate_right(u64 n) const& noexcept { return __private::into_signed(__private::rotate_right( __private::into_unsigned(primitive_value), n.primitive_value)); } diff --git a/sus/num/__private/unsigned_integer_methods.inc b/sus/num/__private/unsigned_integer_methods.inc index 52e879752..2eccc9e06 100644 --- a/sus/num/__private/unsigned_integer_methods.inc +++ b/sus/num/__private/unsigned_integer_methods.inc @@ -627,11 +627,15 @@ sus_pure constexpr inline _self operator~() const& noexcept { /// #[doc.overloads=unsignedint.+] [[nodiscard]] sus_pure_const friend constexpr inline _self operator+( _self l, _self r) noexcept { - const auto out = - __private::add_with_overflow(l.primitive_value, r.primitive_value); - // TODO: Allow opting out of all overflow checks? - ::sus::check(!out.overflow); - return _self(out.value); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = + __private::add_with_overflow(l.primitive_value, r.primitive_value); + ::sus::check(!out.overflow); + return _self(out.value); + } else { + return _self( + __private::unchecked_add(l.primitive_value, r.primitive_value)); + } } #if _pointer /// #[doc.overloads=unsignedint.+] @@ -689,11 +693,15 @@ friend constexpr inline _self operator+(U l, _self r) noexcept = delete; /// #[doc.overloads=unsignedint.-] [[nodiscard]] sus_pure_const friend constexpr inline _self operator-( _self l, _self r) noexcept { - const auto out = - __private::sub_with_overflow(l.primitive_value, r.primitive_value); - // TODO: Allow opting out of all overflow checks? - ::sus::check(!out.overflow); - return _self(out.value); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = + __private::sub_with_overflow(l.primitive_value, r.primitive_value); + ::sus::check(!out.overflow); + return _self(out.value); + } else { + return _self( + __private::unchecked_sub(l.primitive_value, r.primitive_value)); + } } #if _pointer /// #[doc.overloads=unsignedint.-] @@ -751,11 +759,15 @@ friend constexpr inline _self operator-(U l, _self r) noexcept = delete; /// #[doc.overloads=unsignedint.*] [[nodiscard]] sus_pure_const friend constexpr inline _self operator*( _self l, _self r) noexcept { - const auto out = - __private::mul_with_overflow(l.primitive_value, r.primitive_value); - // TODO: Allow opting out of all overflow checks? - ::sus::check(!out.overflow); - return _self(out.value); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = + __private::mul_with_overflow(l.primitive_value, r.primitive_value); + ::sus::check(!out.overflow); + return _self(out.value); + } else { + return _self( + __private::unchecked_mul(l.primitive_value, r.primitive_value)); + } } #if _pointer /// #[doc.overloads=unsignedint.*] @@ -813,7 +825,6 @@ friend constexpr inline _self operator*(U l, _self r) noexcept = delete; /// #[doc.overloads=unsignedint./] [[nodiscard]] sus_pure_const friend constexpr inline _self operator/( _self l, _self r) noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(r.primitive_value != 0u); return _self(__private::unchecked_div(l.primitive_value, r.primitive_value)); } @@ -873,7 +884,6 @@ friend constexpr inline _self operator/(U l, _self r) noexcept = delete; /// #[doc.overloads=unsignedint.%] [[nodiscard]] sus_pure_const friend constexpr inline _self operator%( _self l, _self r) noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(r.primitive_value != 0u); return _self(__private::unchecked_rem(l.primitive_value, r.primitive_value)); } @@ -1101,11 +1111,15 @@ friend constexpr inline _self operator^(U l, _self r) noexcept = delete; /// Satisfies the [`AddAssign<@doc.self>`]($sus::num::AddAssign) concept. constexpr inline void operator+=(_self r) & noexcept { - const auto out = - __private::add_with_overflow(primitive_value, r.primitive_value); - // TODO: Allow opting out of all overflow checks? - ::sus::check(!out.overflow); - primitive_value = out.value; + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = + __private::add_with_overflow(primitive_value, r.primitive_value); + ::sus::check(!out.overflow); + primitive_value = out.value; + } else { + primitive_value = + __private::unchecked_add(primitive_value, r.primitive_value); + } } #if _pointer // uptr can't be created from other safe numerics. constexpr inline void operator+=(_pointer_sized r) & noexcept { @@ -1114,11 +1128,15 @@ constexpr inline void operator+=(_pointer_sized r) & noexcept { #endif /// Satisfies the [`SubAssign<@doc.self>`]($sus::num::SubAssign) concept. constexpr inline void operator-=(_self r) & noexcept { - const auto out = - __private::sub_with_overflow(primitive_value, r.primitive_value); - // TODO: Allow opting out of all overflow checks? - ::sus::check(!out.overflow); - primitive_value = out.value; + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = + __private::sub_with_overflow(primitive_value, r.primitive_value); + ::sus::check(!out.overflow); + primitive_value = out.value; + } else { + primitive_value = + __private::unchecked_sub(primitive_value, r.primitive_value); + } } #if _pointer // uptr can't be created from other safe numerics. constexpr inline void operator-=(_pointer_sized r) & noexcept { @@ -1127,11 +1145,15 @@ constexpr inline void operator-=(_pointer_sized r) & noexcept { #endif /// Satisfies the [`MulAssign<@doc.self>`]($sus::num::MulAssign) concept. constexpr inline void operator*=(_self r) & noexcept { - const auto out = - __private::mul_with_overflow(primitive_value, r.primitive_value); - // TODO: Allow opting out of all overflow checks? - ::sus::check(!out.overflow); - primitive_value = out.value; + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = + __private::mul_with_overflow(primitive_value, r.primitive_value); + ::sus::check(!out.overflow); + primitive_value = out.value; + } else { + primitive_value = + __private::unchecked_mul(primitive_value, r.primitive_value); + } } #if _pointer // uptr can't be created from other safe numerics. constexpr inline void operator*=(_pointer_sized r) & noexcept { @@ -1140,7 +1162,6 @@ constexpr inline void operator*=(_pointer_sized r) & noexcept { #endif /// Satisfies the [`DivAssign<@doc.self>`]($sus::num::DivAssign) concept. constexpr inline void operator/=(_self r) & noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(r.primitive_value != 0u); primitive_value /= r.primitive_value; } @@ -1151,7 +1172,6 @@ constexpr inline void operator/=(_pointer_sized r) & noexcept { #endif /// Satisfies the [`RemAssign<@doc.self>`]($sus::num::RemAssign) concept. constexpr inline void operator%=(_self r) & noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(r.primitive_value != 0u); primitive_value %= r.primitive_value; } @@ -1191,17 +1211,9 @@ constexpr inline void operator^=(_pointer_sized r) & noexcept { } #endif /// Satisfies the [`ShlAssign<@doc.self>`]($sus::num::ShlAssign) concept. -constexpr inline void operator<<=(u32 r) & noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u32(__private::num_bits<_primitive>())); - primitive_value <<= r.primitive_value; -} +constexpr inline void operator<<=(u64 r) & noexcept; /// Satisfies the [`ShrAssign<@doc.self>`]($sus::num::ShrAssign) concept. -constexpr inline void operator>>=(u32 r) & noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u32(__private::num_bits<_primitive>())); - primitive_value >>= r.primitive_value; -} +constexpr inline void operator>>=(u64 r) & noexcept; /// Computes the absolute difference between `self` and `other`. sus_pure constexpr _self abs_diff(_self other) const& noexcept { @@ -1334,7 +1346,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_div( /// # Panics /// This function will panic if `rhs` is 0. sus_pure constexpr _self saturating_div(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return _self(__private::unchecked_div(primitive_value, rhs.primitive_value)); } @@ -1347,7 +1358,6 @@ sus_pure constexpr _self saturating_div(_self rhs) const& noexcept { /// # Panics /// This function will panic if `rhs` is 0. sus_pure constexpr _self wrapping_div(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return _self(__private::unchecked_div(primitive_value, rhs.primitive_value)); } @@ -1463,7 +1473,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_rem( /// # Panics /// This function will panic if `rhs` is 0. sus_pure constexpr _self wrapping_rem(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return _self(__private::unchecked_rem(primitive_value, rhs.primitive_value)); } @@ -1476,7 +1485,6 @@ sus_pure constexpr _self wrapping_rem(_self rhs) const& noexcept { /// # Panics /// This function will panic if `rhs` is 0. sus_pure constexpr _self div_euclid(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return _self(__private::unchecked_div(primitive_value, rhs.primitive_value)); } @@ -1512,7 +1520,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_div_euclid( /// # Panics /// This function will panic if `rhs` is 0. sus_pure constexpr _self wrapping_div_euclid(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return _self(__private::unchecked_div(primitive_value, rhs.primitive_value)); } @@ -1525,7 +1532,6 @@ sus_pure constexpr _self wrapping_div_euclid(_self rhs) const& noexcept { /// # Panics /// This function will panic if `rhs` is 0. sus_pure constexpr _self rem_euclid(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return _self(__private::unchecked_rem(primitive_value, rhs.primitive_value)); } @@ -1562,7 +1568,6 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_rem_euclid( /// # Panics /// This function will panic if `rhs` is 0. sus_pure constexpr _self wrapping_rem_euclid(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return _self(__private::unchecked_rem(primitive_value, rhs.primitive_value)); } @@ -1725,17 +1730,13 @@ sus_pure constexpr _self reverse_bits() const& noexcept { /// truncated bits to the end of the resulting integer. /// /// Please note this isn't the same operation as the `<<` shifting operator! -sus_pure constexpr _self rotate_left(u32 n) const& noexcept { - return _self(__private::rotate_left(primitive_value, n.primitive_value)); -} +sus_pure constexpr _self rotate_left(u64 n) const& noexcept; /// Shifts the bits to the right by a specified amount, n, wrapping the /// truncated bits to the beginning of the resulting integer. /// /// Please note this isn't the same operation as the >> shifting operator! -sus_pure constexpr _self rotate_right(u32 n) const& noexcept { - return _self(__private::rotate_right(primitive_value, n.primitive_value)); -} +sus_pure constexpr _self rotate_right(u64 n) const& noexcept; /// Reverses the byte order of the integer. sus_pure constexpr _self swap_bytes() const& noexcept { @@ -1746,8 +1747,9 @@ sus_pure constexpr _self swap_bytes() const& noexcept { sus_pure constexpr _self pow(u32 rhs) const& noexcept { const auto out = __private::pow_with_overflow(primitive_value, rhs.primitive_value); - // TODO: Allow opting out of all overflow checks? - ::sus::check(!out.overflow); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(!out.overflow); + } return _self(out.value); } diff --git a/sus/num/__private/unsigned_integer_methods_impl.inc b/sus/num/__private/unsigned_integer_methods_impl.inc index bfa92d6cd..199f7b91b 100644 --- a/sus/num/__private/unsigned_integer_methods_impl.inc +++ b/sus/num/__private/unsigned_integer_methods_impl.inc @@ -235,7 +235,6 @@ sus_pure constexpr ::sus::option::Option<_self> _self::checked_div( sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> _self::overflowing_div( _self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return ::sus::tuple_type::Tuple<_self, bool>( _self(__private::unchecked_div(primitive_value, rhs.primitive_value)), @@ -284,7 +283,6 @@ sus_pure constexpr ::sus::option::Option<_self> _self::checked_rem( sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> _self::overflowing_rem( _self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return ::sus::tuple_type::Tuple<_self, bool>( _self(__private::unchecked_rem(primitive_value, rhs.primitive_value)), @@ -303,7 +301,6 @@ sus_pure constexpr ::sus::option::Option<_self> _self::checked_div_euclid( sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> _self::overflowing_div_euclid(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return ::sus::tuple_type::Tuple<_self, bool>( _self(__private::unchecked_div(primitive_value, rhs.primitive_value)), @@ -322,13 +319,21 @@ sus_pure constexpr ::sus::option::Option<_self> _self::checked_rem_euclid( sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> _self::overflowing_rem_euclid(_self rhs) const& noexcept { - // TODO: Allow opting out of all overflow checks? ::sus::check(rhs.primitive_value != 0u); return ::sus::tuple_type::Tuple<_self, bool>( _self(__private::unchecked_rem(primitive_value, rhs.primitive_value)), false); } +constexpr inline void _self::operator<<=(u64 r) & noexcept { + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u64(__private::num_bits<_primitive>())); + primitive_value <<= r.primitive_value; + } else { + primitive_value = wrapping_shl(r).primitive_value; + } +} + sus_pure constexpr ::sus::option::Option<_self> _self::checked_shl( u64 rhs) const& noexcept { const auto out = @@ -351,6 +356,15 @@ sus_pure constexpr _self _self::wrapping_shl(u64 rhs) const& noexcept { __private::shl_with_overflow(primitive_value, rhs.primitive_value).value); } +constexpr inline void _self::operator>>=(u64 r) & noexcept { + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u64(__private::num_bits<_primitive>())); + primitive_value >>= r.primitive_value; + } else { + primitive_value = wrapping_shr(r).primitive_value; + } +} + sus_pure constexpr ::sus::option::Option<_self> _self::checked_shr( u64 rhs) const& noexcept { const auto out = @@ -394,7 +408,6 @@ sus_pure constexpr ::sus::option::Option<_self> _self::checked_pow( u32 rhs) const& noexcept { const auto out = __private::pow_with_overflow(primitive_value, rhs.primitive_value); - // TODO: Allow opting out of all overflow checks? if (!out.overflow) [[likely]] return ::sus::option::Option<_self>(_self(out.value)); else @@ -408,6 +421,14 @@ sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> _self::overflowing_pow( return ::sus::tuple_type::Tuple<_self, bool>(_self(out.value), out.overflow); } +sus_pure constexpr _self _self::rotate_left(u64 n) const& noexcept { + return _self(__private::rotate_left(primitive_value, n.primitive_value)); +} + +sus_pure constexpr _self _self::rotate_right(u64 n) const& noexcept { + return _self(__private::rotate_right(primitive_value, n.primitive_value)); +} + sus_pure constexpr ::sus::option::Option _self::checked_log2() const& noexcept { if (primitive_value == 0u) [[unlikely]] { diff --git a/sus/num/unsigned_integer.h b/sus/num/unsigned_integer.h index 215981edf..bda11d32e 100644 --- a/sus/num/unsigned_integer.h +++ b/sus/num/unsigned_integer.h @@ -32,11 +32,11 @@ #include "sus/mem/move.h" #include "sus/mem/relocate.h" #include "sus/mem/size_of.h" +#include "sus/num/__private/check_integer_overflow.h" #include "sus/num/__private/int_log10.h" #include "sus/num/__private/intrinsics.h" #include "sus/num/__private/literals.h" #include "sus/num/__private/primitive_type.h" -#include "sus/num/__private/check_integer_overflow.h" #include "sus/num/float_concepts.h" #include "sus/num/integer_concepts.h" #include "sus/num/try_from_int_error.h" @@ -273,10 +273,13 @@ constexpr inline P operator>>(P l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.<<] [[nodiscard]] sus_pure_const constexpr inline u8 operator<<( u8 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u8::BITS); - return u8( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u8::BITS); + return u8( + __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shl(u64(r)); + } } /// #[doc.overloads=unsignedint.<<] template @@ -287,10 +290,13 @@ constexpr inline u8 operator<<(u8 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.>>] [[nodiscard]] sus_pure_const constexpr inline u8 operator>>( u8 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u8::BITS); - return u8( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u8::BITS); + return u8( + __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shr(u64(r)); + } } /// #[doc.overloads=unsignedint.>>] template @@ -299,10 +305,13 @@ constexpr inline u8 operator>>(u8 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.<<] [[nodiscard]] sus_pure_const constexpr inline u16 operator<<( u16 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u16::BITS); - return u16( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u16::BITS); + return u16( + __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shl(u64(r)); + } } /// #[doc.overloads=unsignedint.<<] template @@ -311,10 +320,13 @@ constexpr inline u16 operator<<(u16 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.>>] [[nodiscard]] sus_pure_const constexpr inline u16 operator>>( u16 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u16::BITS); - return u16( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u16::BITS); + return u16( + __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shr(u64(r)); + } } /// #[doc.overloads=unsignedint.>>] template @@ -323,10 +335,13 @@ constexpr inline u16 operator>>(u16 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.<<] [[nodiscard]] sus_pure_const constexpr inline u32 operator<<( u32 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u32::BITS); - return u32( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u32::BITS); + return u32( + __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shl(u64(r)); + } } /// #[doc.overloads=unsignedint.<<] template @@ -335,10 +350,13 @@ constexpr inline u32 operator<<(u32 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.>>] [[nodiscard]] sus_pure_const constexpr inline u32 operator>>( u32 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u32::BITS); - return u32( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u32::BITS); + return u32( + __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shr(u64(r)); + } } /// #[doc.overloads=unsignedint.>>] template @@ -347,10 +365,13 @@ constexpr inline u32 operator>>(u32 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.<<] [[nodiscard]] sus_pure_const constexpr inline u64 operator<<( u64 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u64::BITS); - return u64( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u64::BITS); + return u64( + __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shl(u64(r)); + } } /// #[doc.overloads=unsignedint.<<] template @@ -359,10 +380,13 @@ constexpr inline u64 operator<<(u64 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.>>] [[nodiscard]] sus_pure_const constexpr inline u64 operator>>( u64 l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < u64::BITS); - return u64( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < u64::BITS); + return u64( + __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shr(u64(r)); + } } /// #[doc.overloads=unsignedint.>>] template @@ -371,10 +395,13 @@ constexpr inline u64 operator>>(u64 l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.<<] [[nodiscard]] sus_pure_const constexpr inline usize operator<<( usize l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < usize::BITS); - return usize( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < usize::BITS); + return usize( + __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shl(u64(r)); + } } /// #[doc.overloads=unsignedint.<<] template @@ -383,10 +410,13 @@ constexpr inline usize operator<<(usize l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.>>] [[nodiscard]] sus_pure_const constexpr inline usize operator>>( usize l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < usize::BITS); - return usize( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < usize::BITS); + return usize( + __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shr(u64(r)); + } } /// #[doc.overloads=unsignedint.>>] template @@ -395,10 +425,13 @@ constexpr inline usize operator>>(usize l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.<<] [[nodiscard]] sus_pure_const constexpr inline uptr operator<<( uptr l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < uptr::BITS); - return uptr( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < uptr::BITS); + return uptr( + __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shl(u64(r)); + } } /// #[doc.overloads=unsignedint.<<] template @@ -407,10 +440,13 @@ constexpr inline uptr operator<<(uptr l, U r) noexcept = delete; /// #[doc.overloads=unsignedint.>>] [[nodiscard]] sus_pure_const constexpr inline uptr operator>>( uptr l, std::convertible_to auto r) noexcept { - // TODO: Allow opting out of all overflow checks? - ::sus::check(r < uptr::BITS); - return uptr( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + ::sus::check(r < uptr::BITS); + return uptr( + __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + } else { + return l.wrapping_shr(u64(r)); + } } /// #[doc.overloads=unsignedint.>>] template