Skip to content

Commit e44423c

Browse files
author
MarcoFalke
committedFeb 21, 2022
Merge bitcoin/bitcoin#24224: util: Add SaturatingAdd helper
faa7d8a util: Add SaturatingAdd helper (MarcoFalke) Pull request description: Seems good to have this in the repo, as it might be needed to write cleaner code. For example: * bitcoin/bitcoin#24090 (comment) * bitcoin/bitcoin#23418 (comment) * ... ACKs for top commit: MarcoFalke: Added a test. Should be trivial to re-ACK with `git range-diff bitcoin-core/master fa90189cbf faa7d8a` klementtan: reACK faa7d8a vasild: ACK faa7d8a Tree-SHA512: d0e6efdba7dfcbdd16ab4539a7f5e45a97d17792e42586c3c52caaae3fc70612dc9e364359658de5de5718fb8c2a765a59ceb2230098355394fa067a9732bc2a
2 parents bd6b1d0 + faa7d8a commit e44423c

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed
 

‎src/test/fuzz/addition_overflow.cpp

+13-4
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,27 @@ void TestAdditionOverflow(FuzzedDataProvider& fuzzed_data_provider)
2626
const T i = fuzzed_data_provider.ConsumeIntegral<T>();
2727
const T j = fuzzed_data_provider.ConsumeIntegral<T>();
2828
const bool is_addition_overflow_custom = AdditionOverflow(i, j);
29+
const auto maybe_add{CheckedAdd(i, j)};
30+
const auto sat_add{SaturatingAdd(i, j)};
31+
assert(is_addition_overflow_custom == !maybe_add.has_value());
32+
assert(is_addition_overflow_custom == AdditionOverflow(j, i));
33+
assert(maybe_add == CheckedAdd(j, i));
34+
assert(sat_add == SaturatingAdd(j, i));
2935
#if defined(HAVE_BUILTIN_ADD_OVERFLOW)
3036
T result_builtin;
3137
const bool is_addition_overflow_builtin = __builtin_add_overflow(i, j, &result_builtin);
3238
assert(is_addition_overflow_custom == is_addition_overflow_builtin);
3339
if (!is_addition_overflow_custom) {
3440
assert(i + j == result_builtin);
3541
}
36-
#else
37-
if (!is_addition_overflow_custom) {
38-
(void)(i + j);
39-
}
4042
#endif
43+
if (is_addition_overflow_custom) {
44+
assert(sat_add == std::numeric_limits<T>::min() || sat_add == std::numeric_limits<T>::max());
45+
} else {
46+
const auto add{i + j};
47+
assert(add == maybe_add.value());
48+
assert(add == sat_add);
49+
}
4150
}
4251
} // namespace
4352

‎src/test/util_tests.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -1474,9 +1474,17 @@ static void TestAddMatrixOverflow()
14741474
constexpr T MAXI{std::numeric_limits<T>::max()};
14751475
BOOST_CHECK(!CheckedAdd(T{1}, MAXI));
14761476
BOOST_CHECK(!CheckedAdd(MAXI, MAXI));
1477+
BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(T{1}, MAXI));
1478+
BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(MAXI, MAXI));
1479+
14771480
BOOST_CHECK_EQUAL(0, CheckedAdd(T{0}, T{0}).value());
14781481
BOOST_CHECK_EQUAL(MAXI, CheckedAdd(T{0}, MAXI).value());
14791482
BOOST_CHECK_EQUAL(MAXI, CheckedAdd(T{1}, MAXI - 1).value());
1483+
BOOST_CHECK_EQUAL(MAXI - 1, CheckedAdd(T{1}, MAXI - 2).value());
1484+
BOOST_CHECK_EQUAL(0, SaturatingAdd(T{0}, T{0}));
1485+
BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(T{0}, MAXI));
1486+
BOOST_CHECK_EQUAL(MAXI, SaturatingAdd(T{1}, MAXI - 1));
1487+
BOOST_CHECK_EQUAL(MAXI - 1, SaturatingAdd(T{1}, MAXI - 2));
14801488
}
14811489

14821490
/* Check for overflow or underflow */
@@ -1488,9 +1496,17 @@ static void TestAddMatrix()
14881496
constexpr T MAXI{std::numeric_limits<T>::max()};
14891497
BOOST_CHECK(!CheckedAdd(T{-1}, MINI));
14901498
BOOST_CHECK(!CheckedAdd(MINI, MINI));
1499+
BOOST_CHECK_EQUAL(MINI, SaturatingAdd(T{-1}, MINI));
1500+
BOOST_CHECK_EQUAL(MINI, SaturatingAdd(MINI, MINI));
1501+
14911502
BOOST_CHECK_EQUAL(MINI, CheckedAdd(T{0}, MINI).value());
14921503
BOOST_CHECK_EQUAL(MINI, CheckedAdd(T{-1}, MINI + 1).value());
14931504
BOOST_CHECK_EQUAL(-1, CheckedAdd(MINI, MAXI).value());
1505+
BOOST_CHECK_EQUAL(MINI + 1, CheckedAdd(T{-1}, MINI + 2).value());
1506+
BOOST_CHECK_EQUAL(MINI, SaturatingAdd(T{0}, MINI));
1507+
BOOST_CHECK_EQUAL(MINI, SaturatingAdd(T{-1}, MINI + 1));
1508+
BOOST_CHECK_EQUAL(MINI + 1, SaturatingAdd(T{-1}, MINI + 2));
1509+
BOOST_CHECK_EQUAL(-1, SaturatingAdd(MINI, MAXI));
14941510
}
14951511

14961512
BOOST_AUTO_TEST_CASE(util_overflow)

‎src/util/overflow.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ template <class T>
1313
[[nodiscard]] bool AdditionOverflow(const T i, const T j) noexcept
1414
{
1515
static_assert(std::is_integral<T>::value, "Integral required.");
16-
if (std::numeric_limits<T>::is_signed) {
16+
if constexpr (std::numeric_limits<T>::is_signed) {
1717
return (i > 0 && j > std::numeric_limits<T>::max() - i) ||
1818
(i < 0 && j < std::numeric_limits<T>::min() - i);
1919
}
@@ -29,4 +29,22 @@ template <class T>
2929
return i + j;
3030
}
3131

32+
template <class T>
33+
[[nodiscard]] T SaturatingAdd(const T i, const T j) noexcept
34+
{
35+
if constexpr (std::numeric_limits<T>::is_signed) {
36+
if (i > 0 && j > std::numeric_limits<T>::max() - i) {
37+
return std::numeric_limits<T>::max();
38+
}
39+
if (i < 0 && j < std::numeric_limits<T>::min() - i) {
40+
return std::numeric_limits<T>::min();
41+
}
42+
} else {
43+
if (std::numeric_limits<T>::max() - i < j) {
44+
return std::numeric_limits<T>::max();
45+
}
46+
}
47+
return i + j;
48+
}
49+
3250
#endif // BITCOIN_UTIL_OVERFLOW_H

0 commit comments

Comments
 (0)
Please sign in to comment.