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 8#ifndef V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 9#define V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 10 11#include <limits> 12 13#include "src/base/logging.h" 14#include "src/base/macros.h" 15 16namespace v8 { 17namespace base { 18namespace internal { 19 20// The std library doesn't provide a binary max_exponent for integers, however 21// we can compute one by adding one to the number of non-sign bits. This allows 22// for accurate range comparisons between floating point and integer types. 23template <typename NumericType> 24struct MaxExponent { 25 static const int value = std::numeric_limits<NumericType>::is_iec559 26 ? std::numeric_limits<NumericType>::max_exponent 27 : (sizeof(NumericType) * 8 + 1 - 28 std::numeric_limits<NumericType>::is_signed); 29}; 30 31enum IntegerRepresentation { 32 INTEGER_REPRESENTATION_UNSIGNED, 33 INTEGER_REPRESENTATION_SIGNED 34}; 35 36// A range for a given nunmeric Src type is contained for a given numeric Dst 37// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and 38// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true. 39// We implement this as template specializations rather than simple static 40// comparisons to ensure type correctness in our comparisons. 41enum NumericRangeRepresentation { 42 NUMERIC_RANGE_NOT_CONTAINED, 43 NUMERIC_RANGE_CONTAINED 44}; 45 46// Helper templates to statically determine if our destination type can contain 47// maximum and minimum values represented by the source type. 48 49template < 50 typename Dst, 51 typename Src, 52 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed 53 ? INTEGER_REPRESENTATION_SIGNED 54 : INTEGER_REPRESENTATION_UNSIGNED, 55 IntegerRepresentation SrcSign = 56 std::numeric_limits<Src>::is_signed 57 ? INTEGER_REPRESENTATION_SIGNED 58 : INTEGER_REPRESENTATION_UNSIGNED > 59struct StaticDstRangeRelationToSrcRange; 60 61// Same sign: Dst is guaranteed to contain Src only if its range is equal or 62// larger. 63template <typename Dst, typename Src, IntegerRepresentation Sign> 64struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { 65 static const NumericRangeRepresentation value = 66 MaxExponent<Dst>::value >= MaxExponent<Src>::value 67 ? NUMERIC_RANGE_CONTAINED 68 : NUMERIC_RANGE_NOT_CONTAINED; 69}; 70 71// Unsigned to signed: Dst is guaranteed to contain source only if its range is 72// larger. 73template <typename Dst, typename Src> 74struct StaticDstRangeRelationToSrcRange<Dst, 75 Src, 76 INTEGER_REPRESENTATION_SIGNED, 77 INTEGER_REPRESENTATION_UNSIGNED> { 78 static const NumericRangeRepresentation value = 79 MaxExponent<Dst>::value > MaxExponent<Src>::value 80 ? NUMERIC_RANGE_CONTAINED 81 : NUMERIC_RANGE_NOT_CONTAINED; 82}; 83 84// Signed to unsigned: Dst cannot be statically determined to contain Src. 85template <typename Dst, typename Src> 86struct StaticDstRangeRelationToSrcRange<Dst, 87 Src, 88 INTEGER_REPRESENTATION_UNSIGNED, 89 INTEGER_REPRESENTATION_SIGNED> { 90 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; 91}; 92 93enum RangeConstraint { 94 RANGE_VALID = 0x0, // Value can be represented by the destination type. 95 RANGE_UNDERFLOW = 0x1, // Value would overflow. 96 RANGE_OVERFLOW = 0x2, // Value would underflow. 97 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). 98}; 99 100// Helper function for coercing an int back to a RangeContraint. 101inline RangeConstraint GetRangeConstraint(int integer_range_constraint) { 102 DCHECK(integer_range_constraint >= RANGE_VALID && 103 integer_range_constraint <= RANGE_INVALID); 104 return static_cast<RangeConstraint>(integer_range_constraint); 105} 106 107// This function creates a RangeConstraint from an upper and lower bound 108// check by taking advantage of the fact that only NaN can be out of range in 109// both directions at once. 110inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, 111 bool is_in_lower_bound) { 112 return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | 113 (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); 114} 115 116template < 117 typename Dst, 118 typename Src, 119 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed 120 ? INTEGER_REPRESENTATION_SIGNED 121 : INTEGER_REPRESENTATION_UNSIGNED, 122 IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed 123 ? INTEGER_REPRESENTATION_SIGNED 124 : INTEGER_REPRESENTATION_UNSIGNED, 125 NumericRangeRepresentation DstRange = 126 StaticDstRangeRelationToSrcRange<Dst, Src>::value > 127struct DstRangeRelationToSrcRangeImpl; 128 129// The following templates are for ranges that must be verified at runtime. We 130// split it into checks based on signedness to avoid confusing casts and 131// compiler warnings on signed an unsigned comparisons. 132 133// Dst range is statically determined to contain Src: Nothing to check. 134template <typename Dst, 135 typename Src, 136 IntegerRepresentation DstSign, 137 IntegerRepresentation SrcSign> 138struct DstRangeRelationToSrcRangeImpl<Dst, 139 Src, 140 DstSign, 141 SrcSign, 142 NUMERIC_RANGE_CONTAINED> { 143 static RangeConstraint Check(Src value) { return RANGE_VALID; } 144}; 145 146// Signed to signed narrowing: Both the upper and lower boundaries may be 147// exceeded. 148template <typename Dst, typename Src> 149struct DstRangeRelationToSrcRangeImpl<Dst, 150 Src, 151 INTEGER_REPRESENTATION_SIGNED, 152 INTEGER_REPRESENTATION_SIGNED, 153 NUMERIC_RANGE_NOT_CONTAINED> { 154 static RangeConstraint Check(Src value) { 155 return std::numeric_limits<Dst>::is_iec559 156 ? GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), 157 value >= -std::numeric_limits<Dst>::max()) 158 : GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), 159 value >= std::numeric_limits<Dst>::min()); 160 } 161}; 162 163// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. 164template <typename Dst, typename Src> 165struct DstRangeRelationToSrcRangeImpl<Dst, 166 Src, 167 INTEGER_REPRESENTATION_UNSIGNED, 168 INTEGER_REPRESENTATION_UNSIGNED, 169 NUMERIC_RANGE_NOT_CONTAINED> { 170 static RangeConstraint Check(Src value) { 171 return GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), true); 172 } 173}; 174 175// Unsigned to signed: The upper boundary may be exceeded. 176template <typename Dst, typename Src> 177struct DstRangeRelationToSrcRangeImpl<Dst, 178 Src, 179 INTEGER_REPRESENTATION_SIGNED, 180 INTEGER_REPRESENTATION_UNSIGNED, 181 NUMERIC_RANGE_NOT_CONTAINED> { 182 static RangeConstraint Check(Src value) { 183 return sizeof(Dst) > sizeof(Src) 184 ? RANGE_VALID 185 : GetRangeConstraint( 186 value <= static_cast<Src>(std::numeric_limits<Dst>::max()), 187 true); 188 } 189}; 190 191// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, 192// and any negative value exceeds the lower boundary. 193template <typename Dst, typename Src> 194struct DstRangeRelationToSrcRangeImpl<Dst, 195 Src, 196 INTEGER_REPRESENTATION_UNSIGNED, 197 INTEGER_REPRESENTATION_SIGNED, 198 NUMERIC_RANGE_NOT_CONTAINED> { 199 static RangeConstraint Check(Src value) { 200 return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) 201 ? GetRangeConstraint(true, value >= static_cast<Src>(0)) 202 : GetRangeConstraint( 203 value <= static_cast<Src>(std::numeric_limits<Dst>::max()), 204 value >= static_cast<Src>(0)); 205 } 206}; 207 208template <typename Dst, typename Src> 209inline RangeConstraint DstRangeRelationToSrcRange(Src value) { 210 // Both source and destination must be numeric. 211 STATIC_ASSERT(std::numeric_limits<Src>::is_specialized); 212 STATIC_ASSERT(std::numeric_limits<Dst>::is_specialized); 213 return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); 214} 215 216} // namespace internal 217} // namespace base 218} // namespace v8 219 220#endif // V8_BASE_SAFE_CONVERSIONS_IMPL_H_ 221