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