1 // Copyright 2014 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_ 7 8 #include <stddef.h> 9 10 #include <cmath> 11 #include <concepts> 12 #include <limits> 13 #include <type_traits> 14 15 #include "base/numerics/safe_conversions_impl.h" // IWYU pragma: export 16 17 #if defined(__ARMEL__) && !defined(__native_client__) 18 #include "base/numerics/safe_conversions_arm_impl.h" // IWYU pragma: export 19 #define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1) 20 #else 21 #define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0) 22 #endif 23 24 namespace base { 25 namespace internal { 26 27 #if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS 28 template <typename Dst, typename Src> 29 struct SaturateFastAsmOp { 30 static constexpr bool is_supported = false; DoSaturateFastAsmOp31 static constexpr Dst Do(Src) { 32 // Force a compile failure if instantiated. 33 return CheckOnFailure::template HandleFailure<Dst>(); 34 } 35 }; 36 #endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS 37 #undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS 38 39 // The following special case a few specific integer conversions where we can 40 // eke out better performance than range checking. 41 template <typename Dst, typename Src> 42 struct IsValueInRangeFastOp { 43 static constexpr bool is_supported = false; DoIsValueInRangeFastOp44 static constexpr bool Do(Src value) { 45 // Force a compile failure if instantiated. 46 return CheckOnFailure::template HandleFailure<bool>(); 47 } 48 }; 49 50 // Signed to signed range comparison. 51 template <typename Dst, typename Src> 52 requires(std::signed_integral<Dst> && std::signed_integral<Src> && 53 !kIsTypeInRangeForNumericType<Dst, Src>) 54 struct IsValueInRangeFastOp<Dst, Src> { 55 static constexpr bool is_supported = true; 56 57 static constexpr bool Do(Src value) { 58 // Just downcast to the smaller type, sign extend it back to the original 59 // type, and then see if it matches the original value. 60 return value == static_cast<Dst>(value); 61 } 62 }; 63 64 // Signed to unsigned range comparison. 65 template <typename Dst, typename Src> 66 requires(std::unsigned_integral<Dst> && std::signed_integral<Src> && 67 !kIsTypeInRangeForNumericType<Dst, Src>) 68 struct IsValueInRangeFastOp<Dst, Src> { 69 static constexpr bool is_supported = true; 70 71 static constexpr bool Do(Src value) { 72 // We cast a signed as unsigned to overflow negative values to the top, 73 // then compare against whichever maximum is smaller, as our upper bound. 74 return as_unsigned(value) <= as_unsigned(kCommonMax<Src, Dst>); 75 } 76 }; 77 78 // Convenience function that returns true if the supplied value is in range 79 // for the destination type. 80 template <typename Dst, typename Src> 81 requires(UnderlyingType<Src>::is_numeric && std::is_arithmetic_v<Dst> && 82 std::numeric_limits<Dst>::lowest() < std::numeric_limits<Dst>::max()) 83 constexpr bool IsValueInRangeForNumericType(Src value) { 84 using SrcType = typename internal::UnderlyingType<Src>::type; 85 const auto underlying_value = static_cast<SrcType>(value); 86 return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported 87 ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do( 88 underlying_value) 89 : internal::DstRangeRelationToSrcRange<Dst>(underlying_value) 90 .IsValid(); 91 } 92 93 // checked_cast<> is analogous to static_cast<> for numeric types, 94 // except that it CHECKs that the specified numeric conversion will not 95 // overflow or underflow. NaN source will always trigger a CHECK. 96 template <typename Dst, 97 class CheckHandler = internal::CheckOnFailure, 98 typename Src> 99 requires(UnderlyingType<Src>::is_numeric && std::is_arithmetic_v<Dst> && 100 std::numeric_limits<Dst>::lowest() < std::numeric_limits<Dst>::max()) 101 constexpr Dst checked_cast(Src value) { 102 // This throws a compile-time error on evaluating the constexpr if it can be 103 // determined at compile-time as failing, otherwise it will CHECK at runtime. 104 using SrcType = typename internal::UnderlyingType<Src>::type; 105 if (IsValueInRangeForNumericType<Dst>(value)) [[likely]] { 106 return static_cast<Dst>(static_cast<SrcType>(value)); 107 } 108 return CheckHandler::template HandleFailure<Dst>(); 109 } 110 111 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. 112 // You may provide your own limits (e.g. to saturated_cast) so long as you 113 // implement all of the static constexpr member functions in the class below. 114 template <typename T> 115 struct SaturationDefaultLimits : public std::numeric_limits<T> { 116 static constexpr T NaN() { 117 if constexpr (std::numeric_limits<T>::has_quiet_NaN) { 118 return std::numeric_limits<T>::quiet_NaN(); 119 } else { 120 return T(); 121 } 122 } 123 using std::numeric_limits<T>::max; 124 static constexpr T Overflow() { 125 if constexpr (std::numeric_limits<T>::has_infinity) { 126 return std::numeric_limits<T>::infinity(); 127 } else { 128 return std::numeric_limits<T>::max(); 129 } 130 } 131 using std::numeric_limits<T>::lowest; 132 static constexpr T Underflow() { 133 if constexpr (std::numeric_limits<T>::has_infinity) { 134 return std::numeric_limits<T>::infinity() * -1; 135 } else { 136 return std::numeric_limits<T>::lowest(); 137 } 138 } 139 }; 140 141 template <typename Dst, template <typename> class S, typename Src> 142 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { 143 // For some reason clang generates much better code when the branch is 144 // structured exactly this way, rather than a sequence of checks. 145 return !constraint.IsOverflowFlagSet() 146 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) 147 : S<Dst>::Underflow()) 148 // Skip this check for integral Src, which cannot be NaN. 149 : (std::is_integral_v<Src> || !constraint.IsUnderflowFlagSet() 150 ? S<Dst>::Overflow() 151 : S<Dst>::NaN()); 152 } 153 154 // We can reduce the number of conditions and get slightly better performance 155 // for normal signed and unsigned integer ranges. And in the specific case of 156 // Arm, we can use the optimized saturation instructions. 157 template <typename Dst, typename Src> 158 struct SaturateFastOp { 159 static constexpr bool is_supported = false; 160 static constexpr Dst Do(Src value) { 161 // Force a compile failure if instantiated. 162 return CheckOnFailure::template HandleFailure<Dst>(); 163 } 164 }; 165 166 template <typename Dst, typename Src> 167 requires(std::integral<Src> && std::integral<Dst> && 168 SaturateFastAsmOp<Dst, Src>::is_supported) 169 struct SaturateFastOp<Dst, Src> { 170 static constexpr bool is_supported = true; 171 static constexpr Dst Do(Src value) { 172 return SaturateFastAsmOp<Dst, Src>::Do(value); 173 } 174 }; 175 176 template <typename Dst, typename Src> 177 requires(std::integral<Src> && std::integral<Dst> && 178 !SaturateFastAsmOp<Dst, Src>::is_supported) 179 struct SaturateFastOp<Dst, Src> { 180 static constexpr bool is_supported = true; 181 static constexpr Dst Do(Src value) { 182 // The exact order of the following is structured to hit the correct 183 // optimization heuristics across compilers. Do not change without 184 // checking the emitted code. 185 const Dst saturated = CommonMaxOrMin<Dst, Src>( 186 kIsMaxInRangeForNumericType<Dst, Src> || 187 (!kIsMinInRangeForNumericType<Dst, Src> && IsValueNegative(value))); 188 if (IsValueInRangeForNumericType<Dst>(value)) [[likely]] { 189 return static_cast<Dst>(value); 190 } 191 return saturated; 192 } 193 }; 194 195 // saturated_cast<> is analogous to static_cast<> for numeric types, except 196 // that the specified numeric conversion will saturate by default rather than 197 // overflow or underflow, and NaN assignment to an integral will return 0. 198 // All boundary condition behaviors can be overridden with a custom handler. 199 template <typename Dst, 200 template <typename> class SaturationHandler = SaturationDefaultLimits, 201 typename Src> 202 constexpr Dst saturated_cast(Src value) { 203 using SrcType = typename UnderlyingType<Src>::type; 204 const auto underlying_value = static_cast<SrcType>(value); 205 return !std::is_constant_evaluated() && 206 SaturateFastOp<Dst, SrcType>::is_supported && 207 std::is_same_v<SaturationHandler<Dst>, 208 SaturationDefaultLimits<Dst>> 209 ? SaturateFastOp<Dst, SrcType>::Do(underlying_value) 210 : saturated_cast_impl<Dst, SaturationHandler, SrcType>( 211 underlying_value, 212 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>( 213 underlying_value)); 214 } 215 216 // strict_cast<> is analogous to static_cast<> for numeric types, except that 217 // it will cause a compile failure if the destination type is not large enough 218 // to contain any value in the source type. It performs no runtime checking. 219 template <typename Dst, 220 typename Src, 221 typename SrcType = typename UnderlyingType<Src>::type> 222 requires( 223 UnderlyingType<Src>::is_numeric && std::is_arithmetic_v<Dst> && 224 // If you got here from a compiler error, it's because you tried to assign 225 // from a source type to a destination type that has insufficient range. 226 // The solution may be to change the destination type you're assigning to, 227 // and use one large enough to represent the source. 228 // Alternatively, you may be better served with the checked_cast<> or 229 // saturated_cast<> template functions for your particular use case. 230 kStaticDstRangeRelationToSrcRange<Dst, SrcType> == 231 NumericRangeRepresentation::kContained) 232 constexpr Dst strict_cast(Src value) { 233 return static_cast<Dst>(static_cast<SrcType>(value)); 234 } 235 236 // Some wrappers to statically check that a type is in range. 237 template <typename Dst, typename Src> 238 inline constexpr bool kIsNumericRangeContained = false; 239 240 template <typename Dst, typename Src> 241 requires(std::is_arithmetic_v<ArithmeticOrUnderlyingEnum<Dst>> && 242 std::is_arithmetic_v<ArithmeticOrUnderlyingEnum<Src>>) 243 inline constexpr bool kIsNumericRangeContained<Dst, Src> = 244 kStaticDstRangeRelationToSrcRange<Dst, Src> == 245 NumericRangeRepresentation::kContained; 246 247 // StrictNumeric implements compile time range checking between numeric types by 248 // wrapping assignment operations in a strict_cast. This class is intended to be 249 // used for function arguments and return types, to ensure the destination type 250 // can always contain the source type. This is essentially the same as enforcing 251 // -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied 252 // incrementally at API boundaries, making it easier to convert code so that it 253 // compiles cleanly with truncation warnings enabled. 254 // This template should introduce no runtime overhead, but it also provides no 255 // runtime checking of any of the associated mathematical operations. Use 256 // CheckedNumeric for runtime range checks of the actual value being assigned. 257 template <typename T> 258 requires std::is_arithmetic_v<T> 259 class StrictNumeric { 260 public: 261 using type = T; 262 263 constexpr StrictNumeric() : value_(0) {} 264 265 // Copy constructor. 266 template <typename Src> 267 constexpr StrictNumeric(const StrictNumeric<Src>& rhs) 268 : value_(strict_cast<T>(rhs.value_)) {} 269 270 // This is not an explicit constructor because we implicitly upgrade regular 271 // numerics to StrictNumerics to make them easier to use. 272 template <typename Src> 273 // NOLINTNEXTLINE(google-explicit-constructor) 274 constexpr StrictNumeric(Src value) : value_(strict_cast<T>(value)) {} 275 276 // If you got here from a compiler error, it's because you tried to assign 277 // from a source type to a destination type that has insufficient range. 278 // The solution may be to change the destination type you're assigning to, 279 // and use one large enough to represent the source. 280 // If you're assigning from a CheckedNumeric<> class, you may be able to use 281 // the AssignIfValid() member function, specify a narrower destination type to 282 // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one 283 // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)). 284 // If you've encountered an _ambiguous overload_ you can use a static_cast<> 285 // to explicitly cast the result to the destination type. 286 // If none of that works, you may be better served with the checked_cast<> or 287 // saturated_cast<> template functions for your particular use case. 288 template <typename Dst> 289 requires(kIsNumericRangeContained<Dst, T>) 290 constexpr operator Dst() const { // NOLINT(google-explicit-constructor) 291 return static_cast<ArithmeticOrUnderlyingEnum<Dst>>(value_); 292 } 293 294 private: 295 template <typename U> 296 requires std::is_arithmetic_v<U> 297 friend class StrictNumeric; 298 299 T value_; 300 }; 301 302 template <typename T> 303 StrictNumeric(T) -> StrictNumeric<T>; 304 305 // Convenience wrapper returns a StrictNumeric from the provided arithmetic 306 // type. 307 template <typename T> 308 constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum( 309 const T value) { 310 return value; 311 } 312 313 #define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \ 314 template <typename L, typename R> \ 315 requires(internal::kIs##CLASS##Op<L, R>) \ 316 constexpr bool operator OP(L lhs, R rhs) { \ 317 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ 318 typename UnderlyingType<R>::type>(lhs, rhs); \ 319 } 320 321 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <) 322 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=) 323 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >) 324 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=) 325 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==) 326 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=) 327 328 } // namespace internal 329 330 using internal::as_signed; 331 using internal::as_unsigned; 332 using internal::checked_cast; 333 using internal::IsValueInRangeForNumericType; 334 using internal::IsValueNegative; 335 using internal::kIsTypeInRangeForNumericType; 336 using internal::MakeStrictNum; 337 using internal::SafeUnsignedAbs; 338 using internal::saturated_cast; 339 using internal::strict_cast; 340 using internal::StrictNumeric; 341 342 // Explicitly make a shorter size_t alias for convenience. 343 using SizeT = StrictNumeric<size_t>; 344 345 // floating -> integral conversions that saturate and thus can actually return 346 // an integral type. 347 // 348 // Generally, what you want is saturated_cast<Dst>(std::nearbyint(x)), which 349 // rounds correctly according to IEEE-754 (round to nearest, ties go to nearest 350 // even number; this avoids bias). If your code is performance-critical 351 // and you are sure that you will never overflow, you can use std::lrint() 352 // or std::llrint(), which return a long or long long directly. 353 // 354 // Below are convenience functions around similar patterns, except that 355 // they round in nonstandard directions and will generally be slower. 356 357 // Rounds towards negative infinity (i.e., down). 358 template <typename Dst = int, typename Src> 359 requires(std::integral<Dst> && std::floating_point<Src>) 360 Dst ClampFloor(Src value) { 361 return saturated_cast<Dst>(std::floor(value)); 362 } 363 364 // Rounds towards positive infinity (i.e., up). 365 template <typename Dst = int, typename Src> 366 requires(std::integral<Dst> && std::floating_point<Src>) 367 Dst ClampCeil(Src value) { 368 return saturated_cast<Dst>(std::ceil(value)); 369 } 370 371 // Rounds towards nearest integer, with ties away from zero. 372 // This means that 0.5 will be rounded to 1 and 1.5 will be rounded to 2. 373 // Similarly, -0.5 will be rounded to -1 and -1.5 will be rounded to -2. 374 // 375 // This is normally not what you want accuracy-wise (it introduces a small bias 376 // away from zero), and it is not the fastest option, but it is frequently what 377 // existing code expects. Compare with saturated_cast<Dst>(std::nearbyint(x)) 378 // or std::lrint(x), which would round 0.5 and -0.5 to 0 but 1.5 to 2 and 379 // -1.5 to -2. 380 template <typename Dst = int, typename Src> 381 requires(std::integral<Dst> && std::floating_point<Src>) 382 Dst ClampRound(Src value) { 383 const Src rounded = std::round(value); 384 return saturated_cast<Dst>(rounded); 385 } 386 387 } // namespace base 388 389 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ 390