Tue, 06 Jan 2015 21:39:09 +0100
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 */ |