no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / mfbt / CheckedInt.h
blobd784376d8cf5f4e339a4fd87a844011b57fced79
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"
16 #include <limits>
17 #include <type_traits>
19 #define MOZILLA_CHECKEDINT_COMPARABLE_VERSION(major, minor, patch) \
20 (major << 16 | minor << 8 | patch)
22 // Probe for builtin math overflow support. Disabled for 32-bit builds for now
23 // since "gcc -m32" claims to support these but its implementation is buggy.
24 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82274
25 // Also disabled for clang before version 7 (resp. Xcode clang 10.0.1): while
26 // clang 5 and 6 have a working __builtin_add_overflow, it is not constexpr.
27 #if defined(HAVE_64BIT_BUILD)
28 # if defined(__has_builtin) && \
29 (!defined(__clang_major__) || \
30 (!defined(__apple_build_version__) && __clang_major__ >= 7) || \
31 (defined(__apple_build_version__) && \
32 MOZILLA_CHECKEDINT_COMPARABLE_VERSION( \
33 __clang_major__, __clang_minor__, __clang_patchlevel__) >= \
34 MOZILLA_CHECKEDINT_COMPARABLE_VERSION(10, 0, 1)))
35 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__has_builtin(__builtin_add_overflow))
36 # elif defined(__GNUC__)
37 // (clang also defines __GNUC__ but it supports __has_builtin since at least
38 // v3.1 (released in 2012) so it won't get here.)
39 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__GNUC__ >= 5)
40 # else
41 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
42 # endif
43 #else
44 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0)
45 #endif
47 #undef MOZILLA_CHECKEDINT_COMPARABLE_VERSION
49 namespace mozilla {
51 template <typename T>
52 class CheckedInt;
54 namespace detail {
57 * Step 1: manually record supported types
59 * What's nontrivial here is that there are different families of integer
60 * types: basic integer types and stdint types. It is merrily undefined which
61 * types from one family may be just typedefs for a type from another family.
63 * For example, on GCC 4.6, aside from the basic integer types, the only other
64 * type that isn't just a typedef for some of them, is int8_t.
67 struct UnsupportedType {};
69 template <typename IntegerType>
70 struct IsSupportedPass2 {
71 static const bool value = false;
74 template <typename IntegerType>
75 struct IsSupported {
76 static const bool value = IsSupportedPass2<IntegerType>::value;
79 template <>
80 struct IsSupported<int8_t> {
81 static const bool value = true;
84 template <>
85 struct IsSupported<uint8_t> {
86 static const bool value = true;
89 template <>
90 struct IsSupported<int16_t> {
91 static const bool value = true;
94 template <>
95 struct IsSupported<uint16_t> {
96 static const bool value = true;
99 template <>
100 struct IsSupported<int32_t> {
101 static const bool value = true;
104 template <>
105 struct IsSupported<uint32_t> {
106 static const bool value = true;
109 template <>
110 struct IsSupported<int64_t> {
111 static const bool value = true;
114 template <>
115 struct IsSupported<uint64_t> {
116 static const bool value = true;
119 template <>
120 struct IsSupportedPass2<char> {
121 static const bool value = true;
124 template <>
125 struct IsSupportedPass2<signed char> {
126 static const bool value = true;
129 template <>
130 struct IsSupportedPass2<unsigned char> {
131 static const bool value = true;
134 template <>
135 struct IsSupportedPass2<short> {
136 static const bool value = true;
139 template <>
140 struct IsSupportedPass2<unsigned short> {
141 static const bool value = true;
144 template <>
145 struct IsSupportedPass2<int> {
146 static const bool value = true;
149 template <>
150 struct IsSupportedPass2<unsigned int> {
151 static const bool value = true;
154 template <>
155 struct IsSupportedPass2<long> {
156 static const bool value = true;
159 template <>
160 struct IsSupportedPass2<unsigned long> {
161 static const bool value = true;
164 template <>
165 struct IsSupportedPass2<long long> {
166 static const bool value = true;
169 template <>
170 struct IsSupportedPass2<unsigned long long> {
171 static const bool value = true;
175 * Step 2: Implement the actual validity checks.
177 * Ideas taken from IntegerLib, code different.
180 template <typename IntegerType, size_t Size = sizeof(IntegerType)>
181 struct TwiceBiggerType {
182 typedef typename detail::StdintTypeForSizeAndSignedness<
183 sizeof(IntegerType) * 2, std::is_signed_v<IntegerType>>::Type Type;
186 template <typename IntegerType>
187 struct TwiceBiggerType<IntegerType, 8> {
188 typedef UnsupportedType Type;
191 template <typename T>
192 constexpr bool HasSignBit(T aX) {
193 // In C++, right bit shifts on negative values is undefined by the standard.
194 // Notice that signed-to-unsigned conversions are always well-defined in the
195 // standard, as the value congruent modulo 2**n as expected. By contrast,
196 // unsigned-to-signed is only well-defined if the value is representable.
197 return bool(std::make_unsigned_t<T>(aX) >> 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) {
204 return ~aX;
207 template <typename T, typename U, bool IsTSigned = std::is_signed_v<T>,
208 bool IsUSigned = std::is_signed_v<U>>
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 = std::is_signed_v<T>,
227 bool IsUSigned = std::is_signed_v<U>,
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)
262 ? aX >= 0
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
275 T dummy;
276 return !__builtin_add_overflow(aX, aY, &dummy);
277 #else
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 std::make_unsigned_t<T> ux = aX;
285 std::make_unsigned_t<T> uy = aY;
286 std::make_unsigned_t<T> result = ux + uy;
287 return std::is_signed_v<T>
288 ? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY))))
289 : BinaryComplement(aX) >= aY;
290 #endif
293 template <typename T>
294 constexpr bool IsSubValid(T aX, T aY) {
295 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
296 T dummy;
297 return !__builtin_sub_overflow(aX, aY, &dummy);
298 #else
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 std::make_unsigned_t<T> ux = aX;
303 std::make_unsigned_t<T> uy = aY;
304 std::make_unsigned_t<T> result = ux - uy;
306 return std::is_signed_v<T>
307 ? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY))))
308 : aX >= aY;
309 #endif
312 template <typename T, bool IsTSigned = std::is_signed_v<T>,
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) {
333 return true;
335 if (aX > 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 constexpr bool IsMulValid(T aX, T aY) {
353 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
354 T dummy;
355 return !__builtin_mul_overflow(aX, aY, &dummy);
356 #else
357 return IsMulValidImpl<T>::run(aX, aY);
358 #endif
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
364 // abs(min)>max.
365 return aY != 0 && !(std::is_signed_v<T> &&
366 aX == std::numeric_limits<T>::min() && aY == T(-1));
369 template <typename T, bool IsTSigned = std::is_signed_v<T>>
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) {
396 if (aX < 0) {
397 return false;
399 return aY >= 1;
403 template <typename T, bool IsSigned = std::is_signed_v<T>>
404 struct NegateImpl;
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 static_assert(detail::IsInRange<T>(0), "Integer type can't represent 0");
412 return CheckedInt<T>(T(0), aVal.isValid() && aVal.mValue == 0);
416 template <typename T>
417 struct NegateImpl<T, true> {
418 static constexpr CheckedInt<T> negate(const CheckedInt<T>& aVal) {
419 // Watch out for the min-value, which (with twos-complement) can't be
420 // negated as -min-value is then (max-value + 1).
421 if (!aVal.isValid() || aVal.mValue == std::numeric_limits<T>::min()) {
422 return CheckedInt<T>(aVal.mValue, false);
424 /* For some T, arithmetic ops automatically promote to a wider type, so
425 * explitly do the narrowing cast here. The narrowing cast is valid because
426 * we did the check for min value above. */
427 return CheckedInt<T>(T(-aVal.mValue), true);
431 } // namespace detail
434 * Step 3: Now define the CheckedInt class.
438 * @class CheckedInt
439 * @brief Integer wrapper class checking for integer overflow and other errors
440 * @param T the integer type to wrap. Can be any type among the following:
441 * - any basic integer type such as |int|
442 * - any stdint type such as |int8_t|
444 * This class implements guarded integer arithmetic. Do a computation, check
445 * that isValid() returns true, you then have a guarantee that no problem, such
446 * as integer overflow, happened during this computation, and you can call
447 * value() to get the plain integer value.
449 * The arithmetic operators in this class are guaranteed not to raise a signal
450 * (e.g. in case of a division by zero).
452 * For example, suppose that you want to implement a function that computes
453 * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
454 * zero or integer overflow). You could code it as follows:
455 @code
456 bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult)
458 CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
459 if (checkedResult.isValid()) {
460 *aResult = checkedResult.value();
461 return true;
462 } else {
463 return false;
466 @endcode
468 * Implicit conversion from plain integers to checked integers is allowed. The
469 * plain integer is checked to be in range before being casted to the
470 * destination type. This means that the following lines all compile, and the
471 * resulting CheckedInts are correctly detected as valid or invalid:
472 * @code
473 // 1 is of type int, is found to be in range for uint8_t, x is valid
474 CheckedInt<uint8_t> x(1);
475 // -1 is of type int, is found not to be in range for uint8_t, x is invalid
476 CheckedInt<uint8_t> x(-1);
477 // -1 is of type int, is found to be in range for int8_t, x is valid
478 CheckedInt<int8_t> x(-1);
479 // 1000 is of type int16_t, is found not to be in range for int8_t,
480 // x is invalid
481 CheckedInt<int8_t> x(int16_t(1000));
482 // 3123456789 is of type uint32_t, is found not to be in range for int32_t,
483 // x is invalid
484 CheckedInt<int32_t> x(uint32_t(3123456789));
485 * @endcode
486 * Implicit conversion from
487 * checked integers to plain integers is not allowed. As shown in the
488 * above example, to get the value of a checked integer as a normal integer,
489 * call value().
491 * Arithmetic operations between checked and plain integers is allowed; the
492 * result type is the type of the checked integer.
494 * Checked integers of different types cannot be used in the same arithmetic
495 * expression.
497 * There are convenience typedefs for all stdint types, of the following form
498 * (these are just 2 examples):
499 @code
500 typedef CheckedInt<int32_t> CheckedInt32;
501 typedef CheckedInt<uint16_t> CheckedUint16;
502 @endcode
504 template <typename T>
505 class CheckedInt {
506 protected:
507 T mValue;
508 bool mIsValid;
510 template <typename U>
511 constexpr CheckedInt(U aValue, bool aIsValid)
512 : mValue(aValue), mIsValid(aIsValid) {
513 static_assert(std::is_same_v<T, U>,
514 "this constructor must accept only T values");
515 static_assert(detail::IsSupported<T>::value,
516 "This type is not supported by CheckedInt");
519 friend struct detail::NegateImpl<T>;
521 public:
523 * Constructs a checked integer with given @a value. The checked integer is
524 * initialized as valid or invalid depending on whether the @a value
525 * is in range.
527 * This constructor is not explicit. Instead, the type of its argument is a
528 * separate template parameter, ensuring that no conversion is performed
529 * before this constructor is actually called. As explained in the above
530 * documentation for class CheckedInt, this constructor checks that its
531 * argument is valid.
533 template <typename U>
534 MOZ_IMPLICIT MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT constexpr CheckedInt(U aValue)
535 : mValue(T(aValue)), mIsValid(detail::IsInRange<T>(aValue)) {
536 static_assert(
537 detail::IsSupported<T>::value && detail::IsSupported<U>::value,
538 "This type is not supported by CheckedInt");
541 template <typename U>
542 friend class CheckedInt;
544 template <typename U>
545 constexpr CheckedInt<U> toChecked() const {
546 CheckedInt<U> ret(mValue);
547 ret.mIsValid = ret.mIsValid && mIsValid;
548 return ret;
551 /** Constructs a valid checked integer with initial value 0 */
552 constexpr CheckedInt() : mValue(T(0)), mIsValid(true) {
553 static_assert(detail::IsSupported<T>::value,
554 "This type is not supported by CheckedInt");
555 static_assert(detail::IsInRange<T>(0), "Integer type can't represent 0");
558 /** @returns the actual value */
559 constexpr T value() const {
560 MOZ_DIAGNOSTIC_ASSERT(
561 mIsValid,
562 "Invalid checked integer (division by zero or integer overflow)");
563 return mValue;
567 * @returns true if the checked integer is valid, i.e. is not the result
568 * of an invalid operation or of an operation involving an invalid checked
569 * integer
571 constexpr bool isValid() const { return mIsValid; }
573 template <typename U>
574 friend constexpr CheckedInt<U> operator+(const CheckedInt<U>& aLhs,
575 const CheckedInt<U>& aRhs);
576 template <typename U>
577 constexpr CheckedInt& operator+=(U aRhs);
578 constexpr CheckedInt& operator+=(const CheckedInt<T>& aRhs);
580 template <typename U>
581 friend constexpr CheckedInt<U> operator-(const CheckedInt<U>& aLhs,
582 const CheckedInt<U>& aRhs);
583 template <typename U>
584 constexpr CheckedInt& operator-=(U aRhs);
585 constexpr CheckedInt& operator-=(const CheckedInt<T>& aRhs);
587 template <typename U>
588 friend constexpr CheckedInt<U> operator*(const CheckedInt<U>& aLhs,
589 const CheckedInt<U>& aRhs);
590 template <typename U>
591 constexpr CheckedInt& operator*=(U aRhs);
592 constexpr CheckedInt& operator*=(const CheckedInt<T>& aRhs);
594 template <typename U>
595 friend constexpr CheckedInt<U> operator/(const CheckedInt<U>& aLhs,
596 const CheckedInt<U>& aRhs);
597 template <typename U>
598 constexpr CheckedInt& operator/=(U aRhs);
599 constexpr CheckedInt& operator/=(const CheckedInt<T>& aRhs);
601 template <typename U>
602 friend constexpr CheckedInt<U> operator%(const CheckedInt<U>& aLhs,
603 const CheckedInt<U>& aRhs);
604 template <typename U>
605 constexpr CheckedInt& operator%=(U aRhs);
606 constexpr CheckedInt& operator%=(const CheckedInt<T>& aRhs);
608 constexpr CheckedInt operator-() const {
609 return detail::NegateImpl<T>::negate(*this);
613 * @returns true if the left and right hand sides are valid
614 * and have the same value.
616 * Note that these semantics are the reason why we don't offer
617 * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
618 * but that would mean that whenever a or b is invalid, a!=b
619 * is always true, which would be very confusing.
621 * For similar reasons, operators <, >, <=, >= would be very tricky to
622 * specify, so we just avoid offering them.
624 * Notice that these == semantics are made more reasonable by these facts:
625 * 1. a==b implies equality at the raw data level
626 * (the converse is false, as a==b is never true among invalids)
627 * 2. This is similar to the behavior of IEEE floats, where a==b
628 * means that a and b have the same value *and* neither is NaN.
630 constexpr bool operator==(const CheckedInt& aOther) const {
631 return mIsValid && aOther.mIsValid && mValue == aOther.mValue;
634 /** prefix ++ */
635 constexpr CheckedInt& operator++() {
636 *this += 1;
637 return *this;
640 /** postfix ++ */
641 constexpr CheckedInt operator++(int) {
642 CheckedInt tmp = *this;
643 *this += 1;
644 return tmp;
647 /** prefix -- */
648 constexpr CheckedInt& operator--() {
649 *this -= 1;
650 return *this;
653 /** postfix -- */
654 constexpr CheckedInt operator--(int) {
655 CheckedInt tmp = *this;
656 *this -= 1;
657 return tmp;
660 private:
662 * The !=, <, <=, >, >= operators are disabled:
663 * see the comment on operator==.
665 template <typename U>
666 bool operator!=(U aOther) const = delete;
667 template <typename U>
668 bool operator<(U aOther) const = delete;
669 template <typename U>
670 bool operator<=(U aOther) const = delete;
671 template <typename U>
672 bool operator>(U aOther) const = delete;
673 template <typename U>
674 bool operator>=(U aOther) const = delete;
677 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
678 template <typename T> \
679 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \
680 const CheckedInt<T>& aRhs) { \
681 if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
682 static_assert(detail::IsInRange<T>(0), \
683 "Integer type can't represent 0"); \
684 return CheckedInt<T>(T(0), false); \
686 /* For some T, arithmetic ops automatically promote to a wider type, so \
687 * explitly do the narrowing cast here. The narrowing cast is valid \
688 * because we did the "Is##NAME##Valid" check above. */ \
689 return CheckedInt<T>(T(aLhs.mValue OP aRhs.mValue), \
690 aLhs.mIsValid && aRhs.mIsValid); \
693 #if MOZ_HAS_BUILTIN_OP_OVERFLOW
694 # define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(NAME, OP, FUN) \
695 template <typename T> \
696 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \
697 const CheckedInt<T>& aRhs) { \
698 auto result = T{}; \
699 if (FUN(aLhs.mValue, aRhs.mValue, &result)) { \
700 static_assert(detail::IsInRange<T>(0), \
701 "Integer type can't represent 0"); \
702 return CheckedInt<T>(T(0), false); \
704 return CheckedInt<T>(result, aLhs.mIsValid && aRhs.mIsValid); \
706 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add, +, __builtin_add_overflow)
707 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub, -, __builtin_sub_overflow)
708 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul, *, __builtin_mul_overflow)
709 # undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2
710 #else
711 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
712 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
713 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *)
714 #endif
716 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /)
717 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %)
718 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
720 // Implement castToCheckedInt<T>(x), making sure that
721 // - it allows x to be either a CheckedInt<T> or any integer type
722 // that can be casted to T
723 // - if x is already a CheckedInt<T>, we just return a reference to it,
724 // instead of copying it (optimization)
726 namespace detail {
728 template <typename T, typename U>
729 struct CastToCheckedIntImpl {
730 typedef CheckedInt<T> ReturnType;
731 static constexpr CheckedInt<T> run(U aU) { return aU; }
734 template <typename T>
735 struct CastToCheckedIntImpl<T, CheckedInt<T>> {
736 typedef const CheckedInt<T>& ReturnType;
737 static constexpr const CheckedInt<T>& run(const CheckedInt<T>& aU) {
738 return aU;
742 } // namespace detail
744 template <typename T, typename U>
745 constexpr typename detail::CastToCheckedIntImpl<T, U>::ReturnType
746 castToCheckedInt(U aU) {
747 static_assert(detail::IsSupported<T>::value && detail::IsSupported<U>::value,
748 "This type is not supported by CheckedInt");
749 return detail::CastToCheckedIntImpl<T, U>::run(aU);
752 #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
753 template <typename T> \
754 template <typename U> \
755 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) { \
756 *this = *this OP castToCheckedInt<T>(aRhs); \
757 return *this; \
759 template <typename T> \
760 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP( \
761 const CheckedInt<T>& aRhs) { \
762 *this = *this OP aRhs; \
763 return *this; \
765 template <typename T, typename U> \
766 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) { \
767 return aLhs OP castToCheckedInt<T>(aRhs); \
769 template <typename T, typename U> \
770 constexpr CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) { \
771 return castToCheckedInt<T>(aLhs) OP aRhs; \
774 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
775 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
776 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
777 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
778 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
780 #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
782 template <typename T, typename U>
783 constexpr bool operator==(const CheckedInt<T>& aLhs, U aRhs) {
784 return aLhs == castToCheckedInt<T>(aRhs);
787 template <typename T, typename U>
788 constexpr bool operator==(U aLhs, const CheckedInt<T>& aRhs) {
789 return castToCheckedInt<T>(aLhs) == aRhs;
792 // Convenience typedefs.
793 typedef CheckedInt<int8_t> CheckedInt8;
794 typedef CheckedInt<uint8_t> CheckedUint8;
795 typedef CheckedInt<int16_t> CheckedInt16;
796 typedef CheckedInt<uint16_t> CheckedUint16;
797 typedef CheckedInt<int32_t> CheckedInt32;
798 typedef CheckedInt<uint32_t> CheckedUint32;
799 typedef CheckedInt<int64_t> CheckedInt64;
800 typedef CheckedInt<uint64_t> CheckedUint64;
802 } // namespace mozilla
804 #endif /* mozilla_CheckedInt_h */