2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #ifndef CheckedArithmetic_h
27 #define CheckedArithmetic_h
29 #include "wtf/Assertions.h"
30 #include "wtf/TypeTraits.h"
37 * This class provides a mechanism to perform overflow-safe integer arithmetic
38 * without having to manually ensure that you have all the required bounds checks
39 * directly in your code.
41 * There are two modes of operation:
42 * - The default is Checked<T, CrashOnOverflow>, and crashes at the point
43 * and overflow has occurred.
44 * - The alternative is Checked<T, RecordOverflow>, which uses an additional
45 * byte of storage to track whether an overflow has occurred, subsequent
46 * unchecked operations will crash if an overflow has occured
48 * It is possible to provide a custom overflow handler, in which case you need
49 * to support these functions:
50 * - void overflowed();
51 * This function is called when an operation has produced an overflow.
52 * - bool hasOverflowed();
53 * This function must return true if overflowed() has been called on an
54 * instance and false if it has not.
55 * - void clearOverflow();
56 * Used to reset overflow tracking when a value is being overwritten with
59 * Checked<T> works for all integer types, with the following caveats:
60 * - Mixing signedness of operands is only supported for types narrower than
62 * - It does have a performance impact, so tight loops may want to be careful
69 enum class CheckedState
75 class CrashOnOverflow
{
77 NO_RETURN_DUE_TO_CRASH
void overflowed()
82 void clearOverflow() { }
85 bool hasOverflowed() const { return false; }
88 class RecordOverflow
{
102 m_overflowed
= false;
106 bool hasOverflowed() const { return m_overflowed
; }
109 unsigned char m_overflowed
;
112 template <typename T
, class OverflowHandler
= CrashOnOverflow
> class Checked
;
113 template <typename T
> struct RemoveChecked
;
114 template <typename T
> struct RemoveChecked
<Checked
<T
>>;
116 template <typename Target
, typename Source
, bool targetSigned
= std::numeric_limits
<Target
>::is_signed
, bool sourceSigned
= std::numeric_limits
<Source
>::is_signed
> struct BoundsChecker
;
117 template <typename Target
, typename Source
> struct BoundsChecker
<Target
, Source
, false, false> {
118 static bool inBounds(Source value
)
120 // Same signedness so implicit type conversion will always increase precision
122 return value
<= std::numeric_limits
<Target
>::max();
126 template <typename Target
, typename Source
> struct BoundsChecker
<Target
, Source
, true, true> {
127 static bool inBounds(Source value
)
129 // Same signedness so implicit type conversion will always increase precision
131 return std::numeric_limits
<Target
>::min() <= value
&& value
<= std::numeric_limits
<Target
>::max();
135 template <typename Target
, typename Source
> struct BoundsChecker
<Target
, Source
, false, true> {
136 static bool inBounds(Source value
)
138 // Target is unsigned so any value less than zero is clearly unsafe
141 // If our (unsigned) Target is the same or greater width we can
142 // convert value to type Target without losing precision
143 if (sizeof(Target
) >= sizeof(Source
))
144 return static_cast<Target
>(value
) <= std::numeric_limits
<Target
>::max();
145 // The signed Source type has greater precision than the target so
146 // max(Target) -> Source will widen.
147 return value
<= static_cast<Source
>(std::numeric_limits
<Target
>::max());
151 template <typename Target
, typename Source
> struct BoundsChecker
<Target
, Source
, true, false> {
152 static bool inBounds(Source value
)
154 // Signed target with an unsigned source
155 if (sizeof(Target
) <= sizeof(Source
))
156 return value
<= static_cast<Source
>(std::numeric_limits
<Target
>::max());
157 // Target is Wider than Source so we're guaranteed to fit any value in
163 template <typename Target
, typename Source
, bool CanElide
= IsSameType
<Target
, Source
>::value
|| (sizeof(Target
) > sizeof(Source
)) > struct BoundsCheckElider
;
164 template <typename Target
, typename Source
> struct BoundsCheckElider
<Target
, Source
, true> {
165 static bool inBounds(Source
) { return true; }
167 template <typename Target
, typename Source
> struct BoundsCheckElider
<Target
, Source
, false> : public BoundsChecker
<Target
, Source
> {
170 template <typename Target
, typename Source
> static inline bool isInBounds(Source value
)
172 return BoundsCheckElider
<Target
, Source
>::inBounds(value
);
175 template <typename T
> struct RemoveChecked
{
177 static const CleanType DefaultValue
= 0;
180 template <typename T
> struct RemoveChecked
<Checked
<T
, CrashOnOverflow
>> {
181 typedef typename RemoveChecked
<T
>::CleanType CleanType
;
182 static const CleanType DefaultValue
= 0;
185 template <typename T
> struct RemoveChecked
<Checked
<T
, RecordOverflow
>> {
186 typedef typename RemoveChecked
<T
>::CleanType CleanType
;
187 static const CleanType DefaultValue
= 0;
190 // The ResultBase and SignednessSelector are used to workaround typeof not being
192 template <typename U
, typename V
, bool uIsBigger
= (sizeof(U
) > sizeof(V
)), bool sameSize
= (sizeof(U
) == sizeof(V
))> struct ResultBase
;
193 template <typename U
, typename V
> struct ResultBase
<U
, V
, true, false> {
194 typedef U ResultType
;
197 template <typename U
, typename V
> struct ResultBase
<U
, V
, false, false> {
198 typedef V ResultType
;
201 template <typename U
> struct ResultBase
<U
, U
, false, true> {
202 typedef U ResultType
;
205 template <typename U
, typename V
, bool uIsSigned
= std::numeric_limits
<U
>::is_signed
, bool vIsSigned
= std::numeric_limits
<V
>::is_signed
> struct SignednessSelector
;
206 template <typename U
, typename V
> struct SignednessSelector
<U
, V
, true, true> {
207 typedef U ResultType
;
210 template <typename U
, typename V
> struct SignednessSelector
<U
, V
, false, false> {
211 typedef U ResultType
;
214 template <typename U
, typename V
> struct SignednessSelector
<U
, V
, true, false> {
215 typedef V ResultType
;
218 template <typename U
, typename V
> struct SignednessSelector
<U
, V
, false, true> {
219 typedef U ResultType
;
222 template <typename U
, typename V
> struct ResultBase
<U
, V
, false, true> {
223 typedef typename SignednessSelector
<U
, V
>::ResultType ResultType
;
226 template <typename U
, typename V
> struct Result
: ResultBase
<typename RemoveChecked
<U
>::CleanType
, typename RemoveChecked
<V
>::CleanType
> {
229 template <typename LHS
, typename RHS
, typename ResultType
= typename Result
<LHS
, RHS
>::ResultType
,
230 bool lhsSigned
= std::numeric_limits
<LHS
>::is_signed
, bool rhsSigned
= std::numeric_limits
<RHS
>::is_signed
> struct ArithmeticOperations
;
232 template <typename LHS
, typename RHS
, typename ResultType
> struct ArithmeticOperations
<LHS
, RHS
, ResultType
, true, true> {
233 // LHS and RHS are signed types
236 static inline bool signsMatch(LHS lhs
, RHS rhs
)
238 return (lhs
^ rhs
) >= 0;
241 static inline bool add(LHS lhs
, RHS rhs
, ResultType
& result
) WARN_UNUSED_RETURN
243 if (signsMatch(lhs
, rhs
)) {
245 if ((std::numeric_limits
<ResultType
>::max() - rhs
) < lhs
)
248 ResultType temp
= lhs
- std::numeric_limits
<ResultType
>::min();
252 } // if the signs do not match this operation can't overflow
253 result
= static_cast<ResultType
>(lhs
+ rhs
);
257 static inline bool sub(LHS lhs
, RHS rhs
, ResultType
& result
) WARN_UNUSED_RETURN
259 if (!signsMatch(lhs
, rhs
)) {
261 if (lhs
> std::numeric_limits
<ResultType
>::max() + rhs
)
264 if (rhs
> std::numeric_limits
<ResultType
>::max() + lhs
)
267 } // if the signs match this operation can't overflow
268 result
= static_cast<ResultType
>(lhs
- rhs
);
272 static inline bool multiply(LHS lhs
, RHS rhs
, ResultType
& result
) WARN_UNUSED_RETURN
274 if (signsMatch(lhs
, rhs
)) {
276 if (lhs
&& (std::numeric_limits
<ResultType
>::max() / lhs
) < rhs
)
279 if (static_cast<ResultType
>(lhs
) == std::numeric_limits
<ResultType
>::min() || static_cast<ResultType
>(rhs
) == std::numeric_limits
<ResultType
>::min())
281 if ((std::numeric_limits
<ResultType
>::max() / -lhs
) < -rhs
)
286 if (rhs
&& lhs
< (std::numeric_limits
<ResultType
>::min() / rhs
))
289 if (lhs
&& rhs
< (std::numeric_limits
<ResultType
>::min() / lhs
))
293 result
= static_cast<ResultType
>(lhs
* rhs
);
297 static inline bool equals(LHS lhs
, RHS rhs
) { return lhs
== rhs
; }
301 template <typename LHS
, typename RHS
, typename ResultType
> struct ArithmeticOperations
<LHS
, RHS
, ResultType
, false, false> {
302 // LHS and RHS are unsigned types so bounds checks are nice and easy
303 static inline bool add(LHS lhs
, RHS rhs
, ResultType
& result
) WARN_UNUSED_RETURN
305 ResultType temp
= lhs
+ rhs
;
312 static inline bool sub(LHS lhs
, RHS rhs
, ResultType
& result
) WARN_UNUSED_RETURN
314 ResultType temp
= lhs
- rhs
;
321 static inline bool multiply(LHS lhs
, RHS rhs
, ResultType
& result
) WARN_UNUSED_RETURN
327 if (std::numeric_limits
<ResultType
>::max() / lhs
< rhs
)
333 static inline bool equals(LHS lhs
, RHS rhs
) { return lhs
== rhs
; }
337 template <typename ResultType
> struct ArithmeticOperations
<int, unsigned, ResultType
, true, false> {
338 static inline bool add(int64_t lhs
, int64_t rhs
, ResultType
& result
)
340 int64_t temp
= lhs
+ rhs
;
341 if (temp
< std::numeric_limits
<ResultType
>::min())
343 if (temp
> std::numeric_limits
<ResultType
>::max())
345 result
= static_cast<ResultType
>(temp
);
349 static inline bool sub(int64_t lhs
, int64_t rhs
, ResultType
& result
)
351 int64_t temp
= lhs
- rhs
;
352 if (temp
< std::numeric_limits
<ResultType
>::min())
354 if (temp
> std::numeric_limits
<ResultType
>::max())
356 result
= static_cast<ResultType
>(temp
);
360 static inline bool multiply(int64_t lhs
, int64_t rhs
, ResultType
& result
)
362 int64_t temp
= lhs
* rhs
;
363 if (temp
< std::numeric_limits
<ResultType
>::min())
365 if (temp
> std::numeric_limits
<ResultType
>::max())
367 result
= static_cast<ResultType
>(temp
);
371 static inline bool equals(int lhs
, unsigned rhs
)
373 return static_cast<int64_t>(lhs
) == static_cast<int64_t>(rhs
);
377 template <typename ResultType
> struct ArithmeticOperations
<unsigned, int, ResultType
, false, true> {
378 static inline bool add(int64_t lhs
, int64_t rhs
, ResultType
& result
)
380 return ArithmeticOperations
<int, unsigned, ResultType
>::add(rhs
, lhs
, result
);
383 static inline bool sub(int64_t lhs
, int64_t rhs
, ResultType
& result
)
385 return ArithmeticOperations
<int, unsigned, ResultType
>::sub(lhs
, rhs
, result
);
388 static inline bool multiply(int64_t lhs
, int64_t rhs
, ResultType
& result
)
390 return ArithmeticOperations
<int, unsigned, ResultType
>::multiply(rhs
, lhs
, result
);
393 static inline bool equals(unsigned lhs
, int rhs
)
395 return ArithmeticOperations
<int, unsigned, ResultType
>::equals(rhs
, lhs
);
399 template <typename U
, typename V
, typename R
> static inline bool safeAdd(U lhs
, V rhs
, R
& result
)
401 return ArithmeticOperations
<U
, V
, R
>::add(lhs
, rhs
, result
);
404 template <typename U
, typename V
, typename R
> static inline bool safeSub(U lhs
, V rhs
, R
& result
)
406 return ArithmeticOperations
<U
, V
, R
>::sub(lhs
, rhs
, result
);
409 template <typename U
, typename V
, typename R
> static inline bool safeMultiply(U lhs
, V rhs
, R
& result
)
411 return ArithmeticOperations
<U
, V
, R
>::multiply(lhs
, rhs
, result
);
414 template <typename U
, typename V
> static inline bool safeEquals(U lhs
, V rhs
)
416 return ArithmeticOperations
<U
, V
>::equals(lhs
, rhs
);
419 enum ResultOverflowedTag
{ ResultOverflowed
};
421 template <typename T
, class OverflowHandler
> class Checked
: public OverflowHandler
{
423 template <typename _T
, class _OverflowHandler
> friend class Checked
;
429 Checked(ResultOverflowedTag
)
435 template <typename U
> Checked(U value
)
437 if (!isInBounds
<T
>(value
))
439 m_value
= static_cast<T
>(value
);
442 template <typename V
> Checked(const Checked
<T
, V
>& rhs
)
443 : m_value(rhs
.m_value
)
445 if (rhs
.hasOverflowed())
449 template <typename U
> Checked(const Checked
<U
, OverflowHandler
>& rhs
)
450 : OverflowHandler(rhs
)
452 if (!isInBounds
<T
>(rhs
.m_value
))
454 m_value
= static_cast<T
>(rhs
.m_value
);
457 template <typename U
, typename V
> Checked(const Checked
<U
, V
>& rhs
)
459 if (rhs
.hasOverflowed())
461 if (!isInBounds
<T
>(rhs
.m_value
))
463 m_value
= static_cast<T
>(rhs
.m_value
);
466 const Checked
& operator=(Checked rhs
)
468 this->clearOverflow();
469 if (rhs
.hasOverflowed())
471 m_value
= static_cast<T
>(rhs
.m_value
);
475 template <typename U
> const Checked
& operator=(U value
)
477 return *this = Checked(value
);
480 template <typename U
, typename V
> const Checked
& operator=(const Checked
<U
, V
>& rhs
)
482 return *this = Checked(rhs
);
486 const Checked
& operator++()
488 if (m_value
== std::numeric_limits
<T
>::max())
494 const Checked
& operator--()
496 if (m_value
== std::numeric_limits
<T
>::min())
503 const Checked
operator++(int)
505 if (m_value
== std::numeric_limits
<T
>::max())
507 return Checked(m_value
++);
510 const Checked
operator--(int)
512 if (m_value
== std::numeric_limits
<T
>::min())
514 return Checked(m_value
--);
518 bool operator!() const
520 if (this->hasOverflowed())
525 typedef void* (Checked::*UnspecifiedBoolType
);
526 operator UnspecifiedBoolType
*() const
528 if (this->hasOverflowed())
530 return (m_value
) ? reinterpret_cast<UnspecifiedBoolType
*>(1) : 0;
533 // Value accessors. unsafeGet() will crash if there's been an overflow.
536 if (this->hasOverflowed())
541 inline CheckedState
safeGet(T
& value
) const WARN_UNUSED_RETURN
544 if (this->hasOverflowed())
545 return CheckedState::DidOverflow
;
546 return CheckedState::DidNotOverflow
;
549 // Mutating assignment
550 template <typename U
> const Checked
operator+=(U rhs
)
552 if (!safeAdd(m_value
, rhs
, m_value
))
557 template <typename U
> const Checked
operator-=(U rhs
)
559 if (!safeSub(m_value
, rhs
, m_value
))
564 template <typename U
> const Checked
operator*=(U rhs
)
566 if (!safeMultiply(m_value
, rhs
, m_value
))
571 const Checked
operator*=(double rhs
)
573 double result
= rhs
* m_value
;
574 // Handle +/- infinity and NaN
575 if (!(std::numeric_limits
<T
>::min() <= result
&& std::numeric_limits
<T
>::max() >= result
))
581 const Checked
operator*=(float rhs
)
583 return *this *= (double)rhs
;
586 template <typename U
, typename V
> const Checked
operator+=(Checked
<U
, V
> rhs
)
588 if (rhs
.hasOverflowed())
590 return *this += rhs
.m_value
;
593 template <typename U
, typename V
> const Checked
operator-=(Checked
<U
, V
> rhs
)
595 if (rhs
.hasOverflowed())
597 return *this -= rhs
.m_value
;
600 template <typename U
, typename V
> const Checked
operator*=(Checked
<U
, V
> rhs
)
602 if (rhs
.hasOverflowed())
604 return *this *= rhs
.m_value
;
607 // Equality comparisons
608 template <typename V
> bool operator==(Checked
<T
, V
> rhs
)
610 return unsafeGet() == rhs
.unsafeGet();
613 template <typename U
> bool operator==(U rhs
)
615 if (this->hasOverflowed())
617 return safeEquals(m_value
, rhs
);
620 template <typename U
, typename V
> const Checked
operator==(Checked
<U
, V
> rhs
)
622 return unsafeGet() == Checked(rhs
.unsafeGet());
625 template <typename U
> bool operator!=(U rhs
)
627 return !(*this == rhs
);
631 // Disallow implicit conversion of floating point to integer types
634 void operator=(float);
635 void operator=(double);
636 void operator+=(float);
637 void operator+=(double);
638 void operator-=(float);
639 void operator-=(double);
643 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator+(Checked
<U
, OverflowHandler
> lhs
, Checked
<V
, OverflowHandler
> rhs
)
647 bool overflowed
= lhs
.safeGet(x
) == CheckedState::DidOverflow
|| rhs
.safeGet(y
) == CheckedState::DidOverflow
;
648 typename Result
<U
, V
>::ResultType result
= 0;
649 overflowed
|= !safeAdd(x
, y
, result
);
651 return ResultOverflowed
;
655 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator-(Checked
<U
, OverflowHandler
> lhs
, Checked
<V
, OverflowHandler
> rhs
)
659 bool overflowed
= lhs
.safeGet(x
) == CheckedState::DidOverflow
|| rhs
.safeGet(y
) == CheckedState::DidOverflow
;
660 typename Result
<U
, V
>::ResultType result
= 0;
661 overflowed
|= !safeSub(x
, y
, result
);
663 return ResultOverflowed
;
667 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator*(Checked
<U
, OverflowHandler
> lhs
, Checked
<V
, OverflowHandler
> rhs
)
671 bool overflowed
= lhs
.safeGet(x
) == CheckedState::DidOverflow
|| rhs
.safeGet(y
) == CheckedState::DidOverflow
;
672 typename Result
<U
, V
>::ResultType result
= 0;
673 overflowed
|= !safeMultiply(x
, y
, result
);
675 return ResultOverflowed
;
679 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator+(Checked
<U
, OverflowHandler
> lhs
, V rhs
)
681 return lhs
+ Checked
<V
, OverflowHandler
>(rhs
);
684 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator-(Checked
<U
, OverflowHandler
> lhs
, V rhs
)
686 return lhs
- Checked
<V
, OverflowHandler
>(rhs
);
689 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator*(Checked
<U
, OverflowHandler
> lhs
, V rhs
)
691 return lhs
* Checked
<V
, OverflowHandler
>(rhs
);
694 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator+(U lhs
, Checked
<V
, OverflowHandler
> rhs
)
696 return Checked
<U
, OverflowHandler
>(lhs
) + rhs
;
699 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator-(U lhs
, Checked
<V
, OverflowHandler
> rhs
)
701 return Checked
<U
, OverflowHandler
>(lhs
) - rhs
;
704 template <typename U
, typename V
, typename OverflowHandler
> static inline Checked
<typename Result
<U
, V
>::ResultType
, OverflowHandler
> operator*(U lhs
, Checked
<V
, OverflowHandler
> rhs
)
706 return Checked
<U
, OverflowHandler
>(lhs
) * rhs
;
712 using WTF::CheckedState
;
713 using WTF::RecordOverflow
;