summaryrefslogtreecommitdiff
path: root/Source/WTF/wtf/CheckedArithmetic.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WTF/wtf/CheckedArithmetic.h
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WTF/wtf/CheckedArithmetic.h')
-rw-r--r--Source/WTF/wtf/CheckedArithmetic.h173
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