refactor: change the return type of mulDiv to std::optional (#4243)

- Previously, mulDiv had `std::pair<bool, uint64_t>` as the output type.
  - This is an error-prone interface as it is easy to ignore when
    overflow occurs.
- Using a return type of `std::optional` should decrease the likelihood
  of ignoring overflow.
  - It also allows for the use of optional::value_or() as a way to
    explicitly recover from overflow.
- Include limits.h header file preprocessing directive in order to
  satisfy gcc's numeric_limits incomplete_type requirement.

Fix #3495

---------

Co-authored-by: John Freeman <jfreeman08@gmail.com>
This commit is contained in:
Chenna Keshava B S
2023-07-05 20:11:19 -07:00
committed by GitHub
parent 77dc63b549
commit c6fee28b92
12 changed files with 93 additions and 83 deletions

View File

@@ -50,12 +50,17 @@ private:
FeeLevel32 f{10};
FeeLevel32 baseFee{100};
auto drops = mulDiv(baseFee, x, f).second;
auto drops = mulDiv(baseFee, x, f);
BEAST_EXPECT(drops);
BEAST_EXPECT(drops.value() == 1000);
BEAST_EXPECT(
(std::is_same_v<decltype(drops)::unit_type, feeunit::dropTag>));
BEAST_EXPECT((std::is_same_v<decltype(drops), XRPAmount>));
BEAST_EXPECT((std::is_same_v<
std::remove_reference_t<decltype(*drops)>::unit_type,
feeunit::dropTag>));
BEAST_EXPECT((std::is_same_v<
std::remove_reference_t<decltype(*drops)>,
XRPAmount>));
}
{
XRPAmount x{100};
@@ -70,12 +75,16 @@ private:
FeeLevel64 f{10};
FeeLevel64 baseFee{100};
auto drops = mulDiv(baseFee, x, f).second;
auto drops = mulDiv(baseFee, x, f);
BEAST_EXPECT(drops);
BEAST_EXPECT(drops.value() == 1000);
BEAST_EXPECT(
(std::is_same_v<decltype(drops)::unit_type, feeunit::dropTag>));
BEAST_EXPECT((std::is_same_v<decltype(drops), XRPAmount>));
BEAST_EXPECT((std::is_same_v<
std::remove_reference_t<decltype(*drops)>::unit_type,
feeunit::dropTag>));
BEAST_EXPECT((std::is_same_v<
std::remove_reference_t<decltype(*drops)>,
XRPAmount>));
}
{
FeeLevel64 x{1024};
@@ -91,12 +100,16 @@ private:
XRPAmount basefee{10};
FeeLevel64 referencefee{256};
auto drops = mulDiv(x, basefee, referencefee).second;
auto drops = mulDiv(x, basefee, referencefee);
BEAST_EXPECT(drops);
BEAST_EXPECT(drops.value() == 40);
BEAST_EXPECT(
(std::is_same_v<decltype(drops)::unit_type, feeunit::dropTag>));
BEAST_EXPECT((std::is_same_v<decltype(drops), XRPAmount>));
BEAST_EXPECT((std::is_same_v<
std::remove_reference_t<decltype(*drops)>::unit_type,
feeunit::dropTag>));
BEAST_EXPECT((std::is_same_v<
std::remove_reference_t<decltype(*drops)>,
XRPAmount>));
}
}

View File

@@ -32,27 +32,27 @@ struct mulDiv_test : beast::unit_test::suite
const std::uint64_t max32 = std::numeric_limits<std::uint32_t>::max();
auto result = mulDiv(85, 20, 5);
BEAST_EXPECT(result.first && result.second == 340);
BEAST_EXPECT(result && *result == 340);
result = mulDiv(20, 85, 5);
BEAST_EXPECT(result.first && result.second == 340);
BEAST_EXPECT(result && *result == 340);
result = mulDiv(0, max - 1, max - 3);
BEAST_EXPECT(result.first && result.second == 0);
BEAST_EXPECT(result && *result == 0);
result = mulDiv(max - 1, 0, max - 3);
BEAST_EXPECT(result.first && result.second == 0);
BEAST_EXPECT(result && *result == 0);
result = mulDiv(max, 2, max / 2);
BEAST_EXPECT(result.first && result.second == 4);
BEAST_EXPECT(result && *result == 4);
result = mulDiv(max, 1000, max / 1000);
BEAST_EXPECT(result.first && result.second == 1000000);
BEAST_EXPECT(result && *result == 1000000);
result = mulDiv(max, 1000, max / 1001);
BEAST_EXPECT(result.first && result.second == 1001000);
BEAST_EXPECT(result && *result == 1001000);
result = mulDiv(max32 + 1, max32 + 1, 5);
BEAST_EXPECT(result.first && result.second == 3689348814741910323);
BEAST_EXPECT(result && *result == 3689348814741910323);
// Overflow
result = mulDiv(max - 1, max - 2, 5);
BEAST_EXPECT(!result.first && result.second == max);
BEAST_EXPECT(!result);
}
};