⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 safeint.hpp

📁 自定义HttpClient类
💻 HPP
📖 第 1 页 / 共 5 页
字号:
			if(sizeof(T) >= sizeof(U))
				return SafeInt<T>((T)((T)lhs.Value()/(T)rhs));

			return SafeInt<T>((T)((U)lhs.Value()/(U)rhs));
		}
		
		//now we have mixed sign case, which can lead to problems
		//first consider T signed, U unsigned

		if(SafeInt<T>::IsSigned())
		{
			if(sizeof(U) < sizeof(T))
			{
				//simply upcast to T - rhs always fits into T
				return SafeInt<T>((T)(lhs.Value()/(T)rhs));
			}

			if(sizeof(U) < 4 && sizeof(T) < 4)
			{
				//even if U is bigger, upcast to int
				return SafeInt<T>((T)((_int32)lhs.Value()/(_int32)rhs));
			}

			//now U is either 32 or 64-bit, T same size or smaller
			if(sizeof(U) == 4)
			{
				//upcast to 64-bit
				return SafeInt<T>((T)((_int64)lhs.Value()/(_int64)rhs));
			}

			//U is unsigned _int64
			//now it matters whether lhs < 0
			if(lhs.Value() < 0)
			{
				if(rhs > (U)SafeInt<T>::MaxInt() + 1)
					return SafeInt<T>(0);

				//corner case
				if(lhs.Value() == SafeInt<T>::MinInt() &&
					rhs == (U)SafeInt<T>::MaxInt() + 1)
				{
					return SafeInt<T>((T)-1);
				}

				//finally, rhs fits into T - just cast
				return SafeInt<T>((T)(lhs.Value()/(T)rhs));
			}

			//lhs >= 0
			//T now has to fit into U
			return SafeInt<T>((T)((U)lhs.Value()/(U)rhs));
		}

		//now lhs is unsigned, rhs signed
		if(rhs < 0)
			throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

		if(sizeof(T) >= sizeof(U))
		{
			//all U fits into T
			return SafeInt<T>((T)(lhs.Value()/(T)rhs));
		}
			
		//now sizeof(U) > sizeof(T) and rhs > 0
		//all T fits into U
		return SafeInt<T>((T)((U)lhs.Value()/rhs));
	}

	template <typename U> 
	static SafeInt<T> MixedSizeAddition(SafeInt<T> lhs, U rhs)
	{
		if(SafeInt<T>::IsSigned() == SafeInt<U>::IsSigned())
		{
			//easy case - just upcast U to T
			if(sizeof(T) >= sizeof(U))
			{
				return SafeInt<T>(addition(lhs.Value(), (T)rhs));
			}

			//otherwise, U > T
			//where possible, do the upcast inline
			//this avoids range checks inside the addition call
			//and additional range checks on assignment
			if(sizeof(U) == 2)
			{
				//an int can hold any possible range of _int16 + char
				//do bounds checking in constructor
				if(IsSigned())
					return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
				//else unsigned

				return SafeInt<T>((unsigned _int32)lhs.Value() + (unsigned _int32)rhs);
			}

			if(sizeof(U) == 4)
			{
				//it is possibly overkill to go to 64-bit, but other alternatives
				//involve more conditionals. This is likely cheaper overall
				if(IsSigned())
					return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);

				return SafeInt<T>((unsigned _int64)lhs.Value() + (unsigned _int64)rhs);
			}

			//else U is an _int64
			//this will have to be done the expensive way
			{
				return SafeInt<T>(SafeInt<U>(lhs.Value()) + rhs); //more checking here
			}
		}

		//else mixed sign
		//first consider the case of signed T, unsigned U
		if(SafeInt<T>::IsSigned())
		{
			if(sizeof(T) > sizeof(U))
			{
				//piece of cake, U fits in T
				return SafeInt<T>(addition(lhs.Value(), (T)rhs));
			}

			//else sizeof(T) <= sizeof(U)
			if(sizeof(U) < 4)
			{
				//upcast to int, which is what the compiler normally does
				//catch overflows in the constructor
				return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
			}

			if(sizeof(U) == 4)
			{
				return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);
			}

			//else U is unsigned 64-bit
			//this will have to be done the expensive way
			//this particular part of the problem is especially
			//difficult - the result has to fit correctly into a signed int[8|16|32]
			//lhs input could be negative
			//rhs input greater than an _int64 MAX_INT could be legal
			if(sizeof(T) < 4)
			{
				//test if rhs is even possibly legal
				if((unsigned _int64)rhs > (unsigned _int64)SafeInt<unsigned _int16>::MaxInt())
					throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

				//stuff them all into an int, return
				return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
			}
			else
			if(sizeof(T) == 4)
			{
				//you can't possibly add more than 0xFFFFFFFF
				//to a signed int and have it work, so
				if((unsigned _int64)rhs > (unsigned _int64)SafeInt<unsigned _int32>::MaxInt())
					throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

				//now that we know that rhs fits into an unsigned _int32
				//result must fit into an _int64
				return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);
			}
			else
			{
				//T == signed _int64
				if(lhs.Value() >= 0)
				{
					//they both need to fit into an _int64
					//given that lhs is positive, rhs must be <= _int64 MaxInt
					if((unsigned _int64)rhs > (unsigned _int64)SafeInt<_int64>::MaxInt())
						throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
					
					return SafeInt<T>(addition((T)lhs.Value(), (T)rhs));
				}

				//else rhs could range all the way up to a unsigned _int64 MAX_INT
				//rearrange as rhs - (-lhs)
				//this still works even if lhs == _int64 MIN_INT
				return SafeInt<T>(rhs - (unsigned _int64)(-(lhs.Value())));
			}
		}

		//now we have the case of unsigned T, signed U
		//we just solved this problem above - since A+B == B+A
		//code is largely duplicated, but there are optimizations
		//remember that U could be negative

		if(sizeof(T) < 4 && sizeof(U) < 4)
		{
			//upcast to int, which is what the compiler normally does
			//catch overflows in the constructor
			return SafeInt<T>((_int32)lhs.Value() + (_int32)rhs);
		}

		if(sizeof(T) < 8 && sizeof(U) < 8)
		{
			//either T or U are 32-bit
			//all possible combinations fit into an _int64
			return SafeInt<T>((_int64)lhs.Value() + (_int64)rhs);
		}

		if(sizeof(U) == 8 && sizeof(T) < 8)
		{
			//all possible values of T fit into an _int64
			SafeInt<U> u(rhs);
			u += (_int64)lhs.Value();
			return SafeInt<T>(u.Value());
		}

		//else T is unsigned 64-bit
		//this will have to be done the expensive way
		//rhs input could be negative
		if(rhs >= 0)
		{
			//all possible values of rhs can fit into an unsigned _int64
			return SafeInt<T>(addition(lhs.Value(), (T)rhs));
		}

		//rhs is negative
		if((T)(-rhs) > lhs.Value()) //this will never work
			throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

		return SafeInt<T>(lhs.Value() - (T)(-rhs));
	}

	template <typename U> 
	static SafeInt<T> MixedSizeSubtraction(SafeInt<T> lhs, U rhs)
	{
		if(SafeInt<T>::IsSigned() == SafeInt<U>::IsSigned())
		{
			//easy case - just upcast U to T
			if(sizeof(T) >= sizeof(U))
				return SafeInt<T>(subtraction(lhs.Value(), (T)rhs));

			//otherwise, U > T
			if(sizeof(U) == 2)
			{
				//an int can hold any possible range of _int16 - char
				//do bounds checking in constructor
				if(IsSigned())
					return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
				//else unsigned

				return SafeInt<T>((unsigned _int32)lhs.Value() - (unsigned _int32)rhs);
			}

			if(sizeof(U) == 4)
			{
				//it is possibly overkill to go to 64-bit, but other alternatives
				//involve more conditionals. This is likely cheaper overall
				if(IsSigned())
					return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);

				return SafeInt<T>((unsigned _int64)lhs.Value() - (unsigned _int64)rhs);
			}

			//else U is a signed or unsigned _int64
			//this will have to be done the expensive way
			{
				return SafeInt<T>(SafeInt<U>(lhs.Value()) - rhs); //more checking here
			}
		}

		//else mixed sign
		//first consider the case of signed T, unsigned U
		if(IsSigned())
		{
			if(sizeof(T) > sizeof(U))
			{
				//piece of cake, U fits in T
				return SafeInt<T>(subtraction(lhs.Value(), (T)rhs));
			}

			//else sizeof(T) <= sizeof(U)
			if(sizeof(U) < 4)
			{
				//upcast to int, which is what the compiler normally does
				//catch overflows in the constructor
				return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
			}

			if(sizeof(U) == 4)
			{
				//upcast to _int64, which is what the compiler should do
				return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
			}

			//else U is unsigned 64-bit
			//this will have to be done the expensive way
			//this particular part of the problem is especially
			//difficult - the result has to fit correctly into a signed int[8|16|32]
			//lhs input could be negative, rhs input is always positive
			if(sizeof(T) < 4)
			{
				//test if rhs is even possibly legal
				if(rhs > (U)SafeInt<unsigned _int16>::MaxInt())
					throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

				//stuff them all into an int, return
				return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
			}
			else
			if(sizeof(T) == 4)
			{
				//you can't possibly subtract more than 0xFFFFFFFF
				//from a signed int and have it work, so
				if(rhs > (U)SafeInt<unsigned _int32>::MaxInt())
					throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

				return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
			}
			else
			{
				//T = signed _int64
				//U = unsigned _int64

				//need the size of the range between lhs.MinInt and lhs
				//this is the maximum value that can be subtracted from lhs
				//we're actually going to take advantage of rollover
				if(rhs > (U)(lhs.Value() - SafeInt<T>::MinInt()))
					throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

				//else it has to work
				return SafeInt<T>((T)(lhs.Value() - rhs));
			}
		}

		//now we have the case of unsigned T, signed U
		
		//easily deals with 1/4 of the problem space
		if(sizeof(T) < 4 && sizeof(U) < 4)
		{
			//stuff them all into an int, return
			return SafeInt<T>((_int32)lhs.Value() - (_int32)rhs);
		}
		else
		if(sizeof(T) < 8 && sizeof(U) < 8)
		{
			//handles another 5 of 16 cases
			return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
		}

		//case of T = unsigned _int64
		//handles 4 more cases
		if(sizeof(T) == 8)
		{
			//has to work - rhs has to fit into a T
			if(rhs >= 0)
				return SafeInt<T>(subtraction(lhs.Value(), (T)rhs));

			//rhs negative - turn into an addition
			//take care to do an intermediate cast because the
			//unary negation operator returns an int
			//corner case of MinInt still works
			return SafeInt<T>(addition(lhs.Value(), (T)((U)-rhs)));
		}

		//sizeof T < 8, sizeof U == 8
		//maximum possible range for rhs is lhs - MinInt(T)
		//because sizeof T smaller than _int64, upcast
		if(rhs > (U)((_int64)lhs.Value() - (_int64)lhs.MinInt()))
			throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

		return SafeInt<T>((_int64)lhs.Value() - (_int64)rhs);
	}

	template <typename U> 
	static SafeInt<T> MixedSizeMultiply(SafeInt<T> lhs, U rhs)
	{
		//what is U?
		//if T is unsigned, and sizeof(T) >= sizeof(U)
		//T can hold all values of U for rhs > 0
		//if T is unsigned and sizeof(T) < sizeof(U)
		//declare an unsigned SafeInt of same size as U

		//if T is signed and sizeof(T) > sizeof(U)
		//T can hold all values of U
		//if T is signed and sizeof(T) <= sizeof(U)
		//declare a signed SafeInt of same size as U

		if(SafeInt<T>::IsSigned() == SafeInt<U>::IsSigned())
		{
			//simple case - same signedness and U always fits in T
			if(sizeof(T) >= sizeof(U))
			{
				return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
			}

			//simple case - same signedness and U bigger than T
			//looks like a lot of code, but is compile-time constants
			//must handle signed and unsigned in different cases - unless sizeof(U) == 8

			if(sizeof(U) == 8)
			{
				SafeInt<U> u(rhs);
				u *= lhs.m_int;
				return SafeInt<T>(u.Value());
			}

			if(SafeInt<T>::IsSigned())
			{
				if(sizeof(U) < 4)
				{
					//the result must always fit into an int
					return SafeInt<T>((_int32)((_int32)lhs.m_int * (_int32)rhs));
				}

				//result must fit into an _int64
				return SafeInt<T>((_int64)((_int64)lhs.m_int * (_int64)rhs));
			}

			//else unsigned
			if(sizeof(U) < 4)
			{
				//the result must always fit into an int
				return SafeInt<T>((unsigned _int32)((unsigned _int32)lhs.m_int * (unsigned _int32)rhs));
			}

			//result must fit into an _int64
			return SafeInt<T>((unsigned _int64)((unsigned _int64)lhs.m_int * (unsigned _int64)rhs));
		}

		//mixed sign - consider T is signed, U unsigned
		if(SafeInt<T>::IsSigned() && !SafeInt<U>::IsSigned())
		{
			//if T > U, we're OK
			if(sizeof(T) > sizeof(U))
			{
				return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
			}

			//else sizeof(T) <= sizeof(U) - upcast T to signed U
			//otherwise, we have to make an signed next size up from U
			if(sizeof(U) < 4)
			{
				//T is a signed char or _int16, U is unsigned _int16 or char
				//this must also always fit into an int
				return SafeInt<T>((_int32)((_int32)lhs.m_int * (_int32)rhs));
			}
			else if(sizeof(U) == 4)
			{
				//T is signed int or smaller, U is unsigned int
				//result must fit into an _int64
				return SafeInt<T>((_int64)((_int64)lhs.m_int * (_int64)rhs));
			}
			else
			{
				//U is unsigned 64-bit

				//now if rhs > MaxInt(T), overflow unless lhs == 0
				//or the corner case
				if(rhs > (U)SafeInt<T>::MaxInt())
				{
					//corner case -1 * (MaxInt + 1) = MinInt
					//do lhs comparison first, since is cheaper than 64-bit test
					if(lhs.Value() == -1 && ((U)SafeInt<T>::MaxInt()+1) == rhs)
						return SafeInt<T>(SafeInt<T>::MinInt());

					if(lhs.Value() == 0)
						return SafeInt<T>(0);

					throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);
				}

				//now rhs must fit into T
				return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
			}
		}

		//now mixed sign where T is unsigned, U signed
			
		//negative numbers are always bad
		//test here to avoid having to test in constructors below
		//also allows some simplifying assumptions
		if(rhs < 0)
			throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

		if(sizeof(T) >= sizeof(U))
		{
			return SafeInt<T>(multiply(lhs.Value(), (T)rhs));
		}

		//U > T
		//else there is no corner case, but we do have to check overflow
		if(sizeof(U) < 4)
		{
			//both U and T can be contained in an unsigned int
			return SafeInt<T>((unsigned _int32)((unsigned _int32)lhs.m_int * (unsigned _int32)rhs));
		}
		else if(sizeof(U) == 4)
		{
			//now go up to 64-bit
			return SafeInt<T>((unsigned _int64)((unsigned _int64)lhs.m_int * (unsigned _int64)rhs));
		}
		else
		{
			//U = signed _int64, T = unsigned [char|_int16|int]
			SafeInt<U> u(rhs);
			u *= lhs.Value();

			//now bounds check
			if(u.Value() > (U)SafeInt<T>::MaxInt())
				throw SafeIntException(ERROR_ARITHMETIC_OVERFLOW);

			//it has to be safe, since rhs is non-negative
			return SafeInt<T>((T)u.Value());

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -