Bug 1445024 - Implement mozilla::WrappingSubtract. r=froydnj

--HG--
extra : rebase_source : 026268df1cb1cfc56873e61834ea90257645c508
This commit is contained in:
Jeff Walden 2018-03-06 19:22:20 -08:00
parent 83186da179
commit 0eefeabe25
2 changed files with 206 additions and 0 deletions

View File

@ -173,6 +173,56 @@ WrappingAdd(T aX, T aY)
namespace detail { namespace detail {
template<typename T>
struct WrappingSubtractHelper
{
private:
using UnsignedT = typename MakeUnsigned<T>::Type;
public:
MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
static T compute(T aX, T aY)
{
return ToResult<T>(static_cast<UnsignedT>(aX) - static_cast<UnsignedT>(aY));
}
};
} // namespace detail
/**
* Subtract two integers of the same type and return the result converted to
* that type using wraparound semantics, without triggering overflow sanitizers.
*
* For N-bit unsigned integer types, this is equivalent to subtracting the two
* numbers, then taking the result mod 2**N:
*
* WrappingSubtract(uint32_t(42), uint32_t(17)) is 29 (29 mod 2**32);
* WrappingSubtract(uint8_t(5), uint8_t(20)) is 241 (-15 mod 2**8).
*
* Unsigned WrappingSubtract acts exactly like C++ unsigned subtraction.
*
* For N-bit signed integer types, this is equivalent to subtracting the two
* numbers wrapped to unsigned, then wrapping the difference mod 2**N to the
* signed range:
*
* WrappingSubtract(int16_t(32767), int16_t(-5)) is -32764 ((32772 mod 2**16) - 2**16);
* WrappingSubtract(int8_t(-128), int8_t(127)) is 1 (-255 mod 2**8);
* WrappingSubtract(int32_t(-17), int32_t(-42)) is 25 (25 mod 2**32).
*
* There's no equivalent to this operation in C++, as C++ signed subtraction
* that overflows has undefined behavior. But it's how such subtraction *tends*
* to behave with most compilers, unless an optimization or similar -- quite
* permissibly -- triggers different behavior.
*/
template<typename T>
inline T
WrappingSubtract(T aX, T aY)
{
return detail::WrappingSubtractHelper<T>::compute(aX, aY);
}
namespace detail {
template<typename T> template<typename T>
struct WrappingMultiplyHelper struct WrappingMultiplyHelper
{ {

View File

@ -12,6 +12,7 @@
using mozilla::WrappingAdd; using mozilla::WrappingAdd;
using mozilla::WrappingMultiply; using mozilla::WrappingMultiply;
using mozilla::WrapToSigned; using mozilla::WrapToSigned;
using mozilla::WrappingSubtract;
// NOTE: In places below |-FOO_MAX - 1| is used instead of |-FOO_MIN| because // 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 // in C++ numeric literals are full expressions -- the |-| in a negative
@ -223,6 +224,160 @@ TestWrappingAdd()
TestWrappingAdd64(); TestWrappingAdd64();
} }
static void
TestWrappingSubtract8()
{
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint8_t(0), uint8_t(128)),
uint8_t(128)),
"zero minus half is half");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint8_t(17), uint8_t(42)),
uint8_t(231)),
"17 - 42 == -25 added to 256 is 231");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint8_t(0), uint8_t(1)),
uint8_t(255)),
"zero underflows to all bits");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint8_t(128), uint8_t(127)),
uint8_t(1)),
"128 - 127 == 1");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint8_t(128), uint8_t(193)),
uint8_t(191)),
"128 - 193 is -65 so -65 + 256 == 191");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int8_t(0), int8_t(-128)),
int8_t(-128)),
"zero minus high bit wraps to high bit");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int8_t(-126), int8_t(4)),
int8_t(126)),
"underflow to positive");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int8_t(5), int8_t(-123)),
int8_t(-128)),
"overflow to negative");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int8_t(-85), int8_t(-73)),
int8_t(-12)),
"negative minus smaller negative");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int8_t(-128), int8_t(127)),
int8_t(1)),
"underflow to 1");
}
static void
TestWrappingSubtract16()
{
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint16_t(0), uint16_t(32768)),
uint16_t(32768)),
"zero minus half is half");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint16_t(24389), uint16_t(2682)),
uint16_t(21707)),
"24389 - 2682 == 21707");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint16_t(0), uint16_t(1)),
uint16_t(65535)),
"zero underflows to all bits");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint16_t(32768), uint16_t(32767)),
uint16_t(1)),
"high bit minus all lower bits is one");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint16_t(32768), uint16_t(47582)),
uint16_t(50722)),
"32768 - 47582 + 65536 is 50722");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int16_t(0), int16_t(-32768)),
int16_t(-32768)),
"zero minus high bit wraps to high bit");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int16_t(-32766), int16_t(4)),
int16_t(32766)),
"underflow to positive");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int16_t(5), int16_t(-28933)),
int16_t(28938)),
"5 - -28933 is 28938");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int16_t(-23892), int16_t(-12893)),
int16_t(-10999)),
"negative minus smaller negative");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int16_t(-32768), int16_t(32767)),
int16_t(1)),
"underflow to 1");
}
static void
TestWrappingSubtract32()
{
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint32_t(0), uint32_t(2147483648)),
uint32_t(2147483648)),
"zero minus half is half");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint32_t(1398742328), uint32_t(714192829)),
uint32_t(684549499)),
"1398742328 - 714192829 == 684549499");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint32_t(0), uint32_t(1)),
uint32_t(4294967295)),
"zero underflows to all bits");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint32_t(2147483648), uint32_t(2147483647)),
uint32_t(1)),
"high bit minus all lower bits is one");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint32_t(2147483648), uint32_t(3146492712)),
uint32_t(3295958232)),
"2147483648 - 3146492712 + 4294967296 is 3295958232");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int32_t(0), int32_t(-2147483647 - 1)),
int32_t(-2147483647 - 1)),
"zero minus high bit wraps to high bit");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int32_t(-2147483646), int32_t(4)),
int32_t(2147483646)),
"underflow to positive");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int32_t(257), int32_t(-23947248)),
int32_t(23947505)),
"257 - -23947248 is 23947505");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int32_t(-2147483220), int32_t(-12893)),
int32_t(-2147470327)),
"negative minus smaller negative");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int32_t(-2147483647 - 1), int32_t(2147483647)),
int32_t(1)),
"underflow to 1");
}
static void
TestWrappingSubtract64()
{
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint64_t(0), uint64_t(9223372036854775808ULL)),
uint64_t(9223372036854775808ULL)),
"zero minus half is half");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint64_t(70368744177664), uint64_t(3740873592)),
uint64_t(70365003304072)),
"70368744177664 - 3740873592 == 70365003304072");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint64_t(0), uint64_t(1)),
uint64_t(18446744073709551615ULL)),
"zero underflows to all bits");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint64_t(9223372036854775808ULL),
uint64_t(9223372036854775807ULL)),
uint64_t(1)),
"high bit minus all lower bits is one");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(uint64_t(14552598638644786479ULL), uint64_t(3894174382537247221ULL)),
uint64_t(10658424256107539258ULL)),
"14552598638644786479 - 39763621533397112216 is 10658424256107539258L");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int64_t(0), int64_t(-9223372036854775807LL - 1)),
int64_t(-9223372036854775807LL - 1)),
"zero minus high bit wraps to high bit");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int64_t(-9223372036854775802LL), int64_t(8)),
int64_t(9223372036854775806LL)),
"overflow to negative");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int64_t(37482739294298742LL), int64_t(-437843573929483498LL)),
int64_t(475326313223782240)),
"37482739294298742 - -437843573929483498 is 475326313223782240");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int64_t(-9127837934058953374LL), int64_t(-4173572032144775807LL)),
int64_t(-4954265901914177567LL)),
"negative minus smaller negative");
MOZ_RELEASE_ASSERT(TestEqual(WrappingSubtract(int64_t(-9223372036854775807LL - 1), int64_t(9223372036854775807LL)),
int64_t(1)),
"underflow to 1");
}
static void
TestWrappingSubtract()
{
TestWrappingSubtract8();
TestWrappingSubtract16();
TestWrappingSubtract32();
TestWrappingSubtract64();
}
static void static void
TestWrappingMultiply8() TestWrappingMultiply8()
{ {
@ -405,6 +560,7 @@ int
main() main()
{ {
TestWrappingAdd(); TestWrappingAdd();
TestWrappingSubtract();
TestWrappingMultiply(); TestWrappingMultiply();
return 0; return 0;
} }