michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * michael@0: * Copyright (C) 2011 Apple Inc. All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY michael@0: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE michael@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR michael@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR michael@0: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, michael@0: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, michael@0: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR michael@0: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY michael@0: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: #ifndef yarr_CheckedArithmetic_h michael@0: #define yarr_CheckedArithmetic_h michael@0: michael@0: #include "assembler/wtf/Assertions.h" michael@0: michael@0: #include michael@0: #include michael@0: #include "mozilla/TypeTraits.h" michael@0: michael@0: #ifdef _MSC_VER michael@0: # undef min michael@0: # undef max michael@0: #endif michael@0: michael@0: /* Checked michael@0: * michael@0: * This class provides a mechanism to perform overflow-safe integer arithmetic michael@0: * without having to manually ensure that you have all the required bounds checks michael@0: * directly in your code. michael@0: * michael@0: * There are two modes of operation: michael@0: * - The default is Checked, and crashes at the point michael@0: * and overflow has occurred. michael@0: * - The alternative is Checked, which uses an additional michael@0: * byte of storage to track whether an overflow has occurred, subsequent michael@0: * unchecked operations will crash if an overflow has occured michael@0: * michael@0: * It is possible to provide a custom overflow handler, in which case you need michael@0: * to support these functions: michael@0: * - void overflowed(); michael@0: * This function is called when an operation has produced an overflow. michael@0: * - bool hasOverflowed(); michael@0: * This function must return true if overflowed() has been called on an michael@0: * instance and false if it has not. michael@0: * - void clearOverflow(); michael@0: * Used to reset overflow tracking when a value is being overwritten with michael@0: * a new value. michael@0: * michael@0: * Checked works for all integer types, with the following caveats: michael@0: * - Mixing signedness of operands is only supported for types narrower than michael@0: * 64bits. michael@0: * - It does have a performance impact, so tight loops may want to be careful michael@0: * when using it. michael@0: * michael@0: */ michael@0: michael@0: namespace WTF { michael@0: michael@0: class CrashOnOverflow { michael@0: protected: michael@0: void overflowed() michael@0: { michael@0: CRASH(); michael@0: } michael@0: michael@0: void clearOverflow() { } michael@0: michael@0: public: michael@0: bool hasOverflowed() const { return false; } michael@0: }; michael@0: michael@0: class RecordOverflow { michael@0: protected: michael@0: RecordOverflow() michael@0: : m_overflowed(false) michael@0: { michael@0: } michael@0: michael@0: void overflowed() michael@0: { michael@0: m_overflowed = true; michael@0: } michael@0: michael@0: void clearOverflow() michael@0: { michael@0: m_overflowed = false; michael@0: } michael@0: michael@0: public: michael@0: bool hasOverflowed() const { return m_overflowed; } michael@0: michael@0: private: michael@0: unsigned char m_overflowed; michael@0: }; michael@0: michael@0: template class Checked; michael@0: template struct RemoveChecked; michael@0: template struct RemoveChecked >; michael@0: michael@0: template ::is_signed, bool sourceSigned = ::std::numeric_limits::is_signed> struct BoundsChecker; michael@0: template struct BoundsChecker { michael@0: static bool inBounds(Source value) michael@0: { michael@0: // Same signedness so implicit type conversion will always increase precision michael@0: // to widest type michael@0: return value <= ::std::numeric_limits::max(); michael@0: } michael@0: }; michael@0: michael@0: template struct BoundsChecker { michael@0: static bool inBounds(Source value) michael@0: { michael@0: // Same signedness so implicit type conversion will always increase precision michael@0: // to widest type michael@0: return ::std::numeric_limits::min() <= value && value <= ::std::numeric_limits::max(); michael@0: } michael@0: }; michael@0: michael@0: template struct BoundsChecker { michael@0: static bool inBounds(Source value) michael@0: { michael@0: // Target is unsigned so any value less than zero is clearly unsafe michael@0: if (value < 0) michael@0: return false; michael@0: // If our (unsigned) Target is the same or greater width we can michael@0: // convert value to type Target without losing precision michael@0: if (sizeof(Target) >= sizeof(Source)) michael@0: return static_cast(value) <= ::std::numeric_limits::max(); michael@0: // The signed Source type has greater precision than the target so michael@0: // max(Target) -> Source will widen. michael@0: return value <= static_cast(::std::numeric_limits::max()); michael@0: } michael@0: }; michael@0: michael@0: template struct BoundsChecker { michael@0: static bool inBounds(Source value) michael@0: { michael@0: // Signed target with an unsigned source michael@0: if (sizeof(Target) <= sizeof(Source)) michael@0: return value <= static_cast(::std::numeric_limits::max()); michael@0: // Target is Wider than Source so we're guaranteed to fit any value in michael@0: // unsigned Source michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: template ::value> struct BoundsCheckElider; michael@0: template struct BoundsCheckElider { michael@0: static bool inBounds(Source) { return true; } michael@0: }; michael@0: template struct BoundsCheckElider : public BoundsChecker { michael@0: }; michael@0: michael@0: template static inline bool isInBounds(Source value) michael@0: { michael@0: return BoundsCheckElider::inBounds(value); michael@0: } michael@0: michael@0: template struct RemoveChecked { michael@0: typedef T CleanType; michael@0: static const CleanType DefaultValue = 0; michael@0: }; michael@0: michael@0: template struct RemoveChecked > { michael@0: typedef typename RemoveChecked::CleanType CleanType; michael@0: static const CleanType DefaultValue = 0; michael@0: }; michael@0: michael@0: template struct RemoveChecked > { michael@0: typedef typename RemoveChecked::CleanType CleanType; michael@0: static const CleanType DefaultValue = 0; michael@0: }; michael@0: michael@0: // The ResultBase and SignednessSelector are used to workaround typeof not being michael@0: // available in MSVC michael@0: template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; michael@0: template struct ResultBase { michael@0: typedef U ResultType; michael@0: }; michael@0: michael@0: template struct ResultBase { michael@0: typedef V ResultType; michael@0: }; michael@0: michael@0: template struct ResultBase { michael@0: typedef U ResultType; michael@0: }; michael@0: michael@0: template ::is_signed, bool vIsSigned = ::std::numeric_limits::is_signed> struct SignednessSelector; michael@0: template struct SignednessSelector { michael@0: typedef U ResultType; michael@0: }; michael@0: michael@0: template struct SignednessSelector { michael@0: typedef U ResultType; michael@0: }; michael@0: michael@0: template struct SignednessSelector { michael@0: typedef V ResultType; michael@0: }; michael@0: michael@0: template struct SignednessSelector { michael@0: typedef U ResultType; michael@0: }; michael@0: michael@0: template struct ResultBase { michael@0: typedef typename SignednessSelector::ResultType ResultType; michael@0: }; michael@0: michael@0: template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { michael@0: }; michael@0: michael@0: template ::ResultType, michael@0: bool lhsSigned = ::std::numeric_limits::is_signed, bool rhsSigned = ::std::numeric_limits::is_signed> struct ArithmeticOperations; michael@0: michael@0: template struct ArithmeticOperations { michael@0: // LHS and RHS are signed types michael@0: michael@0: // Helper function michael@0: static inline bool signsMatch(LHS lhs, RHS rhs) michael@0: { michael@0: return (lhs ^ rhs) >= 0; michael@0: } michael@0: michael@0: static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN michael@0: { michael@0: if (signsMatch(lhs, rhs)) { michael@0: if (lhs >= 0) { michael@0: if ((::std::numeric_limits::max() - rhs) < lhs) michael@0: return false; michael@0: } else { michael@0: ResultType temp = lhs - ::std::numeric_limits::min(); michael@0: if (rhs < -temp) michael@0: return false; michael@0: } michael@0: } // if the signs do not match this operation can't overflow michael@0: result = lhs + rhs; michael@0: return true; michael@0: } michael@0: michael@0: static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN michael@0: { michael@0: if (!signsMatch(lhs, rhs)) { michael@0: if (lhs >= 0) { michael@0: if (lhs > ::std::numeric_limits::max() + rhs) michael@0: return false; michael@0: } else { michael@0: if (rhs > ::std::numeric_limits::max() + lhs) michael@0: return false; michael@0: } michael@0: } // if the signs match this operation can't overflow michael@0: result = lhs - rhs; michael@0: return true; michael@0: } michael@0: michael@0: static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN michael@0: { michael@0: if (signsMatch(lhs, rhs)) { michael@0: if (lhs >= 0) { michael@0: if (lhs && (::std::numeric_limits::max() / lhs) < rhs) michael@0: return false; michael@0: } else { michael@0: if (lhs == ::std::numeric_limits::min() || rhs == ::std::numeric_limits::min()) michael@0: return false; michael@0: if ((::std::numeric_limits::max() / -lhs) < -rhs) michael@0: return false; michael@0: } michael@0: } else { michael@0: if (lhs < 0) { michael@0: if (rhs && lhs < (::std::numeric_limits::min() / rhs)) michael@0: return false; michael@0: } else { michael@0: if (lhs && rhs < (::std::numeric_limits::min() / lhs)) michael@0: return false; michael@0: } michael@0: } michael@0: result = lhs * rhs; michael@0: return true; michael@0: } michael@0: michael@0: static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } michael@0: michael@0: }; michael@0: michael@0: template struct ArithmeticOperations { michael@0: // LHS and RHS are unsigned types so bounds checks are nice and easy michael@0: static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN michael@0: { michael@0: ResultType temp = lhs + rhs; michael@0: if (temp < lhs) michael@0: return false; michael@0: result = temp; michael@0: return true; michael@0: } michael@0: michael@0: static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN michael@0: { michael@0: ResultType temp = lhs - rhs; michael@0: if (temp > lhs) michael@0: return false; michael@0: result = temp; michael@0: return true; michael@0: } michael@0: michael@0: static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN michael@0: { michael@0: ResultType temp = lhs * rhs; michael@0: if (temp < lhs) michael@0: return false; michael@0: result = temp; michael@0: return true; michael@0: } michael@0: michael@0: static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } michael@0: michael@0: }; michael@0: michael@0: template struct ArithmeticOperations { michael@0: static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) michael@0: { michael@0: int64_t temp = lhs + rhs; michael@0: if (temp < ::std::numeric_limits::min()) michael@0: return false; michael@0: if (temp > ::std::numeric_limits::max()) michael@0: return false; michael@0: result = static_cast(temp); michael@0: return true; michael@0: } michael@0: michael@0: static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) michael@0: { michael@0: int64_t temp = lhs - rhs; michael@0: if (temp < ::std::numeric_limits::min()) michael@0: return false; michael@0: if (temp > ::std::numeric_limits::max()) michael@0: return false; michael@0: result = static_cast(temp); michael@0: return true; michael@0: } michael@0: michael@0: static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) michael@0: { michael@0: int64_t temp = lhs * rhs; michael@0: if (temp < ::std::numeric_limits::min()) michael@0: return false; michael@0: if (temp > ::std::numeric_limits::max()) michael@0: return false; michael@0: result = static_cast(temp); michael@0: return true; michael@0: } michael@0: michael@0: static inline bool equals(int lhs, unsigned rhs) michael@0: { michael@0: return static_cast(lhs) == static_cast(rhs); michael@0: } michael@0: }; michael@0: michael@0: template struct ArithmeticOperations { michael@0: static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) michael@0: { michael@0: return ArithmeticOperations::add(rhs, lhs, result); michael@0: } michael@0: michael@0: static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) michael@0: { michael@0: return ArithmeticOperations::sub(lhs, rhs, result); michael@0: } michael@0: michael@0: static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) michael@0: { michael@0: return ArithmeticOperations::multiply(rhs, lhs, result); michael@0: } michael@0: michael@0: static inline bool equals(unsigned lhs, int rhs) michael@0: { michael@0: return ArithmeticOperations::equals(rhs, lhs); michael@0: } michael@0: }; michael@0: michael@0: template static inline bool safeAdd(U lhs, V rhs, R& result) michael@0: { michael@0: return ArithmeticOperations::add(lhs, rhs, result); michael@0: } michael@0: michael@0: template static inline bool safeSub(U lhs, V rhs, R& result) michael@0: { michael@0: return ArithmeticOperations::sub(lhs, rhs, result); michael@0: } michael@0: michael@0: template static inline bool safeMultiply(U lhs, V rhs, R& result) michael@0: { michael@0: return ArithmeticOperations::multiply(lhs, rhs, result); michael@0: } michael@0: michael@0: template static inline bool safeEquals(U lhs, V rhs) michael@0: { michael@0: return ArithmeticOperations::equals(lhs, rhs); michael@0: } michael@0: michael@0: enum ResultOverflowedTag { ResultOverflowed }; michael@0: michael@0: // FIXME: Needed to workaround http://llvm.org/bugs/show_bug.cgi?id=10801 michael@0: static inline bool workAroundClangBug() { return true; } michael@0: michael@0: template class Checked : public OverflowHandler { michael@0: public: michael@0: template friend class Checked; michael@0: Checked() michael@0: : m_value(0) michael@0: { michael@0: } michael@0: michael@0: Checked(ResultOverflowedTag) michael@0: : m_value(0) michael@0: { michael@0: // FIXME: Remove this when clang fixes http://llvm.org/bugs/show_bug.cgi?id=10801 michael@0: if (workAroundClangBug()) michael@0: this->overflowed(); michael@0: } michael@0: michael@0: template Checked(U value) michael@0: { michael@0: if (!isInBounds(value)) michael@0: this->overflowed(); michael@0: m_value = static_cast(value); michael@0: } michael@0: michael@0: template Checked(const Checked& rhs) michael@0: : m_value(rhs.m_value) michael@0: { michael@0: if (rhs.hasOverflowed()) michael@0: this->overflowed(); michael@0: } michael@0: michael@0: template Checked(const Checked& rhs) michael@0: : OverflowHandler(rhs) michael@0: { michael@0: if (!isInBounds(rhs.m_value)) michael@0: this->overflowed(); michael@0: m_value = static_cast(rhs.m_value); michael@0: } michael@0: michael@0: template Checked(const Checked& rhs) michael@0: { michael@0: if (rhs.hasOverflowed()) michael@0: this->overflowed(); michael@0: if (!isInBounds(rhs.m_value)) michael@0: this->overflowed(); michael@0: m_value = static_cast(rhs.m_value); michael@0: } michael@0: michael@0: const Checked& operator=(Checked rhs) michael@0: { michael@0: this->clearOverflow(); michael@0: if (rhs.hasOverflowed()) michael@0: this->overflowed(); michael@0: m_value = static_cast(rhs.m_value); michael@0: return *this; michael@0: } michael@0: michael@0: template const Checked& operator=(U value) michael@0: { michael@0: return *this = Checked(value); michael@0: } michael@0: michael@0: template const Checked& operator=(const Checked& rhs) michael@0: { michael@0: return *this = Checked(rhs); michael@0: } michael@0: michael@0: // prefix michael@0: const Checked& operator++() michael@0: { michael@0: if (m_value == ::std::numeric_limits::max()) michael@0: this->overflowed(); michael@0: m_value++; michael@0: return *this; michael@0: } michael@0: michael@0: const Checked& operator--() michael@0: { michael@0: if (m_value == ::std::numeric_limits::min()) michael@0: this->overflowed(); michael@0: m_value--; michael@0: return *this; michael@0: } michael@0: michael@0: // postfix operators michael@0: const Checked operator++(int) michael@0: { michael@0: if (m_value == ::std::numeric_limits::max()) michael@0: this->overflowed(); michael@0: return Checked(m_value++); michael@0: } michael@0: michael@0: const Checked operator--(int) michael@0: { michael@0: if (m_value == ::std::numeric_limits::min()) michael@0: this->overflowed(); michael@0: return Checked(m_value--); michael@0: } michael@0: michael@0: // Boolean operators michael@0: bool operator!() const michael@0: { michael@0: if (this->hasOverflowed()) michael@0: CRASH(); michael@0: return !m_value; michael@0: } michael@0: michael@0: typedef void* (Checked::*UnspecifiedBoolType); michael@0: operator UnspecifiedBoolType*() const michael@0: { michael@0: if (this->hasOverflowed()) michael@0: CRASH(); michael@0: return (m_value) ? reinterpret_cast(1) : 0; michael@0: } michael@0: michael@0: // Value accessors. unsafeGet() will crash if there's been an overflow. michael@0: T unsafeGet() const michael@0: { michael@0: if (this->hasOverflowed()) michael@0: CRASH(); michael@0: return m_value; michael@0: } michael@0: michael@0: bool safeGet(T& value) const WARN_UNUSED_RETURN michael@0: { michael@0: value = m_value; michael@0: return this->hasOverflowed(); michael@0: } michael@0: michael@0: // Mutating assignment michael@0: template const Checked operator+=(U rhs) michael@0: { michael@0: if (!safeAdd(m_value, rhs, m_value)) michael@0: this->overflowed(); michael@0: return *this; michael@0: } michael@0: michael@0: template const Checked operator-=(U rhs) michael@0: { michael@0: if (!safeSub(m_value, rhs, m_value)) michael@0: this->overflowed(); michael@0: return *this; michael@0: } michael@0: michael@0: template const Checked operator*=(U rhs) michael@0: { michael@0: if (!safeMultiply(m_value, rhs, m_value)) michael@0: this->overflowed(); michael@0: return *this; michael@0: } michael@0: michael@0: const Checked operator*=(double rhs) michael@0: { michael@0: double result = rhs * m_value; michael@0: // Handle +/- infinity and NaN michael@0: if (!(::std::numeric_limits::min() <= result && ::std::numeric_limits::max() >= result)) michael@0: this->overflowed(); michael@0: m_value = (T)result; michael@0: return *this; michael@0: } michael@0: michael@0: const Checked operator*=(float rhs) michael@0: { michael@0: return *this *= (double)rhs; michael@0: } michael@0: michael@0: template const Checked operator+=(Checked rhs) michael@0: { michael@0: if (rhs.hasOverflowed()) michael@0: this->overflowed(); michael@0: return *this += rhs.m_value; michael@0: } michael@0: michael@0: template const Checked operator-=(Checked rhs) michael@0: { michael@0: if (rhs.hasOverflowed()) michael@0: this->overflowed(); michael@0: return *this -= rhs.m_value; michael@0: } michael@0: michael@0: template const Checked operator*=(Checked rhs) michael@0: { michael@0: if (rhs.hasOverflowed()) michael@0: this->overflowed(); michael@0: return *this *= rhs.m_value; michael@0: } michael@0: michael@0: // Equality comparisons michael@0: template bool operator==(Checked rhs) michael@0: { michael@0: return unsafeGet() == rhs.unsafeGet(); michael@0: } michael@0: michael@0: template bool operator==(U rhs) michael@0: { michael@0: if (this->hasOverflowed()) michael@0: this->overflowed(); michael@0: return safeEquals(m_value, rhs); michael@0: } michael@0: michael@0: template const Checked operator==(Checked rhs) michael@0: { michael@0: return unsafeGet() == Checked(rhs.unsafeGet()); michael@0: } michael@0: michael@0: template bool operator!=(U rhs) michael@0: { michael@0: return !(*this == rhs); michael@0: } michael@0: michael@0: private: michael@0: // Disallow implicit conversion of floating point to integer types michael@0: Checked(float); michael@0: Checked(double); michael@0: void operator=(float); michael@0: void operator=(double); michael@0: void operator+=(float); michael@0: void operator+=(double); michael@0: void operator-=(float); michael@0: void operator-=(double); michael@0: T m_value; michael@0: }; michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) michael@0: { michael@0: U x = 0; michael@0: V y = 0; michael@0: bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); michael@0: typename Result::ResultType result = 0; michael@0: overflowed |= !safeAdd(x, y, result); michael@0: if (overflowed) michael@0: return ResultOverflowed; michael@0: return result; michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) michael@0: { michael@0: U x = 0; michael@0: V y = 0; michael@0: bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); michael@0: typename Result::ResultType result = 0; michael@0: overflowed |= !safeSub(x, y, result); michael@0: if (overflowed) michael@0: return ResultOverflowed; michael@0: return result; michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) michael@0: { michael@0: U x = 0; michael@0: V y = 0; michael@0: bool overflowed = lhs.safeGet(x) || rhs.safeGet(y); michael@0: typename Result::ResultType result = 0; michael@0: overflowed |= !safeMultiply(x, y, result); michael@0: if (overflowed) michael@0: return ResultOverflowed; michael@0: return result; michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) michael@0: { michael@0: return lhs + Checked(rhs); michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) michael@0: { michael@0: return lhs - Checked(rhs); michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) michael@0: { michael@0: return lhs * Checked(rhs); michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) michael@0: { michael@0: return Checked(lhs) + rhs; michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) michael@0: { michael@0: return Checked(lhs) - rhs; michael@0: } michael@0: michael@0: template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) michael@0: { michael@0: return Checked(lhs) * rhs; michael@0: } michael@0: michael@0: } michael@0: michael@0: using WTF::Checked; michael@0: using WTF::RecordOverflow; michael@0: michael@0: #endif /* yarr_CheckedArithmetic_h */