/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Assertions.h" #include "mozilla/WrappingOperations.h" #include using mozilla::WrappingMultiply; using mozilla::WrapToSigned; // NOTE: In places below |-FOO_MAX - 1| is used instead of |-FOO_MIN| because // in C++ numeric literals are full expressions -- the |-| in a negative // number is technically separate. So with most compilers that limit // |int| to the signed 32-bit range, something like |-2147483648| is // operator-() applied to an *unsigned* expression. And MSVC, at least, // warns when you do that. (The operation is well-defined, but it likely // doesn't do what was intended.) So we do the usual workaround for this // (see your local copy of for a likely demo of this), writing // it out by negating the max value and subtracting 1. static_assert(WrapToSigned(uint8_t(17)) == 17, "no wraparound should work, 8-bit"); static_assert(WrapToSigned(uint8_t(128)) == -128, "works for 8-bit numbers, wraparound low end"); static_assert(WrapToSigned(uint8_t(128 + 7)) == -128 + 7, "works for 8-bit numbers, wraparound mid"); static_assert(WrapToSigned(uint8_t(128 + 127)) == -128 + 127, "works for 8-bit numbers, wraparound high end"); static_assert(WrapToSigned(uint16_t(12345)) == 12345, "no wraparound should work, 16-bit"); static_assert(WrapToSigned(uint16_t(32768)) == -32768, "works for 16-bit numbers, wraparound low end"); static_assert(WrapToSigned(uint16_t(32768 + 42)) == -32768 + 42, "works for 16-bit numbers, wraparound mid"); static_assert(WrapToSigned(uint16_t(32768 + 32767)) == -32768 + 32767, "works for 16-bit numbers, wraparound high end"); static_assert(WrapToSigned(uint32_t(8675309)) == 8675309, "no wraparound should work, 32-bit"); static_assert(WrapToSigned(uint32_t(2147483648)) == -2147483647 - 1, "works for 32-bit numbers, wraparound low end"); static_assert(WrapToSigned(uint32_t(2147483648 + 42)) == -2147483647 - 1 + 42, "works for 32-bit numbers, wraparound mid"); static_assert(WrapToSigned(uint32_t(2147483648 + 2147483647)) == -2147483647 - 1 + 2147483647, "works for 32-bit numbers, wraparound high end"); static_assert(WrapToSigned(uint64_t(4152739164)) == 4152739164, "no wraparound should work, 64-bit"); static_assert(WrapToSigned(uint64_t(9223372036854775808ULL)) == -9223372036854775807LL - 1, "works for 64-bit numbers, wraparound low end"); static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 8005552368LL)) == -9223372036854775807LL - 1 + 8005552368LL, "works for 64-bit numbers, wraparound mid"); static_assert(WrapToSigned(uint64_t(9223372036854775808ULL + 9223372036854775807ULL)) == -9223372036854775807LL - 1 + 9223372036854775807LL, "works for 64-bit numbers, wraparound high end"); template inline constexpr bool TestEqual(T aX, T aY) { return aX == aY; } static void TestWrappingMultiply8() { MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(0), uint8_t(128)), uint8_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(128), uint8_t(1)), uint8_t(128)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(2), uint8_t(128)), uint8_t(0)), "2 times high bit overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(8), uint8_t(16)), uint8_t(128)), "multiply that populates the high bit produces that value"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint8_t(127), uint8_t(127)), uint8_t(1)), "multiplying signed maxvals overflows all the way to 1"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(0), int8_t(-128)), int8_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(-128), int8_t(1)), int8_t(-128)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(2), int8_t(-128)), int8_t(0)), "2 times min overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(16), int8_t(24)), int8_t(-128)), "multiply that populates the sign bit produces minval"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(8), int8_t(16)), int8_t(-128)), "multiply that populates the sign bit produces minval"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int8_t(127), int8_t(127)), int8_t(1)), "multiplying maxvals overflows all the way to 1"); } static void TestWrappingMultiply16() { MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(0), uint16_t(32768)), uint16_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(32768), uint16_t(1)), uint16_t(32768)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(2), uint16_t(32768)), uint16_t(0)), "2 times high bit overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(3), uint16_t(32768)), uint16_t(-32768)), "3 * 32768 - 65536 is 32768"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(64), uint16_t(512)), uint16_t(32768)), "multiply that populates the high bit produces that value"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint16_t(32767), uint16_t(32767)), uint16_t(1)), "multiplying signed maxvals overflows all the way to 1"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(0), int16_t(-32768)), int16_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(-32768), int16_t(1)), int16_t(-32768)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(-456), int16_t(123)), int16_t(9448)), "multiply opposite signs, then add 2**16 for the result"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(2), int16_t(-32768)), int16_t(0)), "2 times min overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(64), int16_t(512)), int16_t(-32768)), "multiply that populates the sign bit produces minval"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int16_t(32767), int16_t(32767)), int16_t(1)), "multiplying maxvals overflows all the way to 1"); } static void TestWrappingMultiply32() { MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(0), uint32_t(2147483648)), uint32_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(42), uint32_t(17)), uint32_t(714)), "42 * 17 is 714 without wraparound"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2147483648), uint32_t(1)), uint32_t(2147483648)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2), uint32_t(2147483648)), uint32_t(0)), "2 times high bit overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(8192), uint32_t(262144)), uint32_t(2147483648)), "multiply that populates the high bit produces that value"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint32_t(2147483647), uint32_t(2147483647)), uint32_t(1)), "multiplying signed maxvals overflows all the way to 1"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(0), int32_t(-2147483647 - 1)), int32_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(-2147483647 - 1), int32_t(1)), int32_t(-2147483647 - 1)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(2), int32_t(-2147483647 - 1)), int32_t(0)), "2 times min overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(-7), int32_t(-9)), int32_t(63)), "-7 * -9 is 63, no wraparound needed"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(8192), int32_t(262144)), int32_t(-2147483647 - 1)), "multiply that populates the sign bit produces minval"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int32_t(2147483647), int32_t(2147483647)), int32_t(1)), "multiplying maxvals overflows all the way to 1"); } static void TestWrappingMultiply64() { MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(0), uint64_t(9223372036854775808ULL)), uint64_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(9223372036854775808ULL), uint64_t(1)), uint64_t(9223372036854775808ULL)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(2), uint64_t(9223372036854775808ULL)), uint64_t(0)), "2 times high bit overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(131072), uint64_t(70368744177664)), uint64_t(9223372036854775808ULL)), "multiply that populates the high bit produces that value"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(uint64_t(9223372036854775807), uint64_t(9223372036854775807)), uint64_t(1)), "multiplying signed maxvals overflows all the way to 1"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(0), int64_t(-9223372036854775807 - 1)), int64_t(0)), "zero times anything is zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(-9223372036854775807 - 1), int64_t(1)), int64_t(-9223372036854775807 - 1)), "1 times anything is anything"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(2), int64_t(-9223372036854775807 - 1)), int64_t(0)), "2 times min overflows, produces zero"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(131072), int64_t(70368744177664)), int64_t(-9223372036854775807 - 1)), "multiply that populates the sign bit produces minval"); MOZ_RELEASE_ASSERT(TestEqual(WrappingMultiply(int64_t(9223372036854775807), int64_t(9223372036854775807)), int64_t(1)), "multiplying maxvals overflows all the way to 1"); } int main() { TestWrappingMultiply8(); TestWrappingMultiply16(); TestWrappingMultiply32(); TestWrappingMultiply64(); return 0; }