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 16 namespace v8 { 17 namespace base { 18 namespace 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. 23 template <typename NumericType> 24 struct 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 31 enum 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. 41 enum 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 49 template < 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 > 59 struct StaticDstRangeRelationToSrcRange; 60 61 // Same sign: Dst is guaranteed to contain Src only if its range is equal or 62 // larger. 63 template <typename Dst, typename Src, IntegerRepresentation Sign> 64 struct 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. 73 template <typename Dst, typename Src> 74 struct 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. 85 template <typename Dst, typename Src> 86 struct StaticDstRangeRelationToSrcRange<Dst, 87 Src, 88 INTEGER_REPRESENTATION_UNSIGNED, 89 INTEGER_REPRESENTATION_SIGNED> { 90 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; 91 }; 92 93 enum 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. 101 inline 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. 110 inline 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 116 template < 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 > 127 struct 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. 134 template <typename Dst, 135 typename Src, 136 IntegerRepresentation DstSign, 137 IntegerRepresentation SrcSign> 138 struct 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. 148 template <typename Dst, typename Src> 149 struct 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. 164 template <typename Dst, typename Src> 165 struct 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. 176 template <typename Dst, typename Src> 177 struct 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. 193 template <typename Dst, typename Src> 194 struct 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 208 template <typename Dst, typename Src> 209 inline 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