diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WTF/wtf/CheckedArithmetic.h | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WTF/wtf/CheckedArithmetic.h')
-rw-r--r-- | Source/WTF/wtf/CheckedArithmetic.h | 173 |
1 files changed, 135 insertions, 38 deletions
diff --git a/Source/WTF/wtf/CheckedArithmetic.h b/Source/WTF/wtf/CheckedArithmetic.h index b58da2027..798d6f07c 100644 --- a/Source/WTF/wtf/CheckedArithmetic.h +++ b/Source/WTF/wtf/CheckedArithmetic.h @@ -75,11 +75,16 @@ class CrashOnOverflow { public: static NO_RETURN_DUE_TO_CRASH void overflowed() { - CRASH(); + crash(); } void clearOverflow() { } + static NO_RETURN_DUE_TO_CRASH void crash() + { + CRASH(); + } + public: bool hasOverflowed() const { return false; } }; @@ -101,6 +106,11 @@ protected: m_overflowed = false; } + static NO_RETURN_DUE_TO_CRASH void crash() + { + CRASH(); + } + public: bool hasOverflowed() const { return m_overflowed; } @@ -112,63 +122,84 @@ template <typename T, class OverflowHandler = CrashOnOverflow> class Checked; template <typename T> struct RemoveChecked; template <typename T> struct RemoveChecked<Checked<T>>; -template <typename Target, typename Source, bool targetSigned = std::numeric_limits<Target>::is_signed, bool sourceSigned = std::numeric_limits<Source>::is_signed> struct BoundsChecker; -template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, false> { +template <typename Target, typename Source, bool isTargetBigger = sizeof(Target) >= sizeof(Source), bool targetSigned = std::numeric_limits<Target>::is_signed, bool sourceSigned = std::numeric_limits<Source>::is_signed> struct BoundsChecker; +template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, false, false> { static bool inBounds(Source value) { - // Same signedness so implicit type conversion will always increase precision - // to widest type + // Same signedness so implicit type conversion will always increase precision to widest type. return value <= std::numeric_limits<Target>::max(); } }; - -template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, true> { +template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, true, true> { static bool inBounds(Source value) { - // Same signedness so implicit type conversion will always increase precision - // to widest type + // Same signedness so implicit type conversion will always increase precision to widest type. return std::numeric_limits<Target>::min() <= value && value <= std::numeric_limits<Target>::max(); } }; -template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, true> { +template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, false, true> { static bool inBounds(Source value) { - // Target is unsigned so any value less than zero is clearly unsafe - if (value < 0) - return false; - // If our (unsigned) Target is the same or greater width we can - // convert value to type Target without losing precision - if (sizeof(Target) >= sizeof(Source)) - return static_cast<Target>(value) <= std::numeric_limits<Target>::max(); - // The signed Source type has greater precision than the target so - // max(Target) -> Source will widen. - return value <= static_cast<Source>(std::numeric_limits<Target>::max()); + // When converting value to unsigned Source, value will become a big value if value is negative. + // Casted value will become bigger than Target::max as Source is bigger than Target. + return static_cast<typename std::make_unsigned<Source>::type>(value) <= std::numeric_limits<Target>::max(); } }; -template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, false> { +template <typename Target, typename Source> struct BoundsChecker<Target, Source, false, true, false> { static bool inBounds(Source value) { - // Signed target with an unsigned source - if (sizeof(Target) <= sizeof(Source)) - return value <= static_cast<Source>(std::numeric_limits<Target>::max()); - // Target is Wider than Source so we're guaranteed to fit any value in - // unsigned Source + // The unsigned Source type has greater precision than the target so max(Target) -> Source will widen. + return value <= static_cast<Source>(std::numeric_limits<Target>::max()); + } +}; + +template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, false, false> { + static bool inBounds(Source) + { + // Same sign, greater or same precision. return true; } }; -template <typename Target, typename Source, bool CanElide = std::is_same<Target, Source>::value || (sizeof(Target) > sizeof(Source)) > struct BoundsCheckElider; -template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, true> { - static bool inBounds(Source) { return true; } +template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, true, true> { + static bool inBounds(Source) + { + // Same sign, greater or same precision. + return true; + } }; -template <typename Target, typename Source> struct BoundsCheckElider<Target, Source, false> : public BoundsChecker<Target, Source> { + +template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, true, false> { + static bool inBounds(Source value) + { + // Target is signed with greater or same precision. If strictly greater, it is always safe. + if (sizeof(Target) > sizeof(Source)) + return true; + return value <= static_cast<Source>(std::numeric_limits<Target>::max()); + } +}; + +template <typename Target, typename Source> struct BoundsChecker<Target, Source, true, false, true> { + static bool inBounds(Source value) + { + // Target is unsigned with greater precision. + return value >= 0; + } }; template <typename Target, typename Source> static inline bool isInBounds(Source value) { - return BoundsCheckElider<Target, Source>::inBounds(value); + return BoundsChecker<Target, Source>::inBounds(value); +} + +template <typename Target, typename Source> static inline bool convertSafely(Source input, Target& output) +{ + if (!isInBounds<Target>(input)) + return false; + output = static_cast<Target>(input); + return true; } template <typename T> struct RemoveChecked { @@ -517,23 +548,22 @@ public: bool operator!() const { if (this->hasOverflowed()) - CRASH(); + this->crash(); return !m_value; } - typedef void* (Checked::*UnspecifiedBoolType); - operator UnspecifiedBoolType*() const + explicit operator bool() const { if (this->hasOverflowed()) - CRASH(); - return (m_value) ? reinterpret_cast<UnspecifiedBoolType*>(1) : 0; + this->crash(); + return m_value; } // Value accessors. unsafeGet() will crash if there's been an overflow. T unsafeGet() const { if (this->hasOverflowed()) - CRASH(); + this->crash(); return m_value; } @@ -612,7 +642,7 @@ public: template <typename U> bool operator==(U rhs) { if (this->hasOverflowed()) - this->overflowed(); + this->crash(); return safeEquals(m_value, rhs); } @@ -626,6 +656,47 @@ public: return !(*this == rhs); } + // Other comparisons + template <typename V> bool operator<(Checked<T, V> rhs) const + { + return unsafeGet() < rhs.unsafeGet(); + } + + bool operator<(T rhs) const + { + return unsafeGet() < rhs; + } + + template <typename V> bool operator<=(Checked<T, V> rhs) const + { + return unsafeGet() <= rhs.unsafeGet(); + } + + bool operator<=(T rhs) const + { + return unsafeGet() <= rhs; + } + + template <typename V> bool operator>(Checked<T, V> rhs) const + { + return unsafeGet() > rhs.unsafeGet(); + } + + bool operator>(T rhs) const + { + return unsafeGet() > rhs; + } + + template <typename V> bool operator>=(Checked<T, V> rhs) const + { + return unsafeGet() >= rhs.unsafeGet(); + } + + bool operator>=(T rhs) const + { + return unsafeGet() >= rhs; + } + private: // Disallow implicit conversion of floating point to integer types Checked(float); @@ -735,6 +806,30 @@ template<typename T, typename... Args> bool sumOverflows(Args... args) return checkedSum<T>(args...).hasOverflowed(); } +template<typename T, typename U> bool differenceOverflows(U left, U right) +{ + return (Checked<T, RecordOverflow>(left) - Checked<T, RecordOverflow>(right)).hasOverflowed(); +} + +template<typename T, typename U> +Checked<T, RecordOverflow> checkedProduct(U value) +{ + return Checked<T, RecordOverflow>(value); +} +template<typename T, typename U, typename... Args> +Checked<T, RecordOverflow> checkedProduct(U value, Args... args) +{ + return Checked<T, RecordOverflow>(value) * checkedProduct<T>(args...); +} + +// Sometimes, you just want to check if some math would overflow - the code to do the math is +// already in place, and you want to guard it. + +template<typename T, typename... Args> bool productOverflows(Args... args) +{ + return checkedProduct<T>(args...).hasOverflowed(); +} + } using WTF::Checked; @@ -750,6 +845,8 @@ using WTF::CheckedInt64; using WTF::CheckedUint64; using WTF::CheckedSize; using WTF::checkedSum; +using WTF::differenceOverflows; +using WTF::productOverflows; using WTF::sumOverflows; #endif |