1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mfbt/CheckedInt.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,777 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Provides checked integers, detecting integer overflow and divide-by-0. */ 1.11 + 1.12 +#ifndef mozilla_CheckedInt_h 1.13 +#define mozilla_CheckedInt_h 1.14 + 1.15 +#include <stdint.h> 1.16 +#include "mozilla/Assertions.h" 1.17 +#include "mozilla/IntegerTypeTraits.h" 1.18 + 1.19 +namespace mozilla { 1.20 + 1.21 +template<typename T> class CheckedInt; 1.22 + 1.23 +namespace detail { 1.24 + 1.25 +/* 1.26 + * Step 1: manually record supported types 1.27 + * 1.28 + * What's nontrivial here is that there are different families of integer 1.29 + * types: basic integer types and stdint types. It is merrily undefined which 1.30 + * types from one family may be just typedefs for a type from another family. 1.31 + * 1.32 + * For example, on GCC 4.6, aside from the basic integer types, the only other 1.33 + * type that isn't just a typedef for some of them, is int8_t. 1.34 + */ 1.35 + 1.36 +struct UnsupportedType {}; 1.37 + 1.38 +template<typename IntegerType> 1.39 +struct IsSupportedPass2 1.40 +{ 1.41 + static const bool value = false; 1.42 +}; 1.43 + 1.44 +template<typename IntegerType> 1.45 +struct IsSupported 1.46 +{ 1.47 + static const bool value = IsSupportedPass2<IntegerType>::value; 1.48 +}; 1.49 + 1.50 +template<> 1.51 +struct IsSupported<int8_t> 1.52 +{ static const bool value = true; }; 1.53 + 1.54 +template<> 1.55 +struct IsSupported<uint8_t> 1.56 +{ static const bool value = true; }; 1.57 + 1.58 +template<> 1.59 +struct IsSupported<int16_t> 1.60 +{ static const bool value = true; }; 1.61 + 1.62 +template<> 1.63 +struct IsSupported<uint16_t> 1.64 +{ static const bool value = true; }; 1.65 + 1.66 +template<> 1.67 +struct IsSupported<int32_t> 1.68 +{ static const bool value = true; }; 1.69 + 1.70 +template<> 1.71 +struct IsSupported<uint32_t> 1.72 +{ static const bool value = true; }; 1.73 + 1.74 +template<> 1.75 +struct IsSupported<int64_t> 1.76 +{ static const bool value = true; }; 1.77 + 1.78 +template<> 1.79 +struct IsSupported<uint64_t> 1.80 +{ static const bool value = true; }; 1.81 + 1.82 + 1.83 +template<> 1.84 +struct IsSupportedPass2<char> 1.85 +{ static const bool value = true; }; 1.86 + 1.87 +template<> 1.88 +struct IsSupportedPass2<signed char> 1.89 +{ static const bool value = true; }; 1.90 + 1.91 +template<> 1.92 +struct IsSupportedPass2<unsigned char> 1.93 +{ static const bool value = true; }; 1.94 + 1.95 +template<> 1.96 +struct IsSupportedPass2<short> 1.97 +{ static const bool value = true; }; 1.98 + 1.99 +template<> 1.100 +struct IsSupportedPass2<unsigned short> 1.101 +{ static const bool value = true; }; 1.102 + 1.103 +template<> 1.104 +struct IsSupportedPass2<int> 1.105 +{ static const bool value = true; }; 1.106 + 1.107 +template<> 1.108 +struct IsSupportedPass2<unsigned int> 1.109 +{ static const bool value = true; }; 1.110 + 1.111 +template<> 1.112 +struct IsSupportedPass2<long> 1.113 +{ static const bool value = true; }; 1.114 + 1.115 +template<> 1.116 +struct IsSupportedPass2<unsigned long> 1.117 +{ static const bool value = true; }; 1.118 + 1.119 +template<> 1.120 +struct IsSupportedPass2<long long> 1.121 +{ static const bool value = true; }; 1.122 + 1.123 +template<> 1.124 +struct IsSupportedPass2<unsigned long long> 1.125 +{ static const bool value = true; }; 1.126 + 1.127 +/* 1.128 + * Step 2: Implement the actual validity checks. 1.129 + * 1.130 + * Ideas taken from IntegerLib, code different. 1.131 + */ 1.132 + 1.133 +template<typename IntegerType, size_t Size = sizeof(IntegerType)> 1.134 +struct TwiceBiggerType 1.135 +{ 1.136 + typedef typename detail::StdintTypeForSizeAndSignedness< 1.137 + sizeof(IntegerType) * 2, 1.138 + IsSigned<IntegerType>::value 1.139 + >::Type Type; 1.140 +}; 1.141 + 1.142 +template<typename IntegerType> 1.143 +struct TwiceBiggerType<IntegerType, 8> 1.144 +{ 1.145 + typedef UnsupportedType Type; 1.146 +}; 1.147 + 1.148 +template<typename T> 1.149 +inline bool 1.150 +HasSignBit(T x) 1.151 +{ 1.152 + // In C++, right bit shifts on negative values is undefined by the standard. 1.153 + // Notice that signed-to-unsigned conversions are always well-defined in the 1.154 + // standard, as the value congruent modulo 2**n as expected. By contrast, 1.155 + // unsigned-to-signed is only well-defined if the value is representable. 1.156 + return bool(typename MakeUnsigned<T>::Type(x) >> PositionOfSignBit<T>::value); 1.157 +} 1.158 + 1.159 +// Bitwise ops may return a larger type, so it's good to use this inline 1.160 +// helper guaranteeing that the result is really of type T. 1.161 +template<typename T> 1.162 +inline T 1.163 +BinaryComplement(T x) 1.164 +{ 1.165 + return ~x; 1.166 +} 1.167 + 1.168 +template<typename T, 1.169 + typename U, 1.170 + bool IsTSigned = IsSigned<T>::value, 1.171 + bool IsUSigned = IsSigned<U>::value> 1.172 +struct DoesRangeContainRange 1.173 +{ 1.174 +}; 1.175 + 1.176 +template<typename T, typename U, bool Signedness> 1.177 +struct DoesRangeContainRange<T, U, Signedness, Signedness> 1.178 +{ 1.179 + static const bool value = sizeof(T) >= sizeof(U); 1.180 +}; 1.181 + 1.182 +template<typename T, typename U> 1.183 +struct DoesRangeContainRange<T, U, true, false> 1.184 +{ 1.185 + static const bool value = sizeof(T) > sizeof(U); 1.186 +}; 1.187 + 1.188 +template<typename T, typename U> 1.189 +struct DoesRangeContainRange<T, U, false, true> 1.190 +{ 1.191 + static const bool value = false; 1.192 +}; 1.193 + 1.194 +template<typename T, 1.195 + typename U, 1.196 + bool IsTSigned = IsSigned<T>::value, 1.197 + bool IsUSigned = IsSigned<U>::value, 1.198 + bool DoesTRangeContainURange = DoesRangeContainRange<T, U>::value> 1.199 +struct IsInRangeImpl {}; 1.200 + 1.201 +template<typename T, typename U, bool IsTSigned, bool IsUSigned> 1.202 +struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true> 1.203 +{ 1.204 + static bool run(U) 1.205 + { 1.206 + return true; 1.207 + } 1.208 +}; 1.209 + 1.210 +template<typename T, typename U> 1.211 +struct IsInRangeImpl<T, U, true, true, false> 1.212 +{ 1.213 + static bool run(U x) 1.214 + { 1.215 + return x <= MaxValue<T>::value && x >= MinValue<T>::value; 1.216 + } 1.217 +}; 1.218 + 1.219 +template<typename T, typename U> 1.220 +struct IsInRangeImpl<T, U, false, false, false> 1.221 +{ 1.222 + static bool run(U x) 1.223 + { 1.224 + return x <= MaxValue<T>::value; 1.225 + } 1.226 +}; 1.227 + 1.228 +template<typename T, typename U> 1.229 +struct IsInRangeImpl<T, U, true, false, false> 1.230 +{ 1.231 + static bool run(U x) 1.232 + { 1.233 + return sizeof(T) > sizeof(U) || x <= U(MaxValue<T>::value); 1.234 + } 1.235 +}; 1.236 + 1.237 +template<typename T, typename U> 1.238 +struct IsInRangeImpl<T, U, false, true, false> 1.239 +{ 1.240 + static bool run(U x) 1.241 + { 1.242 + return sizeof(T) >= sizeof(U) 1.243 + ? x >= 0 1.244 + : x >= 0 && x <= U(MaxValue<T>::value); 1.245 + } 1.246 +}; 1.247 + 1.248 +template<typename T, typename U> 1.249 +inline bool 1.250 +IsInRange(U x) 1.251 +{ 1.252 + return IsInRangeImpl<T, U>::run(x); 1.253 +} 1.254 + 1.255 +template<typename T> 1.256 +inline bool 1.257 +IsAddValid(T x, T y) 1.258 +{ 1.259 + // Addition is valid if the sign of x+y is equal to either that of x or that 1.260 + // of y. Since the value of x+y is undefined if we have a signed type, we 1.261 + // compute it using the unsigned type of the same size. 1.262 + // Beware! These bitwise operations can return a larger integer type, 1.263 + // if T was a small type like int8_t, so we explicitly cast to T. 1.264 + 1.265 + typename MakeUnsigned<T>::Type ux = x; 1.266 + typename MakeUnsigned<T>::Type uy = y; 1.267 + typename MakeUnsigned<T>::Type result = ux + uy; 1.268 + return IsSigned<T>::value 1.269 + ? HasSignBit(BinaryComplement(T((result ^ x) & (result ^ y)))) 1.270 + : BinaryComplement(x) >= y; 1.271 +} 1.272 + 1.273 +template<typename T> 1.274 +inline bool 1.275 +IsSubValid(T x, T y) 1.276 +{ 1.277 + // Subtraction is valid if either x and y have same sign, or x-y and x have 1.278 + // same sign. Since the value of x-y is undefined if we have a signed type, 1.279 + // we compute it using the unsigned type of the same size. 1.280 + typename MakeUnsigned<T>::Type ux = x; 1.281 + typename MakeUnsigned<T>::Type uy = y; 1.282 + typename MakeUnsigned<T>::Type result = ux - uy; 1.283 + 1.284 + return IsSigned<T>::value 1.285 + ? HasSignBit(BinaryComplement(T((result ^ x) & (x ^ y)))) 1.286 + : x >= y; 1.287 +} 1.288 + 1.289 +template<typename T, 1.290 + bool IsTSigned = IsSigned<T>::value, 1.291 + bool TwiceBiggerTypeIsSupported = 1.292 + IsSupported<typename TwiceBiggerType<T>::Type>::value> 1.293 +struct IsMulValidImpl {}; 1.294 + 1.295 +template<typename T, bool IsTSigned> 1.296 +struct IsMulValidImpl<T, IsTSigned, true> 1.297 +{ 1.298 + static bool run(T x, T y) 1.299 + { 1.300 + typedef typename TwiceBiggerType<T>::Type TwiceBiggerType; 1.301 + TwiceBiggerType product = TwiceBiggerType(x) * TwiceBiggerType(y); 1.302 + return IsInRange<T>(product); 1.303 + } 1.304 +}; 1.305 + 1.306 +template<typename T> 1.307 +struct IsMulValidImpl<T, true, false> 1.308 +{ 1.309 + static bool run(T x, T y) 1.310 + { 1.311 + const T max = MaxValue<T>::value; 1.312 + const T min = MinValue<T>::value; 1.313 + 1.314 + if (x == 0 || y == 0) 1.315 + return true; 1.316 + 1.317 + if (x > 0) { 1.318 + return y > 0 1.319 + ? x <= max / y 1.320 + : y >= min / x; 1.321 + } 1.322 + 1.323 + // If we reach this point, we know that x < 0. 1.324 + return y > 0 1.325 + ? x >= min / y 1.326 + : y >= max / x; 1.327 + } 1.328 +}; 1.329 + 1.330 +template<typename T> 1.331 +struct IsMulValidImpl<T, false, false> 1.332 +{ 1.333 + static bool run(T x, T y) 1.334 + { 1.335 + return y == 0 || x <= MaxValue<T>::value / y; 1.336 + } 1.337 +}; 1.338 + 1.339 +template<typename T> 1.340 +inline bool 1.341 +IsMulValid(T x, T y) 1.342 +{ 1.343 + return IsMulValidImpl<T>::run(x, y); 1.344 +} 1.345 + 1.346 +template<typename T> 1.347 +inline bool 1.348 +IsDivValid(T x, T y) 1.349 +{ 1.350 + // Keep in mind that in the signed case, min/-1 is invalid because abs(min)>max. 1.351 + return y != 0 && 1.352 + !(IsSigned<T>::value && x == MinValue<T>::value && y == T(-1)); 1.353 +} 1.354 + 1.355 +template<typename T, bool IsTSigned = IsSigned<T>::value> 1.356 +struct IsModValidImpl; 1.357 + 1.358 +template<typename T> 1.359 +inline bool 1.360 +IsModValid(T x, T y) 1.361 +{ 1.362 + return IsModValidImpl<T>::run(x, y); 1.363 +} 1.364 + 1.365 +/* 1.366 + * Mod is pretty simple. 1.367 + * For now, let's just use the ANSI C definition: 1.368 + * If x or y are negative, the results are implementation defined. 1.369 + * Consider these invalid. 1.370 + * Undefined for y=0. 1.371 + * The result will never exceed either x or y. 1.372 + * 1.373 + * Checking that x>=0 is a warning when T is unsigned. 1.374 + */ 1.375 + 1.376 +template<typename T> 1.377 +struct IsModValidImpl<T, false> { 1.378 + static inline bool run(T x, T y) { 1.379 + return y >= 1; 1.380 + } 1.381 +}; 1.382 + 1.383 +template<typename T> 1.384 +struct IsModValidImpl<T, true> { 1.385 + static inline bool run(T x, T y) { 1.386 + if (x < 0) 1.387 + return false; 1.388 + 1.389 + return y >= 1; 1.390 + } 1.391 +}; 1.392 + 1.393 +template<typename T, bool IsSigned = IsSigned<T>::value> 1.394 +struct NegateImpl; 1.395 + 1.396 +template<typename T> 1.397 +struct NegateImpl<T, false> 1.398 +{ 1.399 + static CheckedInt<T> negate(const CheckedInt<T>& val) 1.400 + { 1.401 + // Handle negation separately for signed/unsigned, for simpler code and to 1.402 + // avoid an MSVC warning negating an unsigned value. 1.403 + return CheckedInt<T>(0, val.isValid() && val.mValue == 0); 1.404 + } 1.405 +}; 1.406 + 1.407 +template<typename T> 1.408 +struct NegateImpl<T, true> 1.409 +{ 1.410 + static CheckedInt<T> negate(const CheckedInt<T>& val) 1.411 + { 1.412 + // Watch out for the min-value, which (with twos-complement) can't be 1.413 + // negated as -min-value is then (max-value + 1). 1.414 + if (!val.isValid() || val.mValue == MinValue<T>::value) 1.415 + return CheckedInt<T>(val.mValue, false); 1.416 + return CheckedInt<T>(-val.mValue, true); 1.417 + } 1.418 +}; 1.419 + 1.420 +} // namespace detail 1.421 + 1.422 + 1.423 +/* 1.424 + * Step 3: Now define the CheckedInt class. 1.425 + */ 1.426 + 1.427 +/** 1.428 + * @class CheckedInt 1.429 + * @brief Integer wrapper class checking for integer overflow and other errors 1.430 + * @param T the integer type to wrap. Can be any type among the following: 1.431 + * - any basic integer type such as |int| 1.432 + * - any stdint type such as |int8_t| 1.433 + * 1.434 + * This class implements guarded integer arithmetic. Do a computation, check 1.435 + * that isValid() returns true, you then have a guarantee that no problem, such 1.436 + * as integer overflow, happened during this computation, and you can call 1.437 + * value() to get the plain integer value. 1.438 + * 1.439 + * The arithmetic operators in this class are guaranteed not to raise a signal 1.440 + * (e.g. in case of a division by zero). 1.441 + * 1.442 + * For example, suppose that you want to implement a function that computes 1.443 + * (x+y)/z, that doesn't crash if z==0, and that reports on error (divide by 1.444 + * zero or integer overflow). You could code it as follows: 1.445 + @code 1.446 + bool computeXPlusYOverZ(int x, int y, int z, int *result) 1.447 + { 1.448 + CheckedInt<int> checkedResult = (CheckedInt<int>(x) + y) / z; 1.449 + if (checkedResult.isValid()) { 1.450 + *result = checkedResult.value(); 1.451 + return true; 1.452 + } else { 1.453 + return false; 1.454 + } 1.455 + } 1.456 + @endcode 1.457 + * 1.458 + * Implicit conversion from plain integers to checked integers is allowed. The 1.459 + * plain integer is checked to be in range before being casted to the 1.460 + * destination type. This means that the following lines all compile, and the 1.461 + * resulting CheckedInts are correctly detected as valid or invalid: 1.462 + * @code 1.463 + // 1 is of type int, is found to be in range for uint8_t, x is valid 1.464 + CheckedInt<uint8_t> x(1); 1.465 + // -1 is of type int, is found not to be in range for uint8_t, x is invalid 1.466 + CheckedInt<uint8_t> x(-1); 1.467 + // -1 is of type int, is found to be in range for int8_t, x is valid 1.468 + CheckedInt<int8_t> x(-1); 1.469 + // 1000 is of type int16_t, is found not to be in range for int8_t, 1.470 + // x is invalid 1.471 + CheckedInt<int8_t> x(int16_t(1000)); 1.472 + // 3123456789 is of type uint32_t, is found not to be in range for int32_t, 1.473 + // x is invalid 1.474 + CheckedInt<int32_t> x(uint32_t(3123456789)); 1.475 + * @endcode 1.476 + * Implicit conversion from 1.477 + * checked integers to plain integers is not allowed. As shown in the 1.478 + * above example, to get the value of a checked integer as a normal integer, 1.479 + * call value(). 1.480 + * 1.481 + * Arithmetic operations between checked and plain integers is allowed; the 1.482 + * result type is the type of the checked integer. 1.483 + * 1.484 + * Checked integers of different types cannot be used in the same arithmetic 1.485 + * expression. 1.486 + * 1.487 + * There are convenience typedefs for all stdint types, of the following form 1.488 + * (these are just 2 examples): 1.489 + @code 1.490 + typedef CheckedInt<int32_t> CheckedInt32; 1.491 + typedef CheckedInt<uint16_t> CheckedUint16; 1.492 + @endcode 1.493 + */ 1.494 +template<typename T> 1.495 +class CheckedInt 1.496 +{ 1.497 + protected: 1.498 + T mValue; 1.499 + bool mIsValid; 1.500 + 1.501 + template<typename U> 1.502 + CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid) 1.503 + { 1.504 + static_assert(detail::IsSupported<T>::value && 1.505 + detail::IsSupported<U>::value, 1.506 + "This type is not supported by CheckedInt"); 1.507 + } 1.508 + 1.509 + friend struct detail::NegateImpl<T>; 1.510 + 1.511 + public: 1.512 + /** 1.513 + * Constructs a checked integer with given @a value. The checked integer is 1.514 + * initialized as valid or invalid depending on whether the @a value 1.515 + * is in range. 1.516 + * 1.517 + * This constructor is not explicit. Instead, the type of its argument is a 1.518 + * separate template parameter, ensuring that no conversion is performed 1.519 + * before this constructor is actually called. As explained in the above 1.520 + * documentation for class CheckedInt, this constructor checks that its 1.521 + * argument is valid. 1.522 + */ 1.523 + template<typename U> 1.524 + CheckedInt(U aValue) 1.525 + : mValue(T(aValue)), 1.526 + mIsValid(detail::IsInRange<T>(aValue)) 1.527 + { 1.528 + static_assert(detail::IsSupported<T>::value && 1.529 + detail::IsSupported<U>::value, 1.530 + "This type is not supported by CheckedInt"); 1.531 + } 1.532 + 1.533 + template<typename U> 1.534 + friend class CheckedInt; 1.535 + 1.536 + template<typename U> 1.537 + CheckedInt<U> toChecked() const 1.538 + { 1.539 + CheckedInt<U> ret(mValue); 1.540 + ret.mIsValid = ret.mIsValid && mIsValid; 1.541 + return ret; 1.542 + } 1.543 + 1.544 + /** Constructs a valid checked integer with initial value 0 */ 1.545 + CheckedInt() : mValue(0), mIsValid(true) 1.546 + { 1.547 + static_assert(detail::IsSupported<T>::value, 1.548 + "This type is not supported by CheckedInt"); 1.549 + } 1.550 + 1.551 + /** @returns the actual value */ 1.552 + T value() const 1.553 + { 1.554 + MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)"); 1.555 + return mValue; 1.556 + } 1.557 + 1.558 + /** 1.559 + * @returns true if the checked integer is valid, i.e. is not the result 1.560 + * of an invalid operation or of an operation involving an invalid checked 1.561 + * integer 1.562 + */ 1.563 + bool isValid() const 1.564 + { 1.565 + return mIsValid; 1.566 + } 1.567 + 1.568 + template<typename U> 1.569 + friend CheckedInt<U> operator +(const CheckedInt<U>& lhs, 1.570 + const CheckedInt<U>& rhs); 1.571 + template<typename U> 1.572 + CheckedInt& operator +=(U rhs); 1.573 + 1.574 + template<typename U> 1.575 + friend CheckedInt<U> operator -(const CheckedInt<U>& lhs, 1.576 + const CheckedInt<U>& rhs); 1.577 + template<typename U> 1.578 + CheckedInt& operator -=(U rhs); 1.579 + 1.580 + template<typename U> 1.581 + friend CheckedInt<U> operator *(const CheckedInt<U>& lhs, 1.582 + const CheckedInt<U>& rhs); 1.583 + template<typename U> 1.584 + CheckedInt& operator *=(U rhs); 1.585 + 1.586 + template<typename U> 1.587 + friend CheckedInt<U> operator /(const CheckedInt<U>& lhs, 1.588 + const CheckedInt<U>& rhs); 1.589 + template<typename U> 1.590 + CheckedInt& operator /=(U rhs); 1.591 + 1.592 + template<typename U> 1.593 + friend CheckedInt<U> operator %(const CheckedInt<U>& lhs, 1.594 + const CheckedInt<U>& rhs); 1.595 + template<typename U> 1.596 + CheckedInt& operator %=(U rhs); 1.597 + 1.598 + CheckedInt operator -() const 1.599 + { 1.600 + return detail::NegateImpl<T>::negate(*this); 1.601 + } 1.602 + 1.603 + /** 1.604 + * @returns true if the left and right hand sides are valid 1.605 + * and have the same value. 1.606 + * 1.607 + * Note that these semantics are the reason why we don't offer 1.608 + * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b) 1.609 + * but that would mean that whenever a or b is invalid, a!=b 1.610 + * is always true, which would be very confusing. 1.611 + * 1.612 + * For similar reasons, operators <, >, <=, >= would be very tricky to 1.613 + * specify, so we just avoid offering them. 1.614 + * 1.615 + * Notice that these == semantics are made more reasonable by these facts: 1.616 + * 1. a==b implies equality at the raw data level 1.617 + * (the converse is false, as a==b is never true among invalids) 1.618 + * 2. This is similar to the behavior of IEEE floats, where a==b 1.619 + * means that a and b have the same value *and* neither is NaN. 1.620 + */ 1.621 + bool operator ==(const CheckedInt& other) const 1.622 + { 1.623 + return mIsValid && other.mIsValid && mValue == other.mValue; 1.624 + } 1.625 + 1.626 + /** prefix ++ */ 1.627 + CheckedInt& operator++() 1.628 + { 1.629 + *this += 1; 1.630 + return *this; 1.631 + } 1.632 + 1.633 + /** postfix ++ */ 1.634 + CheckedInt operator++(int) 1.635 + { 1.636 + CheckedInt tmp = *this; 1.637 + *this += 1; 1.638 + return tmp; 1.639 + } 1.640 + 1.641 + /** prefix -- */ 1.642 + CheckedInt& operator--() 1.643 + { 1.644 + *this -= 1; 1.645 + return *this; 1.646 + } 1.647 + 1.648 + /** postfix -- */ 1.649 + CheckedInt operator--(int) 1.650 + { 1.651 + CheckedInt tmp = *this; 1.652 + *this -= 1; 1.653 + return tmp; 1.654 + } 1.655 + 1.656 + private: 1.657 + /** 1.658 + * The !=, <, <=, >, >= operators are disabled: 1.659 + * see the comment on operator==. 1.660 + */ 1.661 + template<typename U> 1.662 + bool operator !=(U other) const MOZ_DELETE; 1.663 + template<typename U> 1.664 + bool operator <(U other) const MOZ_DELETE; 1.665 + template<typename U> 1.666 + bool operator <=(U other) const MOZ_DELETE; 1.667 + template<typename U> 1.668 + bool operator >(U other) const MOZ_DELETE; 1.669 + template<typename U> 1.670 + bool operator >=(U other) const MOZ_DELETE; 1.671 +}; 1.672 + 1.673 +#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \ 1.674 +template<typename T> \ 1.675 +inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, \ 1.676 + const CheckedInt<T> &rhs) \ 1.677 +{ \ 1.678 + if (!detail::Is##NAME##Valid(lhs.mValue, rhs.mValue)) \ 1.679 + return CheckedInt<T>(0, false); \ 1.680 + \ 1.681 + return CheckedInt<T>(lhs.mValue OP rhs.mValue, \ 1.682 + lhs.mIsValid && rhs.mIsValid); \ 1.683 +} 1.684 + 1.685 +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +) 1.686 +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -) 1.687 +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *) 1.688 +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /) 1.689 +MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %) 1.690 + 1.691 +#undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR 1.692 + 1.693 +// Implement castToCheckedInt<T>(x), making sure that 1.694 +// - it allows x to be either a CheckedInt<T> or any integer type 1.695 +// that can be casted to T 1.696 +// - if x is already a CheckedInt<T>, we just return a reference to it, 1.697 +// instead of copying it (optimization) 1.698 + 1.699 +namespace detail { 1.700 + 1.701 +template<typename T, typename U> 1.702 +struct CastToCheckedIntImpl 1.703 +{ 1.704 + typedef CheckedInt<T> ReturnType; 1.705 + static CheckedInt<T> run(U u) { return u; } 1.706 +}; 1.707 + 1.708 +template<typename T> 1.709 +struct CastToCheckedIntImpl<T, CheckedInt<T> > 1.710 +{ 1.711 + typedef const CheckedInt<T>& ReturnType; 1.712 + static const CheckedInt<T>& run(const CheckedInt<T>& u) { return u; } 1.713 +}; 1.714 + 1.715 +} // namespace detail 1.716 + 1.717 +template<typename T, typename U> 1.718 +inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType 1.719 +castToCheckedInt(U u) 1.720 +{ 1.721 + static_assert(detail::IsSupported<T>::value && 1.722 + detail::IsSupported<U>::value, 1.723 + "This type is not supported by CheckedInt"); 1.724 + return detail::CastToCheckedIntImpl<T, U>::run(u); 1.725 +} 1.726 + 1.727 +#define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \ 1.728 +template<typename T> \ 1.729 +template<typename U> \ 1.730 +CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U rhs) \ 1.731 +{ \ 1.732 + *this = *this OP castToCheckedInt<T>(rhs); \ 1.733 + return *this; \ 1.734 +} \ 1.735 +template<typename T, typename U> \ 1.736 +inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, U rhs) \ 1.737 +{ \ 1.738 + return lhs OP castToCheckedInt<T>(rhs); \ 1.739 +} \ 1.740 +template<typename T, typename U> \ 1.741 +inline CheckedInt<T> operator OP(U lhs, const CheckedInt<T> &rhs) \ 1.742 +{ \ 1.743 + return castToCheckedInt<T>(lhs) OP rhs; \ 1.744 +} 1.745 + 1.746 +MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=) 1.747 +MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=) 1.748 +MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=) 1.749 +MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=) 1.750 +MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=) 1.751 + 1.752 +#undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS 1.753 + 1.754 +template<typename T, typename U> 1.755 +inline bool 1.756 +operator ==(const CheckedInt<T> &lhs, U rhs) 1.757 +{ 1.758 + return lhs == castToCheckedInt<T>(rhs); 1.759 +} 1.760 + 1.761 +template<typename T, typename U> 1.762 +inline bool 1.763 +operator ==(U lhs, const CheckedInt<T> &rhs) 1.764 +{ 1.765 + return castToCheckedInt<T>(lhs) == rhs; 1.766 +} 1.767 + 1.768 +// Convenience typedefs. 1.769 +typedef CheckedInt<int8_t> CheckedInt8; 1.770 +typedef CheckedInt<uint8_t> CheckedUint8; 1.771 +typedef CheckedInt<int16_t> CheckedInt16; 1.772 +typedef CheckedInt<uint16_t> CheckedUint16; 1.773 +typedef CheckedInt<int32_t> CheckedInt32; 1.774 +typedef CheckedInt<uint32_t> CheckedUint32; 1.775 +typedef CheckedInt<int64_t> CheckedInt64; 1.776 +typedef CheckedInt<uint64_t> CheckedUint64; 1.777 + 1.778 +} // namespace mozilla 1.779 + 1.780 +#endif /* mozilla_CheckedInt_h */