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"
17 #define MOZILLA_CHECKEDINT_COMPARABLE_VERSION(major, minor, patch) \
18 (major << 16 | minor << 8 | patch)
20 // Probe for builtin math overflow support. Disabled for 32-bit builds for now
21 // since "gcc -m32" claims to support these but its implementation is buggy.
22 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82274
23 // Also disabled for clang before version 7 (resp. Xcode clang 10.0.1): while
24 // clang 5 and 6 have a working __builtin_add_overflow, it is not constexpr.
25 #if defined(HAVE_64BIT_BUILD)
26 # if defined(__has_builtin) && \
27 (!defined(__clang_major__) || \
28 (!defined(__apple_build_version__) && __clang_major__ >= 7) || \
29 (defined(__apple_build_version__) && \
30 MOZILLA_CHECKEDINT_COMPARABLE_VERSION( \
31 __clang_major__, __clang_minor__, __clang_patchlevel__) >= \
32 MOZILLA_CHECKEDINT_COMPARABLE_VERSION(10, 0, 1)))
33 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__has_builtin(__builtin_add_overflow))
34 # elif defined(__GNUC__)
35 // (clang also defines __GNUC__ but it supports __has_builtin since at least
36 // v3.1 (released in 2012) so it won't get here.)
37 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__GNUC__ >= 5)
39 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
42 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
45 #undef MOZILLA_CHECKEDINT_COMPARABLE_VERSION
55 * Step 1: manually record supported types
57 * What's nontrivial here is that there are different families of integer
58 * types: basic integer types and stdint types. It is merrily undefined which
59 * types from one family may be just typedefs for a type from another family.
61 * For example, on GCC 4.6, aside from the basic integer types, the only other
62 * type that isn't just a typedef for some of them, is int8_t.
65 struct UnsupportedType
{};
67 template <typename IntegerType
>
68 struct IsSupportedPass2
{
69 static const bool value
= false;
72 template <typename IntegerType
>
74 static const bool value
= IsSupportedPass2
<IntegerType
>::value
;
78 struct IsSupported
<int8_t> {
79 static const bool value
= true;
83 struct IsSupported
<uint8_t> {
84 static const bool value
= true;
88 struct IsSupported
<int16_t> {
89 static const bool value
= true;
93 struct IsSupported
<uint16_t> {
94 static const bool value
= true;
98 struct IsSupported
<int32_t> {
99 static const bool value
= true;
103 struct IsSupported
<uint32_t> {
104 static const bool value
= true;
108 struct IsSupported
<int64_t> {
109 static const bool value
= true;
113 struct IsSupported
<uint64_t> {
114 static const bool value
= true;
118 struct IsSupportedPass2
<char> {
119 static const bool value
= true;
123 struct IsSupportedPass2
<signed char> {
124 static const bool value
= true;
128 struct IsSupportedPass2
<unsigned char> {
129 static const bool value
= true;
133 struct IsSupportedPass2
<short> {
134 static const bool value
= true;
138 struct IsSupportedPass2
<unsigned short> {
139 static const bool value
= true;
143 struct IsSupportedPass2
<int> {
144 static const bool value
= true;
148 struct IsSupportedPass2
<unsigned int> {
149 static const bool value
= true;
153 struct IsSupportedPass2
<long> {
154 static const bool value
= true;
158 struct IsSupportedPass2
<unsigned long> {
159 static const bool value
= true;
163 struct IsSupportedPass2
<long long> {
164 static const bool value
= true;
168 struct IsSupportedPass2
<unsigned long long> {
169 static const bool value
= true;
173 * Step 2: Implement the actual validity checks.
175 * Ideas taken from IntegerLib, code different.
178 template <typename IntegerType
, size_t Size
= sizeof(IntegerType
)>
179 struct TwiceBiggerType
{
180 typedef typename
detail::StdintTypeForSizeAndSignedness
<
181 sizeof(IntegerType
) * 2, IsSigned
<IntegerType
>::value
>::Type Type
;
184 template <typename IntegerType
>
185 struct TwiceBiggerType
<IntegerType
, 8> {
186 typedef UnsupportedType Type
;
189 template <typename T
>
190 constexpr bool HasSignBit(T aX
) {
191 // In C++, right bit shifts on negative values is undefined by the standard.
192 // Notice that signed-to-unsigned conversions are always well-defined in the
193 // standard, as the value congruent modulo 2**n as expected. By contrast,
194 // unsigned-to-signed is only well-defined if the value is representable.
195 return bool(typename MakeUnsigned
<T
>::Type(aX
) >>
196 PositionOfSignBit
<T
>::value
);
199 // Bitwise ops may return a larger type, so it's good to use this inline
200 // helper guaranteeing that the result is really of type T.
201 template <typename T
>
202 constexpr T
BinaryComplement(T aX
) {
206 template <typename T
, typename U
, bool IsTSigned
= IsSigned
<T
>::value
,
207 bool IsUSigned
= IsSigned
<U
>::value
>
208 struct DoesRangeContainRange
{};
210 template <typename T
, typename U
, bool Signedness
>
211 struct DoesRangeContainRange
<T
, U
, Signedness
, Signedness
> {
212 static const bool value
= sizeof(T
) >= sizeof(U
);
215 template <typename T
, typename U
>
216 struct DoesRangeContainRange
<T
, U
, true, false> {
217 static const bool value
= sizeof(T
) > sizeof(U
);
220 template <typename T
, typename U
>
221 struct DoesRangeContainRange
<T
, U
, false, true> {
222 static const bool value
= false;
225 template <typename T
, typename U
, bool IsTSigned
= IsSigned
<T
>::value
,
226 bool IsUSigned
= IsSigned
<U
>::value
,
227 bool DoesTRangeContainURange
= DoesRangeContainRange
<T
, U
>::value
>
228 struct IsInRangeImpl
{};
230 template <typename T
, typename U
, bool IsTSigned
, bool IsUSigned
>
231 struct IsInRangeImpl
<T
, U
, IsTSigned
, IsUSigned
, true> {
232 static constexpr bool run(U
) { return true; }
235 template <typename T
, typename U
>
236 struct IsInRangeImpl
<T
, U
, true, true, false> {
237 static constexpr bool run(U aX
) {
238 return aX
<= MaxValue
<T
>::value
&& aX
>= MinValue
<T
>::value
;
242 template <typename T
, typename U
>
243 struct IsInRangeImpl
<T
, U
, false, false, false> {
244 static constexpr bool run(U aX
) { return aX
<= MaxValue
<T
>::value
; }
247 template <typename T
, typename U
>
248 struct IsInRangeImpl
<T
, U
, true, false, false> {
249 static constexpr bool run(U aX
) {
250 return sizeof(T
) > sizeof(U
) || aX
<= U(MaxValue
<T
>::value
);
254 template <typename T
, typename U
>
255 struct IsInRangeImpl
<T
, U
, false, true, false> {
256 static constexpr bool run(U aX
) {
257 return sizeof(T
) >= sizeof(U
) ? aX
>= 0
258 : aX
>= 0 && aX
<= U(MaxValue
<T
>::value
);
262 template <typename T
, typename U
>
263 constexpr bool IsInRange(U aX
) {
264 return IsInRangeImpl
<T
, U
>::run(aX
);
267 template <typename T
>
268 constexpr bool IsAddValid(T aX
, T aY
) {
269 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
271 return !__builtin_add_overflow(aX
, aY
, &dummy
);
273 // Addition is valid if the sign of aX+aY is equal to either that of aX or
274 // that of aY. Since the value of aX+aY is undefined if we have a signed
275 // type, we compute it using the unsigned type of the same size. Beware!
276 // These bitwise operations can return a larger integer type, if T was a
277 // small type like int8_t, so we explicitly cast to T.
279 typename MakeUnsigned
<T
>::Type ux
= aX
;
280 typename MakeUnsigned
<T
>::Type uy
= aY
;
281 typename MakeUnsigned
<T
>::Type result
= ux
+ uy
;
282 return IsSigned
<T
>::value
283 ? HasSignBit(BinaryComplement(T((result
^ aX
) & (result
^ aY
))))
284 : BinaryComplement(aX
) >= aY
;
288 template <typename T
>
289 constexpr bool IsSubValid(T aX
, T aY
) {
290 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
292 return !__builtin_sub_overflow(aX
, aY
, &dummy
);
294 // Subtraction is valid if either aX and aY have same sign, or aX-aY and aX
295 // have same sign. Since the value of aX-aY is undefined if we have a signed
296 // type, we compute it using the unsigned type of the same size.
297 typename MakeUnsigned
<T
>::Type ux
= aX
;
298 typename MakeUnsigned
<T
>::Type uy
= aY
;
299 typename MakeUnsigned
<T
>::Type result
= ux
- uy
;
301 return IsSigned
<T
>::value
302 ? HasSignBit(BinaryComplement(T((result
^ aX
) & (aX
^ aY
))))
307 template <typename T
, bool IsTSigned
= IsSigned
<T
>::value
,
308 bool TwiceBiggerTypeIsSupported
=
309 IsSupported
<typename TwiceBiggerType
<T
>::Type
>::value
>
310 struct IsMulValidImpl
{};
312 template <typename T
, bool IsTSigned
>
313 struct IsMulValidImpl
<T
, IsTSigned
, true> {
314 static constexpr bool run(T aX
, T aY
) {
315 typedef typename TwiceBiggerType
<T
>::Type TwiceBiggerType
;
316 TwiceBiggerType product
= TwiceBiggerType(aX
) * TwiceBiggerType(aY
);
317 return IsInRange
<T
>(product
);
321 template <typename T
>
322 struct IsMulValidImpl
<T
, true, false> {
323 static constexpr bool run(T aX
, T aY
) {
324 const T max
= MaxValue
<T
>::value
;
325 const T min
= MinValue
<T
>::value
;
327 if (aX
== 0 || aY
== 0) {
331 return aY
> 0 ? aX
<= max
/ aY
: aY
>= min
/ aX
;
334 // If we reach this point, we know that aX < 0.
335 return aY
> 0 ? aX
>= min
/ aY
: aY
>= max
/ aX
;
339 template <typename T
>
340 struct IsMulValidImpl
<T
, false, false> {
341 static constexpr bool run(T aX
, T aY
) {
342 return aY
== 0 || aX
<= MaxValue
<T
>::value
/ aY
;
346 template <typename T
>
347 inline bool IsMulValid(T aX
, T aY
) {
348 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
350 return !__builtin_mul_overflow(aX
, aY
, &dummy
);
352 return IsMulValidImpl
<T
>::run(aX
, aY
);
356 template <typename T
>
357 constexpr bool IsDivValid(T aX
, T aY
) {
358 // Keep in mind that in the signed case, min/-1 is invalid because
361 !(IsSigned
<T
>::value
&& aX
== MinValue
<T
>::value
&& aY
== T(-1));
364 template <typename T
, bool IsTSigned
= IsSigned
<T
>::value
>
365 struct IsModValidImpl
;
367 template <typename T
>
368 constexpr bool IsModValid(T aX
, T aY
) {
369 return IsModValidImpl
<T
>::run(aX
, aY
);
373 * Mod is pretty simple.
374 * For now, let's just use the ANSI C definition:
375 * If aX or aY are negative, the results are implementation defined.
376 * Consider these invalid.
377 * Undefined for aY=0.
378 * The result will never exceed either aX or aY.
380 * Checking that aX>=0 is a warning when T is unsigned.
383 template <typename T
>
384 struct IsModValidImpl
<T
, false> {
385 static constexpr bool run(T aX
, T aY
) { return aY
>= 1; }
388 template <typename T
>
389 struct IsModValidImpl
<T
, true> {
390 static constexpr bool run(T aX
, T aY
) {
398 template <typename T
, bool IsSigned
= IsSigned
<T
>::value
>
401 template <typename T
>
402 struct NegateImpl
<T
, false> {
403 static constexpr CheckedInt
<T
> negate(const CheckedInt
<T
>& aVal
) {
404 // Handle negation separately for signed/unsigned, for simpler code and to
405 // avoid an MSVC warning negating an unsigned value.
406 return CheckedInt
<T
>(0, aVal
.isValid() && aVal
.mValue
== 0);
410 template <typename T
>
411 struct NegateImpl
<T
, true> {
412 static constexpr CheckedInt
<T
> negate(const CheckedInt
<T
>& aVal
) {
413 // Watch out for the min-value, which (with twos-complement) can't be
414 // negated as -min-value is then (max-value + 1).
415 if (!aVal
.isValid() || aVal
.mValue
== MinValue
<T
>::value
) {
416 return CheckedInt
<T
>(aVal
.mValue
, false);
418 return CheckedInt
<T
>(-aVal
.mValue
, true);
422 } // namespace detail
425 * Step 3: Now define the CheckedInt class.
430 * @brief Integer wrapper class checking for integer overflow and other errors
431 * @param T the integer type to wrap. Can be any type among the following:
432 * - any basic integer type such as |int|
433 * - any stdint type such as |int8_t|
435 * This class implements guarded integer arithmetic. Do a computation, check
436 * that isValid() returns true, you then have a guarantee that no problem, such
437 * as integer overflow, happened during this computation, and you can call
438 * value() to get the plain integer value.
440 * The arithmetic operators in this class are guaranteed not to raise a signal
441 * (e.g. in case of a division by zero).
443 * For example, suppose that you want to implement a function that computes
444 * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
445 * zero or integer overflow). You could code it as follows:
447 bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult)
449 CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
450 if (checkedResult.isValid()) {
451 *aResult = checkedResult.value();
459 * Implicit conversion from plain integers to checked integers is allowed. The
460 * plain integer is checked to be in range before being casted to the
461 * destination type. This means that the following lines all compile, and the
462 * resulting CheckedInts are correctly detected as valid or invalid:
464 // 1 is of type int, is found to be in range for uint8_t, x is valid
465 CheckedInt<uint8_t> x(1);
466 // -1 is of type int, is found not to be in range for uint8_t, x is invalid
467 CheckedInt<uint8_t> x(-1);
468 // -1 is of type int, is found to be in range for int8_t, x is valid
469 CheckedInt<int8_t> x(-1);
470 // 1000 is of type int16_t, is found not to be in range for int8_t,
472 CheckedInt<int8_t> x(int16_t(1000));
473 // 3123456789 is of type uint32_t, is found not to be in range for int32_t,
475 CheckedInt<int32_t> x(uint32_t(3123456789));
477 * Implicit conversion from
478 * checked integers to plain integers is not allowed. As shown in the
479 * above example, to get the value of a checked integer as a normal integer,
482 * Arithmetic operations between checked and plain integers is allowed; the
483 * result type is the type of the checked integer.
485 * Checked integers of different types cannot be used in the same arithmetic
488 * There are convenience typedefs for all stdint types, of the following form
489 * (these are just 2 examples):
491 typedef CheckedInt<int32_t> CheckedInt32;
492 typedef CheckedInt<uint16_t> CheckedUint16;
495 template <typename T
>
501 template <typename U
>
502 constexpr CheckedInt(U aValue
, bool aIsValid
)
503 : mValue(aValue
), mIsValid(aIsValid
) {
505 detail::IsSupported
<T
>::value
&& detail::IsSupported
<U
>::value
,
506 "This type is not supported by CheckedInt");
509 friend struct detail::NegateImpl
<T
>;
513 * Constructs a checked integer with given @a value. The checked integer is
514 * initialized as valid or invalid depending on whether the @a value
517 * This constructor is not explicit. Instead, the type of its argument is a
518 * separate template parameter, ensuring that no conversion is performed
519 * before this constructor is actually called. As explained in the above
520 * documentation for class CheckedInt, this constructor checks that its
523 template <typename U
>
524 MOZ_IMPLICIT MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT
constexpr CheckedInt(U aValue
)
525 : mValue(T(aValue
)), mIsValid(detail::IsInRange
<T
>(aValue
)) {
527 detail::IsSupported
<T
>::value
&& detail::IsSupported
<U
>::value
,
528 "This type is not supported by CheckedInt");
531 template <typename U
>
532 friend class CheckedInt
;
534 template <typename U
>
535 constexpr CheckedInt
<U
> toChecked() const {
536 CheckedInt
<U
> ret(mValue
);
537 ret
.mIsValid
= ret
.mIsValid
&& mIsValid
;
541 /** Constructs a valid checked integer with initial value 0 */
542 constexpr CheckedInt() : mValue(0), mIsValid(true) {
543 static_assert(detail::IsSupported
<T
>::value
,
544 "This type is not supported by CheckedInt");
547 /** @returns the actual value */
548 constexpr T
value() const {
549 MOZ_DIAGNOSTIC_ASSERT(
551 "Invalid checked integer (division by zero or integer overflow)");
556 * @returns true if the checked integer is valid, i.e. is not the result
557 * of an invalid operation or of an operation involving an invalid checked
560 constexpr bool isValid() const { return mIsValid
; }
562 template <typename U
>
563 friend constexpr CheckedInt
<U
> operator+(const CheckedInt
<U
>& aLhs
,
564 const CheckedInt
<U
>& aRhs
);
565 template <typename U
>
566 constexpr CheckedInt
& operator+=(U aRhs
);
567 constexpr CheckedInt
& operator+=(const CheckedInt
<T
>& aRhs
);
569 template <typename U
>
570 friend constexpr CheckedInt
<U
> operator-(const CheckedInt
<U
>& aLhs
,
571 const CheckedInt
<U
>& aRhs
);
572 template <typename U
>
573 constexpr CheckedInt
& operator-=(U aRhs
);
574 constexpr CheckedInt
& operator-=(const CheckedInt
<T
>& aRhs
);
576 template <typename U
>
577 friend constexpr CheckedInt
<U
> operator*(const CheckedInt
<U
>& aLhs
,
578 const CheckedInt
<U
>& aRhs
);
579 template <typename U
>
580 constexpr CheckedInt
& operator*=(U aRhs
);
581 constexpr CheckedInt
& operator*=(const CheckedInt
<T
>& aRhs
);
583 template <typename U
>
584 friend constexpr CheckedInt
<U
> operator/(const CheckedInt
<U
>& aLhs
,
585 const CheckedInt
<U
>& aRhs
);
586 template <typename U
>
587 constexpr CheckedInt
& operator/=(U aRhs
);
588 constexpr CheckedInt
& operator/=(const CheckedInt
<T
>& aRhs
);
590 template <typename U
>
591 friend constexpr CheckedInt
<U
> operator%(const CheckedInt
<U
>& aLhs
,
592 const CheckedInt
<U
>& aRhs
);
593 template <typename U
>
594 constexpr CheckedInt
& operator%=(U aRhs
);
595 constexpr CheckedInt
& operator%=(const CheckedInt
<T
>& aRhs
);
597 constexpr CheckedInt
operator-() const {
598 return detail::NegateImpl
<T
>::negate(*this);
602 * @returns true if the left and right hand sides are valid
603 * and have the same value.
605 * Note that these semantics are the reason why we don't offer
606 * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
607 * but that would mean that whenever a or b is invalid, a!=b
608 * is always true, which would be very confusing.
610 * For similar reasons, operators <, >, <=, >= would be very tricky to
611 * specify, so we just avoid offering them.
613 * Notice that these == semantics are made more reasonable by these facts:
614 * 1. a==b implies equality at the raw data level
615 * (the converse is false, as a==b is never true among invalids)
616 * 2. This is similar to the behavior of IEEE floats, where a==b
617 * means that a and b have the same value *and* neither is NaN.
619 constexpr bool operator==(const CheckedInt
& aOther
) const {
620 return mIsValid
&& aOther
.mIsValid
&& mValue
== aOther
.mValue
;
624 constexpr CheckedInt
& operator++() {
630 constexpr CheckedInt
operator++(int) {
631 CheckedInt tmp
= *this;
637 constexpr CheckedInt
& operator--() {
643 constexpr CheckedInt
operator--(int) {
644 CheckedInt tmp
= *this;
651 * The !=, <, <=, >, >= operators are disabled:
652 * see the comment on operator==.
654 template <typename U
>
655 bool operator!=(U aOther
) const = delete;
656 template <typename U
>
657 bool operator<(U aOther
) const = delete;
658 template <typename U
>
659 bool operator<=(U aOther
) const = delete;
660 template <typename U
>
661 bool operator>(U aOther
) const = delete;
662 template <typename U
>
663 bool operator>=(U aOther
) const = delete;
666 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
667 template <typename T> \
668 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \
669 const CheckedInt<T>& aRhs) { \
670 if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
671 return CheckedInt<T>(0, false); \
673 return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \
674 aLhs.mIsValid && aRhs.mIsValid); \
677 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
678 # define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(NAME, OP, FUN) \
679 template <typename T> \
680 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \
681 const CheckedInt<T>& aRhs) { \
683 if (FUN(aLhs.mValue, aRhs.mValue, &result)) { \
684 return CheckedInt<T>(0, false); \
686 return CheckedInt<T>(result, aLhs.mIsValid && aRhs.mIsValid); \
688 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add
, +, __builtin_add_overflow
)
689 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub
, -, __builtin_sub_overflow
)
690 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul
, *, __builtin_mul_overflow
)
691 # undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2
693 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add
, +)
694 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub
, -)
695 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul
, *)
698 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div
, /)
699 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod
, %)
700 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
702 // Implement castToCheckedInt<T>(x), making sure that
703 // - it allows x to be either a CheckedInt<T> or any integer type
704 // that can be casted to T
705 // - if x is already a CheckedInt<T>, we just return a reference to it,
706 // instead of copying it (optimization)
710 template <typename T
, typename U
>
711 struct CastToCheckedIntImpl
{
712 typedef CheckedInt
<T
> ReturnType
;
713 static constexpr CheckedInt
<T
> run(U aU
) { return aU
; }
716 template <typename T
>
717 struct CastToCheckedIntImpl
<T
, CheckedInt
<T
> > {
718 typedef const CheckedInt
<T
>& ReturnType
;
719 static constexpr const CheckedInt
<T
>& run(const CheckedInt
<T
>& aU
) {
724 } // namespace detail
726 template <typename T
, typename U
>
727 constexpr typename
detail::CastToCheckedIntImpl
<T
, U
>::ReturnType
728 castToCheckedInt(U aU
) {
729 static_assert(detail::IsSupported
<T
>::value
&& detail::IsSupported
<U
>::value
,
730 "This type is not supported by CheckedInt");
731 return detail::CastToCheckedIntImpl
<T
, U
>::run(aU
);
734 #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
735 template <typename T> \
736 template <typename U> \
737 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) { \
738 *this = *this OP castToCheckedInt<T>(aRhs); \
741 template <typename T> \
742 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP( \
743 const CheckedInt<T>& aRhs) { \
744 *this = *this OP aRhs; \
747 template <typename T, typename U> \
748 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) { \
749 return aLhs OP castToCheckedInt<T>(aRhs); \
751 template <typename T, typename U> \
752 constexpr CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) { \
753 return castToCheckedInt<T>(aLhs) OP aRhs; \
756 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
757 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
758 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
759 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
760 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
762 #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
764 template <typename T
, typename U
>
765 inline bool operator==(const CheckedInt
<T
>& aLhs
, U aRhs
) {
766 return aLhs
== castToCheckedInt
<T
>(aRhs
);
769 template <typename T
, typename U
>
770 inline bool operator==(U aLhs
, const CheckedInt
<T
>& aRhs
) {
771 return castToCheckedInt
<T
>(aLhs
) == aRhs
;
774 // Convenience typedefs.
775 typedef CheckedInt
<int8_t> CheckedInt8
;
776 typedef CheckedInt
<uint8_t> CheckedUint8
;
777 typedef CheckedInt
<int16_t> CheckedInt16
;
778 typedef CheckedInt
<uint16_t> CheckedUint16
;
779 typedef CheckedInt
<int32_t> CheckedInt32
;
780 typedef CheckedInt
<uint32_t> CheckedUint32
;
781 typedef CheckedInt
<int64_t> CheckedInt64
;
782 typedef CheckedInt
<uint64_t> CheckedUint64
;
784 } // namespace mozilla
786 #endif /* mozilla_CheckedInt_h */