diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2019-09-04 03:09:54 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2019-09-04 03:09:54 +0000 |
commit | 947f837d67b4112dc3b4377bd01948a8e2f551b4 (patch) | |
tree | 86f6f7906a8523ff6c3254a1f8275b7109450131 /src/mongo/platform/overflow_arithmetic_test.cpp | |
parent | 6225782d0e720a0bde22a572a99cfcae1ada2ccf (diff) | |
download | mongo-947f837d67b4112dc3b4377bd01948a8e2f551b4.tar.gz |
SERVER-43032 Revise platform/overflow_arithmetic_test.cpp
Diffstat (limited to 'src/mongo/platform/overflow_arithmetic_test.cpp')
-rw-r--r-- | src/mongo/platform/overflow_arithmetic_test.cpp | 251 |
1 files changed, 128 insertions, 123 deletions
diff --git a/src/mongo/platform/overflow_arithmetic_test.cpp b/src/mongo/platform/overflow_arithmetic_test.cpp index 95910832c86..e9fa705ed6d 100644 --- a/src/mongo/platform/overflow_arithmetic_test.cpp +++ b/src/mongo/platform/overflow_arithmetic_test.cpp @@ -32,152 +32,157 @@ #include <limits> #include "mongo/platform/overflow_arithmetic.h" +#include "mongo/stdx/type_traits.h" #include "mongo/unittest/unittest.h" namespace mongo { namespace { +template <typename T> +constexpr T kMin = std::numeric_limits<T>::min(); +template <typename T> +constexpr T kMax = std::numeric_limits<T>::max(); + +template <typename T, typename F, typename A, typename B> +bool runTest(bool oflow, F f, A a, B b, T r = {}) { + T result; + bool oflowed = f(a, b, &result); + if (oflowed != oflow) + return false; + if (oflow) + return true; + return result == r; +} + +// Expect `f(a,b) == r`. +template <typename T, typename F, typename A, typename B> +auto test(F f, A a, B b, stdx::type_identity_t<T> r) { + return runTest<T>(false, f, a, b, r); +}; + +// Expect `f(a,b)` overflows. +template <typename T, typename F, typename A, typename B> +auto testOflow(F f, A a, B b) { + return runTest<T>(true, f, a, b); +}; -#define assertArithOverflow(TYPE, FN, LHS, RHS, EXPECT_OVERFLOW, EXPECTED_RESULT) \ - do { \ - const bool expectOverflow = EXPECT_OVERFLOW; \ - TYPE result; \ - ASSERT_EQ(expectOverflow, FN(LHS, RHS, &result)) << #FN "(" #LHS ", " #RHS; \ - if (!expectOverflow) { \ - ASSERT_EQ(TYPE(EXPECTED_RESULT), TYPE(result)) << #FN "(" #LHS ", " #RHS " - >"; \ - } \ - } while (false) - -#define assertSignedMultiplyNoOverflow(LHS, RHS, EXPECTED) \ - assertArithOverflow(int64_t, overflow::mul, LHS, RHS, false, EXPECTED) -#define assertSignedMultiplyWithOverflow(LHS, RHS) \ - assertArithOverflow(int64_t, overflow::mul, LHS, RHS, true, 0) - -#define assertUnsignedMultiplyNoOverflow(LHS, RHS, EXPECTED) \ - assertArithOverflow(uint64_t, overflow::mul, LHS, RHS, false, EXPECTED) -#define assertUnsignedMultiplyWithOverflow(LHS, RHS) \ - assertArithOverflow(uint64_t, overflow::mul, LHS, RHS, true, 0) - -#define assertSignedAddNoOverflow(LHS, RHS, EXPECTED) \ - assertArithOverflow(int64_t, overflow::add, LHS, RHS, false, EXPECTED) -#define assertSignedAddWithOverflow(LHS, RHS) \ - assertArithOverflow(int64_t, overflow::add, LHS, RHS, true, 0) - -#define assertUnsignedAddNoOverflow(LHS, RHS, EXPECTED) \ - assertArithOverflow(uint64_t, overflow::add, LHS, RHS, false, EXPECTED) -#define assertUnsignedAddWithOverflow(LHS, RHS) \ - assertArithOverflow(uint64_t, overflow::add, LHS, RHS, true, 0) - -#define assertSignedSubtractNoOverflow(LHS, RHS, EXPECTED) \ - assertArithOverflow(int64_t, overflow::sub, LHS, RHS, false, EXPECTED) -#define assertSignedSubtractWithOverflow(LHS, RHS) \ - assertArithOverflow(int64_t, overflow::sub, LHS, RHS, true, 0) - -#define assertUnsignedSubtractNoOverflow(LHS, RHS, EXPECTED) \ - assertArithOverflow(uint64_t, overflow::sub, LHS, RHS, false, EXPECTED) -#define assertUnsignedSubtractWithOverflow(LHS, RHS) \ - assertArithOverflow(uint64_t, overflow::sub, LHS, RHS, true, 0) +// Polymorphic lambdas to defer overload resolution until execution time. +constexpr auto polyMul = [](auto&&... a) { return overflow::mul(a...); }; +constexpr auto polyAdd = [](auto&&... a) { return overflow::add(a...); }; +constexpr auto polySub = [](auto&&... a) { return overflow::sub(a...); }; TEST(OverflowArithmetic, SignedMultiplicationTests) { - using limits = std::numeric_limits<int64_t>; - assertSignedMultiplyNoOverflow(0, limits::max(), 0); - assertSignedMultiplyNoOverflow(0, limits::min(), 0); - assertSignedMultiplyNoOverflow(1, limits::max(), limits::max()); - assertSignedMultiplyNoOverflow(1, limits::min(), limits::min()); - assertSignedMultiplyNoOverflow(-1, limits::max(), limits::min() + 1); - assertSignedMultiplyNoOverflow(1000, 57, 57000); - assertSignedMultiplyNoOverflow(1000, -57, -57000); - assertSignedMultiplyNoOverflow(-1000, -57, 57000); - assertSignedMultiplyNoOverflow(0x3fffffffffffffff, 2, 0x7ffffffffffffffe); - assertSignedMultiplyNoOverflow(0x3fffffffffffffff, -2, -0x7ffffffffffffffe); - assertSignedMultiplyNoOverflow(-0x3fffffffffffffff, -2, 0x7ffffffffffffffe); - - assertSignedMultiplyWithOverflow(-1, limits::min()); - assertSignedMultiplyWithOverflow(2, limits::max()); - assertSignedMultiplyWithOverflow(-2, limits::max()); - assertSignedMultiplyWithOverflow(2, limits::min()); - assertSignedMultiplyWithOverflow(-2, limits::min()); - assertSignedMultiplyWithOverflow(limits::min(), limits::max()); - assertSignedMultiplyWithOverflow(limits::max(), limits::max()); - assertSignedMultiplyWithOverflow(limits::min(), limits::min()); - assertSignedMultiplyWithOverflow(1LL << 62, 8); - assertSignedMultiplyWithOverflow(-(1LL << 62), 8); - assertSignedMultiplyWithOverflow(-(1LL << 62), -8); + using T = int64_t; + static constexpr auto f = polyMul; + ASSERT(test<T>(f, 0, kMax<T>, 0)); + ASSERT(test<T>(f, 0, kMin<T>, 0)); + ASSERT(test<T>(f, 1, kMax<T>, kMax<T>)); + ASSERT(test<T>(f, 1, kMin<T>, kMin<T>)); + ASSERT(test<T>(f, -1, kMax<T>, kMin<T> + 1)); + ASSERT(test<T>(f, 1000, 57, 57000)); + ASSERT(test<T>(f, 1000, -57, -57000)); + ASSERT(test<T>(f, -1000, -57, 57000)); + ASSERT(test<T>(f, 0x3fffffffffffffff, 2, 0x7ffffffffffffffe)); + ASSERT(test<T>(f, 0x3fffffffffffffff, -2, -0x7ffffffffffffffe)); + ASSERT(test<T>(f, -0x3fffffffffffffff, -2, 0x7ffffffffffffffe)); + ASSERT(testOflow<T>(f, -1, kMin<T>)); + ASSERT(testOflow<T>(f, 2, kMax<T>)); + ASSERT(testOflow<T>(f, -2, kMax<T>)); + ASSERT(testOflow<T>(f, 2, kMin<T>)); + ASSERT(testOflow<T>(f, -2, kMin<T>)); + ASSERT(testOflow<T>(f, kMin<T>, kMax<T>)); + ASSERT(testOflow<T>(f, kMax<T>, kMax<T>)); + ASSERT(testOflow<T>(f, kMin<T>, kMin<T>)); + ASSERT(testOflow<T>(f, 1LL << 62, 8)); + ASSERT(testOflow<T>(f, -(1LL << 62), 8)); + ASSERT(testOflow<T>(f, -(1LL << 62), -8)); } TEST(OverflowArithmetic, UnignedMultiplicationTests) { - using limits = std::numeric_limits<uint64_t>; - assertUnsignedMultiplyNoOverflow(0, limits::max(), 0); - assertUnsignedMultiplyNoOverflow(1, limits::max(), limits::max()); - assertUnsignedMultiplyNoOverflow(1000, 57, 57000); - assertUnsignedMultiplyNoOverflow(0x3fffffffffffffff, 2, 0x7ffffffffffffffe); - assertUnsignedMultiplyNoOverflow(0x7fffffffffffffff, 2, 0xfffffffffffffffe); - - assertUnsignedMultiplyWithOverflow(2, limits::max()); - assertUnsignedMultiplyWithOverflow(limits::max(), limits::max()); - assertUnsignedMultiplyWithOverflow(1LL << 62, 8); - assertUnsignedMultiplyWithOverflow(0x7fffffffffffffff, 4); + using T = uint64_t; + static constexpr auto f = polyMul; + ASSERT(test<T>(f, 0, kMax<T>, 0)); + ASSERT(test<T>(f, 1, kMax<T>, kMax<T>)); + ASSERT(test<T>(f, 1000, 57, 57000)); + ASSERT(test<T>(f, 0x3fffffffffffffff, 2, 0x7ffffffffffffffe)); + ASSERT(test<T>(f, 0x7fffffffffffffff, 2, 0xfffffffffffffffe)); + ASSERT(testOflow<T>(f, 2, kMax<T>)); + ASSERT(testOflow<T>(f, kMax<T>, kMax<T>)); + ASSERT(testOflow<T>(f, 1LL << 62, 8)); + ASSERT(testOflow<T>(f, 0x7fffffffffffffff, 4)); } TEST(OverflowArithmetic, SignedAdditionTests) { - using limits = std::numeric_limits<int64_t>; - assertSignedAddNoOverflow(0, limits::max(), limits::max()); - assertSignedAddNoOverflow(-1, limits::max(), limits::max() - 1); - assertSignedAddNoOverflow(1, limits::max() - 1, limits::max()); - assertSignedAddNoOverflow(0, limits::min(), limits::min()); - assertSignedAddNoOverflow(1, limits::min(), limits::min() + 1); - assertSignedAddNoOverflow(-1, limits::min() + 1, limits::min()); - assertSignedAddNoOverflow(limits::max(), limits::min(), -1); - assertSignedAddNoOverflow(1, 1, 2); - assertSignedAddNoOverflow(-1, -1, -2); - - assertSignedAddWithOverflow(limits::max(), 1); - assertSignedAddWithOverflow(limits::max(), limits::max()); - assertSignedAddWithOverflow(limits::min(), -1); - assertSignedAddWithOverflow(limits::min(), limits::min()); + using T = int64_t; + static constexpr auto f = polyAdd; + ASSERT(test<T>(f, 0, kMax<T>, kMax<T>)); + ASSERT(test<T>(f, -1, kMax<T>, kMax<T> - 1)); + ASSERT(test<T>(f, 1, kMax<T> - 1, kMax<T>)); + ASSERT(test<T>(f, 0, kMin<T>, kMin<T>)); + ASSERT(test<T>(f, 1, kMin<T>, kMin<T> + 1)); + ASSERT(test<T>(f, -1, kMin<T> + 1, kMin<T>)); + ASSERT(test<T>(f, kMax<T>, kMin<T>, -1)); + ASSERT(test<T>(f, 1, 1, 2)); + ASSERT(test<T>(f, -1, -1, -2)); + ASSERT(testOflow<T>(f, kMax<T>, 1)); + ASSERT(testOflow<T>(f, kMax<T>, kMax<T>)); + ASSERT(testOflow<T>(f, kMin<T>, -1)); + ASSERT(testOflow<T>(f, kMin<T>, kMin<T>)); } TEST(OverflowArithmetic, UnsignedAdditionTests) { - using limits = std::numeric_limits<uint64_t>; - assertUnsignedAddNoOverflow(0, limits::max(), limits::max()); - assertUnsignedAddNoOverflow(1, limits::max() - 1, limits::max()); - assertUnsignedAddNoOverflow(1, 1, 2); - - assertUnsignedAddWithOverflow(limits::max(), 1); - assertUnsignedAddWithOverflow(limits::max(), limits::max()); + using T = uint64_t; + static constexpr auto f = polyAdd; + ASSERT(test<T>(f, 0, kMax<T>, kMax<T>)); + ASSERT(test<T>(f, 1, kMax<T> - 1, kMax<T>)); + ASSERT(test<T>(f, 1, 1, 2)); + ASSERT(testOflow<T>(f, kMax<T>, 1)); + ASSERT(testOflow<T>(f, kMax<T>, kMax<T>)); } TEST(OverflowArithmetic, SignedSubtractionTests) { - using limits = std::numeric_limits<int64_t>; - assertSignedSubtractNoOverflow(limits::max(), 0, limits::max()); - assertSignedSubtractNoOverflow(limits::max(), 1, limits::max() - 1); - assertSignedSubtractNoOverflow(limits::max() - 1, -1, limits::max()); - assertSignedSubtractNoOverflow(limits::min(), 0, limits::min()); - assertSignedSubtractNoOverflow(limits::min(), -1, limits::min() + 1); - assertSignedSubtractNoOverflow(limits::min() + 1, 1, limits::min()); - assertSignedSubtractNoOverflow(limits::max(), limits::max(), 0); - assertSignedSubtractNoOverflow(limits::min(), limits::min(), 0); - assertSignedSubtractNoOverflow(0, 0, 0); - assertSignedSubtractNoOverflow(1, 1, 0); - assertSignedSubtractNoOverflow(0, 1, -1); - - assertSignedSubtractWithOverflow(0, limits::min()); - assertSignedSubtractWithOverflow(limits::max(), -1); - assertSignedSubtractWithOverflow(limits::max(), limits::min()); - assertSignedSubtractWithOverflow(limits::min(), 1); - assertSignedSubtractWithOverflow(limits::min(), limits::max()); + using T = int64_t; + static constexpr auto f = polySub; + ASSERT(test<T>(f, kMax<T>, 0, kMax<T>)); + ASSERT(test<T>(f, kMax<T>, 1, kMax<T> - 1)); + ASSERT(test<T>(f, kMax<T> - 1, -1, kMax<T>)); + ASSERT(test<T>(f, kMin<T>, 0, kMin<T>)); + ASSERT(test<T>(f, kMin<T>, -1, kMin<T> + 1)); + ASSERT(test<T>(f, kMin<T> + 1, 1, kMin<T>)); + ASSERT(test<T>(f, kMax<T>, kMax<T>, 0)); + ASSERT(test<T>(f, kMin<T>, kMin<T>, 0)); + ASSERT(test<T>(f, 0, 0, 0)); + ASSERT(test<T>(f, 1, 1, 0)); + ASSERT(test<T>(f, 0, 1, -1)); + ASSERT(testOflow<T>(f, 0, kMin<T>)); + ASSERT(testOflow<T>(f, kMax<T>, -1)); + ASSERT(testOflow<T>(f, kMax<T>, kMin<T>)); + ASSERT(testOflow<T>(f, kMin<T>, 1)); + ASSERT(testOflow<T>(f, kMin<T>, kMax<T>)); } TEST(OverflowArithmetic, UnsignedSubtractionTests) { - using limits = std::numeric_limits<uint64_t>; - assertUnsignedSubtractNoOverflow(limits::max(), 0, limits::max()); - assertUnsignedSubtractNoOverflow(limits::max(), 1, limits::max() - 1); - assertUnsignedSubtractNoOverflow(limits::max(), limits::max(), 0); - assertUnsignedSubtractNoOverflow(0, 0, 0); - assertUnsignedSubtractNoOverflow(1, 1, 0); - - assertUnsignedSubtractWithOverflow(0, 1); - assertUnsignedSubtractWithOverflow(0, limits::max()); + using T = uint64_t; + static constexpr auto f = polySub; + ASSERT(test<T>(f, kMax<T>, 0, kMax<T>)); + ASSERT(test<T>(f, kMax<T>, 1, kMax<T> - 1)); + ASSERT(test<T>(f, kMax<T>, kMax<T>, 0)); + ASSERT(test<T>(f, 0, 0, 0)); + ASSERT(test<T>(f, 1, 1, 0)); + ASSERT(testOflow<T>(f, 0, 1)); + ASSERT(testOflow<T>(f, 0, kMax<T>)); +} + +TEST(OverflowArithmetic, HeterogeneousArguments) { + { + int r; + ASSERT_FALSE(overflow::mul(long{1}, (unsigned long long){2}, &r)); + ASSERT_EQ(r, 2); + } + { + unsigned long long r; + ASSERT_TRUE(overflow::mul(-1, 2, &r)); + } } } // namespace |