mfbt/CheckedInt.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial