1 // Copyright 2017 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 #ifndef BASE_NUMERICS_CHECKED_MATH_H_ 6 #define BASE_NUMERICS_CHECKED_MATH_H_ 7 8 #include <stddef.h> 9 10 #include <limits> 11 #include <type_traits> 12 13 #include "anglebase/numerics/checked_math_impl.h" 14 15 namespace angle 16 { 17 namespace base 18 { 19 namespace internal 20 { 21 22 template <typename T> 23 class CheckedNumeric 24 { 25 static_assert(std::is_arithmetic<T>::value, "CheckedNumeric<T>: T must be a numeric type."); 26 27 public: 28 template <typename Src> 29 friend class CheckedNumeric; 30 31 using type = T; 32 33 constexpr CheckedNumeric() = default; 34 35 // Copy constructor. 36 template <typename Src> CheckedNumeric(const CheckedNumeric<Src> & rhs)37 constexpr CheckedNumeric(const CheckedNumeric<Src> &rhs) 38 : state_(rhs.state_.value(), rhs.IsValid()) 39 {} 40 41 // This is not an explicit constructor because we implicitly upgrade regular 42 // numerics to CheckedNumerics to make them easier to use. 43 template <typename Src> CheckedNumeric(Src value)44 constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) 45 : state_(value) 46 { 47 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); 48 } 49 50 // This is not an explicit constructor because we want a seamless conversion 51 // from StrictNumeric types. 52 template <typename Src> CheckedNumeric(StrictNumeric<Src> value)53 constexpr CheckedNumeric(StrictNumeric<Src> value) // NOLINT(runtime/explicit) 54 : state_(static_cast<Src>(value)) 55 {} 56 57 // IsValid() - The public API to test if a CheckedNumeric is currently valid. 58 // A range checked destination type can be supplied using the Dst template 59 // parameter. 60 template <typename Dst = T> IsValid()61 constexpr bool IsValid() const 62 { 63 return state_.is_valid() && IsValueInRangeForNumericType<Dst>(state_.value()); 64 } 65 66 // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid 67 // and is within the range supported by the destination type. Returns true if 68 // successful and false otherwise. 69 template <typename Dst> 70 #if defined(__clang__) || defined(__GNUC__) 71 __attribute__((warn_unused_result)) 72 #elif defined(_MSC_VER) 73 _Check_return_ 74 #endif 75 constexpr bool AssignIfValid(Dst * result)76 AssignIfValid(Dst *result) const 77 { 78 return BASE_NUMERICS_LIKELY(IsValid<Dst>()) 79 ? ((*result = static_cast<Dst>(state_.value())), true) 80 : false; 81 } 82 83 // ValueOrDie() - The primary accessor for the underlying value. If the 84 // current state is not valid it will CHECK and crash. 85 // A range checked destination type can be supplied using the Dst template 86 // parameter, which will trigger a CHECK if the value is not in bounds for 87 // the destination. 88 // The CHECK behavior can be overridden by supplying a handler as a 89 // template parameter, for test code, etc. However, the handler cannot access 90 // the underlying value, and it is not available through other means. 91 template <typename Dst = T, class CheckHandler = CheckOnFailure> ValueOrDie()92 constexpr StrictNumeric<Dst> ValueOrDie() const 93 { 94 return BASE_NUMERICS_LIKELY(IsValid<Dst>()) ? static_cast<Dst>(state_.value()) 95 : CheckHandler::template HandleFailure<Dst>(); 96 } 97 98 // ValueOrDefault(T default_value) - A convenience method that returns the 99 // current value if the state is valid, and the supplied default_value for 100 // any other state. 101 // A range checked destination type can be supplied using the Dst template 102 // parameter. WARNING: This function may fail to compile or CHECK at runtime 103 // if the supplied default_value is not within range of the destination type. 104 template <typename Dst = T, typename Src> ValueOrDefault(const Src default_value)105 constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const 106 { 107 return BASE_NUMERICS_LIKELY(IsValid<Dst>()) ? static_cast<Dst>(state_.value()) 108 : checked_cast<Dst>(default_value); 109 } 110 111 // Returns a checked numeric of the specified type, cast from the current 112 // CheckedNumeric. If the current state is invalid or the destination cannot 113 // represent the result then the returned CheckedNumeric will be invalid. 114 template <typename Dst> Cast()115 constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const 116 { 117 return *this; 118 } 119 120 // This friend method is available solely for providing more detailed logging 121 // in the tests. Do not implement it in production code, because the 122 // underlying values may change at any time. 123 template <typename U> 124 friend U GetNumericValueForTest(const CheckedNumeric<U> &src); 125 126 // Prototypes for the supported arithmetic operator overloads. 127 template <typename Src> 128 constexpr CheckedNumeric &operator+=(const Src rhs); 129 template <typename Src> 130 constexpr CheckedNumeric &operator-=(const Src rhs); 131 template <typename Src> 132 constexpr CheckedNumeric &operator*=(const Src rhs); 133 template <typename Src> 134 constexpr CheckedNumeric &operator/=(const Src rhs); 135 template <typename Src> 136 constexpr CheckedNumeric &operator%=(const Src rhs); 137 template <typename Src> 138 constexpr CheckedNumeric &operator<<=(const Src rhs); 139 template <typename Src> 140 constexpr CheckedNumeric &operator>>=(const Src rhs); 141 template <typename Src> 142 constexpr CheckedNumeric &operator&=(const Src rhs); 143 template <typename Src> 144 constexpr CheckedNumeric &operator|=(const Src rhs); 145 template <typename Src> 146 constexpr CheckedNumeric &operator^=(const Src rhs); 147 148 constexpr CheckedNumeric operator-() const 149 { 150 // Use an optimized code path for a known run-time variable. 151 if (!MustTreatAsConstexpr(state_.value()) && std::is_signed<T>::value && 152 std::is_floating_point<T>::value) 153 { 154 return FastRuntimeNegate(); 155 } 156 // The negation of two's complement int min is int min. 157 const bool is_valid = 158 IsValid() && (!std::is_signed<T>::value || std::is_floating_point<T>::value || 159 NegateWrapper(state_.value()) != std::numeric_limits<T>::lowest()); 160 return CheckedNumeric<T>(NegateWrapper(state_.value()), is_valid); 161 } 162 163 constexpr CheckedNumeric operator~() const 164 { 165 return CheckedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(state_.value()), 166 IsValid()); 167 } 168 Abs()169 constexpr CheckedNumeric Abs() const 170 { 171 return !IsValueNegative(state_.value()) ? *this : -*this; 172 } 173 174 template <typename U> Max(const U rhs)175 constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(const U rhs) const 176 { 177 return CheckMax(*this, rhs); 178 } 179 180 template <typename U> Min(const U rhs)181 constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(const U rhs) const 182 { 183 return CheckMin(*this, rhs); 184 } 185 186 // This function is available only for integral types. It returns an unsigned 187 // integer of the same width as the source type, containing the absolute value 188 // of the source, and properly handling signed min. UnsignedAbs()189 constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const 190 { 191 return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>( 192 SafeUnsignedAbs(state_.value()), state_.is_valid()); 193 } 194 195 constexpr CheckedNumeric &operator++() 196 { 197 *this += 1; 198 return *this; 199 } 200 201 constexpr CheckedNumeric operator++(int) 202 { 203 CheckedNumeric value = *this; 204 *this += 1; 205 return value; 206 } 207 208 constexpr CheckedNumeric &operator--() 209 { 210 *this -= 1; 211 return *this; 212 } 213 214 constexpr CheckedNumeric operator--(int) 215 { 216 // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20. 217 const CheckedNumeric value = *this; 218 *this -= 1; 219 return value; 220 } 221 222 // These perform the actual math operations on the CheckedNumerics. 223 // Binary arithmetic operations. 224 template <template <typename, typename, typename> class M, typename L, typename R> MathOp(const L lhs,const R rhs)225 static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) 226 { 227 using Math = typename MathWrapper<M, L, R>::math; 228 T result = 0; 229 const bool is_valid = Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && 230 Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); 231 return CheckedNumeric<T>(result, is_valid); 232 } 233 234 // Assignment arithmetic operations. 235 template <template <typename, typename, typename> class M, typename R> MathOp(const R rhs)236 constexpr CheckedNumeric &MathOp(const R rhs) 237 { 238 using Math = typename MathWrapper<M, T, R>::math; 239 T result = 0; // Using T as the destination saves a range check. 240 const bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) && 241 Math::Do(state_.value(), Wrapper<R>::value(rhs), &result); 242 *this = CheckedNumeric<T>(result, is_valid); 243 return *this; 244 } 245 246 private: 247 CheckedNumericState<T> state_; 248 FastRuntimeNegate()249 CheckedNumeric FastRuntimeNegate() const 250 { 251 T result; 252 const bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result); 253 return CheckedNumeric<T>(result, IsValid() && success); 254 } 255 256 template <typename Src> CheckedNumeric(Src value,bool is_valid)257 constexpr CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) 258 {} 259 260 // These wrappers allow us to handle state the same way for both 261 // CheckedNumeric and POD arithmetic types. 262 template <typename Src> 263 struct Wrapper 264 { is_validWrapper265 static constexpr bool is_valid(Src) { return true; } valueWrapper266 static constexpr Src value(Src value) { return value; } 267 }; 268 269 template <typename Src> 270 struct Wrapper<CheckedNumeric<Src>> 271 { 272 static constexpr bool is_valid(const CheckedNumeric<Src> v) { return v.IsValid(); } 273 static constexpr Src value(const CheckedNumeric<Src> v) { return v.state_.value(); } 274 }; 275 276 template <typename Src> 277 struct Wrapper<StrictNumeric<Src>> 278 { 279 static constexpr bool is_valid(const StrictNumeric<Src>) { return true; } 280 static constexpr Src value(const StrictNumeric<Src> v) { return static_cast<Src>(v); } 281 }; 282 }; 283 284 // Convenience functions to avoid the ugly template disambiguator syntax. 285 template <typename Dst, typename Src> 286 constexpr bool IsValidForType(const CheckedNumeric<Src> value) 287 { 288 return value.template IsValid<Dst>(); 289 } 290 291 template <typename Dst, typename Src> 292 constexpr StrictNumeric<Dst> ValueOrDieForType(const CheckedNumeric<Src> value) 293 { 294 return value.template ValueOrDie<Dst>(); 295 } 296 297 template <typename Dst, typename Src, typename Default> 298 constexpr StrictNumeric<Dst> ValueOrDefaultForType(const CheckedNumeric<Src> value, 299 const Default default_value) 300 { 301 return value.template ValueOrDefault<Dst>(default_value); 302 } 303 304 // Convience wrapper to return a new CheckedNumeric from the provided arithmetic 305 // or CheckedNumericType. 306 template <typename T> 307 constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(const T value) 308 { 309 return value; 310 } 311 312 // These implement the variadic wrapper for the math operations. 313 template <template <typename, typename, typename> class M, typename L, typename R> 314 constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(const L lhs, const R rhs) 315 { 316 using Math = typename MathWrapper<M, L, R>::math; 317 return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs, rhs); 318 } 319 320 // General purpose wrapper template for arithmetic operations. 321 template <template <typename, typename, typename> class M, typename L, typename R, typename... Args> 322 constexpr auto CheckMathOp(const L lhs, const R rhs, const Args... args) 323 { 324 return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...); 325 } 326 327 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=) 328 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=) 329 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=) 330 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=) 331 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=) 332 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=) 333 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=) 334 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=) 335 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=) 336 BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=) 337 BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max) 338 BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min) 339 340 // These are some extra StrictNumeric operators to support simple pointer 341 // arithmetic with our result types. Since wrapping on a pointer is always 342 // bad, we trigger the CHECK condition here. 343 template <typename L, typename R> 344 L *operator+(L *lhs, const StrictNumeric<R> rhs) 345 { 346 const uintptr_t result = 347 CheckAdd(reinterpret_cast<uintptr_t>(lhs), CheckMul(sizeof(L), static_cast<R>(rhs))) 348 .template ValueOrDie<uintptr_t>(); 349 return reinterpret_cast<L *>(result); 350 } 351 352 template <typename L, typename R> 353 L *operator-(L *lhs, const StrictNumeric<R> rhs) 354 { 355 const uintptr_t result = 356 CheckSub(reinterpret_cast<uintptr_t>(lhs), CheckMul(sizeof(L), static_cast<R>(rhs))) 357 .template ValueOrDie<uintptr_t>(); 358 return reinterpret_cast<L *>(result); 359 } 360 361 } // namespace internal 362 363 using internal::CheckAdd; 364 using internal::CheckAnd; 365 using internal::CheckDiv; 366 using internal::CheckedNumeric; 367 using internal::CheckLsh; 368 using internal::CheckMax; 369 using internal::CheckMin; 370 using internal::CheckMod; 371 using internal::CheckMul; 372 using internal::CheckOr; 373 using internal::CheckRsh; 374 using internal::CheckSub; 375 using internal::CheckXor; 376 using internal::IsValidForType; 377 using internal::MakeCheckedNum; 378 using internal::ValueOrDefaultForType; 379 using internal::ValueOrDieForType; 380 381 } // namespace base 382 } // namespace angle 383 384 #endif // BASE_NUMERICS_CHECKED_MATH_H_ 385