/* Copyright 2016 MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #include "mongo/platform/basic.h" #include #include "mongo/platform/overflow_arithmetic.h" #include "mongo/unittest/unittest.h" namespace mongo { namespace { #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, mongoSignedMultiplyOverflow64, LHS, RHS, false, EXPECTED) #define assertSignedMultiplyWithOverflow(LHS, RHS) \ assertArithOverflow(int64_t, mongoSignedMultiplyOverflow64, LHS, RHS, true, 0) #define assertUnsignedMultiplyNoOverflow(LHS, RHS, EXPECTED) \ assertArithOverflow(uint64_t, mongoUnsignedMultiplyOverflow64, LHS, RHS, false, EXPECTED) #define assertUnsignedMultiplyWithOverflow(LHS, RHS) \ assertArithOverflow(uint64_t, mongoUnsignedMultiplyOverflow64, LHS, RHS, true, 0) #define assertSignedAddNoOverflow(LHS, RHS, EXPECTED) \ assertArithOverflow(int64_t, mongoSignedAddOverflow64, LHS, RHS, false, EXPECTED) #define assertSignedAddWithOverflow(LHS, RHS) \ assertArithOverflow(int64_t, mongoSignedAddOverflow64, LHS, RHS, true, 0) #define assertUnsignedAddNoOverflow(LHS, RHS, EXPECTED) \ assertArithOverflow(uint64_t, mongoUnsignedAddOverflow64, LHS, RHS, false, EXPECTED) #define assertUnsignedAddWithOverflow(LHS, RHS) \ assertArithOverflow(uint64_t, mongoUnsignedAddOverflow64, LHS, RHS, true, 0) #define assertSignedSubtractNoOverflow(LHS, RHS, EXPECTED) \ assertArithOverflow(int64_t, mongoSignedSubtractOverflow64, LHS, RHS, false, EXPECTED) #define assertSignedSubtractWithOverflow(LHS, RHS) \ assertArithOverflow(int64_t, mongoSignedSubtractOverflow64, LHS, RHS, true, 0) #define assertUnsignedSubtractNoOverflow(LHS, RHS, EXPECTED) \ assertArithOverflow(uint64_t, mongoUnsignedSubtractOverflow64, LHS, RHS, false, EXPECTED) #define assertUnsignedSubtractWithOverflow(LHS, RHS) \ assertArithOverflow(uint64_t, mongoUnsignedSubtractOverflow64, LHS, RHS, true, 0) TEST(OverflowArithmetic, SignedMultiplicationTests) { using limits = std::numeric_limits; 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); } TEST(OverflowArithmetic, UnignedMultiplicationTests) { using limits = std::numeric_limits; 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); } TEST(OverflowArithmetic, SignedAdditionTests) { using limits = std::numeric_limits; 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()); } TEST(OverflowArithmetic, UnsignedAdditionTests) { using limits = std::numeric_limits; 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()); } TEST(OverflowArithmetic, SignedSubtractionTests) { using limits = std::numeric_limits; 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()); } TEST(OverflowArithmetic, UnsignedSubtractionTests) { using limits = std::numeric_limits; 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()); } } // namespace } // namespace mongo