1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Slightly adapted for inclusion in V8. 6// Copyright 2014 the V8 project authors. All rights reserved. 7// List of adaptations: 8// - include guard names 9// - wrap in v8 namespace 10// - formatting (git cl format) 11 12#ifndef V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 13#define V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 14 15#include <stddef.h> 16#include <stdint.h> 17 18#include <limits> 19#include <type_traits> 20 21#if defined(__GNUC__) || defined(__clang__) 22#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1) 23#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0) 24#else 25#define BASE_NUMERICS_LIKELY(x) (x) 26#define BASE_NUMERICS_UNLIKELY(x) (x) 27#endif 28 29namespace v8 { 30namespace base { 31namespace internal { 32 33// The std library doesn't provide a binary max_exponent for integers, however 34// we can compute an analog using std::numeric_limits<>::digits. 35template <typename NumericType> 36struct MaxExponent { 37 static const int value = std::is_floating_point<NumericType>::value 38 ? std::numeric_limits<NumericType>::max_exponent 39 : std::numeric_limits<NumericType>::digits + 1; 40}; 41 42// The number of bits (including the sign) in an integer. Eliminates sizeof 43// hacks. 44template <typename NumericType> 45struct IntegerBitsPlusSign { 46 static const int value = std::numeric_limits<NumericType>::digits + 47 std::is_signed<NumericType>::value; 48}; 49 50// Helper templates for integer manipulations. 51 52template <typename Integer> 53struct PositionOfSignBit { 54 static const size_t value = IntegerBitsPlusSign<Integer>::value - 1; 55}; 56 57// Determines if a numeric value is negative without throwing compiler 58// warnings on: unsigned(value) < 0. 59template <typename T, 60 typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> 61constexpr bool IsValueNegative(T value) { 62 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); 63 return value < 0; 64} 65 66template <typename T, 67 typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> 68constexpr bool IsValueNegative(T) { 69 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); 70 return false; 71} 72 73// This performs a fast negation, returning a signed value. It works on unsigned 74// arguments, but probably doesn't do what you want for any unsigned value 75// larger than max / 2 + 1 (i.e. signed min cast to unsigned). 76template <typename T> 77constexpr typename std::make_signed<T>::type ConditionalNegate( 78 T x, bool is_negative) { 79 static_assert(std::is_integral<T>::value, "Type must be integral"); 80 using SignedT = typename std::make_signed<T>::type; 81 using UnsignedT = typename std::make_unsigned<T>::type; 82 return static_cast<SignedT>( 83 (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative); 84} 85 86// This performs a safe, absolute value via unsigned overflow. 87template <typename T> 88constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) { 89 static_assert(std::is_integral<T>::value, "Type must be integral"); 90 using UnsignedT = typename std::make_unsigned<T>::type; 91 return IsValueNegative(value) 92 ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value)) 93 : static_cast<UnsignedT>(value); 94} 95 96// This allows us to switch paths on known compile-time constants. 97#if defined(__clang__) || defined(__GNUC__) 98constexpr bool CanDetectCompileTimeConstant() { return true; } 99template <typename T> 100constexpr bool IsCompileTimeConstant(const T v) { 101 return __builtin_constant_p(v); 102} 103#else 104constexpr bool CanDetectCompileTimeConstant() { return false; } 105template <typename T> 106constexpr bool IsCompileTimeConstant(const T) { 107 return false; 108} 109#endif 110template <typename T> 111constexpr bool MustTreatAsConstexpr(const T v) { 112 // Either we can't detect a compile-time constant, and must always use the 113 // constexpr path, or we know we have a compile-time constant. 114 return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v); 115} 116 117// Forces a crash, like a CHECK(false). Used for numeric boundary errors. 118// Also used in a constexpr template to trigger a compilation failure on 119// an error condition. 120struct CheckOnFailure { 121 template <typename T> 122 static T HandleFailure() { 123#if defined(_MSC_VER) 124 __debugbreak(); 125#elif defined(__GNUC__) || defined(__clang__) 126 __builtin_trap(); 127#else 128 ((void)(*(volatile char*)0 = 0)); 129#endif 130 return T(); 131 } 132}; 133 134enum IntegerRepresentation { 135 INTEGER_REPRESENTATION_UNSIGNED, 136 INTEGER_REPRESENTATION_SIGNED 137}; 138 139// A range for a given nunmeric Src type is contained for a given numeric Dst 140// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and 141// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true. 142// We implement this as template specializations rather than simple static 143// comparisons to ensure type correctness in our comparisons. 144enum NumericRangeRepresentation { 145 NUMERIC_RANGE_NOT_CONTAINED, 146 NUMERIC_RANGE_CONTAINED 147}; 148 149// Helper templates to statically determine if our destination type can contain 150// maximum and minimum values represented by the source type. 151 152template <typename Dst, typename Src, 153 IntegerRepresentation DstSign = std::is_signed<Dst>::value 154 ? INTEGER_REPRESENTATION_SIGNED 155 : INTEGER_REPRESENTATION_UNSIGNED, 156 IntegerRepresentation SrcSign = std::is_signed<Src>::value 157 ? INTEGER_REPRESENTATION_SIGNED 158 : INTEGER_REPRESENTATION_UNSIGNED> 159struct StaticDstRangeRelationToSrcRange; 160 161// Same sign: Dst is guaranteed to contain Src only if its range is equal or 162// larger. 163template <typename Dst, typename Src, IntegerRepresentation Sign> 164struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { 165 static const NumericRangeRepresentation value = 166 MaxExponent<Dst>::value >= MaxExponent<Src>::value 167 ? NUMERIC_RANGE_CONTAINED 168 : NUMERIC_RANGE_NOT_CONTAINED; 169}; 170 171// Unsigned to signed: Dst is guaranteed to contain source only if its range is 172// larger. 173template <typename Dst, typename Src> 174struct StaticDstRangeRelationToSrcRange<Dst, 175 Src, 176 INTEGER_REPRESENTATION_SIGNED, 177 INTEGER_REPRESENTATION_UNSIGNED> { 178 static const NumericRangeRepresentation value = 179 MaxExponent<Dst>::value > MaxExponent<Src>::value 180 ? NUMERIC_RANGE_CONTAINED 181 : NUMERIC_RANGE_NOT_CONTAINED; 182}; 183 184// Signed to unsigned: Dst cannot be statically determined to contain Src. 185template <typename Dst, typename Src> 186struct StaticDstRangeRelationToSrcRange<Dst, 187 Src, 188 INTEGER_REPRESENTATION_UNSIGNED, 189 INTEGER_REPRESENTATION_SIGNED> { 190 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; 191}; 192 193// This class wraps the range constraints as separate booleans so the compiler 194// can identify constants and eliminate unused code paths. 195class RangeCheck { 196 public: 197 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) 198 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} 199 constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {} 200 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } 201 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } 202 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } 203 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } 204 constexpr bool IsOverflowFlagSet() const { return is_overflow_; } 205 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } 206 constexpr bool operator==(const RangeCheck rhs) const { 207 return is_underflow_ == rhs.is_underflow_ && 208 is_overflow_ == rhs.is_overflow_; 209 } 210 constexpr bool operator!=(const RangeCheck rhs) const { 211 return !(*this == rhs); 212 } 213 214 private: 215 // Do not change the order of these member variables. The integral conversion 216 // optimization depends on this exact order. 217 const bool is_underflow_; 218 const bool is_overflow_; 219}; 220 221// The following helper template addresses a corner case in range checks for 222// conversion from a floating-point type to an integral type of smaller range 223// but larger precision (e.g. float -> unsigned). The problem is as follows: 224// 1. Integral maximum is always one less than a power of two, so it must be 225// truncated to fit the mantissa of the floating point. The direction of 226// rounding is implementation defined, but by default it's always IEEE 227// floats, which round to nearest and thus result in a value of larger 228// magnitude than the integral value. 229// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX 230// // is 4294967295u. 231// 2. If the floating point value is equal to the promoted integral maximum 232// value, a range check will erroneously pass. 233// Example: (4294967296f <= 4294967295u) // This is true due to a precision 234// // loss in rounding up to float. 235// 3. When the floating point value is then converted to an integral, the 236// resulting value is out of range for the target integral type and 237// thus is implementation defined. 238// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. 239// To fix this bug we manually truncate the maximum value when the destination 240// type is an integral of larger precision than the source floating-point type, 241// such that the resulting maximum is represented exactly as a floating point. 242template <typename Dst, typename Src, template <typename> class Bounds> 243struct NarrowingRange { 244 using SrcLimits = std::numeric_limits<Src>; 245 using DstLimits = typename std::numeric_limits<Dst>; 246 247 // Computes the mask required to make an accurate comparison between types. 248 static const int kShift = 249 (MaxExponent<Src>::value > MaxExponent<Dst>::value && 250 SrcLimits::digits < DstLimits::digits) 251 ? (DstLimits::digits - SrcLimits::digits) 252 : 0; 253 template <typename T, typename std::enable_if< 254 std::is_integral<T>::value>::type* = nullptr> 255 256 // Masks out the integer bits that are beyond the precision of the 257 // intermediate type used for comparison. 258 static constexpr T Adjust(T value) { 259 static_assert(std::is_same<T, Dst>::value, ""); 260 static_assert(kShift < DstLimits::digits, ""); 261 return static_cast<T>( 262 ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)), 263 IsValueNegative(value))); 264 } 265 266 template <typename T, typename std::enable_if< 267 std::is_floating_point<T>::value>::type* = nullptr> 268 static constexpr T Adjust(T value) { 269 static_assert(std::is_same<T, Dst>::value, ""); 270 static_assert(kShift == 0, ""); 271 return value; 272 } 273 274 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } 275 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } 276}; 277 278template <typename Dst, typename Src, template <typename> class Bounds, 279 IntegerRepresentation DstSign = std::is_signed<Dst>::value 280 ? INTEGER_REPRESENTATION_SIGNED 281 : INTEGER_REPRESENTATION_UNSIGNED, 282 IntegerRepresentation SrcSign = std::is_signed<Src>::value 283 ? INTEGER_REPRESENTATION_SIGNED 284 : INTEGER_REPRESENTATION_UNSIGNED, 285 NumericRangeRepresentation DstRange = 286 StaticDstRangeRelationToSrcRange<Dst, Src>::value> 287struct DstRangeRelationToSrcRangeImpl; 288 289// The following templates are for ranges that must be verified at runtime. We 290// split it into checks based on signedness to avoid confusing casts and 291// compiler warnings on signed an unsigned comparisons. 292 293// Same sign narrowing: The range is contained for normal limits. 294template <typename Dst, typename Src, template <typename> class Bounds, 295 IntegerRepresentation DstSign, IntegerRepresentation SrcSign> 296struct DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds, DstSign, SrcSign, 297 NUMERIC_RANGE_CONTAINED> { 298 static constexpr RangeCheck Check(Src value) { 299 using SrcLimits = std::numeric_limits<Src>; 300 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 301 return RangeCheck( 302 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || 303 static_cast<Dst>(value) >= DstLimits::lowest(), 304 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || 305 static_cast<Dst>(value) <= DstLimits::max()); 306 } 307}; 308 309// Signed to signed narrowing: Both the upper and lower boundaries may be 310// exceeded for standard limits. 311template <typename Dst, typename Src, template <typename> class Bounds> 312struct DstRangeRelationToSrcRangeImpl< 313 Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED, 314 INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 315 static constexpr RangeCheck Check(Src value) { 316 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 317 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); 318 } 319}; 320 321// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for 322// standard limits. 323template <typename Dst, typename Src, template <typename> class Bounds> 324struct DstRangeRelationToSrcRangeImpl< 325 Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED, 326 INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 327 static constexpr RangeCheck Check(Src value) { 328 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 329 return RangeCheck( 330 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), 331 value <= DstLimits::max()); 332 } 333}; 334 335// Unsigned to signed: Only the upper bound can be exceeded for standard limits. 336template <typename Dst, typename Src, template <typename> class Bounds> 337struct DstRangeRelationToSrcRangeImpl< 338 Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED, 339 INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 340 static constexpr RangeCheck Check(Src value) { 341 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 342 using Promotion = decltype(Src() + Dst()); 343 return RangeCheck(DstLimits::lowest() <= Dst(0) || 344 static_cast<Promotion>(value) >= 345 static_cast<Promotion>(DstLimits::lowest()), 346 static_cast<Promotion>(value) <= 347 static_cast<Promotion>(DstLimits::max())); 348 } 349}; 350 351// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, 352// and any negative value exceeds the lower boundary for standard limits. 353template <typename Dst, typename Src, template <typename> class Bounds> 354struct DstRangeRelationToSrcRangeImpl< 355 Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED, 356 INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { 357 static constexpr RangeCheck Check(Src value) { 358 using SrcLimits = std::numeric_limits<Src>; 359 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 360 using Promotion = decltype(Src() + Dst()); 361 bool ge_zero = false; 362 // Converting floating-point to integer will discard fractional part, so 363 // values in (-1.0, -0.0) will truncate to 0 and fit in Dst. 364 if (std::is_floating_point<Src>::value) { 365 ge_zero = value > Src(-1); 366 } else { 367 ge_zero = value >= Src(0); 368 } 369 return RangeCheck( 370 ge_zero && (DstLimits::lowest() == 0 || 371 static_cast<Dst>(value) >= DstLimits::lowest()), 372 static_cast<Promotion>(SrcLimits::max()) <= 373 static_cast<Promotion>(DstLimits::max()) || 374 static_cast<Promotion>(value) <= 375 static_cast<Promotion>(DstLimits::max())); 376 } 377}; 378 379// Simple wrapper for statically checking if a type's range is contained. 380template <typename Dst, typename Src> 381struct IsTypeInRangeForNumericType { 382 static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == 383 NUMERIC_RANGE_CONTAINED; 384}; 385 386template <typename Dst, template <typename> class Bounds = std::numeric_limits, 387 typename Src> 388constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { 389 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); 390 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); 391 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); 392 return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); 393} 394 395// Integer promotion templates used by the portable checked integer arithmetic. 396template <size_t Size, bool IsSigned> 397struct IntegerForDigitsAndSign; 398 399#define INTEGER_FOR_DIGITS_AND_SIGN(I) \ 400 template <> \ 401 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ 402 std::is_signed<I>::value> { \ 403 using type = I; \ 404 } 405 406INTEGER_FOR_DIGITS_AND_SIGN(int8_t); 407INTEGER_FOR_DIGITS_AND_SIGN(uint8_t); 408INTEGER_FOR_DIGITS_AND_SIGN(int16_t); 409INTEGER_FOR_DIGITS_AND_SIGN(uint16_t); 410INTEGER_FOR_DIGITS_AND_SIGN(int32_t); 411INTEGER_FOR_DIGITS_AND_SIGN(uint32_t); 412INTEGER_FOR_DIGITS_AND_SIGN(int64_t); 413INTEGER_FOR_DIGITS_AND_SIGN(uint64_t); 414#undef INTEGER_FOR_DIGITS_AND_SIGN 415 416// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to 417// support 128-bit math, then the ArithmeticPromotion template below will need 418// to be updated (or more likely replaced with a decltype expression). 419static_assert(IntegerBitsPlusSign<intmax_t>::value == 64, 420 "Max integer size not supported for this toolchain."); 421 422template <typename Integer, bool IsSigned = std::is_signed<Integer>::value> 423struct TwiceWiderInteger { 424 using type = 425 typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2, 426 IsSigned>::type; 427}; 428 429enum ArithmeticPromotionCategory { 430 LEFT_PROMOTION, // Use the type of the left-hand argument. 431 RIGHT_PROMOTION // Use the type of the right-hand argument. 432}; 433 434// Determines the type that can represent the largest positive value. 435template <typename Lhs, typename Rhs, 436 ArithmeticPromotionCategory Promotion = 437 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) 438 ? LEFT_PROMOTION 439 : RIGHT_PROMOTION> 440struct MaxExponentPromotion; 441 442template <typename Lhs, typename Rhs> 443struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { 444 using type = Lhs; 445}; 446 447template <typename Lhs, typename Rhs> 448struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { 449 using type = Rhs; 450}; 451 452// Determines the type that can represent the lowest arithmetic value. 453template <typename Lhs, typename Rhs, 454 ArithmeticPromotionCategory Promotion = 455 std::is_signed<Lhs>::value 456 ? (std::is_signed<Rhs>::value 457 ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value 458 ? LEFT_PROMOTION 459 : RIGHT_PROMOTION) 460 : LEFT_PROMOTION) 461 : (std::is_signed<Rhs>::value 462 ? RIGHT_PROMOTION 463 : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value 464 ? LEFT_PROMOTION 465 : RIGHT_PROMOTION))> 466struct LowestValuePromotion; 467 468template <typename Lhs, typename Rhs> 469struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> { 470 using type = Lhs; 471}; 472 473template <typename Lhs, typename Rhs> 474struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> { 475 using type = Rhs; 476}; 477 478// Determines the type that is best able to represent an arithmetic result. 479template < 480 typename Lhs, typename Rhs = Lhs, 481 bool is_intmax_type = 482 std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&& 483 IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>:: 484 value == IntegerBitsPlusSign<intmax_t>::value, 485 bool is_max_exponent = 486 StaticDstRangeRelationToSrcRange< 487 typename MaxExponentPromotion<Lhs, Rhs>::type, Lhs>::value == 488 NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< 489 typename MaxExponentPromotion<Lhs, Rhs>::type, Rhs>::value == 490 NUMERIC_RANGE_CONTAINED> 491struct BigEnoughPromotion; 492 493// The side with the max exponent is big enough. 494template <typename Lhs, typename Rhs, bool is_intmax_type> 495struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { 496 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 497 static const bool is_contained = true; 498}; 499 500// We can use a twice wider type to fit. 501template <typename Lhs, typename Rhs> 502struct BigEnoughPromotion<Lhs, Rhs, false, false> { 503 using type = 504 typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, 505 std::is_signed<Lhs>::value || 506 std::is_signed<Rhs>::value>::type; 507 static const bool is_contained = true; 508}; 509 510// No type is large enough. 511template <typename Lhs, typename Rhs> 512struct BigEnoughPromotion<Lhs, Rhs, true, false> { 513 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 514 static const bool is_contained = false; 515}; 516 517// We can statically check if operations on the provided types can wrap, so we 518// can skip the checked operations if they're not needed. So, for an integer we 519// care if the destination type preserves the sign and is twice the width of 520// the source. 521template <typename T, typename Lhs, typename Rhs = Lhs> 522struct IsIntegerArithmeticSafe { 523 static const bool value = 524 !std::is_floating_point<T>::value && 525 !std::is_floating_point<Lhs>::value && 526 !std::is_floating_point<Rhs>::value && 527 std::is_signed<T>::value >= std::is_signed<Lhs>::value && 528 IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) && 529 std::is_signed<T>::value >= std::is_signed<Rhs>::value && 530 IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value); 531}; 532 533// Promotes to a type that can represent any possible result of a binary 534// arithmetic operation with the source types. 535template <typename Lhs, typename Rhs, 536 bool is_promotion_possible = IsIntegerArithmeticSafe< 537 typename std::conditional<std::is_signed<Lhs>::value || 538 std::is_signed<Rhs>::value, 539 intmax_t, uintmax_t>::type, 540 typename MaxExponentPromotion<Lhs, Rhs>::type>::value> 541struct FastIntegerArithmeticPromotion; 542 543template <typename Lhs, typename Rhs> 544struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> { 545 using type = 546 typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, 547 std::is_signed<Lhs>::value || 548 std::is_signed<Rhs>::value>::type; 549 static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, ""); 550 static const bool is_contained = true; 551}; 552 553template <typename Lhs, typename Rhs> 554struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> { 555 using type = typename BigEnoughPromotion<Lhs, Rhs>::type; 556 static const bool is_contained = false; 557}; 558 559// Extracts the underlying type from an enum. 560template <typename T, bool is_enum = std::is_enum<T>::value> 561struct ArithmeticOrUnderlyingEnum; 562 563template <typename T> 564struct ArithmeticOrUnderlyingEnum<T, true> { 565 using type = typename std::underlying_type<T>::type; 566 static const bool value = std::is_arithmetic<type>::value; 567}; 568 569template <typename T> 570struct ArithmeticOrUnderlyingEnum<T, false> { 571 using type = T; 572 static const bool value = std::is_arithmetic<type>::value; 573}; 574 575// The following are helper templates used in the CheckedNumeric class. 576template <typename T> 577class CheckedNumeric; 578 579template <typename T> 580class ClampedNumeric; 581 582template <typename T> 583class StrictNumeric; 584 585// Used to treat CheckedNumeric and arithmetic underlying types the same. 586template <typename T> 587struct UnderlyingType { 588 using type = typename ArithmeticOrUnderlyingEnum<T>::type; 589 static const bool is_numeric = std::is_arithmetic<type>::value; 590 static const bool is_checked = false; 591 static const bool is_clamped = false; 592 static const bool is_strict = false; 593}; 594 595template <typename T> 596struct UnderlyingType<CheckedNumeric<T>> { 597 using type = T; 598 static const bool is_numeric = true; 599 static const bool is_checked = true; 600 static const bool is_clamped = false; 601 static const bool is_strict = false; 602}; 603 604template <typename T> 605struct UnderlyingType<ClampedNumeric<T>> { 606 using type = T; 607 static const bool is_numeric = true; 608 static const bool is_checked = false; 609 static const bool is_clamped = true; 610 static const bool is_strict = false; 611}; 612 613template <typename T> 614struct UnderlyingType<StrictNumeric<T>> { 615 using type = T; 616 static const bool is_numeric = true; 617 static const bool is_checked = false; 618 static const bool is_clamped = false; 619 static const bool is_strict = true; 620}; 621 622template <typename L, typename R> 623struct IsCheckedOp { 624 static const bool value = 625 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 626 (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); 627}; 628 629template <typename L, typename R> 630struct IsClampedOp { 631 static const bool value = 632 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 633 (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) && 634 !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); 635}; 636 637template <typename L, typename R> 638struct IsStrictOp { 639 static const bool value = 640 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 641 (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) && 642 !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) && 643 !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped); 644}; 645 646// as_signed<> returns the supplied integral value (or integral castable 647// Numeric template) cast as a signed integral of equivalent precision. 648// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) 649template <typename Src> 650constexpr typename std::make_signed< 651 typename base::internal::UnderlyingType<Src>::type>::type 652as_signed(const Src value) { 653 static_assert(std::is_integral<decltype(as_signed(value))>::value, 654 "Argument must be a signed or unsigned integer type."); 655 return static_cast<decltype(as_signed(value))>(value); 656} 657 658// as_unsigned<> returns the supplied integral value (or integral castable 659// Numeric template) cast as an unsigned integral of equivalent precision. 660// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) 661template <typename Src> 662constexpr typename std::make_unsigned< 663 typename base::internal::UnderlyingType<Src>::type>::type 664as_unsigned(const Src value) { 665 static_assert(std::is_integral<decltype(as_unsigned(value))>::value, 666 "Argument must be a signed or unsigned integer type."); 667 return static_cast<decltype(as_unsigned(value))>(value); 668} 669 670template <typename L, typename R> 671constexpr bool IsLessImpl(const L lhs, const R rhs, const RangeCheck l_range, 672 const RangeCheck r_range) { 673 return l_range.IsUnderflow() || r_range.IsOverflow() || 674 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) < 675 static_cast<decltype(lhs + rhs)>(rhs)); 676} 677 678template <typename L, typename R> 679struct IsLess { 680 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 681 "Types must be numeric."); 682 static constexpr bool Test(const L lhs, const R rhs) { 683 return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 684 DstRangeRelationToSrcRange<L>(rhs)); 685 } 686}; 687 688template <typename L, typename R> 689constexpr bool IsLessOrEqualImpl(const L lhs, const R rhs, 690 const RangeCheck l_range, 691 const RangeCheck r_range) { 692 return l_range.IsUnderflow() || r_range.IsOverflow() || 693 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <= 694 static_cast<decltype(lhs + rhs)>(rhs)); 695} 696 697template <typename L, typename R> 698struct IsLessOrEqual { 699 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 700 "Types must be numeric."); 701 static constexpr bool Test(const L lhs, const R rhs) { 702 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 703 DstRangeRelationToSrcRange<L>(rhs)); 704 } 705}; 706 707template <typename L, typename R> 708constexpr bool IsGreaterImpl(const L lhs, const R rhs, const RangeCheck l_range, 709 const RangeCheck r_range) { 710 return l_range.IsOverflow() || r_range.IsUnderflow() || 711 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) > 712 static_cast<decltype(lhs + rhs)>(rhs)); 713} 714 715template <typename L, typename R> 716struct IsGreater { 717 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 718 "Types must be numeric."); 719 static constexpr bool Test(const L lhs, const R rhs) { 720 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 721 DstRangeRelationToSrcRange<L>(rhs)); 722 } 723}; 724 725template <typename L, typename R> 726constexpr bool IsGreaterOrEqualImpl(const L lhs, const R rhs, 727 const RangeCheck l_range, 728 const RangeCheck r_range) { 729 return l_range.IsOverflow() || r_range.IsUnderflow() || 730 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >= 731 static_cast<decltype(lhs + rhs)>(rhs)); 732} 733 734template <typename L, typename R> 735struct IsGreaterOrEqual { 736 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 737 "Types must be numeric."); 738 static constexpr bool Test(const L lhs, const R rhs) { 739 return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 740 DstRangeRelationToSrcRange<L>(rhs)); 741 } 742}; 743 744template <typename L, typename R> 745struct IsEqual { 746 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 747 "Types must be numeric."); 748 static constexpr bool Test(const L lhs, const R rhs) { 749 return DstRangeRelationToSrcRange<R>(lhs) == 750 DstRangeRelationToSrcRange<L>(rhs) && 751 static_cast<decltype(lhs + rhs)>(lhs) == 752 static_cast<decltype(lhs + rhs)>(rhs); 753 } 754}; 755 756template <typename L, typename R> 757struct IsNotEqual { 758 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 759 "Types must be numeric."); 760 static constexpr bool Test(const L lhs, const R rhs) { 761 return DstRangeRelationToSrcRange<R>(lhs) != 762 DstRangeRelationToSrcRange<L>(rhs) || 763 static_cast<decltype(lhs + rhs)>(lhs) != 764 static_cast<decltype(lhs + rhs)>(rhs); 765 } 766}; 767 768// These perform the actual math operations on the CheckedNumerics. 769// Binary arithmetic operations. 770template <template <typename, typename> class C, typename L, typename R> 771constexpr bool SafeCompare(const L lhs, const R rhs) { 772 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, 773 "Types must be numeric."); 774 using Promotion = BigEnoughPromotion<L, R>; 775 using BigType = typename Promotion::type; 776 return Promotion::is_contained 777 // Force to a larger type for speed if both are contained. 778 ? C<BigType, BigType>::Test( 779 static_cast<BigType>(static_cast<L>(lhs)), 780 static_cast<BigType>(static_cast<R>(rhs))) 781 // Let the template functions figure it out for mixed types. 782 : C<L, R>::Test(lhs, rhs); 783} 784 785template <typename Dst, typename Src> 786constexpr bool IsMaxInRangeForNumericType() { 787 return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(), 788 std::numeric_limits<Src>::max()); 789} 790 791template <typename Dst, typename Src> 792constexpr bool IsMinInRangeForNumericType() { 793 return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(), 794 std::numeric_limits<Src>::lowest()); 795} 796 797template <typename Dst, typename Src> 798constexpr Dst CommonMax() { 799 return !IsMaxInRangeForNumericType<Dst, Src>() 800 ? Dst(std::numeric_limits<Dst>::max()) 801 : Dst(std::numeric_limits<Src>::max()); 802} 803 804template <typename Dst, typename Src> 805constexpr Dst CommonMin() { 806 return !IsMinInRangeForNumericType<Dst, Src>() 807 ? Dst(std::numeric_limits<Dst>::lowest()) 808 : Dst(std::numeric_limits<Src>::lowest()); 809} 810 811// This is a wrapper to generate return the max or min for a supplied type. 812// If the argument is false, the returned value is the maximum. If true the 813// returned value is the minimum. 814template <typename Dst, typename Src = Dst> 815constexpr Dst CommonMaxOrMin(bool is_min) { 816 return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>(); 817} 818 819} // namespace internal 820} // namespace base 821} // namespace v8 822 823#endif // V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 824