Bumping manifests a=b2g-bump
[gecko.git] / mfbt / CheckedInt.h
blob714e8a57ab492a849666d6531936cb15cde4f0b7
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/IntegerTypeTraits.h"
16 namespace mozilla {
18 template<typename T> class CheckedInt;
20 namespace detail {
23 * Step 1: manually record supported types
25 * What's nontrivial here is that there are different families of integer
26 * types: basic integer types and stdint types. It is merrily undefined which
27 * types from one family may be just typedefs for a type from another family.
29 * For example, on GCC 4.6, aside from the basic integer types, the only other
30 * type that isn't just a typedef for some of them, is int8_t.
33 struct UnsupportedType {};
35 template<typename IntegerType>
36 struct IsSupportedPass2
38 static const bool value = false;
41 template<typename IntegerType>
42 struct IsSupported
44 static const bool value = IsSupportedPass2<IntegerType>::value;
47 template<>
48 struct IsSupported<int8_t>
49 { static const bool value = true; };
51 template<>
52 struct IsSupported<uint8_t>
53 { static const bool value = true; };
55 template<>
56 struct IsSupported<int16_t>
57 { static const bool value = true; };
59 template<>
60 struct IsSupported<uint16_t>
61 { static const bool value = true; };
63 template<>
64 struct IsSupported<int32_t>
65 { static const bool value = true; };
67 template<>
68 struct IsSupported<uint32_t>
69 { static const bool value = true; };
71 template<>
72 struct IsSupported<int64_t>
73 { static const bool value = true; };
75 template<>
76 struct IsSupported<uint64_t>
77 { static const bool value = true; };
80 template<>
81 struct IsSupportedPass2<char>
82 { static const bool value = true; };
84 template<>
85 struct IsSupportedPass2<signed char>
86 { static const bool value = true; };
88 template<>
89 struct IsSupportedPass2<unsigned char>
90 { static const bool value = true; };
92 template<>
93 struct IsSupportedPass2<short>
94 { static const bool value = true; };
96 template<>
97 struct IsSupportedPass2<unsigned short>
98 { static const bool value = true; };
100 template<>
101 struct IsSupportedPass2<int>
102 { static const bool value = true; };
104 template<>
105 struct IsSupportedPass2<unsigned int>
106 { static const bool value = true; };
108 template<>
109 struct IsSupportedPass2<long>
110 { static const bool value = true; };
112 template<>
113 struct IsSupportedPass2<unsigned long>
114 { static const bool value = true; };
116 template<>
117 struct IsSupportedPass2<long long>
118 { static const bool value = true; };
120 template<>
121 struct IsSupportedPass2<unsigned long long>
122 { static const bool value = true; };
125 * Step 2: Implement the actual validity checks.
127 * Ideas taken from IntegerLib, code different.
130 template<typename IntegerType, size_t Size = sizeof(IntegerType)>
131 struct TwiceBiggerType
133 typedef typename detail::StdintTypeForSizeAndSignedness<
134 sizeof(IntegerType) * 2,
135 IsSigned<IntegerType>::value
136 >::Type Type;
139 template<typename IntegerType>
140 struct TwiceBiggerType<IntegerType, 8>
142 typedef UnsupportedType Type;
145 template<typename T>
146 inline bool
147 HasSignBit(T aX)
149 // In C++, right bit shifts on negative values is undefined by the standard.
150 // Notice that signed-to-unsigned conversions are always well-defined in the
151 // standard, as the value congruent modulo 2**n as expected. By contrast,
152 // unsigned-to-signed is only well-defined if the value is representable.
153 return bool(typename MakeUnsigned<T>::Type(aX) >>
154 PositionOfSignBit<T>::value);
157 // Bitwise ops may return a larger type, so it's good to use this inline
158 // helper guaranteeing that the result is really of type T.
159 template<typename T>
160 inline T
161 BinaryComplement(T aX)
163 return ~aX;
166 template<typename T,
167 typename U,
168 bool IsTSigned = IsSigned<T>::value,
169 bool IsUSigned = IsSigned<U>::value>
170 struct DoesRangeContainRange
174 template<typename T, typename U, bool Signedness>
175 struct DoesRangeContainRange<T, U, Signedness, Signedness>
177 static const bool value = sizeof(T) >= sizeof(U);
180 template<typename T, typename U>
181 struct DoesRangeContainRange<T, U, true, false>
183 static const bool value = sizeof(T) > sizeof(U);
186 template<typename T, typename U>
187 struct DoesRangeContainRange<T, U, false, true>
189 static const bool value = false;
192 template<typename T,
193 typename U,
194 bool IsTSigned = IsSigned<T>::value,
195 bool IsUSigned = IsSigned<U>::value,
196 bool DoesTRangeContainURange = DoesRangeContainRange<T, U>::value>
197 struct IsInRangeImpl {};
199 template<typename T, typename U, bool IsTSigned, bool IsUSigned>
200 struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true>
202 static bool run(U)
204 return true;
208 template<typename T, typename U>
209 struct IsInRangeImpl<T, U, true, true, false>
211 static bool run(U aX)
213 return aX <= MaxValue<T>::value && aX >= MinValue<T>::value;
217 template<typename T, typename U>
218 struct IsInRangeImpl<T, U, false, false, false>
220 static bool run(U aX)
222 return aX <= MaxValue<T>::value;
226 template<typename T, typename U>
227 struct IsInRangeImpl<T, U, true, false, false>
229 static bool run(U aX)
231 return sizeof(T) > sizeof(U) || aX <= U(MaxValue<T>::value);
235 template<typename T, typename U>
236 struct IsInRangeImpl<T, U, false, true, false>
238 static bool run(U aX)
240 return sizeof(T) >= sizeof(U)
241 ? aX >= 0
242 : aX >= 0 && aX <= U(MaxValue<T>::value);
246 template<typename T, typename U>
247 inline bool
248 IsInRange(U aX)
250 return IsInRangeImpl<T, U>::run(aX);
253 template<typename T>
254 inline bool
255 IsAddValid(T aX, T aY)
257 // Addition is valid if the sign of aX+aY is equal to either that of aX or
258 // that of aY. Since the value of aX+aY is undefined if we have a signed
259 // type, we compute it using the unsigned type of the same size. Beware!
260 // These bitwise operations can return a larger integer type, if T was a
261 // small type like int8_t, so we explicitly cast to T.
263 typename MakeUnsigned<T>::Type ux = aX;
264 typename MakeUnsigned<T>::Type uy = aY;
265 typename MakeUnsigned<T>::Type result = ux + uy;
266 return IsSigned<T>::value
267 ? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY))))
268 : BinaryComplement(aX) >= aY;
271 template<typename T>
272 inline bool
273 IsSubValid(T aX, T aY)
275 // Subtraction is valid if either aX and aY have same sign, or aX-aY and aX
276 // have same sign. Since the value of aX-aY is undefined if we have a signed
277 // type, we compute it using the unsigned type of the same size.
278 typename MakeUnsigned<T>::Type ux = aX;
279 typename MakeUnsigned<T>::Type uy = aY;
280 typename MakeUnsigned<T>::Type result = ux - uy;
282 return IsSigned<T>::value
283 ? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY))))
284 : aX >= aY;
287 template<typename T,
288 bool IsTSigned = IsSigned<T>::value,
289 bool TwiceBiggerTypeIsSupported =
290 IsSupported<typename TwiceBiggerType<T>::Type>::value>
291 struct IsMulValidImpl {};
293 template<typename T, bool IsTSigned>
294 struct IsMulValidImpl<T, IsTSigned, true>
296 static bool run(T aX, T aY)
298 typedef typename TwiceBiggerType<T>::Type TwiceBiggerType;
299 TwiceBiggerType product = TwiceBiggerType(aX) * TwiceBiggerType(aY);
300 return IsInRange<T>(product);
304 template<typename T>
305 struct IsMulValidImpl<T, true, false>
307 static bool run(T aX, T aY)
309 const T max = MaxValue<T>::value;
310 const T min = MinValue<T>::value;
312 if (aX == 0 || aY == 0) {
313 return true;
315 if (aX > 0) {
316 return aY > 0
317 ? aX <= max / aY
318 : aY >= min / aX;
321 // If we reach this point, we know that aX < 0.
322 return aY > 0
323 ? aX >= min / aY
324 : aY >= max / aX;
328 template<typename T>
329 struct IsMulValidImpl<T, false, false>
331 static bool run(T aX, T aY)
333 return aY == 0 || aX <= MaxValue<T>::value / aY;
337 template<typename T>
338 inline bool
339 IsMulValid(T aX, T aY)
341 return IsMulValidImpl<T>::run(aX, aY);
344 template<typename T>
345 inline bool
346 IsDivValid(T aX, T aY)
348 // Keep in mind that in the signed case, min/-1 is invalid because
349 // abs(min)>max.
350 return aY != 0 &&
351 !(IsSigned<T>::value && aX == MinValue<T>::value && aY == T(-1));
354 template<typename T, bool IsTSigned = IsSigned<T>::value>
355 struct IsModValidImpl;
357 template<typename T>
358 inline bool
359 IsModValid(T aX, T aY)
361 return IsModValidImpl<T>::run(aX, aY);
365 * Mod is pretty simple.
366 * For now, let's just use the ANSI C definition:
367 * If aX or aY are negative, the results are implementation defined.
368 * Consider these invalid.
369 * Undefined for aY=0.
370 * The result will never exceed either aX or aY.
372 * Checking that aX>=0 is a warning when T is unsigned.
375 template<typename T>
376 struct IsModValidImpl<T, false>
378 static inline bool run(T aX, T aY)
380 return aY >= 1;
384 template<typename T>
385 struct IsModValidImpl<T, true>
387 static inline bool run(T aX, T aY)
389 if (aX < 0) {
390 return false;
392 return aY >= 1;
396 template<typename T, bool IsSigned = IsSigned<T>::value>
397 struct NegateImpl;
399 template<typename T>
400 struct NegateImpl<T, false>
402 static 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>
413 static CheckedInt<T> negate(const CheckedInt<T>& aVal)
415 // Watch out for the min-value, which (with twos-complement) can't be
416 // negated as -min-value is then (max-value + 1).
417 if (!aVal.isValid() || aVal.mValue == MinValue<T>::value) {
418 return CheckedInt<T>(aVal.mValue, false);
420 return CheckedInt<T>(-aVal.mValue, true);
424 } // namespace detail
428 * Step 3: Now define the CheckedInt class.
432 * @class CheckedInt
433 * @brief Integer wrapper class checking for integer overflow and other errors
434 * @param T the integer type to wrap. Can be any type among the following:
435 * - any basic integer type such as |int|
436 * - any stdint type such as |int8_t|
438 * This class implements guarded integer arithmetic. Do a computation, check
439 * that isValid() returns true, you then have a guarantee that no problem, such
440 * as integer overflow, happened during this computation, and you can call
441 * value() to get the plain integer value.
443 * The arithmetic operators in this class are guaranteed not to raise a signal
444 * (e.g. in case of a division by zero).
446 * For example, suppose that you want to implement a function that computes
447 * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
448 * zero or integer overflow). You could code it as follows:
449 @code
450 bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult)
452 CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
453 if (checkedResult.isValid()) {
454 *aResult = checkedResult.value();
455 return true;
456 } else {
457 return false;
460 @endcode
462 * Implicit conversion from plain integers to checked integers is allowed. The
463 * plain integer is checked to be in range before being casted to the
464 * destination type. This means that the following lines all compile, and the
465 * resulting CheckedInts are correctly detected as valid or invalid:
466 * @code
467 // 1 is of type int, is found to be in range for uint8_t, x is valid
468 CheckedInt<uint8_t> x(1);
469 // -1 is of type int, is found not to be in range for uint8_t, x is invalid
470 CheckedInt<uint8_t> x(-1);
471 // -1 is of type int, is found to be in range for int8_t, x is valid
472 CheckedInt<int8_t> x(-1);
473 // 1000 is of type int16_t, is found not to be in range for int8_t,
474 // x is invalid
475 CheckedInt<int8_t> x(int16_t(1000));
476 // 3123456789 is of type uint32_t, is found not to be in range for int32_t,
477 // x is invalid
478 CheckedInt<int32_t> x(uint32_t(3123456789));
479 * @endcode
480 * Implicit conversion from
481 * checked integers to plain integers is not allowed. As shown in the
482 * above example, to get the value of a checked integer as a normal integer,
483 * call value().
485 * Arithmetic operations between checked and plain integers is allowed; the
486 * result type is the type of the checked integer.
488 * Checked integers of different types cannot be used in the same arithmetic
489 * expression.
491 * There are convenience typedefs for all stdint types, of the following form
492 * (these are just 2 examples):
493 @code
494 typedef CheckedInt<int32_t> CheckedInt32;
495 typedef CheckedInt<uint16_t> CheckedUint16;
496 @endcode
498 template<typename T>
499 class CheckedInt
501 protected:
502 T mValue;
503 bool mIsValid;
505 template<typename U>
506 CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid)
508 static_assert(detail::IsSupported<T>::value &&
509 detail::IsSupported<U>::value,
510 "This type is not supported by CheckedInt");
513 friend struct detail::NegateImpl<T>;
515 public:
517 * Constructs a checked integer with given @a value. The checked integer is
518 * initialized as valid or invalid depending on whether the @a value
519 * is in range.
521 * This constructor is not explicit. Instead, the type of its argument is a
522 * separate template parameter, ensuring that no conversion is performed
523 * before this constructor is actually called. As explained in the above
524 * documentation for class CheckedInt, this constructor checks that its
525 * argument is valid.
527 template<typename U>
528 CheckedInt(U aValue)
529 : mValue(T(aValue)),
530 mIsValid(detail::IsInRange<T>(aValue))
532 static_assert(detail::IsSupported<T>::value &&
533 detail::IsSupported<U>::value,
534 "This type is not supported by CheckedInt");
537 template<typename U>
538 friend class CheckedInt;
540 template<typename U>
541 CheckedInt<U> toChecked() const
543 CheckedInt<U> ret(mValue);
544 ret.mIsValid = ret.mIsValid && mIsValid;
545 return ret;
548 /** Constructs a valid checked integer with initial value 0 */
549 CheckedInt() : mValue(0), mIsValid(true)
551 static_assert(detail::IsSupported<T>::value,
552 "This type is not supported by CheckedInt");
555 /** @returns the actual value */
556 T value() const
558 MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)");
559 return mValue;
563 * @returns true if the checked integer is valid, i.e. is not the result
564 * of an invalid operation or of an operation involving an invalid checked
565 * integer
567 bool isValid() const
569 return mIsValid;
572 template<typename U>
573 friend CheckedInt<U> operator +(const CheckedInt<U>& aLhs,
574 const CheckedInt<U>& aRhs);
575 template<typename U>
576 CheckedInt& operator +=(U aRhs);
578 template<typename U>
579 friend CheckedInt<U> operator -(const CheckedInt<U>& aLhs,
580 const CheckedInt<U>& aRhs);
581 template<typename U>
582 CheckedInt& operator -=(U aRhs);
584 template<typename U>
585 friend CheckedInt<U> operator *(const CheckedInt<U>& aLhs,
586 const CheckedInt<U>& aRhs);
587 template<typename U>
588 CheckedInt& operator *=(U aRhs);
590 template<typename U>
591 friend CheckedInt<U> operator /(const CheckedInt<U>& aLhs,
592 const CheckedInt<U>& aRhs);
593 template<typename U>
594 CheckedInt& operator /=(U aRhs);
596 template<typename U>
597 friend CheckedInt<U> operator %(const CheckedInt<U>& aLhs,
598 const CheckedInt<U>& aRhs);
599 template<typename U>
600 CheckedInt& operator %=(U aRhs);
602 CheckedInt operator -() const
604 return detail::NegateImpl<T>::negate(*this);
608 * @returns true if the left and right hand sides are valid
609 * and have the same value.
611 * Note that these semantics are the reason why we don't offer
612 * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
613 * but that would mean that whenever a or b is invalid, a!=b
614 * is always true, which would be very confusing.
616 * For similar reasons, operators <, >, <=, >= would be very tricky to
617 * specify, so we just avoid offering them.
619 * Notice that these == semantics are made more reasonable by these facts:
620 * 1. a==b implies equality at the raw data level
621 * (the converse is false, as a==b is never true among invalids)
622 * 2. This is similar to the behavior of IEEE floats, where a==b
623 * means that a and b have the same value *and* neither is NaN.
625 bool operator ==(const CheckedInt& aOther) const
627 return mIsValid && aOther.mIsValid && mValue == aOther.mValue;
630 /** prefix ++ */
631 CheckedInt& operator++()
633 *this += 1;
634 return *this;
637 /** postfix ++ */
638 CheckedInt operator++(int)
640 CheckedInt tmp = *this;
641 *this += 1;
642 return tmp;
645 /** prefix -- */
646 CheckedInt& operator--()
648 *this -= 1;
649 return *this;
652 /** postfix -- */
653 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> bool operator !=(U aOther) const MOZ_DELETE;
666 template<typename U> bool operator < (U aOther) const MOZ_DELETE;
667 template<typename U> bool operator <=(U aOther) const MOZ_DELETE;
668 template<typename U> bool operator > (U aOther) const MOZ_DELETE;
669 template<typename U> bool operator >=(U aOther) const MOZ_DELETE;
672 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
673 template<typename T> \
674 inline CheckedInt<T> \
675 operator OP(const CheckedInt<T>& aLhs, const CheckedInt<T>& aRhs) \
677 if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
678 return CheckedInt<T>(0, false); \
680 return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \
681 aLhs.mIsValid && aRhs.mIsValid); \
684 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
685 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
686 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *)
687 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /)
688 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %)
690 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
692 // Implement castToCheckedInt<T>(x), making sure that
693 // - it allows x to be either a CheckedInt<T> or any integer type
694 // that can be casted to T
695 // - if x is already a CheckedInt<T>, we just return a reference to it,
696 // instead of copying it (optimization)
698 namespace detail {
700 template<typename T, typename U>
701 struct CastToCheckedIntImpl
703 typedef CheckedInt<T> ReturnType;
704 static CheckedInt<T> run(U aU) { return aU; }
707 template<typename T>
708 struct CastToCheckedIntImpl<T, CheckedInt<T> >
710 typedef const CheckedInt<T>& ReturnType;
711 static const CheckedInt<T>& run(const CheckedInt<T>& aU) { return aU; }
714 } // namespace detail
716 template<typename T, typename U>
717 inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType
718 castToCheckedInt(U aU)
720 static_assert(detail::IsSupported<T>::value &&
721 detail::IsSupported<U>::value,
722 "This type is not supported by CheckedInt");
723 return detail::CastToCheckedIntImpl<T, U>::run(aU);
726 #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
727 template<typename T> \
728 template<typename U> \
729 CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) \
731 *this = *this OP castToCheckedInt<T>(aRhs); \
732 return *this; \
734 template<typename T, typename U> \
735 inline CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) \
737 return aLhs OP castToCheckedInt<T>(aRhs); \
739 template<typename T, typename U> \
740 inline CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) \
742 return castToCheckedInt<T>(aLhs) OP aRhs; \
745 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
746 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
747 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
748 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
749 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
751 #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
753 template<typename T, typename U>
754 inline bool
755 operator ==(const CheckedInt<T>& aLhs, U aRhs)
757 return aLhs == castToCheckedInt<T>(aRhs);
760 template<typename T, typename U>
761 inline bool
762 operator ==(U aLhs, const CheckedInt<T>& aRhs)
764 return castToCheckedInt<T>(aLhs) == aRhs;
767 // Convenience typedefs.
768 typedef CheckedInt<int8_t> CheckedInt8;
769 typedef CheckedInt<uint8_t> CheckedUint8;
770 typedef CheckedInt<int16_t> CheckedInt16;
771 typedef CheckedInt<uint16_t> CheckedUint16;
772 typedef CheckedInt<int32_t> CheckedInt32;
773 typedef CheckedInt<uint32_t> CheckedUint32;
774 typedef CheckedInt<int64_t> CheckedInt64;
775 typedef CheckedInt<uint64_t> CheckedUint64;
777 } // namespace mozilla
779 #endif /* mozilla_CheckedInt_h */