mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1445024 - Implement mozilla::WrappingSubtract. r=froydnj
--HG-- extra : rebase_source : 026268df1cb1cfc56873e61834ea90257645c508
This commit is contained in:
parent
83186da179
commit
0eefeabe25
@ -173,6 +173,56 @@ WrappingAdd(T aX, T aY)
|
||||
|
||||
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>
|
||||
struct WrappingMultiplyHelper
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
using mozilla::WrappingAdd;
|
||||
using mozilla::WrappingMultiply;
|
||||
using mozilla::WrapToSigned;
|
||||
using mozilla::WrappingSubtract;
|
||||
|
||||
// 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
|
||||
@ -223,6 +224,160 @@ TestWrappingAdd()
|
||||
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
|
||||
TestWrappingMultiply8()
|
||||
{
|
||||
@ -405,6 +560,7 @@ int
|
||||
main()
|
||||
{
|
||||
TestWrappingAdd();
|
||||
TestWrappingSubtract();
|
||||
TestWrappingMultiply();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user