tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CheckedInt.h (21551B)


      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/. */
      6 
      7 /* Provides checked integers, detecting integer overflow and divide-by-0. */
      8 
      9 #ifndef mozilla_CheckedInt_h
     10 #define mozilla_CheckedInt_h
     11 
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/Attributes.h"
     14 #include "mozilla/CheckedArithmetic.h"
     15 
     16 #include <cstdint>
     17 #include <limits>
     18 #include <type_traits>
     19 
     20 namespace mozilla {
     21 
     22 template <typename T>
     23 class CheckedInt;
     24 
     25 namespace detail {
     26 
     27 /*
     28 * Step 1: manually record supported types
     29 *
     30 * What's nontrivial here is that there are different families of integer
     31 * types: basic integer types and stdint types. It is merrily undefined which
     32 * types from one family may be just typedefs for a type from another family.
     33 *
     34 * For example, on GCC 4.6, aside from the basic integer types, the only other
     35 * type that isn't just a typedef for some of them, is int8_t.
     36 */
     37 
     38 template <typename IntegerType>
     39 struct IsSupportedPass2 {
     40  static const bool value = false;
     41 };
     42 
     43 template <typename IntegerType>
     44 struct IsSupported {
     45  static const bool value = IsSupportedPass2<IntegerType>::value;
     46 };
     47 
     48 template <>
     49 struct IsSupported<int8_t> {
     50  static const bool value = true;
     51 };
     52 
     53 template <>
     54 struct IsSupported<uint8_t> {
     55  static const bool value = true;
     56 };
     57 
     58 template <>
     59 struct IsSupported<int16_t> {
     60  static const bool value = true;
     61 };
     62 
     63 template <>
     64 struct IsSupported<uint16_t> {
     65  static const bool value = true;
     66 };
     67 
     68 template <>
     69 struct IsSupported<int32_t> {
     70  static const bool value = true;
     71 };
     72 
     73 template <>
     74 struct IsSupported<uint32_t> {
     75  static const bool value = true;
     76 };
     77 
     78 template <>
     79 struct IsSupported<int64_t> {
     80  static const bool value = true;
     81 };
     82 
     83 template <>
     84 struct IsSupported<uint64_t> {
     85  static const bool value = true;
     86 };
     87 
     88 template <>
     89 struct IsSupportedPass2<char> {
     90  static const bool value = true;
     91 };
     92 
     93 template <>
     94 struct IsSupportedPass2<signed char> {
     95  static const bool value = true;
     96 };
     97 
     98 template <>
     99 struct IsSupportedPass2<unsigned char> {
    100  static const bool value = true;
    101 };
    102 
    103 template <>
    104 struct IsSupportedPass2<short> {
    105  static const bool value = true;
    106 };
    107 
    108 template <>
    109 struct IsSupportedPass2<unsigned short> {
    110  static const bool value = true;
    111 };
    112 
    113 template <>
    114 struct IsSupportedPass2<int> {
    115  static const bool value = true;
    116 };
    117 
    118 template <>
    119 struct IsSupportedPass2<unsigned int> {
    120  static const bool value = true;
    121 };
    122 
    123 template <>
    124 struct IsSupportedPass2<long> {
    125  static const bool value = true;
    126 };
    127 
    128 template <>
    129 struct IsSupportedPass2<unsigned long> {
    130  static const bool value = true;
    131 };
    132 
    133 template <>
    134 struct IsSupportedPass2<long long> {
    135  static const bool value = true;
    136 };
    137 
    138 template <>
    139 struct IsSupportedPass2<unsigned long long> {
    140  static const bool value = true;
    141 };
    142 
    143 /*
    144 * Step 2: Implement the actual validity checks.
    145 *
    146 * Ideas taken from IntegerLib, code different.
    147 */
    148 
    149 template <typename T, typename U, bool IsTSigned = std::is_signed_v<T>,
    150          bool IsUSigned = std::is_signed_v<U>>
    151 struct DoesRangeContainRange {};
    152 
    153 template <typename T, typename U, bool Signedness>
    154 struct DoesRangeContainRange<T, U, Signedness, Signedness> {
    155  static const bool value = sizeof(T) >= sizeof(U);
    156 };
    157 
    158 template <typename T, typename U>
    159 struct DoesRangeContainRange<T, U, true, false> {
    160  static const bool value = sizeof(T) > sizeof(U);
    161 };
    162 
    163 template <typename T, typename U>
    164 struct DoesRangeContainRange<T, U, false, true> {
    165  static const bool value = false;
    166 };
    167 
    168 template <typename T, typename U, bool IsTSigned = std::is_signed_v<T>,
    169          bool IsUSigned = std::is_signed_v<U>,
    170          bool DoesTRangeContainURange = DoesRangeContainRange<T, U>::value>
    171 struct IsInRangeImpl {};
    172 
    173 template <typename T, typename U, bool IsTSigned, bool IsUSigned>
    174 struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true> {
    175  static constexpr bool run(U) { return true; }
    176 };
    177 
    178 template <typename T, typename U>
    179 struct IsInRangeImpl<T, U, true, true, false> {
    180  static constexpr bool run(U aX) {
    181    return aX <= std::numeric_limits<T>::max() &&
    182           aX >= std::numeric_limits<T>::min();
    183  }
    184 };
    185 
    186 template <typename T, typename U>
    187 struct IsInRangeImpl<T, U, false, false, false> {
    188  static constexpr bool run(U aX) {
    189    return aX <= std::numeric_limits<T>::max();
    190  }
    191 };
    192 
    193 template <typename T, typename U>
    194 struct IsInRangeImpl<T, U, true, false, false> {
    195  static constexpr bool run(U aX) {
    196    return sizeof(T) > sizeof(U) || aX <= U(std::numeric_limits<T>::max());
    197  }
    198 };
    199 
    200 template <typename T, typename U>
    201 struct IsInRangeImpl<T, U, false, true, false> {
    202  static constexpr bool run(U aX) {
    203    return sizeof(T) >= sizeof(U)
    204               ? aX >= 0
    205               : aX >= 0 && aX <= U(std::numeric_limits<T>::max());
    206  }
    207 };
    208 
    209 template <typename T, typename U>
    210 constexpr bool IsInRange(U aX) {
    211  return IsInRangeImpl<T, U>::run(aX);
    212 }
    213 
    214 template <typename T>
    215 constexpr bool IsDivValid(T aX, T aY) {
    216  // Keep in mind that in the signed case, min/-1 is invalid because
    217  // abs(min)>max.
    218  return aY != 0 && !(std::is_signed_v<T> &&
    219                      aX == std::numeric_limits<T>::min() && aY == T(-1));
    220 }
    221 
    222 template <typename T, bool IsTSigned = std::is_signed_v<T>>
    223 struct IsModValidImpl;
    224 
    225 template <typename T>
    226 constexpr bool IsModValid(T aX, T aY) {
    227  return IsModValidImpl<T>::run(aX, aY);
    228 }
    229 
    230 /*
    231 * Mod is pretty simple.
    232 * For now, let's just use the ANSI C definition:
    233 * If aX or aY are negative, the results are implementation defined.
    234 *   Consider these invalid.
    235 * Undefined for aY=0.
    236 * The result will never exceed either aX or aY.
    237 *
    238 * Checking that aX>=0 is a warning when T is unsigned.
    239 */
    240 
    241 template <typename T>
    242 struct IsModValidImpl<T, false> {
    243  static constexpr bool run(T aX, T aY) { return aY >= 1; }
    244 };
    245 
    246 template <typename T>
    247 struct IsModValidImpl<T, true> {
    248  static constexpr bool run(T aX, T aY) {
    249    if (aX < 0) {
    250      return false;
    251    }
    252    return aY >= 1;
    253  }
    254 };
    255 
    256 template <typename T, bool IsSigned = std::is_signed_v<T>>
    257 struct NegateImpl;
    258 
    259 template <typename T>
    260 struct NegateImpl<T, false> {
    261  static constexpr CheckedInt<T> negate(const CheckedInt<T>& aVal) {
    262    // Handle negation separately for signed/unsigned, for simpler code and to
    263    // avoid an MSVC warning negating an unsigned value.
    264    static_assert(detail::IsInRange<T>(0), "Integer type can't represent 0");
    265    return CheckedInt<T>(T(0), aVal.isValid() && aVal.mValue == 0);
    266  }
    267 };
    268 
    269 template <typename T>
    270 struct NegateImpl<T, true> {
    271  static constexpr CheckedInt<T> negate(const CheckedInt<T>& aVal) {
    272    // Watch out for the min-value, which (with twos-complement) can't be
    273    // negated as -min-value is then (max-value + 1).
    274    if (!aVal.isValid() || aVal.mValue == std::numeric_limits<T>::min()) {
    275      return CheckedInt<T>(aVal.mValue, false);
    276    }
    277    /* For some T, arithmetic ops automatically promote to a wider type, so
    278     * explitly do the narrowing cast here.  The narrowing cast is valid because
    279     * we did the check for min value above. */
    280    return CheckedInt<T>(T(-aVal.mValue), true);
    281  }
    282 };
    283 
    284 }  // namespace detail
    285 
    286 /*
    287 * Step 3: Now define the CheckedInt class.
    288 */
    289 
    290 /**
    291 * @class CheckedInt
    292 * @brief Integer wrapper class checking for integer overflow and other errors
    293 * @param T the integer type to wrap. Can be any type among the following:
    294 *            - any basic integer type such as |int|
    295 *            - any stdint type such as |int8_t|
    296 *
    297 * This class implements guarded integer arithmetic. Do a computation, check
    298 * that isValid() returns true, you then have a guarantee that no problem, such
    299 * as integer overflow, happened during this computation, and you can call
    300 * value() to get the plain integer value.
    301 *
    302 * The arithmetic operators in this class are guaranteed not to raise a signal
    303 * (e.g. in case of a division by zero).
    304 *
    305 * For example, suppose that you want to implement a function that computes
    306 * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
    307 * zero or integer overflow). You could code it as follows:
    308   @code
    309   bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult)
    310   {
    311     CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
    312     if (checkedResult.isValid()) {
    313       *aResult = checkedResult.value();
    314       return true;
    315     } else {
    316       return false;
    317     }
    318   }
    319   @endcode
    320 *
    321 * Implicit conversion from plain integers to checked integers is allowed. The
    322 * plain integer is checked to be in range before being casted to the
    323 * destination type. This means that the following lines all compile, and the
    324 * resulting CheckedInts are correctly detected as valid or invalid:
    325 * @code
    326   // 1 is of type int, is found to be in range for uint8_t, x is valid
    327   CheckedInt<uint8_t> x(1);
    328   // -1 is of type int, is found not to be in range for uint8_t, x is invalid
    329   CheckedInt<uint8_t> x(-1);
    330   // -1 is of type int, is found to be in range for int8_t, x is valid
    331   CheckedInt<int8_t> x(-1);
    332   // 1000 is of type int16_t, is found not to be in range for int8_t,
    333   // x is invalid
    334   CheckedInt<int8_t> x(int16_t(1000));
    335   // 3123456789 is of type uint32_t, is found not to be in range for int32_t,
    336   // x is invalid
    337   CheckedInt<int32_t> x(uint32_t(3123456789));
    338 * @endcode
    339 * Implicit conversion from
    340 * checked integers to plain integers is not allowed. As shown in the
    341 * above example, to get the value of a checked integer as a normal integer,
    342 * call value().
    343 *
    344 * Arithmetic operations between checked and plain integers is allowed; the
    345 * result type is the type of the checked integer.
    346 *
    347 * Checked integers of different types cannot be used in the same arithmetic
    348 * expression.
    349 *
    350 * There are convenience typedefs for all stdint types, of the following form
    351 * (these are just 2 examples):
    352   @code
    353   typedef CheckedInt<int32_t> CheckedInt32;
    354   typedef CheckedInt<uint16_t> CheckedUint16;
    355   @endcode
    356 */
    357 template <typename T>
    358 class CheckedInt {
    359 protected:
    360  T mValue;
    361  bool mIsValid;
    362 
    363  template <typename U>
    364  constexpr CheckedInt(U aValue, bool aIsValid)
    365      : mValue(aValue), mIsValid(aIsValid) {
    366    static_assert(std::is_same_v<T, U>,
    367                  "this constructor must accept only T values");
    368    static_assert(detail::IsSupported<T>::value,
    369                  "This type is not supported by CheckedInt");
    370  }
    371 
    372  friend struct detail::NegateImpl<T>;
    373 
    374 public:
    375  /**
    376   * Constructs a checked integer with given @a value. The checked integer is
    377   * initialized as valid or invalid depending on whether the @a value
    378   * is in range.
    379   *
    380   * This constructor is not explicit. Instead, the type of its argument is a
    381   * separate template parameter, ensuring that no conversion is performed
    382   * before this constructor is actually called. As explained in the above
    383   * documentation for class CheckedInt, this constructor checks that its
    384   * argument is valid.
    385   */
    386  template <typename U>
    387  MOZ_IMPLICIT MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT constexpr CheckedInt(U aValue)
    388      : mValue(T(aValue)), mIsValid(detail::IsInRange<T>(aValue)) {
    389    static_assert(
    390        detail::IsSupported<T>::value && detail::IsSupported<U>::value,
    391        "This type is not supported by CheckedInt");
    392  }
    393 
    394  template <typename U>
    395  friend class CheckedInt;
    396 
    397  template <typename U>
    398  constexpr CheckedInt<U> toChecked() const {
    399    CheckedInt<U> ret(mValue);
    400    ret.mIsValid = ret.mIsValid && mIsValid;
    401    return ret;
    402  }
    403 
    404  /** Constructs a valid checked integer with initial value 0 */
    405  constexpr CheckedInt() : mValue(T(0)), mIsValid(true) {
    406    static_assert(detail::IsSupported<T>::value,
    407                  "This type is not supported by CheckedInt");
    408    static_assert(detail::IsInRange<T>(0), "Integer type can't represent 0");
    409  }
    410 
    411  /** @returns the actual value */
    412  constexpr T value() const {
    413    MOZ_DIAGNOSTIC_ASSERT(
    414        mIsValid,
    415        "Invalid checked integer (division by zero or integer overflow)");
    416    return mValue;
    417  }
    418 
    419  /**
    420   * @returns true if the checked integer is valid, i.e. is not the result
    421   * of an invalid operation or of an operation involving an invalid checked
    422   * integer
    423   */
    424  constexpr bool isValid() const { return mIsValid; }
    425 
    426  template <typename U>
    427  friend constexpr CheckedInt<U> operator+(const CheckedInt<U>& aLhs,
    428                                           const CheckedInt<U>& aRhs);
    429  template <typename U>
    430  constexpr CheckedInt& operator+=(U aRhs);
    431  constexpr CheckedInt& operator+=(const CheckedInt<T>& aRhs);
    432 
    433  template <typename U>
    434  friend constexpr CheckedInt<U> operator-(const CheckedInt<U>& aLhs,
    435                                           const CheckedInt<U>& aRhs);
    436  template <typename U>
    437  constexpr CheckedInt& operator-=(U aRhs);
    438  constexpr CheckedInt& operator-=(const CheckedInt<T>& aRhs);
    439 
    440  template <typename U>
    441  friend constexpr CheckedInt<U> operator*(const CheckedInt<U>& aLhs,
    442                                           const CheckedInt<U>& aRhs);
    443  template <typename U>
    444  constexpr CheckedInt& operator*=(U aRhs);
    445  constexpr CheckedInt& operator*=(const CheckedInt<T>& aRhs);
    446 
    447  template <typename U>
    448  friend constexpr CheckedInt<U> operator/(const CheckedInt<U>& aLhs,
    449                                           const CheckedInt<U>& aRhs);
    450  template <typename U>
    451  constexpr CheckedInt& operator/=(U aRhs);
    452  constexpr CheckedInt& operator/=(const CheckedInt<T>& aRhs);
    453 
    454  template <typename U>
    455  friend constexpr CheckedInt<U> operator%(const CheckedInt<U>& aLhs,
    456                                           const CheckedInt<U>& aRhs);
    457  template <typename U>
    458  constexpr CheckedInt& operator%=(U aRhs);
    459  constexpr CheckedInt& operator%=(const CheckedInt<T>& aRhs);
    460 
    461  constexpr CheckedInt operator-() const {
    462    return detail::NegateImpl<T>::negate(*this);
    463  }
    464 
    465  /**
    466   * @returns true if the left and right hand sides are valid
    467   * and have the same value.
    468   *
    469   * Note that these semantics are the reason why we don't offer
    470   * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
    471   * but that would mean that whenever a or b is invalid, a!=b
    472   * is always true, which would be very confusing.
    473   *
    474   * For similar reasons, operators <, >, <=, >= would be very tricky to
    475   * specify, so we just avoid offering them.
    476   *
    477   * Notice that these == semantics are made more reasonable by these facts:
    478   *  1. a==b implies equality at the raw data level
    479   *     (the converse is false, as a==b is never true among invalids)
    480   *  2. This is similar to the behavior of IEEE floats, where a==b
    481   *     means that a and b have the same value *and* neither is NaN.
    482   */
    483  constexpr bool operator==(const CheckedInt& aOther) const {
    484    return mIsValid && aOther.mIsValid && mValue == aOther.mValue;
    485  }
    486 
    487  /** prefix ++ */
    488  constexpr CheckedInt& operator++() {
    489    *this += 1;
    490    return *this;
    491  }
    492 
    493  /** postfix ++ */
    494  constexpr CheckedInt operator++(int) {
    495    CheckedInt tmp = *this;
    496    *this += 1;
    497    return tmp;
    498  }
    499 
    500  /** prefix -- */
    501  constexpr CheckedInt& operator--() {
    502    *this -= 1;
    503    return *this;
    504  }
    505 
    506  /** postfix -- */
    507  constexpr CheckedInt operator--(int) {
    508    CheckedInt tmp = *this;
    509    *this -= 1;
    510    return tmp;
    511  }
    512 
    513 private:
    514  /**
    515   * The !=, <, <=, >, >= operators are disabled:
    516   * see the comment on operator==.
    517   */
    518  template <typename U>
    519  bool operator!=(U aOther) const = delete;
    520  template <typename U>
    521  bool operator<(U aOther) const = delete;
    522  template <typename U>
    523  bool operator<=(U aOther) const = delete;
    524  template <typename U>
    525  bool operator>(U aOther) const = delete;
    526  template <typename U>
    527  bool operator>=(U aOther) const = delete;
    528 };
    529 
    530 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP)                      \
    531  template <typename T>                                                     \
    532  constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs,            \
    533                                      const CheckedInt<T>& aRhs) {          \
    534    if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) {               \
    535      static_assert(detail::IsInRange<T>(0),                                \
    536                    "Integer type can't represent 0");                      \
    537      return CheckedInt<T>(T(0), false);                                    \
    538    }                                                                       \
    539    /* For some T, arithmetic ops automatically promote to a wider type, so \
    540     * explicitly do the narrowing cast here.  The narrowing cast is valid  \
    541     * because we did the "Is##NAME##Valid" check above. */                 \
    542    return CheckedInt<T>(T(aLhs.mValue OP aRhs.mValue),                     \
    543                         aLhs.mIsValid && aRhs.mIsValid);                   \
    544  }
    545 
    546 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(NAME, OP, FUN)       \
    547  template <typename T>                                            \
    548  constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs,   \
    549                                      const CheckedInt<T>& aRhs) { \
    550    auto result = T{};                                             \
    551    if (MOZ_UNLIKELY(!FUN(aLhs.mValue, aRhs.mValue, &result))) {   \
    552      static_assert(detail::IsInRange<T>(0),                       \
    553                    "Integer type can't represent 0");             \
    554      return CheckedInt<T>(T(0), false);                           \
    555    }                                                              \
    556    return CheckedInt<T>(result, aLhs.mIsValid && aRhs.mIsValid);  \
    557  }
    558 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add, +, SafeAdd)
    559 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub, -, SafeSub)
    560 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul, *, SafeMul)
    561 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2
    562 
    563 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /)
    564 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %)
    565 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
    566 
    567 // Implement castToCheckedInt<T>(x), making sure that
    568 //  - it allows x to be either a CheckedInt<T> or any integer type
    569 //    that can be cast to T
    570 //  - if x is already a CheckedInt<T>, we just return a reference to it,
    571 //    instead of copying it (optimization)
    572 
    573 namespace detail {
    574 
    575 template <typename T, typename U>
    576 struct CastToCheckedIntImpl {
    577  typedef CheckedInt<T> ReturnType;
    578  static constexpr CheckedInt<T> run(U aU) { return aU; }
    579 };
    580 
    581 template <typename T>
    582 struct CastToCheckedIntImpl<T, CheckedInt<T>> {
    583  typedef const CheckedInt<T>& ReturnType;
    584  static constexpr const CheckedInt<T>& run(const CheckedInt<T>& aU) {
    585    return aU;
    586  }
    587 };
    588 
    589 }  // namespace detail
    590 
    591 template <typename T, typename U>
    592 constexpr typename detail::CastToCheckedIntImpl<T, U>::ReturnType
    593 castToCheckedInt(U aU) {
    594  static_assert(detail::IsSupported<T>::value && detail::IsSupported<U>::value,
    595                "This type is not supported by CheckedInt");
    596  return detail::CastToCheckedIntImpl<T, U>::run(aU);
    597 }
    598 
    599 #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP)       \
    600  template <typename T>                                                    \
    601  template <typename U>                                                    \
    602  constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) {   \
    603    *this = *this OP castToCheckedInt<T>(aRhs);                            \
    604    return *this;                                                          \
    605  }                                                                        \
    606  template <typename T>                                                    \
    607  constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(            \
    608      const CheckedInt<T>& aRhs) {                                         \
    609    *this = *this OP aRhs;                                                 \
    610    return *this;                                                          \
    611  }                                                                        \
    612  template <typename T, typename U>                                        \
    613  constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) { \
    614    return aLhs OP castToCheckedInt<T>(aRhs);                              \
    615  }                                                                        \
    616  template <typename T, typename U>                                        \
    617  constexpr CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) { \
    618    return castToCheckedInt<T>(aLhs) OP aRhs;                              \
    619  }
    620 
    621 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
    622 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
    623 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
    624 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
    625 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
    626 
    627 #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
    628 
    629 template <typename T, typename U>
    630 constexpr bool operator==(const CheckedInt<T>& aLhs, U aRhs) {
    631  return aLhs == castToCheckedInt<T>(aRhs);
    632 }
    633 
    634 template <typename T, typename U>
    635 constexpr bool operator==(U aLhs, const CheckedInt<T>& aRhs) {
    636  return castToCheckedInt<T>(aLhs) == aRhs;
    637 }
    638 
    639 // Convenience typedefs.
    640 typedef CheckedInt<int8_t> CheckedInt8;
    641 typedef CheckedInt<uint8_t> CheckedUint8;
    642 typedef CheckedInt<int16_t> CheckedInt16;
    643 typedef CheckedInt<uint16_t> CheckedUint16;
    644 typedef CheckedInt<int32_t> CheckedInt32;
    645 typedef CheckedInt<uint32_t> CheckedUint32;
    646 typedef CheckedInt<int64_t> CheckedInt64;
    647 typedef CheckedInt<uint64_t> CheckedUint64;
    648 
    649 }  // namespace mozilla
    650 
    651 #endif /* mozilla_CheckedInt_h */