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