/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. *//* Provides checked integers, detecting integer overflow and divide-by-0. */#ifndef mozilla_CheckedInt_h#define mozilla_CheckedInt_h#include<stdint.h>#include"mozilla/Assertions.h"#include"mozilla/Attributes.h"#include"mozilla/IntegerTypeTraits.h"namespacemozilla{template<typenameT>classCheckedInt;namespacedetail{/* * Step 1: manually record supported types * * What's nontrivial here is that there are different families of integer * types: basic integer types and stdint types. It is merrily undefined which * types from one family may be just typedefs for a type from another family. * * For example, on GCC 4.6, aside from the basic integer types, the only other * type that isn't just a typedef for some of them, is int8_t. */structUnsupportedType{};template<typenameIntegerType>structIsSupportedPass2{staticconstboolvalue=false;};template<typenameIntegerType>structIsSupported{staticconstboolvalue=IsSupportedPass2<IntegerType>::value;};template<>structIsSupported<int8_t>{staticconstboolvalue=true;};template<>structIsSupported<uint8_t>{staticconstboolvalue=true;};template<>structIsSupported<int16_t>{staticconstboolvalue=true;};template<>structIsSupported<uint16_t>{staticconstboolvalue=true;};template<>structIsSupported<int32_t>{staticconstboolvalue=true;};template<>structIsSupported<uint32_t>{staticconstboolvalue=true;};template<>structIsSupported<int64_t>{staticconstboolvalue=true;};template<>structIsSupported<uint64_t>{staticconstboolvalue=true;};template<>structIsSupportedPass2<char>{staticconstboolvalue=true;};template<>structIsSupportedPass2<signedchar>{staticconstboolvalue=true;};template<>structIsSupportedPass2<unsignedchar>{staticconstboolvalue=true;};template<>structIsSupportedPass2<short>{staticconstboolvalue=true;};template<>structIsSupportedPass2<unsignedshort>{staticconstboolvalue=true;};template<>structIsSupportedPass2<int>{staticconstboolvalue=true;};template<>structIsSupportedPass2<unsignedint>{staticconstboolvalue=true;};template<>structIsSupportedPass2<long>{staticconstboolvalue=true;};template<>structIsSupportedPass2<unsignedlong>{staticconstboolvalue=true;};template<>structIsSupportedPass2<longlong>{staticconstboolvalue=true;};template<>structIsSupportedPass2<unsignedlonglong>{staticconstboolvalue=true;};/* * Step 2: Implement the actual validity checks. * * Ideas taken from IntegerLib, code different. */template<typenameIntegerType,size_tSize=sizeof(IntegerType)>structTwiceBiggerType{typedeftypenamedetail::StdintTypeForSizeAndSignedness<sizeof(IntegerType)*2,IsSigned<IntegerType>::value>::TypeType;};template<typenameIntegerType>structTwiceBiggerType<IntegerType,8>{typedefUnsupportedTypeType;};template<typenameT>inlineboolHasSignBit(TaX){// In C++, right bit shifts on negative values is undefined by the standard.// Notice that signed-to-unsigned conversions are always well-defined in the// standard, as the value congruent modulo 2**n as expected. By contrast,// unsigned-to-signed is only well-defined if the value is representable.returnbool(typenameMakeUnsigned<T>::Type(aX)>>PositionOfSignBit<T>::value);}// Bitwise ops may return a larger type, so it's good to use this inline// helper guaranteeing that the result is really of type T.template<typenameT>inlineTBinaryComplement(TaX){return~aX;}template<typenameT,typenameU,boolIsTSigned=IsSigned<T>::value,boolIsUSigned=IsSigned<U>::value>structDoesRangeContainRange{};template<typenameT,typenameU,boolSignedness>structDoesRangeContainRange<T,U,Signedness,Signedness>{staticconstboolvalue=sizeof(T)>=sizeof(U);};template<typenameT,typenameU>structDoesRangeContainRange<T,U,true,false>{staticconstboolvalue=sizeof(T)>sizeof(U);};template<typenameT,typenameU>structDoesRangeContainRange<T,U,false,true>{staticconstboolvalue=false;};template<typenameT,typenameU,boolIsTSigned=IsSigned<T>::value,boolIsUSigned=IsSigned<U>::value,boolDoesTRangeContainURange=DoesRangeContainRange<T,U>::value>structIsInRangeImpl{};template<typenameT,typenameU,boolIsTSigned,boolIsUSigned>structIsInRangeImpl<T,U,IsTSigned,IsUSigned,true>{staticboolrun(U){returntrue;}};template<typenameT,typenameU>structIsInRangeImpl<T,U,true,true,false>{staticboolrun(UaX){returnaX<=MaxValue<T>::value&&aX>=MinValue<T>::value;}};template<typenameT,typenameU>structIsInRangeImpl<T,U,false,false,false>{staticboolrun(UaX){returnaX<=MaxValue<T>::value;}};template<typenameT,typenameU>structIsInRangeImpl<T,U,true,false,false>{staticboolrun(UaX){returnsizeof(T)>sizeof(U)||aX<=U(MaxValue<T>::value);}};template<typenameT,typenameU>structIsInRangeImpl<T,U,false,true,false>{staticboolrun(UaX){returnsizeof(T)>=sizeof(U)?aX>=0:aX>=0&&aX<=U(MaxValue<T>::value);}};template<typenameT,typenameU>inlineboolIsInRange(UaX){returnIsInRangeImpl<T,U>::run(aX);}template<typenameT>inlineboolIsAddValid(TaX,TaY){// Addition is valid if the sign of aX+aY is equal to either that of aX or// that of aY. Since the value of aX+aY is undefined if we have a signed// type, we compute it using the unsigned type of the same size. Beware!// These bitwise operations can return a larger integer type, if T was a// small type like int8_t, so we explicitly cast to T.typenameMakeUnsigned<T>::Typeux=aX;typenameMakeUnsigned<T>::Typeuy=aY;typenameMakeUnsigned<T>::Typeresult=ux+uy;returnIsSigned<T>::value?HasSignBit(BinaryComplement(T((result^aX)&(result^aY)))):BinaryComplement(aX)>=aY;}template<typenameT>inlineboolIsSubValid(TaX,TaY){// Subtraction is valid if either aX and aY have same sign, or aX-aY and aX// have same sign. Since the value of aX-aY is undefined if we have a signed// type, we compute it using the unsigned type of the same size.typenameMakeUnsigned<T>::Typeux=aX;typenameMakeUnsigned<T>::Typeuy=aY;typenameMakeUnsigned<T>::Typeresult=ux-uy;returnIsSigned<T>::value?HasSignBit(BinaryComplement(T((result^aX)&(aX^aY)))):aX>=aY;}template<typenameT,boolIsTSigned=IsSigned<T>::value,boolTwiceBiggerTypeIsSupported=IsSupported<typenameTwiceBiggerType<T>::Type>::value>structIsMulValidImpl{};template<typenameT,boolIsTSigned>structIsMulValidImpl<T,IsTSigned,true>{staticboolrun(TaX,TaY){typedeftypenameTwiceBiggerType<T>::TypeTwiceBiggerType;TwiceBiggerTypeproduct=TwiceBiggerType(aX)*TwiceBiggerType(aY);returnIsInRange<T>(product);}};template<typenameT>structIsMulValidImpl<T,true,false>{staticboolrun(TaX,TaY){constTmax=MaxValue<T>::value;constTmin=MinValue<T>::value;if(aX==0||aY==0){returntrue;}if(aX>0){returnaY>0?aX<=max/aY:aY>=min/aX;}// If we reach this point, we know that aX < 0.returnaY>0?aX>=min/aY:aY>=max/aX;}};template<typenameT>structIsMulValidImpl<T,false,false>{staticboolrun(TaX,TaY){returnaY==0||aX<=MaxValue<T>::value/aY;}};template<typenameT>inlineboolIsMulValid(TaX,TaY){returnIsMulValidImpl<T>::run(aX,aY);}template<typenameT>inlineboolIsDivValid(TaX,TaY){// Keep in mind that in the signed case, min/-1 is invalid because// abs(min)>max.returnaY!=0&&!(IsSigned<T>::value&&aX==MinValue<T>::value&&aY==T(-1));}template<typenameT,boolIsTSigned=IsSigned<T>::value>structIsModValidImpl;template<typenameT>inlineboolIsModValid(TaX,TaY){returnIsModValidImpl<T>::run(aX,aY);}/* * Mod is pretty simple. * For now, let's just use the ANSI C definition: * If aX or aY are negative, the results are implementation defined. * Consider these invalid. * Undefined for aY=0. * The result will never exceed either aX or aY. * * Checking that aX>=0 is a warning when T is unsigned. */template<typenameT>structIsModValidImpl<T,false>{staticinlineboolrun(TaX,TaY){returnaY>=1;}};template<typenameT>structIsModValidImpl<T,true>{staticinlineboolrun(TaX,TaY){if(aX<0){returnfalse;}returnaY>=1;}};template<typenameT,boolIsSigned=IsSigned<T>::value>structNegateImpl;template<typenameT>structNegateImpl<T,false>{staticCheckedInt<T>negate(constCheckedInt<T>&aVal){// Handle negation separately for signed/unsigned, for simpler code and to// avoid an MSVC warning negating an unsigned value.returnCheckedInt<T>(0,aVal.isValid()&&aVal.mValue==0);}};template<typenameT>structNegateImpl<T,true>{staticCheckedInt<T>negate(constCheckedInt<T>&aVal){// Watch out for the min-value, which (with twos-complement) can't be// negated as -min-value is then (max-value + 1).if(!aVal.isValid()||aVal.mValue==MinValue<T>::value){returnCheckedInt<T>(aVal.mValue,false);}returnCheckedInt<T>(-aVal.mValue,true);}};}// namespace detail/* * Step 3: Now define the CheckedInt class. *//** * @class CheckedInt * @brief Integer wrapper class checking for integer overflow and other errors * @param T the integer type to wrap. Can be any type among the following: * - any basic integer type such as |int| * - any stdint type such as |int8_t| * * This class implements guarded integer arithmetic. Do a computation, check * that isValid() returns true, you then have a guarantee that no problem, such * as integer overflow, happened during this computation, and you can call * value() to get the plain integer value. * * The arithmetic operators in this class are guaranteed not to raise a signal * (e.g. in case of a division by zero). * * For example, suppose that you want to implement a function that computes * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by * zero or integer overflow). You could code it as follows: @code bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult) { CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ; if (checkedResult.isValid()) { *aResult = checkedResult.value(); return true; } else { return false; } } @endcode * * Implicit conversion from plain integers to checked integers is allowed. The * plain integer is checked to be in range before being casted to the * destination type. This means that the following lines all compile, and the * resulting CheckedInts are correctly detected as valid or invalid: * @code // 1 is of type int, is found to be in range for uint8_t, x is valid CheckedInt<uint8_t> x(1); // -1 is of type int, is found not to be in range for uint8_t, x is invalid CheckedInt<uint8_t> x(-1); // -1 is of type int, is found to be in range for int8_t, x is valid CheckedInt<int8_t> x(-1); // 1000 is of type int16_t, is found not to be in range for int8_t, // x is invalid CheckedInt<int8_t> x(int16_t(1000)); // 3123456789 is of type uint32_t, is found not to be in range for int32_t, // x is invalid CheckedInt<int32_t> x(uint32_t(3123456789)); * @endcode * Implicit conversion from * checked integers to plain integers is not allowed. As shown in the * above example, to get the value of a checked integer as a normal integer, * call value(). * * Arithmetic operations between checked and plain integers is allowed; the * result type is the type of the checked integer. * * Checked integers of different types cannot be used in the same arithmetic * expression. * * There are convenience typedefs for all stdint types, of the following form * (these are just 2 examples): @code typedef CheckedInt<int32_t> CheckedInt32; typedef CheckedInt<uint16_t> CheckedUint16; @endcode */template<typenameT>classCheckedInt{protected:TmValue;boolmIsValid;template<typenameU>CheckedInt(UaValue,boolaIsValid):mValue(aValue),mIsValid(aIsValid){static_assert(detail::IsSupported<T>::value&&detail::IsSupported<U>::value,"This type is not supported by CheckedInt");}friendstructdetail::NegateImpl<T>;public:/** * Constructs a checked integer with given @a value. The checked integer is * initialized as valid or invalid depending on whether the @a value * is in range. * * This constructor is not explicit. Instead, the type of its argument is a * separate template parameter, ensuring that no conversion is performed * before this constructor is actually called. As explained in the above * documentation for class CheckedInt, this constructor checks that its * argument is valid. */template<typenameU>MOZ_IMPLICITCheckedInt(UaValue)MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT:mValue(T(aValue)),mIsValid(detail::IsInRange<T>(aValue)){static_assert(detail::IsSupported<T>::value&&detail::IsSupported<U>::value,"This type is not supported by CheckedInt");}template<typenameU>friendclassCheckedInt;template<typenameU>CheckedInt<U>toChecked()const{CheckedInt<U>ret(mValue);ret.mIsValid=ret.mIsValid&&mIsValid;returnret;}/** Constructs a valid checked integer with initial value 0 */CheckedInt():mValue(0),mIsValid(true){static_assert(detail::IsSupported<T>::value,"This type is not supported by CheckedInt");}/** @returns the actual value */Tvalue()const{MOZ_ASSERT(mIsValid,"Invalid checked integer (division by zero or integer overflow)");returnmValue;}/** * @returns true if the checked integer is valid, i.e. is not the result * of an invalid operation or of an operation involving an invalid checked * integer */boolisValid()const{returnmIsValid;}template<typenameU>friendCheckedInt<U>operator+(constCheckedInt<U>&aLhs,constCheckedInt<U>&aRhs);template<typenameU>CheckedInt&operator+=(UaRhs);CheckedInt&operator+=(constCheckedInt<T>&aRhs);template<typenameU>friendCheckedInt<U>operator-(constCheckedInt<U>&aLhs,constCheckedInt<U>&aRhs);template<typenameU>CheckedInt&operator-=(UaRhs);CheckedInt&operator-=(constCheckedInt<T>&aRhs);template<typenameU>friendCheckedInt<U>operator*(constCheckedInt<U>&aLhs,constCheckedInt<U>&aRhs);template<typenameU>CheckedInt&operator*=(UaRhs);CheckedInt&operator*=(constCheckedInt<T>&aRhs);template<typenameU>friendCheckedInt<U>operator/(constCheckedInt<U>&aLhs,constCheckedInt<U>&aRhs);template<typenameU>CheckedInt&operator/=(UaRhs);CheckedInt&operator/=(constCheckedInt<T>&aRhs);template<typenameU>friendCheckedInt<U>operator%(constCheckedInt<U>&aLhs,constCheckedInt<U>&aRhs);template<typenameU>CheckedInt&operator%=(UaRhs);CheckedInt&operator%=(constCheckedInt<T>&aRhs);CheckedIntoperator-()const{returndetail::NegateImpl<T>::negate(*this);}/** * @returns true if the left and right hand sides are valid * and have the same value. * * Note that these semantics are the reason why we don't offer * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b) * but that would mean that whenever a or b is invalid, a!=b * is always true, which would be very confusing. * * For similar reasons, operators <, >, <=, >= would be very tricky to * specify, so we just avoid offering them. * * Notice that these == semantics are made more reasonable by these facts: * 1. a==b implies equality at the raw data level * (the converse is false, as a==b is never true among invalids) * 2. This is similar to the behavior of IEEE floats, where a==b * means that a and b have the same value *and* neither is NaN. */booloperator==(constCheckedInt&aOther)const{returnmIsValid&&aOther.mIsValid&&mValue==aOther.mValue;}/** prefix ++ */CheckedInt&operator++(){*this+=1;return*this;}/** postfix ++ */CheckedIntoperator++(int){CheckedInttmp=*this;*this+=1;returntmp;}/** prefix -- */CheckedInt&operator--(){*this-=1;return*this;}/** postfix -- */CheckedIntoperator--(int){CheckedInttmp=*this;*this-=1;returntmp;}private:/** * The !=, <, <=, >, >= operators are disabled: * see the comment on operator==. */template<typenameU>booloperator!=(UaOther)const=delete;template<typenameU>booloperator<(UaOther)const=delete;template<typenameU>booloperator<=(UaOther)const=delete;template<typenameU>booloperator>(UaOther)const=delete;template<typenameU>booloperator>=(UaOther)const=delete;};#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \ template<typename T> \ inline CheckedInt<T> \ operator OP(const CheckedInt<T>& aLhs, const CheckedInt<T>& aRhs) \ { \ if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \ return CheckedInt<T>(0, false); \ } \ return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \ aLhs.mIsValid && aRhs.mIsValid); \ }MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add,+)MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub,-)MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul,*)MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div,/)MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod,%)#undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR// Implement castToCheckedInt<T>(x), making sure that// - it allows x to be either a CheckedInt<T> or any integer type// that can be casted to T// - if x is already a CheckedInt<T>, we just return a reference to it,// instead of copying it (optimization)namespacedetail{template<typenameT,typenameU>structCastToCheckedIntImpl{typedefCheckedInt<T>ReturnType;staticCheckedInt<T>run(UaU){returnaU;}};template<typenameT>structCastToCheckedIntImpl<T,CheckedInt<T>>{typedefconstCheckedInt<T>&ReturnType;staticconstCheckedInt<T>&run(constCheckedInt<T>&aU){returnaU;}};}// namespace detailtemplate<typenameT,typenameU>inlinetypenamedetail::CastToCheckedIntImpl<T,U>::ReturnTypecastToCheckedInt(UaU){static_assert(detail::IsSupported<T>::value&&detail::IsSupported<U>::value,"This type is not supported by CheckedInt");returndetail::CastToCheckedIntImpl<T,U>::run(aU);}#define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \ template<typename T> \ template<typename U> \ CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) \ { \ *this = *this OP castToCheckedInt<T>(aRhs); \ return *this; \ } \ template<typename T> \ CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(const CheckedInt<T>& aRhs) \ { \ *this = *this OP aRhs; \ return *this; \ } \ template<typename T, typename U> \ inline CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) \ { \ return aLhs OP castToCheckedInt<T>(aRhs); \ } \ template<typename T, typename U> \ inline CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) \ { \ return castToCheckedInt<T>(aLhs) OP aRhs; \ }MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+,+=)MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*,*=)MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-,-=)MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/,/=)MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%,%=)#undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORStemplate<typenameT,typenameU>inlinebooloperator==(constCheckedInt<T>&aLhs,UaRhs){returnaLhs==castToCheckedInt<T>(aRhs);}template<typenameT,typenameU>inlinebooloperator==(UaLhs,constCheckedInt<T>&aRhs){returncastToCheckedInt<T>(aLhs)==aRhs;}// Convenience typedefs.typedefCheckedInt<int8_t>CheckedInt8;typedefCheckedInt<uint8_t>CheckedUint8;typedefCheckedInt<int16_t>CheckedInt16;typedefCheckedInt<uint16_t>CheckedUint16;typedefCheckedInt<int32_t>CheckedInt32;typedefCheckedInt<uint32_t>CheckedUint32;typedefCheckedInt<int64_t>CheckedInt64;typedefCheckedInt<uint64_t>CheckedUint64;}// namespace mozilla#endif /* mozilla_CheckedInt_h */