1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* Provides checked integers, detecting integer overflow and divide-by-0. */
9 #ifndef mozilla_CheckedInt_h
10 #define mozilla_CheckedInt_h
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/IntegerTypeTraits.h"
18 #define MOZILLA_CHECKEDINT_COMPARABLE_VERSION(major, minor, patch) \
19 (major << 16 | minor << 8 | patch)
21 // Probe for builtin math overflow support. Disabled for 32-bit builds for now
22 // since "gcc -m32" claims to support these but its implementation is buggy.
23 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82274
24 // Also disabled for clang before version 7 (resp. Xcode clang 10.0.1): while
25 // clang 5 and 6 have a working __builtin_add_overflow, it is not constexpr.
26 #if defined(HAVE_64BIT_BUILD)
27 # if defined(__has_builtin) && \
28 (!defined(__clang_major__) || \
29 (!defined(__apple_build_version__) && __clang_major__ >= 7) || \
30 (defined(__apple_build_version__) && \
31 MOZILLA_CHECKEDINT_COMPARABLE_VERSION( \
32 __clang_major__, __clang_minor__, __clang_patchlevel__) >= \
33 MOZILLA_CHECKEDINT_COMPARABLE_VERSION(10, 0, 1)))
34 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__has_builtin(__builtin_add_overflow))
35 # elif defined(__GNUC__)
36 // (clang also defines __GNUC__ but it supports __has_builtin since at least
37 // v3.1 (released in 2012) so it won't get here.)
38 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__GNUC__ >= 5)
40 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
43 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
46 #undef MOZILLA_CHECKEDINT_COMPARABLE_VERSION
56 * Step 1: manually record supported types
58 * What's nontrivial here is that there are different families of integer
59 * types: basic integer types and stdint types. It is merrily undefined which
60 * types from one family may be just typedefs for a type from another family.
62 * For example, on GCC 4.6, aside from the basic integer types, the only other
63 * type that isn't just a typedef for some of them, is int8_t.
66 struct UnsupportedType
{};
68 template <typename IntegerType
>
69 struct IsSupportedPass2
{
70 static const bool value
= false;
73 template <typename IntegerType
>
75 static const bool value
= IsSupportedPass2
<IntegerType
>::value
;
79 struct IsSupported
<int8_t> {
80 static const bool value
= true;
84 struct IsSupported
<uint8_t> {
85 static const bool value
= true;
89 struct IsSupported
<int16_t> {
90 static const bool value
= true;
94 struct IsSupported
<uint16_t> {
95 static const bool value
= true;
99 struct IsSupported
<int32_t> {
100 static const bool value
= true;
104 struct IsSupported
<uint32_t> {
105 static const bool value
= true;
109 struct IsSupported
<int64_t> {
110 static const bool value
= true;
114 struct IsSupported
<uint64_t> {
115 static const bool value
= true;
119 struct IsSupportedPass2
<char> {
120 static const bool value
= true;
124 struct IsSupportedPass2
<signed char> {
125 static const bool value
= true;
129 struct IsSupportedPass2
<unsigned char> {
130 static const bool value
= true;
134 struct IsSupportedPass2
<short> {
135 static const bool value
= true;
139 struct IsSupportedPass2
<unsigned short> {
140 static const bool value
= true;
144 struct IsSupportedPass2
<int> {
145 static const bool value
= true;
149 struct IsSupportedPass2
<unsigned int> {
150 static const bool value
= true;
154 struct IsSupportedPass2
<long> {
155 static const bool value
= true;
159 struct IsSupportedPass2
<unsigned long> {
160 static const bool value
= true;
164 struct IsSupportedPass2
<long long> {
165 static const bool value
= true;
169 struct IsSupportedPass2
<unsigned long long> {
170 static const bool value
= true;
174 * Step 2: Implement the actual validity checks.
176 * Ideas taken from IntegerLib, code different.
179 template <typename IntegerType
, size_t Size
= sizeof(IntegerType
)>
180 struct TwiceBiggerType
{
181 typedef typename
detail::StdintTypeForSizeAndSignedness
<
182 sizeof(IntegerType
) * 2, IsSigned
<IntegerType
>::value
>::Type Type
;
185 template <typename IntegerType
>
186 struct TwiceBiggerType
<IntegerType
, 8> {
187 typedef UnsupportedType Type
;
190 template <typename T
>
191 constexpr bool HasSignBit(T aX
) {
192 // In C++, right bit shifts on negative values is undefined by the standard.
193 // Notice that signed-to-unsigned conversions are always well-defined in the
194 // standard, as the value congruent modulo 2**n as expected. By contrast,
195 // unsigned-to-signed is only well-defined if the value is representable.
196 return bool(typename MakeUnsigned
<T
>::Type(aX
) >>
197 PositionOfSignBit
<T
>::value
);
200 // Bitwise ops may return a larger type, so it's good to use this inline
201 // helper guaranteeing that the result is really of type T.
202 template <typename T
>
203 constexpr T
BinaryComplement(T aX
) {
207 template <typename T
, typename U
, bool IsTSigned
= IsSigned
<T
>::value
,
208 bool IsUSigned
= IsSigned
<U
>::value
>
209 struct DoesRangeContainRange
{};
211 template <typename T
, typename U
, bool Signedness
>
212 struct DoesRangeContainRange
<T
, U
, Signedness
, Signedness
> {
213 static const bool value
= sizeof(T
) >= sizeof(U
);
216 template <typename T
, typename U
>
217 struct DoesRangeContainRange
<T
, U
, true, false> {
218 static const bool value
= sizeof(T
) > sizeof(U
);
221 template <typename T
, typename U
>
222 struct DoesRangeContainRange
<T
, U
, false, true> {
223 static const bool value
= false;
226 template <typename T
, typename U
, bool IsTSigned
= IsSigned
<T
>::value
,
227 bool IsUSigned
= IsSigned
<U
>::value
,
228 bool DoesTRangeContainURange
= DoesRangeContainRange
<T
, U
>::value
>
229 struct IsInRangeImpl
{};
231 template <typename T
, typename U
, bool IsTSigned
, bool IsUSigned
>
232 struct IsInRangeImpl
<T
, U
, IsTSigned
, IsUSigned
, true> {
233 static constexpr bool run(U
) { return true; }
236 template <typename T
, typename U
>
237 struct IsInRangeImpl
<T
, U
, true, true, false> {
238 static constexpr bool run(U aX
) {
239 return aX
<= std::numeric_limits
<T
>::max() &&
240 aX
>= std::numeric_limits
<T
>::min();
244 template <typename T
, typename U
>
245 struct IsInRangeImpl
<T
, U
, false, false, false> {
246 static constexpr bool run(U aX
) {
247 return aX
<= std::numeric_limits
<T
>::max();
251 template <typename T
, typename U
>
252 struct IsInRangeImpl
<T
, U
, true, false, false> {
253 static constexpr bool run(U aX
) {
254 return sizeof(T
) > sizeof(U
) || aX
<= U(std::numeric_limits
<T
>::max());
258 template <typename T
, typename U
>
259 struct IsInRangeImpl
<T
, U
, false, true, false> {
260 static constexpr bool run(U aX
) {
261 return sizeof(T
) >= sizeof(U
)
263 : aX
>= 0 && aX
<= U(std::numeric_limits
<T
>::max());
267 template <typename T
, typename U
>
268 constexpr bool IsInRange(U aX
) {
269 return IsInRangeImpl
<T
, U
>::run(aX
);
272 template <typename T
>
273 constexpr bool IsAddValid(T aX
, T aY
) {
274 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
276 return !__builtin_add_overflow(aX
, aY
, &dummy
);
278 // Addition is valid if the sign of aX+aY is equal to either that of aX or
279 // that of aY. Since the value of aX+aY is undefined if we have a signed
280 // type, we compute it using the unsigned type of the same size. Beware!
281 // These bitwise operations can return a larger integer type, if T was a
282 // small type like int8_t, so we explicitly cast to T.
284 typename MakeUnsigned
<T
>::Type ux
= aX
;
285 typename MakeUnsigned
<T
>::Type uy
= aY
;
286 typename MakeUnsigned
<T
>::Type result
= ux
+ uy
;
287 return IsSigned
<T
>::value
288 ? HasSignBit(BinaryComplement(T((result
^ aX
) & (result
^ aY
))))
289 : BinaryComplement(aX
) >= aY
;
293 template <typename T
>
294 constexpr bool IsSubValid(T aX
, T aY
) {
295 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
297 return !__builtin_sub_overflow(aX
, aY
, &dummy
);
299 // Subtraction is valid if either aX and aY have same sign, or aX-aY and aX
300 // have same sign. Since the value of aX-aY is undefined if we have a signed
301 // type, we compute it using the unsigned type of the same size.
302 typename MakeUnsigned
<T
>::Type ux
= aX
;
303 typename MakeUnsigned
<T
>::Type uy
= aY
;
304 typename MakeUnsigned
<T
>::Type result
= ux
- uy
;
306 return IsSigned
<T
>::value
307 ? HasSignBit(BinaryComplement(T((result
^ aX
) & (aX
^ aY
))))
312 template <typename T
, bool IsTSigned
= IsSigned
<T
>::value
,
313 bool TwiceBiggerTypeIsSupported
=
314 IsSupported
<typename TwiceBiggerType
<T
>::Type
>::value
>
315 struct IsMulValidImpl
{};
317 template <typename T
, bool IsTSigned
>
318 struct IsMulValidImpl
<T
, IsTSigned
, true> {
319 static constexpr bool run(T aX
, T aY
) {
320 typedef typename TwiceBiggerType
<T
>::Type TwiceBiggerType
;
321 TwiceBiggerType product
= TwiceBiggerType(aX
) * TwiceBiggerType(aY
);
322 return IsInRange
<T
>(product
);
326 template <typename T
>
327 struct IsMulValidImpl
<T
, true, false> {
328 static constexpr bool run(T aX
, T aY
) {
329 const T max
= std::numeric_limits
<T
>::max();
330 const T min
= std::numeric_limits
<T
>::min();
332 if (aX
== 0 || aY
== 0) {
336 return aY
> 0 ? aX
<= max
/ aY
: aY
>= min
/ aX
;
339 // If we reach this point, we know that aX < 0.
340 return aY
> 0 ? aX
>= min
/ aY
: aY
>= max
/ aX
;
344 template <typename T
>
345 struct IsMulValidImpl
<T
, false, false> {
346 static constexpr bool run(T aX
, T aY
) {
347 return aY
== 0 || aX
<= std::numeric_limits
<T
>::max() / aY
;
351 template <typename T
>
352 inline bool IsMulValid(T aX
, T aY
) {
353 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
355 return !__builtin_mul_overflow(aX
, aY
, &dummy
);
357 return IsMulValidImpl
<T
>::run(aX
, aY
);
361 template <typename T
>
362 constexpr bool IsDivValid(T aX
, T aY
) {
363 // Keep in mind that in the signed case, min/-1 is invalid because
365 return aY
!= 0 && !(IsSigned
<T
>::value
&&
366 aX
== std::numeric_limits
<T
>::min() && aY
== T(-1));
369 template <typename T
, bool IsTSigned
= IsSigned
<T
>::value
>
370 struct IsModValidImpl
;
372 template <typename T
>
373 constexpr bool IsModValid(T aX
, T aY
) {
374 return IsModValidImpl
<T
>::run(aX
, aY
);
378 * Mod is pretty simple.
379 * For now, let's just use the ANSI C definition:
380 * If aX or aY are negative, the results are implementation defined.
381 * Consider these invalid.
382 * Undefined for aY=0.
383 * The result will never exceed either aX or aY.
385 * Checking that aX>=0 is a warning when T is unsigned.
388 template <typename T
>
389 struct IsModValidImpl
<T
, false> {
390 static constexpr bool run(T aX
, T aY
) { return aY
>= 1; }
393 template <typename T
>
394 struct IsModValidImpl
<T
, true> {
395 static constexpr bool run(T aX
, T aY
) {
403 template <typename T
, bool IsSigned
= IsSigned
<T
>::value
>
406 template <typename T
>
407 struct NegateImpl
<T
, false> {
408 static constexpr CheckedInt
<T
> negate(const CheckedInt
<T
>& aVal
) {
409 // Handle negation separately for signed/unsigned, for simpler code and to
410 // avoid an MSVC warning negating an unsigned value.
411 return CheckedInt
<T
>(0, aVal
.isValid() && aVal
.mValue
== 0);
415 template <typename T
>
416 struct NegateImpl
<T
, true> {
417 static constexpr CheckedInt
<T
> negate(const CheckedInt
<T
>& aVal
) {
418 // Watch out for the min-value, which (with twos-complement) can't be
419 // negated as -min-value is then (max-value + 1).
420 if (!aVal
.isValid() || aVal
.mValue
== std::numeric_limits
<T
>::min()) {
421 return CheckedInt
<T
>(aVal
.mValue
, false);
423 return CheckedInt
<T
>(-aVal
.mValue
, true);
427 } // namespace detail
430 * Step 3: Now define the CheckedInt class.
435 * @brief Integer wrapper class checking for integer overflow and other errors
436 * @param T the integer type to wrap. Can be any type among the following:
437 * - any basic integer type such as |int|
438 * - any stdint type such as |int8_t|
440 * This class implements guarded integer arithmetic. Do a computation, check
441 * that isValid() returns true, you then have a guarantee that no problem, such
442 * as integer overflow, happened during this computation, and you can call
443 * value() to get the plain integer value.
445 * The arithmetic operators in this class are guaranteed not to raise a signal
446 * (e.g. in case of a division by zero).
448 * For example, suppose that you want to implement a function that computes
449 * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
450 * zero or integer overflow). You could code it as follows:
452 bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult)
454 CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
455 if (checkedResult.isValid()) {
456 *aResult = checkedResult.value();
464 * Implicit conversion from plain integers to checked integers is allowed. The
465 * plain integer is checked to be in range before being casted to the
466 * destination type. This means that the following lines all compile, and the
467 * resulting CheckedInts are correctly detected as valid or invalid:
469 // 1 is of type int, is found to be in range for uint8_t, x is valid
470 CheckedInt<uint8_t> x(1);
471 // -1 is of type int, is found not to be in range for uint8_t, x is invalid
472 CheckedInt<uint8_t> x(-1);
473 // -1 is of type int, is found to be in range for int8_t, x is valid
474 CheckedInt<int8_t> x(-1);
475 // 1000 is of type int16_t, is found not to be in range for int8_t,
477 CheckedInt<int8_t> x(int16_t(1000));
478 // 3123456789 is of type uint32_t, is found not to be in range for int32_t,
480 CheckedInt<int32_t> x(uint32_t(3123456789));
482 * Implicit conversion from
483 * checked integers to plain integers is not allowed. As shown in the
484 * above example, to get the value of a checked integer as a normal integer,
487 * Arithmetic operations between checked and plain integers is allowed; the
488 * result type is the type of the checked integer.
490 * Checked integers of different types cannot be used in the same arithmetic
493 * There are convenience typedefs for all stdint types, of the following form
494 * (these are just 2 examples):
496 typedef CheckedInt<int32_t> CheckedInt32;
497 typedef CheckedInt<uint16_t> CheckedUint16;
500 template <typename T
>
506 template <typename U
>
507 constexpr CheckedInt(U aValue
, bool aIsValid
)
508 : mValue(aValue
), mIsValid(aIsValid
) {
510 detail::IsSupported
<T
>::value
&& detail::IsSupported
<U
>::value
,
511 "This type is not supported by CheckedInt");
514 friend struct detail::NegateImpl
<T
>;
518 * Constructs a checked integer with given @a value. The checked integer is
519 * initialized as valid or invalid depending on whether the @a value
522 * This constructor is not explicit. Instead, the type of its argument is a
523 * separate template parameter, ensuring that no conversion is performed
524 * before this constructor is actually called. As explained in the above
525 * documentation for class CheckedInt, this constructor checks that its
528 template <typename U
>
529 MOZ_IMPLICIT MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT
constexpr CheckedInt(U aValue
)
530 : mValue(T(aValue
)), mIsValid(detail::IsInRange
<T
>(aValue
)) {
532 detail::IsSupported
<T
>::value
&& detail::IsSupported
<U
>::value
,
533 "This type is not supported by CheckedInt");
536 template <typename U
>
537 friend class CheckedInt
;
539 template <typename U
>
540 constexpr CheckedInt
<U
> toChecked() const {
541 CheckedInt
<U
> ret(mValue
);
542 ret
.mIsValid
= ret
.mIsValid
&& mIsValid
;
546 /** Constructs a valid checked integer with initial value 0 */
547 constexpr CheckedInt() : mValue(0), mIsValid(true) {
548 static_assert(detail::IsSupported
<T
>::value
,
549 "This type is not supported by CheckedInt");
552 /** @returns the actual value */
553 constexpr T
value() const {
554 MOZ_DIAGNOSTIC_ASSERT(
556 "Invalid checked integer (division by zero or integer overflow)");
561 * @returns true if the checked integer is valid, i.e. is not the result
562 * of an invalid operation or of an operation involving an invalid checked
565 constexpr bool isValid() const { return mIsValid
; }
567 template <typename U
>
568 friend constexpr CheckedInt
<U
> operator+(const CheckedInt
<U
>& aLhs
,
569 const CheckedInt
<U
>& aRhs
);
570 template <typename U
>
571 constexpr CheckedInt
& operator+=(U aRhs
);
572 constexpr CheckedInt
& operator+=(const CheckedInt
<T
>& aRhs
);
574 template <typename U
>
575 friend constexpr CheckedInt
<U
> operator-(const CheckedInt
<U
>& aLhs
,
576 const CheckedInt
<U
>& aRhs
);
577 template <typename U
>
578 constexpr CheckedInt
& operator-=(U aRhs
);
579 constexpr CheckedInt
& operator-=(const CheckedInt
<T
>& aRhs
);
581 template <typename U
>
582 friend constexpr CheckedInt
<U
> operator*(const CheckedInt
<U
>& aLhs
,
583 const CheckedInt
<U
>& aRhs
);
584 template <typename U
>
585 constexpr CheckedInt
& operator*=(U aRhs
);
586 constexpr CheckedInt
& operator*=(const CheckedInt
<T
>& aRhs
);
588 template <typename U
>
589 friend constexpr CheckedInt
<U
> operator/(const CheckedInt
<U
>& aLhs
,
590 const CheckedInt
<U
>& aRhs
);
591 template <typename U
>
592 constexpr CheckedInt
& operator/=(U aRhs
);
593 constexpr CheckedInt
& operator/=(const CheckedInt
<T
>& aRhs
);
595 template <typename U
>
596 friend constexpr CheckedInt
<U
> operator%(const CheckedInt
<U
>& aLhs
,
597 const CheckedInt
<U
>& aRhs
);
598 template <typename U
>
599 constexpr CheckedInt
& operator%=(U aRhs
);
600 constexpr CheckedInt
& operator%=(const CheckedInt
<T
>& aRhs
);
602 constexpr CheckedInt
operator-() const {
603 return detail::NegateImpl
<T
>::negate(*this);
607 * @returns true if the left and right hand sides are valid
608 * and have the same value.
610 * Note that these semantics are the reason why we don't offer
611 * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
612 * but that would mean that whenever a or b is invalid, a!=b
613 * is always true, which would be very confusing.
615 * For similar reasons, operators <, >, <=, >= would be very tricky to
616 * specify, so we just avoid offering them.
618 * Notice that these == semantics are made more reasonable by these facts:
619 * 1. a==b implies equality at the raw data level
620 * (the converse is false, as a==b is never true among invalids)
621 * 2. This is similar to the behavior of IEEE floats, where a==b
622 * means that a and b have the same value *and* neither is NaN.
624 constexpr bool operator==(const CheckedInt
& aOther
) const {
625 return mIsValid
&& aOther
.mIsValid
&& mValue
== aOther
.mValue
;
629 constexpr CheckedInt
& operator++() {
635 constexpr CheckedInt
operator++(int) {
636 CheckedInt tmp
= *this;
642 constexpr CheckedInt
& operator--() {
648 constexpr CheckedInt
operator--(int) {
649 CheckedInt tmp
= *this;
656 * The !=, <, <=, >, >= operators are disabled:
657 * see the comment on operator==.
659 template <typename U
>
660 bool operator!=(U aOther
) const = delete;
661 template <typename U
>
662 bool operator<(U aOther
) const = delete;
663 template <typename U
>
664 bool operator<=(U aOther
) const = delete;
665 template <typename U
>
666 bool operator>(U aOther
) const = delete;
667 template <typename U
>
668 bool operator>=(U aOther
) const = delete;
671 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
672 template <typename T> \
673 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \
674 const CheckedInt<T>& aRhs) { \
675 if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
676 return CheckedInt<T>(0, false); \
678 return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \
679 aLhs.mIsValid && aRhs.mIsValid); \
682 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
683 # define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(NAME, OP, FUN) \
684 template <typename T> \
685 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \
686 const CheckedInt<T>& aRhs) { \
688 if (FUN(aLhs.mValue, aRhs.mValue, &result)) { \
689 return CheckedInt<T>(0, false); \
691 return CheckedInt<T>(result, aLhs.mIsValid && aRhs.mIsValid); \
693 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add
, +, __builtin_add_overflow
)
694 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub
, -, __builtin_sub_overflow
)
695 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul
, *, __builtin_mul_overflow
)
696 # undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2
698 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add
, +)
699 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub
, -)
700 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul
, *)
703 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div
, /)
704 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod
, %)
705 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
707 // Implement castToCheckedInt<T>(x), making sure that
708 // - it allows x to be either a CheckedInt<T> or any integer type
709 // that can be casted to T
710 // - if x is already a CheckedInt<T>, we just return a reference to it,
711 // instead of copying it (optimization)
715 template <typename T
, typename U
>
716 struct CastToCheckedIntImpl
{
717 typedef CheckedInt
<T
> ReturnType
;
718 static constexpr CheckedInt
<T
> run(U aU
) { return aU
; }
721 template <typename T
>
722 struct CastToCheckedIntImpl
<T
, CheckedInt
<T
> > {
723 typedef const CheckedInt
<T
>& ReturnType
;
724 static constexpr const CheckedInt
<T
>& run(const CheckedInt
<T
>& aU
) {
729 } // namespace detail
731 template <typename T
, typename U
>
732 constexpr typename
detail::CastToCheckedIntImpl
<T
, U
>::ReturnType
733 castToCheckedInt(U aU
) {
734 static_assert(detail::IsSupported
<T
>::value
&& detail::IsSupported
<U
>::value
,
735 "This type is not supported by CheckedInt");
736 return detail::CastToCheckedIntImpl
<T
, U
>::run(aU
);
739 #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
740 template <typename T> \
741 template <typename U> \
742 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) { \
743 *this = *this OP castToCheckedInt<T>(aRhs); \
746 template <typename T> \
747 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP( \
748 const CheckedInt<T>& aRhs) { \
749 *this = *this OP aRhs; \
752 template <typename T, typename U> \
753 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) { \
754 return aLhs OP castToCheckedInt<T>(aRhs); \
756 template <typename T, typename U> \
757 constexpr CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) { \
758 return castToCheckedInt<T>(aLhs) OP aRhs; \
761 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
762 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
763 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
764 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
765 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
767 #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
769 template <typename T
, typename U
>
770 inline bool operator==(const CheckedInt
<T
>& aLhs
, U aRhs
) {
771 return aLhs
== castToCheckedInt
<T
>(aRhs
);
774 template <typename T
, typename U
>
775 inline bool operator==(U aLhs
, const CheckedInt
<T
>& aRhs
) {
776 return castToCheckedInt
<T
>(aLhs
) == aRhs
;
779 // Convenience typedefs.
780 typedef CheckedInt
<int8_t> CheckedInt8
;
781 typedef CheckedInt
<uint8_t> CheckedUint8
;
782 typedef CheckedInt
<int16_t> CheckedInt16
;
783 typedef CheckedInt
<uint16_t> CheckedUint16
;
784 typedef CheckedInt
<int32_t> CheckedInt32
;
785 typedef CheckedInt
<uint32_t> CheckedUint32
;
786 typedef CheckedInt
<int64_t> CheckedInt64
;
787 typedef CheckedInt
<uint64_t> CheckedUint64
;
789 } // namespace mozilla
791 #endif /* mozilla_CheckedInt_h */