| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
| 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
| 7 | 7 |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <limits> | 10 #include <limits> |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 159 | 159 |
| 160 // Signed to unsigned: Dst cannot be statically determined to contain Src. | 160 // Signed to unsigned: Dst cannot be statically determined to contain Src. |
| 161 template <typename Dst, typename Src> | 161 template <typename Dst, typename Src> |
| 162 struct StaticDstRangeRelationToSrcRange<Dst, | 162 struct StaticDstRangeRelationToSrcRange<Dst, |
| 163 Src, | 163 Src, |
| 164 INTEGER_REPRESENTATION_UNSIGNED, | 164 INTEGER_REPRESENTATION_UNSIGNED, |
| 165 INTEGER_REPRESENTATION_SIGNED> { | 165 INTEGER_REPRESENTATION_SIGNED> { |
| 166 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; | 166 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; |
| 167 }; | 167 }; |
| 168 | 168 |
| 169 enum RangeConstraintEnum { | 169 enum RangeConstraint { |
| 170 RANGE_VALID = 0x0, // Value can be represented by the destination type. | 170 RANGE_VALID = 0x0, // Value can be represented by the destination type. |
| 171 RANGE_OVERFLOW = 0x1, // Value would overflow. | 171 RANGE_UNDERFLOW = 0x1, // Value would overflow. |
| 172 RANGE_UNDERFLOW = 0x2, // Value would underflow. | 172 RANGE_OVERFLOW = 0x2, // Value would underflow. |
| 173 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). | 173 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). |
| 174 }; | 174 }; |
| 175 | 175 |
| 176 // This class wraps the range constraints as separate booleans so the compiler | 176 // Helper function for coercing an int back to a RangeContraint. |
| 177 // can identify constants and eliminate unused code paths. | 177 constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint) { |
| 178 class RangeConstraint { | 178 // TODO(jschuh): Once we get full C++14 support we want this |
| 179 public: | 179 // assert(integer_range_constraint >= RANGE_VALID && |
| 180 constexpr RangeConstraint(bool is_in_upper_bound, bool is_in_lower_bound) | 180 // integer_range_constraint <= RANGE_INVALID) |
| 181 : is_overflow_(!is_in_upper_bound), is_underflow_(!is_in_lower_bound) {} | 181 return static_cast<RangeConstraint>(integer_range_constraint); |
| 182 constexpr RangeConstraint() : is_overflow_(0), is_underflow_(0) {} | 182 } |
| 183 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } | |
| 184 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } | |
| 185 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } | |
| 186 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } | |
| 187 | 183 |
| 188 // These are some wrappers to make the tests a bit cleaner. | 184 // This function creates a RangeConstraint from an upper and lower bound |
| 189 constexpr operator RangeConstraintEnum() const { | 185 // check by taking advantage of the fact that only NaN can be out of range in |
| 190 return static_cast<RangeConstraintEnum>( | 186 // both directions at once. |
| 191 static_cast<int>(is_overflow_) | static_cast<int>(is_underflow_) << 1); | 187 constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, |
| 192 } | 188 bool is_in_lower_bound) { |
| 193 constexpr bool operator==(const RangeConstraintEnum rhs) const { | 189 return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | |
| 194 return rhs == static_cast<RangeConstraintEnum>(*this); | 190 (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); |
| 195 } | 191 } |
| 196 | |
| 197 private: | |
| 198 const bool is_overflow_; | |
| 199 const bool is_underflow_; | |
| 200 }; | |
| 201 | 192 |
| 202 // The following helper template addresses a corner case in range checks for | 193 // The following helper template addresses a corner case in range checks for |
| 203 // conversion from a floating-point type to an integral type of smaller range | 194 // conversion from a floating-point type to an integral type of smaller range |
| 204 // but larger precision (e.g. float -> unsigned). The problem is as follows: | 195 // but larger precision (e.g. float -> unsigned). The problem is as follows: |
| 205 // 1. Integral maximum is always one less than a power of two, so it must be | 196 // 1. Integral maximum is always one less than a power of two, so it must be |
| 206 // truncated to fit the mantissa of the floating point. The direction of | 197 // truncated to fit the mantissa of the floating point. The direction of |
| 207 // rounding is implementation defined, but by default it's always IEEE | 198 // rounding is implementation defined, but by default it's always IEEE |
| 208 // floats, which round to nearest and thus result in a value of larger | 199 // floats, which round to nearest and thus result in a value of larger |
| 209 // magnitude than the integral value. | 200 // magnitude than the integral value. |
| 210 // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX | 201 // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX |
| 211 // // is 4294967295u. | 202 // // is 4294967295u. |
| 212 // 2. If the floating point value is equal to the promoted integral maximum | 203 // 2. If the floating point value is equal to the promoted integral maximum |
| 213 // value, a range check will erroneously pass. | 204 // value, a range check will erroneously pass. |
| 214 // Example: (4294967296f <= 4294967295u) // This is true due to a precision | 205 // Example: (4294967296f <= 4294967295u) // This is true due to a precision |
| 215 // // loss in rounding up to float. | 206 // // loss in rounding up to float. |
| 216 // 3. When the floating point value is then converted to an integral, the | 207 // 3. When the floating point value is then converted to an integral, the |
| 217 // resulting value is out of range for the target integral type and | 208 // resulting value is out of range for the target integral type and |
| 218 // thus is implementation defined. | 209 // thus is implementation defined. |
| 219 // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. | 210 // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. |
| 220 // To fix this bug we manually truncate the maximum value when the destination | 211 // To fix this bug we manually truncate the maximum value when the destination |
| 221 // type is an integral of larger precision than the source floating-point type, | 212 // type is an integral of larger precision than the source floating-point type, |
| 222 // such that the resulting maximum is represented exactly as a floating point. | 213 // such that the resulting maximum is represented exactly as a floating point. |
| 223 template <typename Dst, typename Src, template <typename> class Bounds> | 214 template <typename Dst, |
| 215 typename Src, |
| 216 template <typename> class Bounds = std::numeric_limits> |
| 224 struct NarrowingRange { | 217 struct NarrowingRange { |
| 225 using SrcLimits = std::numeric_limits<Src>; | 218 using SrcLimits = typename std::numeric_limits<Src>; |
| 226 using DstLimits = typename std::numeric_limits<Dst>; | 219 using DstLimits = typename std::numeric_limits<Dst>; |
| 227 | 220 |
| 228 // Computes the mask required to make an accurate comparison between types. | 221 // Computes the mask required to make an accurate comparison between types. |
| 229 static const int kShift = | 222 static const int kShift = |
| 230 (MaxExponent<Src>::value > MaxExponent<Dst>::value && | 223 (MaxExponent<Src>::value > MaxExponent<Dst>::value && |
| 231 SrcLimits::digits < DstLimits::digits) | 224 SrcLimits::digits < DstLimits::digits) |
| 232 ? (DstLimits::digits - SrcLimits::digits) | 225 ? (DstLimits::digits - SrcLimits::digits) |
| 233 : 0; | 226 : 0; |
| 234 template < | 227 template < |
| 235 typename T, | 228 typename T, |
| (...skipping 17 matching lines...) Expand all Loading... |
| 253 static_assert(kShift == 0, ""); | 246 static_assert(kShift == 0, ""); |
| 254 return value; | 247 return value; |
| 255 } | 248 } |
| 256 | 249 |
| 257 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } | 250 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } |
| 258 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } | 251 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } |
| 259 }; | 252 }; |
| 260 | 253 |
| 261 template <typename Dst, | 254 template <typename Dst, |
| 262 typename Src, | 255 typename Src, |
| 263 template <typename> class Bounds, | |
| 264 IntegerRepresentation DstSign = std::is_signed<Dst>::value | 256 IntegerRepresentation DstSign = std::is_signed<Dst>::value |
| 265 ? INTEGER_REPRESENTATION_SIGNED | 257 ? INTEGER_REPRESENTATION_SIGNED |
| 266 : INTEGER_REPRESENTATION_UNSIGNED, | 258 : INTEGER_REPRESENTATION_UNSIGNED, |
| 267 IntegerRepresentation SrcSign = std::is_signed<Src>::value | 259 IntegerRepresentation SrcSign = std::is_signed<Src>::value |
| 268 ? INTEGER_REPRESENTATION_SIGNED | 260 ? INTEGER_REPRESENTATION_SIGNED |
| 269 : INTEGER_REPRESENTATION_UNSIGNED, | 261 : INTEGER_REPRESENTATION_UNSIGNED, |
| 270 NumericRangeRepresentation DstRange = | 262 NumericRangeRepresentation DstRange = |
| 271 StaticDstRangeRelationToSrcRange<Dst, Src>::value> | 263 StaticDstRangeRelationToSrcRange<Dst, Src>::value> |
| 272 struct DstRangeRelationToSrcRangeImpl; | 264 struct DstRangeRelationToSrcRangeImpl; |
| 273 | 265 |
| 274 // The following templates are for ranges that must be verified at runtime. We | 266 // The following templates are for ranges that must be verified at runtime. We |
| 275 // split it into checks based on signedness to avoid confusing casts and | 267 // split it into checks based on signedness to avoid confusing casts and |
| 276 // compiler warnings on signed an unsigned comparisons. | 268 // compiler warnings on signed an unsigned comparisons. |
| 277 | 269 |
| 278 // Same sign narrowing: The range is contained for normal limits. | 270 // Dst range is statically determined to contain Src: Nothing to check. |
| 279 template <typename Dst, | 271 template <typename Dst, |
| 280 typename Src, | 272 typename Src, |
| 281 template <typename> class Bounds, | |
| 282 IntegerRepresentation DstSign, | 273 IntegerRepresentation DstSign, |
| 283 IntegerRepresentation SrcSign> | 274 IntegerRepresentation SrcSign> |
| 284 struct DstRangeRelationToSrcRangeImpl<Dst, | 275 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 285 Src, | 276 Src, |
| 286 Bounds, | |
| 287 DstSign, | 277 DstSign, |
| 288 SrcSign, | 278 SrcSign, |
| 289 NUMERIC_RANGE_CONTAINED> { | 279 NUMERIC_RANGE_CONTAINED> { |
| 290 static constexpr RangeConstraint Check(Src value) { | 280 static constexpr RangeConstraint Check(Src value) { return RANGE_VALID; } |
| 291 using SrcLimits = std::numeric_limits<Src>; | |
| 292 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | |
| 293 return RangeConstraint( | |
| 294 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || | |
| 295 static_cast<Dst>(value) <= DstLimits::max(), | |
| 296 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || | |
| 297 static_cast<Dst>(value) >= DstLimits::lowest()); | |
| 298 } | |
| 299 }; | 281 }; |
| 300 | 282 |
| 301 // Signed to signed narrowing: Both the upper and lower boundaries may be | 283 // Signed to signed narrowing: Both the upper and lower boundaries may be |
| 302 // exceeded for standard limits. | 284 // exceeded. |
| 303 template <typename Dst, typename Src, template <typename> class Bounds> | 285 template <typename Dst, typename Src> |
| 304 struct DstRangeRelationToSrcRangeImpl<Dst, | 286 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 305 Src, | 287 Src, |
| 306 Bounds, | |
| 307 INTEGER_REPRESENTATION_SIGNED, | 288 INTEGER_REPRESENTATION_SIGNED, |
| 308 INTEGER_REPRESENTATION_SIGNED, | 289 INTEGER_REPRESENTATION_SIGNED, |
| 309 NUMERIC_RANGE_NOT_CONTAINED> { | 290 NUMERIC_RANGE_NOT_CONTAINED> { |
| 310 static constexpr RangeConstraint Check(Src value) { | 291 static constexpr RangeConstraint Check(Src value) { |
| 311 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 292 return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()), |
| 312 return RangeConstraint(value <= DstLimits::max(), | 293 (value >= NarrowingRange<Dst, Src>::lowest())); |
| 313 value >= DstLimits::lowest()); | |
| 314 } | 294 } |
| 315 }; | 295 }; |
| 316 | 296 |
| 317 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for | 297 // Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. |
| 318 // standard limits. | 298 template <typename Dst, typename Src> |
| 319 template <typename Dst, typename Src, template <typename> class Bounds> | |
| 320 struct DstRangeRelationToSrcRangeImpl<Dst, | 299 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 321 Src, | 300 Src, |
| 322 Bounds, | |
| 323 INTEGER_REPRESENTATION_UNSIGNED, | 301 INTEGER_REPRESENTATION_UNSIGNED, |
| 324 INTEGER_REPRESENTATION_UNSIGNED, | 302 INTEGER_REPRESENTATION_UNSIGNED, |
| 325 NUMERIC_RANGE_NOT_CONTAINED> { | 303 NUMERIC_RANGE_NOT_CONTAINED> { |
| 326 static constexpr RangeConstraint Check(Src value) { | 304 static constexpr RangeConstraint Check(Src value) { |
| 327 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 305 return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true); |
| 328 return RangeConstraint( | |
| 329 value <= DstLimits::max(), | |
| 330 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest()); | |
| 331 } | 306 } |
| 332 }; | 307 }; |
| 333 | 308 |
| 334 // Unsigned to signed: Only the upper bound can be exceeded for standard limits. | 309 // Unsigned to signed: The upper boundary may be exceeded. |
| 335 template <typename Dst, typename Src, template <typename> class Bounds> | 310 template <typename Dst, typename Src> |
| 336 struct DstRangeRelationToSrcRangeImpl<Dst, | 311 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 337 Src, | 312 Src, |
| 338 Bounds, | |
| 339 INTEGER_REPRESENTATION_SIGNED, | 313 INTEGER_REPRESENTATION_SIGNED, |
| 340 INTEGER_REPRESENTATION_UNSIGNED, | 314 INTEGER_REPRESENTATION_UNSIGNED, |
| 341 NUMERIC_RANGE_NOT_CONTAINED> { | 315 NUMERIC_RANGE_NOT_CONTAINED> { |
| 342 static constexpr RangeConstraint Check(Src value) { | 316 static constexpr RangeConstraint Check(Src value) { |
| 343 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 317 return IntegerBitsPlusSign<Dst>::value > IntegerBitsPlusSign<Src>::value |
| 344 using Promotion = decltype(Src() + Dst()); | 318 ? RANGE_VALID |
| 345 return RangeConstraint(static_cast<Promotion>(value) <= | 319 : GetRangeConstraint( |
| 346 static_cast<Promotion>(DstLimits::max()), | 320 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), |
| 347 DstLimits::lowest() <= Dst(0) || | 321 true); |
| 348 static_cast<Promotion>(value) >= | |
| 349 static_cast<Promotion>(DstLimits::lowest())); | |
| 350 } | 322 } |
| 351 }; | 323 }; |
| 352 | 324 |
| 353 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, | 325 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, |
| 354 // and any negative value exceeds the lower boundary for standard limits. | 326 // and any negative value exceeds the lower boundary. |
| 355 template <typename Dst, typename Src, template <typename> class Bounds> | 327 template <typename Dst, typename Src> |
| 356 struct DstRangeRelationToSrcRangeImpl<Dst, | 328 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 357 Src, | 329 Src, |
| 358 Bounds, | |
| 359 INTEGER_REPRESENTATION_UNSIGNED, | 330 INTEGER_REPRESENTATION_UNSIGNED, |
| 360 INTEGER_REPRESENTATION_SIGNED, | 331 INTEGER_REPRESENTATION_SIGNED, |
| 361 NUMERIC_RANGE_NOT_CONTAINED> { | 332 NUMERIC_RANGE_NOT_CONTAINED> { |
| 362 static constexpr RangeConstraint Check(Src value) { | 333 static constexpr RangeConstraint Check(Src value) { |
| 363 using SrcLimits = std::numeric_limits<Src>; | 334 return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) |
| 364 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 335 ? GetRangeConstraint(true, value >= static_cast<Src>(0)) |
| 365 using Promotion = decltype(Src() + Dst()); | 336 : GetRangeConstraint( |
| 366 return RangeConstraint( | 337 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), |
| 367 static_cast<Promotion>(SrcLimits::max()) <= | 338 value >= static_cast<Src>(0)); |
| 368 static_cast<Promotion>(DstLimits::max()) || | |
| 369 static_cast<Promotion>(value) <= | |
| 370 static_cast<Promotion>(DstLimits::max()), | |
| 371 value >= Src(0) && (DstLimits::lowest() == 0 || | |
| 372 static_cast<Dst>(value) >= DstLimits::lowest())); | |
| 373 } | 339 } |
| 374 }; | 340 }; |
| 375 | 341 |
| 376 template <typename Dst, | 342 template <typename Dst, typename Src> |
| 377 template <typename> class Bounds = std::numeric_limits, | |
| 378 typename Src> | |
| 379 constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) { | 343 constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) { |
| 380 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); | 344 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); |
| 381 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 345 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); |
| 382 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); | 346 return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); |
| 383 return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); | |
| 384 } | 347 } |
| 385 | 348 |
| 386 // Integer promotion templates used by the portable checked integer arithmetic. | 349 // Integer promotion templates used by the portable checked integer arithmetic. |
| 387 template <size_t Size, bool IsSigned> | 350 template <size_t Size, bool IsSigned> |
| 388 struct IntegerForDigitsAndSign; | 351 struct IntegerForDigitsAndSign; |
| 389 | 352 |
| 390 #define INTEGER_FOR_DIGITS_AND_SIGN(I) \ | 353 #define INTEGER_FOR_DIGITS_AND_SIGN(I) \ |
| 391 template <> \ | 354 template <> \ |
| 392 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ | 355 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ |
| 393 std::is_signed<I>::value> { \ | 356 std::is_signed<I>::value> { \ |
| (...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 731 static_cast<BigType>(static_cast<L>(lhs)), | 694 static_cast<BigType>(static_cast<L>(lhs)), |
| 732 static_cast<BigType>(static_cast<R>(rhs))) | 695 static_cast<BigType>(static_cast<R>(rhs))) |
| 733 // Let the template functions figure it out for mixed types. | 696 // Let the template functions figure it out for mixed types. |
| 734 : C<L, R>::Test(lhs, rhs); | 697 : C<L, R>::Test(lhs, rhs); |
| 735 }; | 698 }; |
| 736 | 699 |
| 737 } // namespace internal | 700 } // namespace internal |
| 738 } // namespace base | 701 } // namespace base |
| 739 | 702 |
| 740 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 703 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
| OLD | NEW |