Bug 1606095 [wpt PR 20924] - [LayoutInstability] Fix flaky tests, a=testonly
[gecko.git] / mfbt / CheckedInt.h
blob8b3f6352acc35084074a81a08e6e58cff86a0529
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
12 #include <stdint.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)
38 # else
39 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
40 # endif
41 #else
42 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
43 #endif
45 #undef MOZILLA_CHECKEDINT_COMPARABLE_VERSION
47 namespace mozilla {
49 template <typename T>
50 class CheckedInt;
52 namespace detail {
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>
73 struct IsSupported {
74 static const bool value = IsSupportedPass2<IntegerType>::value;
77 template <>
78 struct IsSupported<int8_t> {
79 static const bool value = true;
82 template <>
83 struct IsSupported<uint8_t> {
84 static const bool value = true;
87 template <>
88 struct IsSupported<int16_t> {
89 static const bool value = true;
92 template <>
93 struct IsSupported<uint16_t> {
94 static const bool value = true;
97 template <>
98 struct IsSupported<int32_t> {
99 static const bool value = true;
102 template <>
103 struct IsSupported<uint32_t> {
104 static const bool value = true;
107 template <>
108 struct IsSupported<int64_t> {
109 static const bool value = true;
112 template <>
113 struct IsSupported<uint64_t> {
114 static const bool value = true;
117 template <>
118 struct IsSupportedPass2<char> {
119 static const bool value = true;
122 template <>
123 struct IsSupportedPass2<signed char> {
124 static const bool value = true;
127 template <>
128 struct IsSupportedPass2<unsigned char> {
129 static const bool value = true;
132 template <>
133 struct IsSupportedPass2<short> {
134 static const bool value = true;
137 template <>
138 struct IsSupportedPass2<unsigned short> {
139 static const bool value = true;
142 template <>
143 struct IsSupportedPass2<int> {
144 static const bool value = true;
147 template <>
148 struct IsSupportedPass2<unsigned int> {
149 static const bool value = true;
152 template <>
153 struct IsSupportedPass2<long> {
154 static const bool value = true;
157 template <>
158 struct IsSupportedPass2<unsigned long> {
159 static const bool value = true;
162 template <>
163 struct IsSupportedPass2<long long> {
164 static const bool value = true;
167 template <>
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) {
203 return ~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
270 T dummy;
271 return !__builtin_add_overflow(aX, aY, &dummy);
272 #else
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;
285 #endif
288 template <typename T>
289 constexpr bool IsSubValid(T aX, T aY) {
290 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
291 T dummy;
292 return !__builtin_sub_overflow(aX, aY, &dummy);
293 #else
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))))
303 : aX >= aY;
304 #endif
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) {
328 return true;
330 if (aX > 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
349 T dummy;
350 return !__builtin_mul_overflow(aX, aY, &dummy);
351 #else
352 return IsMulValidImpl<T>::run(aX, aY);
353 #endif
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
359 // abs(min)>max.
360 return aY != 0 &&
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) {
391 if (aX < 0) {
392 return false;
394 return aY >= 1;
398 template <typename T, bool IsSigned = IsSigned<T>::value>
399 struct NegateImpl;
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.
429 * @class CheckedInt
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:
446 @code
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();
452 return true;
453 } else {
454 return false;
457 @endcode
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:
463 * @code
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,
471 // x is invalid
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,
474 // x is invalid
475 CheckedInt<int32_t> x(uint32_t(3123456789));
476 * @endcode
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,
480 * call value().
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
486 * expression.
488 * There are convenience typedefs for all stdint types, of the following form
489 * (these are just 2 examples):
490 @code
491 typedef CheckedInt<int32_t> CheckedInt32;
492 typedef CheckedInt<uint16_t> CheckedUint16;
493 @endcode
495 template <typename T>
496 class CheckedInt {
497 protected:
498 T mValue;
499 bool mIsValid;
501 template <typename U>
502 constexpr CheckedInt(U aValue, bool aIsValid)
503 : mValue(aValue), mIsValid(aIsValid) {
504 static_assert(
505 detail::IsSupported<T>::value && detail::IsSupported<U>::value,
506 "This type is not supported by CheckedInt");
509 friend struct detail::NegateImpl<T>;
511 public:
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
515 * is in range.
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
521 * argument is valid.
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)) {
526 static_assert(
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;
538 return ret;
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(
550 mIsValid,
551 "Invalid checked integer (division by zero or integer overflow)");
552 return mValue;
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
558 * integer
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;
623 /** prefix ++ */
624 constexpr CheckedInt& operator++() {
625 *this += 1;
626 return *this;
629 /** postfix ++ */
630 constexpr CheckedInt operator++(int) {
631 CheckedInt tmp = *this;
632 *this += 1;
633 return tmp;
636 /** prefix -- */
637 constexpr CheckedInt& operator--() {
638 *this -= 1;
639 return *this;
642 /** postfix -- */
643 constexpr CheckedInt operator--(int) {
644 CheckedInt tmp = *this;
645 *this -= 1;
646 return tmp;
649 private:
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) { \
682 auto result = T{}; \
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
692 #else
693 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
694 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
695 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *)
696 #endif
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)
708 namespace detail {
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) {
720 return 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); \
739 return *this; \
741 template <typename T> \
742 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP( \
743 const CheckedInt<T>& aRhs) { \
744 *this = *this OP aRhs; \
745 return *this; \
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 */