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/. */ 6 7 /* Provides checked integers, detecting integer overflow and divide-by-0. */ 8 9 #ifndef mozilla_CheckedInt_h 10 #define mozilla_CheckedInt_h 11 12 #include <stdint.h> 13 #include "mozilla/Assertions.h" 14 #include "mozilla/Attributes.h" 15 #include "mozilla/IntegerTypeTraits.h" 16 #include <limits> 17 #include <type_traits> 18 19 #define MOZILLA_CHECKEDINT_COMPARABLE_VERSION(major, minor, patch) \ 20 (major << 16 | minor << 8 | patch) 21 22 // Probe for builtin math overflow support. Disabled for 32-bit builds for now 23 // since "gcc -m32" claims to support these but its implementation is buggy. 24 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82274 25 // Also disabled for clang before version 7 (resp. Xcode clang 10.0.1): while 26 // clang 5 and 6 have a working __builtin_add_overflow, it is not constexpr. 27 #if defined(HAVE_64BIT_BUILD) 28 # if defined(__has_builtin) && \ 29 (!defined(__clang_major__) || \ 30 (!defined(__apple_build_version__) && __clang_major__ >= 7) || \ 31 (defined(__apple_build_version__) && \ 32 MOZILLA_CHECKEDINT_COMPARABLE_VERSION( \ 33 __clang_major__, __clang_minor__, __clang_patchlevel__) >= \ 34 MOZILLA_CHECKEDINT_COMPARABLE_VERSION(10, 0, 1))) 35 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__has_builtin(__builtin_add_overflow)) 36 # elif defined(__GNUC__) 37 // (clang also defines __GNUC__ but it supports __has_builtin since at least 38 // v3.1 (released in 2012) so it won't get here.) 39 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (__GNUC__ >= 5) 40 # else 41 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0) 42 # endif 43 #else 44 # define MOZ_HAS_BUILTIN_OP_OVERFLOW (0) 45 #endif 46 47 #undef MOZILLA_CHECKEDINT_COMPARABLE_VERSION 48 49 namespace mozilla { 50 51 template <typename T> 52 class CheckedInt; 53 54 namespace detail { 55 56 /* 57 * Step 1: manually record supported types 58 * 59 * What's nontrivial here is that there are different families of integer 60 * types: basic integer types and stdint types. It is merrily undefined which 61 * types from one family may be just typedefs for a type from another family. 62 * 63 * For example, on GCC 4.6, aside from the basic integer types, the only other 64 * type that isn't just a typedef for some of them, is int8_t. 65 */ 66 67 struct UnsupportedType {}; 68 69 template <typename IntegerType> 70 struct IsSupportedPass2 { 71 static const bool value = false; 72 }; 73 74 template <typename IntegerType> 75 struct IsSupported { 76 static const bool value = IsSupportedPass2<IntegerType>::value; 77 }; 78 79 template <> 80 struct IsSupported<int8_t> { 81 static const bool value = true; 82 }; 83 84 template <> 85 struct IsSupported<uint8_t> { 86 static const bool value = true; 87 }; 88 89 template <> 90 struct IsSupported<int16_t> { 91 static const bool value = true; 92 }; 93 94 template <> 95 struct IsSupported<uint16_t> { 96 static const bool value = true; 97 }; 98 99 template <> 100 struct IsSupported<int32_t> { 101 static const bool value = true; 102 }; 103 104 template <> 105 struct IsSupported<uint32_t> { 106 static const bool value = true; 107 }; 108 109 template <> 110 struct IsSupported<int64_t> { 111 static const bool value = true; 112 }; 113 114 template <> 115 struct IsSupported<uint64_t> { 116 static const bool value = true; 117 }; 118 119 template <> 120 struct IsSupportedPass2<char> { 121 static const bool value = true; 122 }; 123 124 template <> 125 struct IsSupportedPass2<signed char> { 126 static const bool value = true; 127 }; 128 129 template <> 130 struct IsSupportedPass2<unsigned char> { 131 static const bool value = true; 132 }; 133 134 template <> 135 struct IsSupportedPass2<short> { 136 static const bool value = true; 137 }; 138 139 template <> 140 struct IsSupportedPass2<unsigned short> { 141 static const bool value = true; 142 }; 143 144 template <> 145 struct IsSupportedPass2<int> { 146 static const bool value = true; 147 }; 148 149 template <> 150 struct IsSupportedPass2<unsigned int> { 151 static const bool value = true; 152 }; 153 154 template <> 155 struct IsSupportedPass2<long> { 156 static const bool value = true; 157 }; 158 159 template <> 160 struct IsSupportedPass2<unsigned long> { 161 static const bool value = true; 162 }; 163 164 template <> 165 struct IsSupportedPass2<long long> { 166 static const bool value = true; 167 }; 168 169 template <> 170 struct IsSupportedPass2<unsigned long long> { 171 static const bool value = true; 172 }; 173 174 /* 175 * Step 2: Implement the actual validity checks. 176 * 177 * Ideas taken from IntegerLib, code different. 178 */ 179 180 template <typename IntegerType, size_t Size = sizeof(IntegerType)> 181 struct TwiceBiggerType { 182 typedef typename detail::StdintTypeForSizeAndSignedness< 183 sizeof(IntegerType) * 2, std::is_signed_v<IntegerType>>::Type Type; 184 }; 185 186 template <typename IntegerType> 187 struct TwiceBiggerType<IntegerType, 8> { 188 typedef UnsupportedType Type; 189 }; 190 191 template <typename T> 192 constexpr bool HasSignBit(T aX) { 193 // In C++, right bit shifts on negative values is undefined by the standard. 194 // Notice that signed-to-unsigned conversions are always well-defined in the 195 // standard, as the value congruent modulo 2**n as expected. By contrast, 196 // unsigned-to-signed is only well-defined if the value is representable. 197 return bool(std::make_unsigned_t<T>(aX) >> PositionOfSignBit<T>::value); 198 } 199 200 // Bitwise ops may return a larger type, so it's good to use this inline 201 // helper guaranteeing that the result is really of type T. 202 template <typename T> 203 constexpr T BinaryComplement(T aX) { 204 return ~aX; 205 } 206 207 template <typename T, typename U, bool IsTSigned = std::is_signed_v<T>, 208 bool IsUSigned = std::is_signed_v<U>> 209 struct DoesRangeContainRange {}; 210 211 template <typename T, typename U, bool Signedness> 212 struct DoesRangeContainRange<T, U, Signedness, Signedness> { 213 static const bool value = sizeof(T) >= sizeof(U); 214 }; 215 216 template <typename T, typename U> 217 struct DoesRangeContainRange<T, U, true, false> { 218 static const bool value = sizeof(T) > sizeof(U); 219 }; 220 221 template <typename T, typename U> 222 struct DoesRangeContainRange<T, U, false, true> { 223 static const bool value = false; 224 }; 225 226 template <typename T, typename U, bool IsTSigned = std::is_signed_v<T>, 227 bool IsUSigned = std::is_signed_v<U>, 228 bool DoesTRangeContainURange = DoesRangeContainRange<T, U>::value> 229 struct IsInRangeImpl {}; 230 231 template <typename T, typename U, bool IsTSigned, bool IsUSigned> 232 struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true> { 233 static constexpr bool run(U) { return true; } 234 }; 235 236 template <typename T, typename U> 237 struct IsInRangeImpl<T, U, true, true, false> { 238 static constexpr bool run(U aX) { 239 return aX <= std::numeric_limits<T>::max() && 240 aX >= std::numeric_limits<T>::min(); 241 } 242 }; 243 244 template <typename T, typename U> 245 struct IsInRangeImpl<T, U, false, false, false> { 246 static constexpr bool run(U aX) { 247 return aX <= std::numeric_limits<T>::max(); 248 } 249 }; 250 251 template <typename T, typename U> 252 struct IsInRangeImpl<T, U, true, false, false> { 253 static constexpr bool run(U aX) { 254 return sizeof(T) > sizeof(U) || aX <= U(std::numeric_limits<T>::max()); 255 } 256 }; 257 258 template <typename T, typename U> 259 struct IsInRangeImpl<T, U, false, true, false> { 260 static constexpr bool run(U aX) { 261 return sizeof(T) >= sizeof(U) 262 ? aX >= 0 263 : aX >= 0 && aX <= U(std::numeric_limits<T>::max()); 264 } 265 }; 266 267 template <typename T, typename U> 268 constexpr bool IsInRange(U aX) { 269 return IsInRangeImpl<T, U>::run(aX); 270 } 271 272 template <typename T> 273 constexpr bool IsAddValid(T aX, T aY) { 274 #if MOZ_HAS_BUILTIN_OP_OVERFLOW 275 T dummy; 276 return !__builtin_add_overflow(aX, aY, &dummy); 277 #else 278 // Addition is valid if the sign of aX+aY is equal to either that of aX or 279 // that of aY. Since the value of aX+aY is undefined if we have a signed 280 // type, we compute it using the unsigned type of the same size. Beware! 281 // These bitwise operations can return a larger integer type, if T was a 282 // small type like int8_t, so we explicitly cast to T. 283 284 std::make_unsigned_t<T> ux = aX; 285 std::make_unsigned_t<T> uy = aY; 286 std::make_unsigned_t<T> result = ux + uy; 287 return std::is_signed_v<T> 288 ? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY)))) 289 : BinaryComplement(aX) >= aY; 290 #endif 291 } 292 293 template <typename T> 294 constexpr bool IsSubValid(T aX, T aY) { 295 #if MOZ_HAS_BUILTIN_OP_OVERFLOW 296 T dummy; 297 return !__builtin_sub_overflow(aX, aY, &dummy); 298 #else 299 // Subtraction is valid if either aX and aY have same sign, or aX-aY and aX 300 // have same sign. Since the value of aX-aY is undefined if we have a signed 301 // type, we compute it using the unsigned type of the same size. 302 std::make_unsigned_t<T> ux = aX; 303 std::make_unsigned_t<T> uy = aY; 304 std::make_unsigned_t<T> result = ux - uy; 305 306 return std::is_signed_v<T> 307 ? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY)))) 308 : aX >= aY; 309 #endif 310 } 311 312 template <typename T, bool IsTSigned = std::is_signed_v<T>, 313 bool TwiceBiggerTypeIsSupported = 314 IsSupported<typename TwiceBiggerType<T>::Type>::value> 315 struct IsMulValidImpl {}; 316 317 template <typename T, bool IsTSigned> 318 struct IsMulValidImpl<T, IsTSigned, true> { 319 static constexpr bool run(T aX, T aY) { 320 typedef typename TwiceBiggerType<T>::Type TwiceBiggerType; 321 TwiceBiggerType product = TwiceBiggerType(aX) * TwiceBiggerType(aY); 322 return IsInRange<T>(product); 323 } 324 }; 325 326 template <typename T> 327 struct IsMulValidImpl<T, true, false> { 328 static constexpr bool run(T aX, T aY) { 329 const T max = std::numeric_limits<T>::max(); 330 const T min = std::numeric_limits<T>::min(); 331 332 if (aX == 0 || aY == 0) { 333 return true; 334 } 335 if (aX > 0) { 336 return aY > 0 ? aX <= max / aY : aY >= min / aX; 337 } 338 339 // If we reach this point, we know that aX < 0. 340 return aY > 0 ? aX >= min / aY : aY >= max / aX; 341 } 342 }; 343 344 template <typename T> 345 struct IsMulValidImpl<T, false, false> { 346 static constexpr bool run(T aX, T aY) { 347 return aY == 0 || aX <= std::numeric_limits<T>::max() / aY; 348 } 349 }; 350 351 template <typename T> 352 inline bool IsMulValid(T aX, T aY) { 353 #if MOZ_HAS_BUILTIN_OP_OVERFLOW 354 T dummy; 355 return !__builtin_mul_overflow(aX, aY, &dummy); 356 #else 357 return IsMulValidImpl<T>::run(aX, aY); 358 #endif 359 } 360 361 template <typename T> 362 constexpr bool IsDivValid(T aX, T aY) { 363 // Keep in mind that in the signed case, min/-1 is invalid because 364 // abs(min)>max. 365 return aY != 0 && !(std::is_signed_v<T> && 366 aX == std::numeric_limits<T>::min() && aY == T(-1)); 367 } 368 369 template <typename T, bool IsTSigned = std::is_signed_v<T>> 370 struct IsModValidImpl; 371 372 template <typename T> 373 constexpr bool IsModValid(T aX, T aY) { 374 return IsModValidImpl<T>::run(aX, aY); 375 } 376 377 /* 378 * Mod is pretty simple. 379 * For now, let's just use the ANSI C definition: 380 * If aX or aY are negative, the results are implementation defined. 381 * Consider these invalid. 382 * Undefined for aY=0. 383 * The result will never exceed either aX or aY. 384 * 385 * Checking that aX>=0 is a warning when T is unsigned. 386 */ 387 388 template <typename T> 389 struct IsModValidImpl<T, false> { 390 static constexpr bool run(T aX, T aY) { return aY >= 1; } 391 }; 392 393 template <typename T> 394 struct IsModValidImpl<T, true> { 395 static constexpr bool run(T aX, T aY) { 396 if (aX < 0) { 397 return false; 398 } 399 return aY >= 1; 400 } 401 }; 402 403 template <typename T, bool IsSigned = std::is_signed_v<T>> 404 struct NegateImpl; 405 406 template <typename T> 407 struct NegateImpl<T, false> { 408 static constexpr CheckedInt<T> negate(const CheckedInt<T>& aVal) { 409 // Handle negation separately for signed/unsigned, for simpler code and to 410 // avoid an MSVC warning negating an unsigned value. 411 static_assert(detail::IsInRange<T>(0), "Integer type can't represent 0"); 412 return CheckedInt<T>(T(0), aVal.isValid() && aVal.mValue == 0); 413 } 414 }; 415 416 template <typename T> 417 struct NegateImpl<T, true> { 418 static constexpr CheckedInt<T> negate(const CheckedInt<T>& aVal) { 419 // Watch out for the min-value, which (with twos-complement) can't be 420 // negated as -min-value is then (max-value + 1). 421 if (!aVal.isValid() || aVal.mValue == std::numeric_limits<T>::min()) { 422 return CheckedInt<T>(aVal.mValue, false); 423 } 424 /* For some T, arithmetic ops automatically promote to a wider type, so 425 * explitly do the narrowing cast here. The narrowing cast is valid because 426 * we did the check for min value above. */ 427 return CheckedInt<T>(T(-aVal.mValue), true); 428 } 429 }; 430 431 } // namespace detail 432 433 /* 434 * Step 3: Now define the CheckedInt class. 435 */ 436 437 /** 438 * @class CheckedInt 439 * @brief Integer wrapper class checking for integer overflow and other errors 440 * @param T the integer type to wrap. Can be any type among the following: 441 * - any basic integer type such as |int| 442 * - any stdint type such as |int8_t| 443 * 444 * This class implements guarded integer arithmetic. Do a computation, check 445 * that isValid() returns true, you then have a guarantee that no problem, such 446 * as integer overflow, happened during this computation, and you can call 447 * value() to get the plain integer value. 448 * 449 * The arithmetic operators in this class are guaranteed not to raise a signal 450 * (e.g. in case of a division by zero). 451 * 452 * For example, suppose that you want to implement a function that computes 453 * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by 454 * zero or integer overflow). You could code it as follows: 455 @code 456 bool computeXPlusYOverZ(int aX, int aY, int aZ, int* aResult) 457 { 458 CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ; 459 if (checkedResult.isValid()) { 460 *aResult = checkedResult.value(); 461 return true; 462 } else { 463 return false; 464 } 465 } 466 @endcode 467 * 468 * Implicit conversion from plain integers to checked integers is allowed. The 469 * plain integer is checked to be in range before being casted to the 470 * destination type. This means that the following lines all compile, and the 471 * resulting CheckedInts are correctly detected as valid or invalid: 472 * @code 473 // 1 is of type int, is found to be in range for uint8_t, x is valid 474 CheckedInt<uint8_t> x(1); 475 // -1 is of type int, is found not to be in range for uint8_t, x is invalid 476 CheckedInt<uint8_t> x(-1); 477 // -1 is of type int, is found to be in range for int8_t, x is valid 478 CheckedInt<int8_t> x(-1); 479 // 1000 is of type int16_t, is found not to be in range for int8_t, 480 // x is invalid 481 CheckedInt<int8_t> x(int16_t(1000)); 482 // 3123456789 is of type uint32_t, is found not to be in range for int32_t, 483 // x is invalid 484 CheckedInt<int32_t> x(uint32_t(3123456789)); 485 * @endcode 486 * Implicit conversion from 487 * checked integers to plain integers is not allowed. As shown in the 488 * above example, to get the value of a checked integer as a normal integer, 489 * call value(). 490 * 491 * Arithmetic operations between checked and plain integers is allowed; the 492 * result type is the type of the checked integer. 493 * 494 * Checked integers of different types cannot be used in the same arithmetic 495 * expression. 496 * 497 * There are convenience typedefs for all stdint types, of the following form 498 * (these are just 2 examples): 499 @code 500 typedef CheckedInt<int32_t> CheckedInt32; 501 typedef CheckedInt<uint16_t> CheckedUint16; 502 @endcode 503 */ 504 template <typename T> 505 class CheckedInt { 506 protected: 507 T mValue; 508 bool mIsValid; 509 510 template <typename U> 511 constexpr CheckedInt(U aValue, bool aIsValid) 512 : mValue(aValue), mIsValid(aIsValid) { 513 static_assert(std::is_same_v<T, U>, 514 "this constructor must accept only T values"); 515 static_assert(detail::IsSupported<T>::value, 516 "This type is not supported by CheckedInt"); 517 } 518 519 friend struct detail::NegateImpl<T>; 520 521 public: 522 /** 523 * Constructs a checked integer with given @a value. The checked integer is 524 * initialized as valid or invalid depending on whether the @a value 525 * is in range. 526 * 527 * This constructor is not explicit. Instead, the type of its argument is a 528 * separate template parameter, ensuring that no conversion is performed 529 * before this constructor is actually called. As explained in the above 530 * documentation for class CheckedInt, this constructor checks that its 531 * argument is valid. 532 */ 533 template <typename U> 534 MOZ_IMPLICIT MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT constexpr CheckedInt(U aValue) 535 : mValue(T(aValue)), mIsValid(detail::IsInRange<T>(aValue)) { 536 static_assert( 537 detail::IsSupported<T>::value && detail::IsSupported<U>::value, 538 "This type is not supported by CheckedInt"); 539 } 540 541 template <typename U> 542 friend class CheckedInt; 543 544 template <typename U> 545 constexpr CheckedInt<U> toChecked() const { 546 CheckedInt<U> ret(mValue); 547 ret.mIsValid = ret.mIsValid && mIsValid; 548 return ret; 549 } 550 551 /** Constructs a valid checked integer with initial value 0 */ 552 constexpr CheckedInt() : mValue(T(0)), mIsValid(true) { 553 static_assert(detail::IsSupported<T>::value, 554 "This type is not supported by CheckedInt"); 555 static_assert(detail::IsInRange<T>(0), "Integer type can't represent 0"); 556 } 557 558 /** @returns the actual value */ 559 constexpr T value() const { 560 MOZ_DIAGNOSTIC_ASSERT( 561 mIsValid, 562 "Invalid checked integer (division by zero or integer overflow)"); 563 return mValue; 564 } 565 566 /** 567 * @returns true if the checked integer is valid, i.e. is not the result 568 * of an invalid operation or of an operation involving an invalid checked 569 * integer 570 */ 571 constexpr bool isValid() const { return mIsValid; } 572 573 template <typename U> 574 friend constexpr CheckedInt<U> operator+(const CheckedInt<U>& aLhs, 575 const CheckedInt<U>& aRhs); 576 template <typename U> 577 constexpr CheckedInt& operator+=(U aRhs); 578 constexpr CheckedInt& operator+=(const CheckedInt<T>& aRhs); 579 580 template <typename U> 581 friend constexpr CheckedInt<U> operator-(const CheckedInt<U>& aLhs, 582 const CheckedInt<U>& aRhs); 583 template <typename U> 584 constexpr CheckedInt& operator-=(U aRhs); 585 constexpr CheckedInt& operator-=(const CheckedInt<T>& aRhs); 586 587 template <typename U> 588 friend constexpr CheckedInt<U> operator*(const CheckedInt<U>& aLhs, 589 const CheckedInt<U>& aRhs); 590 template <typename U> 591 constexpr CheckedInt& operator*=(U aRhs); 592 constexpr CheckedInt& operator*=(const CheckedInt<T>& aRhs); 593 594 template <typename U> 595 friend constexpr CheckedInt<U> operator/(const CheckedInt<U>& aLhs, 596 const CheckedInt<U>& aRhs); 597 template <typename U> 598 constexpr CheckedInt& operator/=(U aRhs); 599 constexpr CheckedInt& operator/=(const CheckedInt<T>& aRhs); 600 601 template <typename U> 602 friend constexpr CheckedInt<U> operator%(const CheckedInt<U>& aLhs, 603 const CheckedInt<U>& aRhs); 604 template <typename U> 605 constexpr CheckedInt& operator%=(U aRhs); 606 constexpr CheckedInt& operator%=(const CheckedInt<T>& aRhs); 607 608 constexpr CheckedInt operator-() const { 609 return detail::NegateImpl<T>::negate(*this); 610 } 611 612 /** 613 * @returns true if the left and right hand sides are valid 614 * and have the same value. 615 * 616 * Note that these semantics are the reason why we don't offer 617 * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b) 618 * but that would mean that whenever a or b is invalid, a!=b 619 * is always true, which would be very confusing. 620 * 621 * For similar reasons, operators <, >, <=, >= would be very tricky to 622 * specify, so we just avoid offering them. 623 * 624 * Notice that these == semantics are made more reasonable by these facts: 625 * 1. a==b implies equality at the raw data level 626 * (the converse is false, as a==b is never true among invalids) 627 * 2. This is similar to the behavior of IEEE floats, where a==b 628 * means that a and b have the same value *and* neither is NaN. 629 */ 630 constexpr bool operator==(const CheckedInt& aOther) const { 631 return mIsValid && aOther.mIsValid && mValue == aOther.mValue; 632 } 633 634 /** prefix ++ */ 635 constexpr CheckedInt& operator++() { 636 *this += 1; 637 return *this; 638 } 639 640 /** postfix ++ */ 641 constexpr CheckedInt operator++(int) { 642 CheckedInt tmp = *this; 643 *this += 1; 644 return tmp; 645 } 646 647 /** prefix -- */ 648 constexpr CheckedInt& operator--() { 649 *this -= 1; 650 return *this; 651 } 652 653 /** postfix -- */ 654 constexpr CheckedInt operator--(int) { 655 CheckedInt tmp = *this; 656 *this -= 1; 657 return tmp; 658 } 659 660 private: 661 /** 662 * The !=, <, <=, >, >= operators are disabled: 663 * see the comment on operator==. 664 */ 665 template <typename U> 666 bool operator!=(U aOther) const = delete; 667 template <typename U> 668 bool operator<(U aOther) const = delete; 669 template <typename U> 670 bool operator<=(U aOther) const = delete; 671 template <typename U> 672 bool operator>(U aOther) const = delete; 673 template <typename U> 674 bool operator>=(U aOther) const = delete; 675 }; 676 677 #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \ 678 template <typename T> \ 679 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \ 680 const CheckedInt<T>& aRhs) { \ 681 if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \ 682 static_assert(detail::IsInRange<T>(0), \ 683 "Integer type can't represent 0"); \ 684 return CheckedInt<T>(T(0), false); \ 685 } \ 686 /* For some T, arithmetic ops automatically promote to a wider type, so \ 687 * explitly do the narrowing cast here. The narrowing cast is valid \ 688 * because we did the "Is##NAME##Valid" check above. */ \ 689 return CheckedInt<T>(T(aLhs.mValue OP aRhs.mValue), \ 690 aLhs.mIsValid && aRhs.mIsValid); \ 691 } 692 693 #if MOZ_HAS_BUILTIN_OP_OVERFLOW 694 # define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(NAME, OP, FUN) \ 695 template <typename T> \ 696 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, \ 697 const CheckedInt<T>& aRhs) { \ 698 auto result = T{}; \ 699 if (FUN(aLhs.mValue, aRhs.mValue, &result)) { \ 700 static_assert(detail::IsInRange<T>(0), \ 701 "Integer type can't represent 0"); \ 702 return CheckedInt<T>(T(0), false); \ 703 } \ 704 return CheckedInt<T>(result, aLhs.mIsValid && aRhs.mIsValid); \ 705 } 706 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Add, +, __builtin_add_overflow) 707 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Sub, -, __builtin_sub_overflow) 708 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2(Mul, *, __builtin_mul_overflow) 709 # undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR2 710 #else 711 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +) 712 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -) 713 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *) 714 #endif 715 716 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Div, /) 717 MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mod, %) 718 #undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR 719 720 // Implement castToCheckedInt<T>(x), making sure that 721 // - it allows x to be either a CheckedInt<T> or any integer type 722 // that can be casted to T 723 // - if x is already a CheckedInt<T>, we just return a reference to it, 724 // instead of copying it (optimization) 725 726 namespace detail { 727 728 template <typename T, typename U> 729 struct CastToCheckedIntImpl { 730 typedef CheckedInt<T> ReturnType; 731 static constexpr CheckedInt<T> run(U aU) { return aU; } 732 }; 733 734 template <typename T> 735 struct CastToCheckedIntImpl<T, CheckedInt<T>> { 736 typedef const CheckedInt<T>& ReturnType; 737 static constexpr const CheckedInt<T>& run(const CheckedInt<T>& aU) { 738 return aU; 739 } 740 }; 741 742 } // namespace detail 743 744 template <typename T, typename U> 745 constexpr typename detail::CastToCheckedIntImpl<T, U>::ReturnType 746 castToCheckedInt(U aU) { 747 static_assert(detail::IsSupported<T>::value && detail::IsSupported<U>::value, 748 "This type is not supported by CheckedInt"); 749 return detail::CastToCheckedIntImpl<T, U>::run(aU); 750 } 751 752 #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \ 753 template <typename T> \ 754 template <typename U> \ 755 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) { \ 756 *this = *this OP castToCheckedInt<T>(aRhs); \ 757 return *this; \ 758 } \ 759 template <typename T> \ 760 constexpr CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP( \ 761 const CheckedInt<T>& aRhs) { \ 762 *this = *this OP aRhs; \ 763 return *this; \ 764 } \ 765 template <typename T, typename U> \ 766 constexpr CheckedInt<T> operator OP(const CheckedInt<T>& aLhs, U aRhs) { \ 767 return aLhs OP castToCheckedInt<T>(aRhs); \ 768 } \ 769 template <typename T, typename U> \ 770 constexpr CheckedInt<T> operator OP(U aLhs, const CheckedInt<T>& aRhs) { \ 771 return castToCheckedInt<T>(aLhs) OP aRhs; \ 772 } 773 774 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=) 775 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=) 776 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=) 777 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=) 778 MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=) 779 780 #undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS 781 782 template <typename T, typename U> 783 inline bool operator==(const CheckedInt<T>& aLhs, U aRhs) { 784 return aLhs == castToCheckedInt<T>(aRhs); 785 } 786 787 template <typename T, typename U> 788 inline bool operator==(U aLhs, const CheckedInt<T>& aRhs) { 789 return castToCheckedInt<T>(aLhs) == aRhs; 790 } 791 792 // Convenience typedefs. 793 typedef CheckedInt<int8_t> CheckedInt8; 794 typedef CheckedInt<uint8_t> CheckedUint8; 795 typedef CheckedInt<int16_t> CheckedInt16; 796 typedef CheckedInt<uint16_t> CheckedUint16; 797 typedef CheckedInt<int32_t> CheckedInt32; 798 typedef CheckedInt<uint32_t> CheckedUint32; 799 typedef CheckedInt<int64_t> CheckedInt64; 800 typedef CheckedInt<uint64_t> CheckedUint64; 801 802 } // namespace mozilla 803 804 #endif /* mozilla_CheckedInt_h */ 805