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 // Enable relying of Mozilla's MFBT for possibly-available C++11 features
13 #define MOZ_CHECKEDINT_USE_MFBT
17 #ifdef MOZ_CHECKEDINT_USE_MFBT
18 # include "mozilla/Assertions.h"
21 # define MOZ_ASSERT(cond, reason) assert((cond) && reason)
30 template<typename T
> class CheckedInt
;
35 * Step 1: manually record supported types
37 * What's nontrivial here is that there are different families of integer
38 * types: basic integer types and stdint types. It is merrily undefined which
39 * types from one family may be just typedefs for a type from another family.
41 * For example, on GCC 4.6, aside from the basic integer types, the only other
42 * type that isn't just a typedef for some of them, is int8_t.
45 struct UnsupportedType
{};
47 template<typename IntegerType
>
48 struct IsSupportedPass2
50 static const bool value
= false;
53 template<typename IntegerType
>
56 static const bool value
= IsSupportedPass2
<IntegerType
>::value
;
60 struct IsSupported
<int8_t>
61 { static const bool value
= true; };
64 struct IsSupported
<uint8_t>
65 { static const bool value
= true; };
68 struct IsSupported
<int16_t>
69 { static const bool value
= true; };
72 struct IsSupported
<uint16_t>
73 { static const bool value
= true; };
76 struct IsSupported
<int32_t>
77 { static const bool value
= true; };
80 struct IsSupported
<uint32_t>
81 { static const bool value
= true; };
84 struct IsSupported
<int64_t>
85 { static const bool value
= true; };
88 struct IsSupported
<uint64_t>
89 { static const bool value
= true; };
93 struct IsSupportedPass2
<char>
94 { static const bool value
= true; };
97 struct IsSupportedPass2
<signed char>
98 { static const bool value
= true; };
101 struct IsSupportedPass2
<unsigned char>
102 { static const bool value
= true; };
105 struct IsSupportedPass2
<short>
106 { static const bool value
= true; };
109 struct IsSupportedPass2
<unsigned short>
110 { static const bool value
= true; };
113 struct IsSupportedPass2
<int>
114 { static const bool value
= true; };
117 struct IsSupportedPass2
<unsigned int>
118 { static const bool value
= true; };
121 struct IsSupportedPass2
<long>
122 { static const bool value
= true; };
125 struct IsSupportedPass2
<unsigned long>
126 { static const bool value
= true; };
129 struct IsSupportedPass2
<long long>
130 { static const bool value
= true; };
133 struct IsSupportedPass2
<unsigned long long>
134 { static const bool value
= true; };
137 * Step 2: some integer-traits kind of stuff.
140 template<size_t Size
, bool Signedness
>
141 struct StdintTypeForSizeAndSignedness
145 struct StdintTypeForSizeAndSignedness
<1, true>
146 { typedef int8_t Type
; };
149 struct StdintTypeForSizeAndSignedness
<1, false>
150 { typedef uint8_t Type
; };
153 struct StdintTypeForSizeAndSignedness
<2, true>
154 { typedef int16_t Type
; };
157 struct StdintTypeForSizeAndSignedness
<2, false>
158 { typedef uint16_t Type
; };
161 struct StdintTypeForSizeAndSignedness
<4, true>
162 { typedef int32_t Type
; };
165 struct StdintTypeForSizeAndSignedness
<4, false>
166 { typedef uint32_t Type
; };
169 struct StdintTypeForSizeAndSignedness
<8, true>
170 { typedef int64_t Type
; };
173 struct StdintTypeForSizeAndSignedness
<8, false>
174 { typedef uint64_t Type
; };
176 template<typename IntegerType
>
179 typedef typename StdintTypeForSizeAndSignedness
<sizeof(IntegerType
),
183 template<typename IntegerType
>
186 static const bool value
= IntegerType(-1) <= IntegerType(0);
189 template<typename IntegerType
, size_t Size
= sizeof(IntegerType
)>
190 struct TwiceBiggerType
192 typedef typename StdintTypeForSizeAndSignedness
<
193 sizeof(IntegerType
) * 2,
194 IsSigned
<IntegerType
>::value
198 template<typename IntegerType
>
199 struct TwiceBiggerType
<IntegerType
, 8>
201 typedef UnsupportedType Type
;
204 template<typename IntegerType
>
205 struct PositionOfSignBit
207 static const size_t value
= CHAR_BIT
* sizeof(IntegerType
) - 1;
210 template<typename IntegerType
>
214 typedef typename UnsignedType
<IntegerType
>::Type UnsignedIntegerType
;
215 static const size_t PosOfSignBit
= PositionOfSignBit
<IntegerType
>::value
;
218 // Bitwise ops may return a larger type, that's why we cast explicitly.
219 // In C++, left bit shifts on signed values is undefined by the standard
220 // unless the shifted value is representable.
221 // Notice that signed-to-unsigned conversions are always well-defined in
222 // the standard as the value congruent to 2**n, as expected. By contrast,
223 // unsigned-to-signed is only well-defined if the value is representable.
224 static const IntegerType value
=
225 IsSigned
<IntegerType
>::value
226 ? IntegerType(UnsignedIntegerType(1) << PosOfSignBit
)
230 template<typename IntegerType
>
233 // Tricksy, but covered by the unit test.
234 // Relies heavily on the type of MinValue<IntegerType>::value
235 // being IntegerType.
236 static const IntegerType value
= ~MinValue
<IntegerType
>::value
;
240 * Step 3: Implement the actual validity checks.
242 * Ideas taken from IntegerLib, code different.
249 // In C++, right bit shifts on negative values is undefined by the standard.
250 // Notice that signed-to-unsigned conversions are always well-defined in the
251 // standard, as the value congruent modulo 2**n as expected. By contrast,
252 // unsigned-to-signed is only well-defined if the value is representable.
253 return bool(typename UnsignedType
<T
>::Type(x
)
254 >> PositionOfSignBit
<T
>::value
);
257 // Bitwise ops may return a larger type, so it's good to use this inline
258 // helper guaranteeing that the result is really of type T.
261 BinaryComplement(T x
)
268 bool IsTSigned
= IsSigned
<T
>::value
,
269 bool IsUSigned
= IsSigned
<U
>::value
>
270 struct DoesRangeContainRange
274 template<typename T
, typename U
, bool Signedness
>
275 struct DoesRangeContainRange
<T
, U
, Signedness
, Signedness
>
277 static const bool value
= sizeof(T
) >= sizeof(U
);
280 template<typename T
, typename U
>
281 struct DoesRangeContainRange
<T
, U
, true, false>
283 static const bool value
= sizeof(T
) > sizeof(U
);
286 template<typename T
, typename U
>
287 struct DoesRangeContainRange
<T
, U
, false, true>
289 static const bool value
= false;
294 bool IsTSigned
= IsSigned
<T
>::value
,
295 bool IsUSigned
= IsSigned
<U
>::value
,
296 bool DoesTRangeContainURange
= DoesRangeContainRange
<T
, U
>::value
>
297 struct IsInRangeImpl
{};
299 template<typename T
, typename U
, bool IsTSigned
, bool IsUSigned
>
300 struct IsInRangeImpl
<T
, U
, IsTSigned
, IsUSigned
, true>
308 template<typename T
, typename U
>
309 struct IsInRangeImpl
<T
, U
, true, true, false>
313 return x
<= MaxValue
<T
>::value
&& x
>= MinValue
<T
>::value
;
317 template<typename T
, typename U
>
318 struct IsInRangeImpl
<T
, U
, false, false, false>
322 return x
<= MaxValue
<T
>::value
;
326 template<typename T
, typename U
>
327 struct IsInRangeImpl
<T
, U
, true, false, false>
331 return sizeof(T
) > sizeof(U
) || x
<= U(MaxValue
<T
>::value
);
335 template<typename T
, typename U
>
336 struct IsInRangeImpl
<T
, U
, false, true, false>
340 return sizeof(T
) >= sizeof(U
)
342 : x
>= 0 && x
<= U(MaxValue
<T
>::value
);
346 template<typename T
, typename U
>
350 return IsInRangeImpl
<T
, U
>::run(x
);
357 // Addition is valid if the sign of x+y is equal to either that of x or that
358 // of y. Since the value of x+y is undefined if we have a signed type, we
359 // compute it using the unsigned type of the same size.
360 // Beware! These bitwise operations can return a larger integer type,
361 // if T was a small type like int8_t, so we explicitly cast to T.
363 typename UnsignedType
<T
>::Type ux
= x
;
364 typename UnsignedType
<T
>::Type uy
= y
;
365 typename UnsignedType
<T
>::Type result
= ux
+ uy
;
366 return IsSigned
<T
>::value
367 ? HasSignBit(BinaryComplement(T((result
^ x
) & (result
^ y
))))
368 : BinaryComplement(x
) >= y
;
375 // Subtraction is valid if either x and y have same sign, or x-y and x have
376 // same sign. Since the value of x-y is undefined if we have a signed type,
377 // we compute it using the unsigned type of the same size.
378 typename UnsignedType
<T
>::Type ux
= x
;
379 typename UnsignedType
<T
>::Type uy
= y
;
380 typename UnsignedType
<T
>::Type result
= ux
- uy
;
382 return IsSigned
<T
>::value
383 ? HasSignBit(BinaryComplement(T((result
^ x
) & (x
^ y
))))
388 bool IsTSigned
= IsSigned
<T
>::value
,
389 bool TwiceBiggerTypeIsSupported
=
390 IsSupported
<typename TwiceBiggerType
<T
>::Type
>::value
>
391 struct IsMulValidImpl
{};
393 template<typename T
, bool IsTSigned
>
394 struct IsMulValidImpl
<T
, IsTSigned
, true>
396 static bool run(T x
, T y
)
398 typedef typename TwiceBiggerType
<T
>::Type TwiceBiggerType
;
399 TwiceBiggerType product
= TwiceBiggerType(x
) * TwiceBiggerType(y
);
400 return IsInRange
<T
>(product
);
405 struct IsMulValidImpl
<T
, true, false>
407 static bool run(T x
, T y
)
409 const T max
= MaxValue
<T
>::value
;
410 const T min
= MinValue
<T
>::value
;
412 if (x
== 0 || y
== 0)
421 // If we reach this point, we know that x < 0.
429 struct IsMulValidImpl
<T
, false, false>
431 static bool run(T x
, T y
)
433 return y
== 0 || x
<= MaxValue
<T
>::value
/ y
;
441 return IsMulValidImpl
<T
>::run(x
, y
);
448 // Keep in mind that in the signed case, min/-1 is invalid because abs(min)>max.
450 !(IsSigned
<T
>::value
&& x
== MinValue
<T
>::value
&& y
== T(-1));
453 template<typename T
, bool IsTSigned
= IsSigned
<T
>::value
>
454 struct IsModValidImpl
;
460 return IsModValidImpl
<T
>::run(x
, y
);
464 * Mod is pretty simple.
465 * For now, let's just use the ANSI C definition:
466 * If x or y are negative, the results are implementation defined.
467 * Consider these invalid.
469 * The result will never exceed either x or y.
471 * Checking that x>=0 is a warning when T is unsigned.
475 struct IsModValidImpl
<T
, false> {
476 static inline bool run(T x
, T y
) {
482 struct IsModValidImpl
<T
, true> {
483 static inline bool run(T x
, T y
) {
491 template<typename T
, bool IsSigned
= IsSigned
<T
>::value
>
495 struct NegateImpl
<T
, false>
497 static CheckedInt
<T
> negate(const CheckedInt
<T
>& val
)
499 // Handle negation separately for signed/unsigned, for simpler code and to
500 // avoid an MSVC warning negating an unsigned value.
501 return CheckedInt
<T
>(0, val
.isValid() && val
.mValue
== 0);
506 struct NegateImpl
<T
, true>
508 static CheckedInt
<T
> negate(const CheckedInt
<T
>& val
)
510 // Watch out for the min-value, which (with twos-complement) can't be
511 // negated as -min-value is then (max-value + 1).
512 if (!val
.isValid() || val
.mValue
== MinValue
<T
>::value
)
513 return CheckedInt
<T
>(val
.mValue
, false);
514 return CheckedInt
<T
>(-val
.mValue
, true);
518 } // namespace detail
522 * Step 4: Now define the CheckedInt class.
527 * @brief Integer wrapper class checking for integer overflow and other errors
528 * @param T the integer type to wrap. Can be any type among the following:
529 * - any basic integer type such as |int|
530 * - any stdint type such as |int8_t|
532 * This class implements guarded integer arithmetic. Do a computation, check
533 * that isValid() returns true, you then have a guarantee that no problem, such
534 * as integer overflow, happened during this computation, and you can call
535 * value() to get the plain integer value.
537 * The arithmetic operators in this class are guaranteed not to raise a signal
538 * (e.g. in case of a division by zero).
540 * For example, suppose that you want to implement a function that computes
541 * (x+y)/z, that doesn't crash if z==0, and that reports on error (divide by
542 * zero or integer overflow). You could code it as follows:
544 bool computeXPlusYOverZ(int x, int y, int z, int *result)
546 CheckedInt<int> checkedResult = (CheckedInt<int>(x) + y) / z;
547 if (checkedResult.isValid()) {
548 *result = checkedResult.value();
556 * Implicit conversion from plain integers to checked integers is allowed. The
557 * plain integer is checked to be in range before being casted to the
558 * destination type. This means that the following lines all compile, and the
559 * resulting CheckedInts are correctly detected as valid or invalid:
561 // 1 is of type int, is found to be in range for uint8_t, x is valid
562 CheckedInt<uint8_t> x(1);
563 // -1 is of type int, is found not to be in range for uint8_t, x is invalid
564 CheckedInt<uint8_t> x(-1);
565 // -1 is of type int, is found to be in range for int8_t, x is valid
566 CheckedInt<int8_t> x(-1);
567 // 1000 is of type int16_t, is found not to be in range for int8_t,
569 CheckedInt<int8_t> x(int16_t(1000));
570 // 3123456789 is of type uint32_t, is found not to be in range for int32_t,
572 CheckedInt<int32_t> x(uint32_t(3123456789));
574 * Implicit conversion from
575 * checked integers to plain integers is not allowed. As shown in the
576 * above example, to get the value of a checked integer as a normal integer,
579 * Arithmetic operations between checked and plain integers is allowed; the
580 * result type is the type of the checked integer.
582 * Checked integers of different types cannot be used in the same arithmetic
585 * There are convenience typedefs for all stdint types, of the following form
586 * (these are just 2 examples):
588 typedef CheckedInt<int32_t> CheckedInt32;
589 typedef CheckedInt<uint16_t> CheckedUint16;
600 CheckedInt(U value
, bool isValid
) : mValue(value
), mIsValid(isValid
)
602 static_assert(detail::IsSupported
<T
>::value
&&
603 detail::IsSupported
<U
>::value
,
604 "This type is not supported by CheckedInt");
607 friend struct detail::NegateImpl
<T
>;
611 * Constructs a checked integer with given @a value. The checked integer is
612 * initialized as valid or invalid depending on whether the @a value
615 * This constructor is not explicit. Instead, the type of its argument is a
616 * separate template parameter, ensuring that no conversion is performed
617 * before this constructor is actually called. As explained in the above
618 * documentation for class CheckedInt, this constructor checks that its
624 mIsValid(detail::IsInRange
<T
>(value
))
626 static_assert(detail::IsSupported
<T
>::value
&&
627 detail::IsSupported
<U
>::value
,
628 "This type is not supported by CheckedInt");
632 friend class CheckedInt
;
635 CheckedInt
<U
> toChecked() const
637 CheckedInt
<U
> ret(mValue
);
638 ret
.mIsValid
= ret
.mIsValid
&& mIsValid
;
642 /** Constructs a valid checked integer with initial value 0 */
643 CheckedInt() : mValue(0), mIsValid(true)
645 static_assert(detail::IsSupported
<T
>::value
,
646 "This type is not supported by CheckedInt");
649 /** @returns the actual value */
652 MOZ_ASSERT(mIsValid
, "Invalid checked integer (division by zero or integer overflow)");
657 * @returns true if the checked integer is valid, i.e. is not the result
658 * of an invalid operation or of an operation involving an invalid checked
667 friend CheckedInt
<U
> operator +(const CheckedInt
<U
>& lhs
,
668 const CheckedInt
<U
>& rhs
);
670 CheckedInt
& operator +=(U rhs
);
673 friend CheckedInt
<U
> operator -(const CheckedInt
<U
>& lhs
,
674 const CheckedInt
<U
>& rhs
);
676 CheckedInt
& operator -=(U rhs
);
679 friend CheckedInt
<U
> operator *(const CheckedInt
<U
>& lhs
,
680 const CheckedInt
<U
>& rhs
);
682 CheckedInt
& operator *=(U rhs
);
685 friend CheckedInt
<U
> operator /(const CheckedInt
<U
>& lhs
,
686 const CheckedInt
<U
>& rhs
);
688 CheckedInt
& operator /=(U rhs
);
691 friend CheckedInt
<U
> operator %(const CheckedInt
<U
>& lhs
,
692 const CheckedInt
<U
>& rhs
);
694 CheckedInt
& operator %=(U rhs
);
696 CheckedInt
operator -() const
698 return detail::NegateImpl
<T
>::negate(*this);
702 * @returns true if the left and right hand sides are valid
703 * and have the same value.
705 * Note that these semantics are the reason why we don't offer
706 * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
707 * but that would mean that whenever a or b is invalid, a!=b
708 * is always true, which would be very confusing.
710 * For similar reasons, operators <, >, <=, >= would be very tricky to
711 * specify, so we just avoid offering them.
713 * Notice that these == semantics are made more reasonable by these facts:
714 * 1. a==b implies equality at the raw data level
715 * (the converse is false, as a==b is never true among invalids)
716 * 2. This is similar to the behavior of IEEE floats, where a==b
717 * means that a and b have the same value *and* neither is NaN.
719 bool operator ==(const CheckedInt
& other
) const
721 return mIsValid
&& other
.mIsValid
&& mValue
== other
.mValue
;
725 CheckedInt
& operator++()
732 CheckedInt
operator++(int)
734 CheckedInt tmp
= *this;
740 CheckedInt
& operator--()
747 CheckedInt
operator--(int)
749 CheckedInt tmp
= *this;
756 * The !=, <, <=, >, >= operators are disabled:
757 * see the comment on operator==.
760 bool operator !=(U other
) const MOZ_DELETE
;
762 bool operator <(U other
) const MOZ_DELETE
;
764 bool operator <=(U other
) const MOZ_DELETE
;
766 bool operator >(U other
) const MOZ_DELETE
;
768 bool operator >=(U other
) const MOZ_DELETE
;
771 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
772 template<typename T> \
773 inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, \
774 const CheckedInt<T> &rhs) \
776 if (!detail::Is##NAME##Valid(lhs.mValue, rhs.mValue)) \
777 return CheckedInt<T>(0, false); \
779 return CheckedInt<T>(lhs.mValue OP rhs.mValue, \
780 lhs.mIsValid && rhs.mIsValid); \
783 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add
, +)
784 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub
, -)
785 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul
, *)
786 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div
, /)
787 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod
, %)
789 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
791 // Implement castToCheckedInt<T>(x), making sure that
792 // - it allows x to be either a CheckedInt<T> or any integer type
793 // that can be casted to T
794 // - if x is already a CheckedInt<T>, we just return a reference to it,
795 // instead of copying it (optimization)
799 template<typename T
, typename U
>
800 struct CastToCheckedIntImpl
802 typedef CheckedInt
<T
> ReturnType
;
803 static CheckedInt
<T
> run(U u
) { return u
; }
807 struct CastToCheckedIntImpl
<T
, CheckedInt
<T
> >
809 typedef const CheckedInt
<T
>& ReturnType
;
810 static const CheckedInt
<T
>& run(const CheckedInt
<T
>& u
) { return u
; }
813 } // namespace detail
815 template<typename T
, typename U
>
816 inline typename
detail::CastToCheckedIntImpl
<T
, U
>::ReturnType
817 castToCheckedInt(U u
)
819 static_assert(detail::IsSupported
<T
>::value
&&
820 detail::IsSupported
<U
>::value
,
821 "This type is not supported by CheckedInt");
822 return detail::CastToCheckedIntImpl
<T
, U
>::run(u
);
825 #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
826 template<typename T> \
827 template<typename U> \
828 CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U rhs) \
830 *this = *this OP castToCheckedInt<T>(rhs); \
833 template<typename T, typename U> \
834 inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, U rhs) \
836 return lhs OP castToCheckedInt<T>(rhs); \
838 template<typename T, typename U> \
839 inline CheckedInt<T> operator OP(U lhs, const CheckedInt<T> &rhs) \
841 return castToCheckedInt<T>(lhs) OP rhs; \
844 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
845 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
846 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
847 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
848 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
850 #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
852 template<typename T
, typename U
>
854 operator ==(const CheckedInt
<T
> &lhs
, U rhs
)
856 return lhs
== castToCheckedInt
<T
>(rhs
);
859 template<typename T
, typename U
>
861 operator ==(U lhs
, const CheckedInt
<T
> &rhs
)
863 return castToCheckedInt
<T
>(lhs
) == rhs
;
866 // Convenience typedefs.
867 typedef CheckedInt
<int8_t> CheckedInt8
;
868 typedef CheckedInt
<uint8_t> CheckedUint8
;
869 typedef CheckedInt
<int16_t> CheckedInt16
;
870 typedef CheckedInt
<uint16_t> CheckedUint16
;
871 typedef CheckedInt
<int32_t> CheckedInt32
;
872 typedef CheckedInt
<uint32_t> CheckedUint32
;
873 typedef CheckedInt
<int64_t> CheckedInt64
;
874 typedef CheckedInt
<uint64_t> CheckedUint64
;
876 } // namespace mozilla
878 #endif /* mozilla_CheckedInt_h */