From 0b7e24a1156b8d2f9a5c9115eda471bbfd3a6a12 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Sun, 3 Dec 2023 17:20:46 -0500 Subject: [PATCH 1/2] Add div_ceil() for unsigned integers. These were stabilized in Rust 1.73. https://blog.rust-lang.org/2023/10/05/Rust-1.73.0.html Part of #221. --- .../__private/unsigned_integer_methods.inc | 30 ++++++++++++++++ sus/num/u16_overflow_unittest.cc | 17 +++++++++ sus/num/u16_unittest.cc | 2 ++ sus/num/u32_overflow_unittest.cc | 17 +++++++++ sus/num/u32_unittest.cc | 36 +++++++++++++++++++ sus/num/u64_overflow_unittest.cc | 17 +++++++++ sus/num/u64_unittest.cc | 2 ++ sus/num/u8_overflow_unittest.cc | 17 +++++++++ sus/num/u8_unittest.cc | 2 ++ sus/num/uptr_overflow_unittest.cc | 17 +++++++++ sus/num/uptr_unittest.cc | 10 ++++++ sus/num/usize_overflow_unittest.cc | 17 +++++++++ sus/num/usize_unittest.cc | 2 ++ 13 files changed, 186 insertions(+) diff --git a/sus/num/__private/unsigned_integer_methods.inc b/sus/num/__private/unsigned_integer_methods.inc index 3b842cfc3..3ff0eaae7 100644 --- a/sus/num/__private/unsigned_integer_methods.inc +++ b/sus/num/__private/unsigned_integer_methods.inc @@ -1629,6 +1629,36 @@ sus_pure constexpr _self wrapping_rem(U rhs) const& noexcept { } #endif +/// Calculates the quotient of itself and `rhs`, rounding the result towards +/// positive infinity. +/// +/// # Panics +/// This function will panic if `rhs` is zero. +/// +/// # Examples +/// Basic usage: +/// ``` +/// assert_eq!((7_u8).div_ceil(4u), 2u); +/// ``` +sus_pure constexpr _self div_ceil(_self rhs) const& noexcept { + ::sus::check_with_message(rhs.primitive_value != _primitive{0}, + "attempt to divide by zero"); + _self d = + _self(__private::unchecked_div(primitive_value, rhs.primitive_value)); + _self r = + _self(__private::unchecked_rem(primitive_value, rhs.primitive_value)); + return (r > _primitive{0}) ? d + _primitive{1} : d; +} + +#if _pointer +template + requires((UnsignedNumeric || UnsignedPrimitiveInteger) && + ::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure constexpr _self div_ceil(U rhs) const& noexcept { + return div_ceil(_self(_primitive{rhs})); +} +#endif + /// Performs Euclidean division. /// /// Since, for the positive integers, all common definitions of division are diff --git a/sus/num/u16_overflow_unittest.cc b/sus/num/u16_overflow_unittest.cc index c9b28543d..221ebe733 100644 --- a/sus/num/u16_overflow_unittest.cc +++ b/sus/num/u16_overflow_unittest.cc @@ -251,4 +251,21 @@ TEST(u16OverflowDeathTest, WrappingRemEuclidOverflow) { #endif } +TEST(u16OverflowDeathTest, DivCeilDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u16).div_ceil(0_u16); + ensure_use(&x); + }, + "attempt to divide by zero"); + EXPECT_DEATH( + { + auto x = u16::MAX.div_ceil(0_u16); + ensure_use(&x); + }, + "attempt to divide by zero"); +#endif +} + } // namespace diff --git a/sus/num/u16_unittest.cc b/sus/num/u16_unittest.cc index ffa4b8af1..039d9be19 100644 --- a/sus/num/u16_unittest.cc +++ b/sus/num/u16_unittest.cc @@ -658,6 +658,8 @@ TEST(u16, InvokeEverything) { (void)i.overflowing_rem_euclid(j); (void)i.wrapping_rem_euclid(j); + (void)i.div_ceil(j); + (void)i.checked_shl(1_u32); (void)i.overflowing_shl(1_u32); (void)i.wrapping_shl(1_u32); diff --git a/sus/num/u32_overflow_unittest.cc b/sus/num/u32_overflow_unittest.cc index 12338ccc6..b49d3a6ee 100644 --- a/sus/num/u32_overflow_unittest.cc +++ b/sus/num/u32_overflow_unittest.cc @@ -256,4 +256,21 @@ TEST(u32Overlow, NextPowerOfTwoOutOfBounds) { EXPECT_EQ(u32::MAX.next_power_of_two(), 0_u32); } +TEST(u32OverflowDeathTest, DivCeilDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u32).div_ceil(0_u32); + ensure_use(&x); + }, + "attempt to divide by zero"); + EXPECT_DEATH( + { + auto x = u32::MAX.div_ceil(0_u32); + ensure_use(&x); + }, + "attempt to divide by zero"); +#endif +} + } // namespace diff --git a/sus/num/u32_unittest.cc b/sus/num/u32_unittest.cc index 21c9d04b8..83791b95f 100644 --- a/sus/num/u32_unittest.cc +++ b/sus/num/u32_unittest.cc @@ -2168,4 +2168,40 @@ static_assert([] { return v.as_mut_ptr() == &v.primitive_value; }()); +TEST(u32, DivCeil) { + EXPECT_EQ((7_u32).div_ceil(1u), 7u); + EXPECT_EQ((7_u32).div_ceil(2u), 4u); + EXPECT_EQ((7_u32).div_ceil(3u), 3u); + EXPECT_EQ((7_u32).div_ceil(4u), 2u); + EXPECT_EQ((7_u32).div_ceil(5u), 2u); + EXPECT_EQ((7_u32).div_ceil(6u), 2u); + EXPECT_EQ((7_u32).div_ceil(7u), 1u); + EXPECT_EQ((7_u32).div_ceil(8u), 1u); + + EXPECT_EQ((6_u32).div_ceil(1u), 6u); + EXPECT_EQ((6_u32).div_ceil(2u), 3u); + EXPECT_EQ((6_u32).div_ceil(3u), 2u); + EXPECT_EQ((6_u32).div_ceil(4u), 2u); + EXPECT_EQ((6_u32).div_ceil(5u), 2u); + EXPECT_EQ((6_u32).div_ceil(6u), 1u); + EXPECT_EQ((6_u32).div_ceil(7u), 1u); +} + +TEST(u32DeathTest, DivCeilDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u32).div_ceil(0_u32); + ensure_use(&x); + }, + "attempt to divide by zero"); + EXPECT_DEATH( + { + auto x = u32::MAX.div_ceil(0_u32); + ensure_use(&x); + }, + "attempt to divide by zero"); +#endif +} + } // namespace diff --git a/sus/num/u64_overflow_unittest.cc b/sus/num/u64_overflow_unittest.cc index 0fe543a4f..05f01b1df 100644 --- a/sus/num/u64_overflow_unittest.cc +++ b/sus/num/u64_overflow_unittest.cc @@ -251,4 +251,21 @@ TEST(u64OverflowDeathTest, WrappingRemEuclidOverflow) { #endif } +TEST(u64OverflowDeathTest, DivCeilDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u64).div_ceil(0_u64); + ensure_use(&x); + }, + "attempt to divide by zero"); + EXPECT_DEATH( + { + auto x = u64::MAX.div_ceil(0_u64); + ensure_use(&x); + }, + "attempt to divide by zero"); +#endif +} + } // namespace diff --git a/sus/num/u64_unittest.cc b/sus/num/u64_unittest.cc index 625587182..f37f99903 100644 --- a/sus/num/u64_unittest.cc +++ b/sus/num/u64_unittest.cc @@ -674,6 +674,8 @@ TEST(u64, InvokeEverything) { (void)i.overflowing_rem_euclid(j); (void)i.wrapping_rem_euclid(j); + (void)i.div_ceil(j); + (void)i.checked_shl(1_u32); (void)i.overflowing_shl(1_u32); (void)i.wrapping_shl(1_u32); diff --git a/sus/num/u8_overflow_unittest.cc b/sus/num/u8_overflow_unittest.cc index f65e03f3e..fa945afff 100644 --- a/sus/num/u8_overflow_unittest.cc +++ b/sus/num/u8_overflow_unittest.cc @@ -227,4 +227,21 @@ TEST(u8OverflowDeathTest, WrappingRemEuclidOverflow) { #endif } +TEST(u8OverflowDeathTest, DivCeilDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u8).div_ceil(0_u8); + ensure_use(&x); + }, + "attempt to divide by zero"); + EXPECT_DEATH( + { + auto x = u8::MAX.div_ceil(0_u8); + ensure_use(&x); + }, + "attempt to divide by zero"); +#endif +} + } // namespace diff --git a/sus/num/u8_unittest.cc b/sus/num/u8_unittest.cc index 9d9d5fad6..5a44fac0c 100644 --- a/sus/num/u8_unittest.cc +++ b/sus/num/u8_unittest.cc @@ -652,6 +652,8 @@ TEST(u8, InvokeEverything) { (void)i.overflowing_rem_euclid(j); (void)i.wrapping_rem_euclid(j); + (void)i.div_ceil(j); + (void)i.checked_shl(1_u32); (void)i.overflowing_shl(1_u32); (void)i.wrapping_shl(1_u32); diff --git a/sus/num/uptr_overflow_unittest.cc b/sus/num/uptr_overflow_unittest.cc index 2dd6daf15..35c1535ee 100644 --- a/sus/num/uptr_overflow_unittest.cc +++ b/sus/num/uptr_overflow_unittest.cc @@ -253,4 +253,21 @@ TEST(uptrOverflowDeathTest, WrappingRemEuclidOverflow) { #endif } +TEST(uptrOverflowDeathTest, DivCeilDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = uptr().with_addr(0_usize).div_ceil(0_u32); + ensure_use(&x); + }, + "attempt to divide by zero"); + EXPECT_DEATH( + { + auto x = uptr::MAX_BIT_PATTERN.div_ceil(0_u64); + ensure_use(&x); + }, + "attempt to divide by zero"); +#endif +} + } // namespace diff --git a/sus/num/uptr_unittest.cc b/sus/num/uptr_unittest.cc index c8b3b00ea..0dd2c16df 100644 --- a/sus/num/uptr_unittest.cc +++ b/sus/num/uptr_unittest.cc @@ -653,6 +653,8 @@ TEST(uptr, InvokeEverything) { (void)i.overflowing_rem_euclid(j); (void)i.wrapping_rem_euclid(j); + (void)i.div_ceil(j); + (void)i.checked_shl(1_u32); (void)i.overflowing_shl(1_u32); (void)i.wrapping_shl(1_u32); @@ -1061,6 +1063,14 @@ TEST(uptr, ArithemticWithSmallerIntegers) { EXPECT_EQ(i.wrapping_rem_euclid(p), i.wrapping_rem_euclid(i)); EXPECT_EQ(i.wrapping_rem_euclid(u), i.wrapping_rem_euclid(i)); + // Ceil math. + + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + EXPECT_EQ(i.div_ceil(p), i.div_ceil(i)); + EXPECT_EQ(i.div_ceil(u), i.div_ceil(i)); + // Log math. static_assert(std::same_as); static_assert(std::same_as); diff --git a/sus/num/usize_overflow_unittest.cc b/sus/num/usize_overflow_unittest.cc index 9eb8a3b41..71b3fe133 100644 --- a/sus/num/usize_overflow_unittest.cc +++ b/sus/num/usize_overflow_unittest.cc @@ -253,4 +253,21 @@ TEST(usizeOverflowDeathTest, WrappingRemEuclidOverflow) { #endif } +TEST(usizeOverflowDeathTest, DivCeilDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_usize).div_ceil(0_usize); + ensure_use(&x); + }, + "attempt to divide by zero"); + EXPECT_DEATH( + { + auto x = usize::MAX.div_ceil(0_usize); + ensure_use(&x); + }, + "attempt to divide by zero"); +#endif +} + } // namespace diff --git a/sus/num/usize_unittest.cc b/sus/num/usize_unittest.cc index 4568284f4..cddc344dd 100644 --- a/sus/num/usize_unittest.cc +++ b/sus/num/usize_unittest.cc @@ -707,6 +707,8 @@ TEST(usize, InvokeEverything) { (void)i.overflowing_rem_euclid(j); (void)i.wrapping_rem_euclid(j); + (void)i.div_ceil(j); + (void)i.checked_shl(1_u32); (void)i.overflowing_shl(1_u32); (void)i.wrapping_shl(1_u32); From 5c0104f6931eb233abe8368ec17f0164b3062704 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Sun, 3 Dec 2023 19:34:52 -0500 Subject: [PATCH 2/2] Add next_multiple_of() and checked_next_multiple_of() for unsigned ints These were added in Rust 1.73: https://blog.rust-lang.org/2023/10/05/Rust-1.73.0.html Signed versions exist but are unstable, so they are not added here yet. --- .../__private/unsigned_integer_methods.inc | 57 ++++++++++++++++++- .../unsigned_integer_methods_impl.inc | 24 +++++++- sus/num/u16_overflow_unittest.cc | 23 ++++++++ sus/num/u32_overflow_unittest.cc | 23 ++++++++ sus/num/u32_unittest.cc | 56 ++++++++++++++++++ sus/num/u64_overflow_unittest.cc | 23 ++++++++ sus/num/u8_overflow_unittest.cc | 23 ++++++++ sus/num/uptr_overflow_unittest.cc | 25 ++++++++ sus/num/uptr_unittest.cc | 16 +++++- sus/num/usize_overflow_unittest.cc | 23 ++++++++ 10 files changed, 289 insertions(+), 4 deletions(-) diff --git a/sus/num/__private/unsigned_integer_methods.inc b/sus/num/__private/unsigned_integer_methods.inc index 3ff0eaae7..b2d258f32 100644 --- a/sus/num/__private/unsigned_integer_methods.inc +++ b/sus/num/__private/unsigned_integer_methods.inc @@ -1638,7 +1638,7 @@ sus_pure constexpr _self wrapping_rem(U rhs) const& noexcept { /// # Examples /// Basic usage: /// ``` -/// assert_eq!((7_u8).div_ceil(4u), 2u); +/// sus::check((7_u8).div_ceil(4u) == 2u); /// ``` sus_pure constexpr _self div_ceil(_self rhs) const& noexcept { ::sus::check_with_message(rhs.primitive_value != _primitive{0}, @@ -2138,6 +2138,59 @@ sus_pure constexpr bool is_power_of_two() const& noexcept { return count_ones() == _self(_primitive{1u}); } +/// Calculates the smallest value greater than or equal to itself that is a +/// multiple of `rhs`. +/// +/// # Panics +/// This function will panic if `rhs` is zero. +/// +/// ## Overflow behavior +/// On overflow, this function will panic if overflow checks are enabled (they +/// are by default) and wrap if overflow checks are disabled (not the default). +/// +/// # Examples +/// Basic usage: +/// ``` +/// sus::check((16_u32).next_multiple_of(8u) == 16u); +/// sus::check((23_u32).next_multiple_of(8u) == 24u); +/// ``` +sus_pure constexpr _self next_multiple_of(_self rhs) const& noexcept { + const _self r = *this % rhs; + return r == _primitive{0} ? *this : *this + (rhs - r); +} + +#if _pointer +template + requires((UnsignedNumeric || UnsignedPrimitiveInteger) && + ::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure constexpr _self next_multiple_of(U rhs) const& noexcept { + return next_multiple_of(_self(_primitive{rhs})); +} +#endif + +/// Calculates the smallest value greater than or equal to itself that is a +/// multiple of `rhs`. Returns `None` if `rhs` is zero or the operation would +/// result in overflow. +/// +/// # Examples +/// Basic usage: +/// ``` +/// sus::check((16_u32).checked_next_multiple_of(8u) == sus::some(16u)); +/// sus::check((23_u32).checked_next_multiple_of(8u) == sus::some(24u)); +/// sus::check((1_u32).checked_next_multiple_of(0u) == sus::none()); +/// sus::check(u32::MAX.checked_next_multiple_of(2u) == sus::none()); +/// ``` +sus_pure constexpr ::sus::option::Option<_self> checked_next_multiple_of( + _self rhs) const& noexcept; + +#if _pointer +template + requires((UnsignedNumeric || UnsignedPrimitiveInteger) && + ::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure constexpr ::sus::option::Option<_self> checked_next_multiple_of( + U rhs) const& noexcept; +#endif + /// Returns the smallest power of two greater than or equal to self. /// /// # Panics @@ -2146,7 +2199,7 @@ sus_pure constexpr bool is_power_of_two() const& noexcept { sus_pure constexpr _self next_power_of_two() const& noexcept { const auto one_less = __private::one_less_than_next_power_of_two(primitive_value); - return _self(one_less) + _self(_primitive{1u}); + return _self(one_less) + _self(_primitive{1}); } /// Returns the smallest power of two greater than or equal to `self`. diff --git a/sus/num/__private/unsigned_integer_methods_impl.inc b/sus/num/__private/unsigned_integer_methods_impl.inc index 2edfd183e..ade1fe35b 100644 --- a/sus/num/__private/unsigned_integer_methods_impl.inc +++ b/sus/num/__private/unsigned_integer_methods_impl.inc @@ -655,6 +655,28 @@ _self::checked_next_power_of_two() const& noexcept { return _self(one_less).checked_add(_self(_primitive{1u})); } +sus_pure constexpr ::sus::option::Option<_self> _self::checked_next_multiple_of( + _self rhs) const& noexcept { + return checked_rem(rhs).and_then([s = *this, rhs](_self r) { + return r == _primitive{0} + ? Option<_self>(s) + // SAFETY: rhs - r cannot overflow because `r` is smaller than + // `rhs`, as it's the remainder of `*this / rhs`. + : s.checked_add(__private::unchecked_sub(rhs.primitive_value, + r.primitive_value)); + }); +} + +#if _pointer +template + requires((UnsignedNumeric || UnsignedPrimitiveInteger) && + ::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure constexpr ::sus::option::Option<_self> _self::checked_next_multiple_of( + U rhs) const& noexcept { + return checked_next_multiple_of(_self(_primitive{rhs})); +} +#endif + sus_pure constexpr ::sus::collections::Array()> _self::to_be_bytes() const& noexcept { @@ -715,7 +737,7 @@ sus_pure constexpr _self _self::from_ne_bytes( for (usize i; i < ::sus::mem::size_of<_primitive>(); i += 1u) { // size_of<_primitive>() is < u32::MAX so the cast() is not lossy. val |= bytes[i] << ::sus::cast(::sus::mem::size_of<_primitive>() - - 1u - i); + 1u - i); } } else { ::sus::ptr::copy_nonoverlapping( diff --git a/sus/num/u16_overflow_unittest.cc b/sus/num/u16_overflow_unittest.cc index 221ebe733..c6753d53e 100644 --- a/sus/num/u16_overflow_unittest.cc +++ b/sus/num/u16_overflow_unittest.cc @@ -268,4 +268,27 @@ TEST(u16OverflowDeathTest, DivCeilDivByZero) { #endif } +TEST(u16OverflowDeathTest, NextMultipleOfDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u16).next_multiple_of(0_u16); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = u16::MAX.next_multiple_of(0_u16); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); +#endif + + // Overflow occurs but is not checked. + EXPECT_EQ(u16::MAX.next_multiple_of(2_u16), 0u); + EXPECT_EQ(u16::MAX.next_multiple_of(3_u16), u16::MAX); + EXPECT_EQ(u16::MAX.next_multiple_of(4_u16), 0u); + EXPECT_EQ(u16::MAX.next_multiple_of(5_u16), u16::MAX); +} + } // namespace diff --git a/sus/num/u32_overflow_unittest.cc b/sus/num/u32_overflow_unittest.cc index b49d3a6ee..c835e3d5c 100644 --- a/sus/num/u32_overflow_unittest.cc +++ b/sus/num/u32_overflow_unittest.cc @@ -273,4 +273,27 @@ TEST(u32OverflowDeathTest, DivCeilDivByZero) { #endif } +TEST(u32OverflowDeathTest, NextMultipleOfDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u32).next_multiple_of(0_u32); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = u32::MAX.next_multiple_of(0_u32); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); +#endif + + // Overflow occurs but is not checked. + EXPECT_EQ(u32::MAX.next_multiple_of(2_u32), 0u); + EXPECT_EQ(u32::MAX.next_multiple_of(3_u32), u32::MAX); + EXPECT_EQ(u32::MAX.next_multiple_of(4_u32), 0u); + EXPECT_EQ(u32::MAX.next_multiple_of(5_u32), u32::MAX); +} + } // namespace diff --git a/sus/num/u32_unittest.cc b/sus/num/u32_unittest.cc index 83791b95f..ab2913d3c 100644 --- a/sus/num/u32_unittest.cc +++ b/sus/num/u32_unittest.cc @@ -2204,4 +2204,60 @@ TEST(u32DeathTest, DivCeilDivByZero) { #endif } +TEST(u32, NextMultipleOf_Example) { + sus::check((16_u32).next_multiple_of(8u) == 16u); + sus::check((23_u32).next_multiple_of(8u) == 24u); +} + +TEST(u32, NextMultipleOf) { + EXPECT_EQ((0_u32).next_multiple_of(1u), 0u); + EXPECT_EQ((1_u32).next_multiple_of(1u), 1u); + EXPECT_EQ((1_u32).next_multiple_of(5u), 5u); + EXPECT_EQ((5_u32).next_multiple_of(1u), 5u); + EXPECT_EQ((16_u32).next_multiple_of(8u), 16u); + EXPECT_EQ((23_u32).next_multiple_of(8u), 24u); +} + +TEST(u32DeathTest, NextMultipleOfDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u32).next_multiple_of(0_u32); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = u32::MAX.next_multiple_of(0_u32); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = u32::MAX.next_multiple_of(2_u32); + ensure_use(&x); + }, + "attempt to add with overflow"); +#endif +} + +TEST(u32, CheckedNextMultipleOf_Example) { + sus::check((16_u32).checked_next_multiple_of(8u) == sus::some(16u)); + sus::check((23_u32).checked_next_multiple_of(8u) == sus::some(24u)); + sus::check((1_u32).checked_next_multiple_of(0u) == sus::none()); + sus::check(u32::MAX.checked_next_multiple_of(2u) == sus::none()); +} + +TEST(u32, CheckedNextMultipleOf) { + EXPECT_EQ((0_u32).checked_next_multiple_of(1u), sus::some(0u)); + EXPECT_EQ((1_u32).checked_next_multiple_of(1u), sus::some(1u)); + EXPECT_EQ((1_u32).checked_next_multiple_of(5u), sus::some(5u)); + EXPECT_EQ((5_u32).checked_next_multiple_of(1u), sus::some(5u)); + EXPECT_EQ((16_u32).checked_next_multiple_of(8u), sus::some(16u)); + EXPECT_EQ((23_u32).checked_next_multiple_of(8u), sus::some(24u)); + + EXPECT_EQ((23_u32).checked_next_multiple_of(0u), sus::none()); + EXPECT_EQ(u32::MAX.checked_next_multiple_of(20u), sus::none()); +} + } // namespace diff --git a/sus/num/u64_overflow_unittest.cc b/sus/num/u64_overflow_unittest.cc index 05f01b1df..5d4f24c6c 100644 --- a/sus/num/u64_overflow_unittest.cc +++ b/sus/num/u64_overflow_unittest.cc @@ -268,4 +268,27 @@ TEST(u64OverflowDeathTest, DivCeilDivByZero) { #endif } +TEST(u64OverflowDeathTest, NextMultipleOfDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u64).next_multiple_of(0_u64); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = u64::MAX.next_multiple_of(0_u64); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); +#endif + + // Overflow occurs but is not checked. + EXPECT_EQ(u64::MAX.next_multiple_of(2_u64), 0u); + EXPECT_EQ(u64::MAX.next_multiple_of(3_u64), u64::MAX); + EXPECT_EQ(u64::MAX.next_multiple_of(4_u64), 0u); + EXPECT_EQ(u64::MAX.next_multiple_of(5_u64), u64::MAX); +} + } // namespace diff --git a/sus/num/u8_overflow_unittest.cc b/sus/num/u8_overflow_unittest.cc index fa945afff..1b8dbc6b0 100644 --- a/sus/num/u8_overflow_unittest.cc +++ b/sus/num/u8_overflow_unittest.cc @@ -244,4 +244,27 @@ TEST(u8OverflowDeathTest, DivCeilDivByZero) { #endif } +TEST(u8OverflowDeathTest, NextMultipleOfDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_u8).next_multiple_of(0_u8); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = u8::MAX.next_multiple_of(0_u8); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); +#endif + + // Overflow occurs but is not checked. + EXPECT_EQ(u8::MAX.next_multiple_of(2_u8), 0u); + EXPECT_EQ(u8::MAX.next_multiple_of(3_u8), u8::MAX); + EXPECT_EQ(u8::MAX.next_multiple_of(4_u8), 0u); + EXPECT_EQ(u8::MAX.next_multiple_of(5_u8), u8::MAX); +} + } // namespace diff --git a/sus/num/uptr_overflow_unittest.cc b/sus/num/uptr_overflow_unittest.cc index 35c1535ee..bb932342a 100644 --- a/sus/num/uptr_overflow_unittest.cc +++ b/sus/num/uptr_overflow_unittest.cc @@ -270,4 +270,29 @@ TEST(uptrOverflowDeathTest, DivCeilDivByZero) { #endif } +TEST(uptrOverflowDeathTest, NextMultipleOfDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = uptr().with_addr(0_usize).next_multiple_of(0_u32); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = uptr::MAX_BIT_PATTERN.next_multiple_of(0_u32); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); +#endif + + // Overflow occurs but is not checked. + EXPECT_EQ(uptr::MAX_BIT_PATTERN.next_multiple_of(2_u32), 0u); + EXPECT_EQ(uptr::MAX_BIT_PATTERN.next_multiple_of(3_u32), + uptr::MAX_BIT_PATTERN); + EXPECT_EQ(uptr::MAX_BIT_PATTERN.next_multiple_of(4_u32), 0u); + EXPECT_EQ(uptr::MAX_BIT_PATTERN.next_multiple_of(5_u32), + uptr::MAX_BIT_PATTERN); +} + } // namespace diff --git a/sus/num/uptr_unittest.cc b/sus/num/uptr_unittest.cc index 0dd2c16df..23ef8f578 100644 --- a/sus/num/uptr_unittest.cc +++ b/sus/num/uptr_unittest.cc @@ -1064,7 +1064,6 @@ TEST(uptr, ArithemticWithSmallerIntegers) { EXPECT_EQ(i.wrapping_rem_euclid(u), i.wrapping_rem_euclid(i)); // Ceil math. - static_assert(std::same_as); static_assert(std::same_as); static_assert(std::same_as); @@ -1082,6 +1081,21 @@ TEST(uptr, ArithemticWithSmallerIntegers) { static_assert(std::same_as, decltype(i.checked_log(u))>); EXPECT_EQ(i.checked_log(p), i.checked_log(i)); EXPECT_EQ(i.checked_log(u), i.checked_log(i)); + + // Next multiple of. + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + EXPECT_EQ(i.next_multiple_of(p), i.next_multiple_of(i)); + EXPECT_EQ(i.next_multiple_of(u), i.next_multiple_of(i)); + static_assert( + std::same_as, decltype(i.checked_next_multiple_of(i))>); + static_assert( + std::same_as, decltype(i.checked_next_multiple_of(p))>); + static_assert( + std::same_as, decltype(i.checked_next_multiple_of(u))>); + EXPECT_EQ(i.checked_next_multiple_of(p), i.checked_next_multiple_of(i)); + EXPECT_EQ(i.checked_next_multiple_of(u), i.checked_next_multiple_of(i)); } TEST(uptr, fmt) { diff --git a/sus/num/usize_overflow_unittest.cc b/sus/num/usize_overflow_unittest.cc index 71b3fe133..df33c271b 100644 --- a/sus/num/usize_overflow_unittest.cc +++ b/sus/num/usize_overflow_unittest.cc @@ -270,4 +270,27 @@ TEST(usizeOverflowDeathTest, DivCeilDivByZero) { #endif } +TEST(usizeOverflowDeathTest, NextMultipleOfDivByZero) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEATH( + { + auto x = (0_usize).next_multiple_of(0_usize); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); + EXPECT_DEATH( + { + auto x = usize::MAX.next_multiple_of(0_usize); + ensure_use(&x); + }, + "attempt to calculate the remainder with a divisor of zero"); +#endif + + // Overflow occurs but is not checked. + EXPECT_EQ(usize::MAX.next_multiple_of(2_usize), 0u); + EXPECT_EQ(usize::MAX.next_multiple_of(3_usize), usize::MAX); + EXPECT_EQ(usize::MAX.next_multiple_of(4_usize), 0u); + EXPECT_EQ(usize::MAX.next_multiple_of(5_usize), usize::MAX); +} + } // namespace