Chromium Code Reviews| 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_MATH_H_ | 5 #ifndef BASE_NUMERICS_SAFE_MATH_H_ |
| 6 #define BASE_NUMERICS_SAFE_MATH_H_ | 6 #define BASE_NUMERICS_SAFE_MATH_H_ |
| 7 | 7 |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 | 9 |
| 10 #include <limits> | 10 #include <limits> |
| 11 #include <type_traits> | 11 #include <type_traits> |
| 12 | 12 |
| 13 #include "base/numerics/safe_math_impl.h" | 13 #include "base/numerics/safe_math_impl.h" |
| 14 | 14 |
| 15 namespace base { | 15 namespace base { |
| 16 | |
| 17 namespace internal { | 16 namespace internal { |
| 18 | 17 |
| 19 // CheckedNumeric implements all the logic and operators for detecting integer | 18 // CheckedNumeric implements all the logic and operators for detecting integer |
| 20 // boundary conditions such as overflow, underflow, and invalid conversions. | 19 // boundary conditions such as overflow, underflow, and invalid conversions. |
| 21 // The CheckedNumeric type implicitly converts from floating point and integer | 20 // The CheckedNumeric type implicitly converts from floating point and integer |
| 22 // data types, and contains overloads for basic arithmetic operations (i.e.: +, | 21 // data types, and contains overloads for basic arithmetic operations (i.e.: +, |
| 23 // -, *, /, %). | 22 // -, *, /, %). |
| 24 // | 23 // |
| 25 // The following methods convert from CheckedNumeric to standard numeric values: | 24 // The following methods convert from CheckedNumeric to standard numeric values: |
| 26 // IsValid() - Returns true if the underlying numeric value is valid (i.e. has | 25 // IsValid() - Returns true if the underlying numeric value is valid (i.e. has |
| 27 // has not wrapped and is not the result of an invalid conversion). | 26 // has not wrapped and is not the result of an invalid conversion). |
| 28 // ValueOrDie() - Returns the underlying value. If the state is not valid this | 27 // ValueOrDie() - Returns the underlying value. If the state is not valid this |
| 29 // call will crash on a CHECK. | 28 // call will crash on a CHECK. |
| 30 // ValueOrDefault() - Returns the current value, or the supplied default if the | 29 // ValueOrDefault() - Returns the current value, or the supplied default if the |
| 31 // state is not valid. | 30 // state is not valid (will not trigger a CHECK). |
| 32 // ValueFloating() - Returns the underlying floating point value (valid only | 31 // ValueFloating() - Returns the underlying floating point value (valid only |
| 33 // only for floating point CheckedNumeric types). | 32 // only for floating point CheckedNumeric types; will not |
| 33 // trigger a CHECK). | |
| 34 // | |
| 35 // You may also use one of the variadic convenience functions, which accept | |
| 36 // standard arithmetic or CheckedNumeric types, perform arithmetic operations, | |
| 37 // and return a CheckedNumeric result. The supported functions are: | |
| 38 // * CheckedAdd() - addition | |
|
Tom Sepez
2016/11/23 16:54:58
Can I explicitly specialize these, for instance if
jschuh
2016/11/23 19:18:00
That's a good idea. I can add it.
Tom Sepez
2016/11/23 19:47:08
I guess I worry about something getting silently p
Tom Sepez
2016/11/23 19:47:08
Yeah, I kinda worry about something getting silent
jschuh
2016/11/23 21:49:22
Okay, we might have a miscommunication here. I hav
| |
| 39 // * CheckedSub() - subtraction | |
| 40 // * CheckedMul() - multiplication | |
| 41 // * CheckedDiv() - division | |
| 42 // * CheckedMod() - modulous (integer only) | |
| 43 // * CheckedLsh() - left integer shift (integer only) | |
| 44 // * CheckedRsh() - right integer shift (integer only) | |
| 34 // | 45 // |
| 35 // Bitwise operations are explicitly not supported, because correct | 46 // Bitwise operations are explicitly not supported, because correct |
| 36 // handling of some cases (e.g. sign manipulation) is ambiguous. Comparison | 47 // handling of some cases (e.g. sign manipulation) is ambiguous. Comparison |
| 37 // operations are explicitly not supported because they could result in a crash | 48 // operations are explicitly not supported because they could result in a crash |
| 38 // on a CHECK condition. You should use patterns like the following for these | 49 // on a CHECK condition. You should use patterns like the following for these |
| 39 // operations: | 50 // operations: |
| 40 // Bitwise operation: | 51 // Bitwise operation: |
| 41 // CheckedNumeric<int> checked_int = untrusted_input_value; | 52 // CheckedNumeric<int> checked_int = untrusted_input_value; |
| 42 // int x = checked_int.ValueOrDefault(0) | kFlagValues; | 53 // int x = checked_int.ValueOrDefault(0) | kFlagValues; |
| 43 // Comparison: | 54 // Comparison: |
| 44 // CheckedNumeric<size_t> checked_size = untrusted_input_value; | 55 // CheckedNumeric<size_t> checked_size = untrusted_input_value; |
| 45 // checked_size += HEADER LENGTH; | 56 // checked_size += HEADER LENGTH; |
| 46 // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) | 57 // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) |
| 47 // Do stuff... | 58 // Do stuff... |
| 48 | 59 |
| 49 template <typename T> | 60 template <typename T> |
| 50 class CheckedNumeric; | |
| 51 | |
| 52 // Used to treat CheckedNumeric and arithmetic underlying types the same. | |
| 53 template <typename T> | |
| 54 struct UnderlyingType { | |
| 55 using type = T; | |
| 56 static const bool is_numeric = std::is_arithmetic<T>::value; | |
| 57 static const bool is_checked = false; | |
| 58 }; | |
| 59 | |
| 60 template <typename T> | |
| 61 struct UnderlyingType<CheckedNumeric<T>> { | |
| 62 using type = T; | |
| 63 static const bool is_numeric = true; | |
| 64 static const bool is_checked = true; | |
| 65 }; | |
| 66 | |
| 67 template <typename T> | |
| 68 class CheckedNumeric { | 61 class CheckedNumeric { |
| 69 static_assert(std::is_arithmetic<T>::value, | 62 static_assert(std::is_arithmetic<T>::value, |
| 70 "CheckedNumeric<T>: T must be a numeric type."); | 63 "CheckedNumeric<T>: T must be a numeric type."); |
| 71 | 64 |
| 72 public: | 65 public: |
| 73 typedef T type; | 66 typedef T type; |
| 74 | 67 |
| 75 constexpr CheckedNumeric() {} | 68 constexpr CheckedNumeric() {} |
| 76 | 69 |
| 77 // Copy constructor. | 70 // Copy constructor. |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 *this -= 1; | 183 *this -= 1; |
| 191 return value; | 184 return value; |
| 192 } | 185 } |
| 193 | 186 |
| 194 // These perform the actual math operations on the CheckedNumerics. | 187 // These perform the actual math operations on the CheckedNumerics. |
| 195 // Binary arithmetic operations. | 188 // Binary arithmetic operations. |
| 196 template <template <typename, typename, typename> class M, | 189 template <template <typename, typename, typename> class M, |
| 197 typename L, | 190 typename L, |
| 198 typename R> | 191 typename R> |
| 199 static CheckedNumeric MathOp(const L& lhs, const R& rhs) { | 192 static CheckedNumeric MathOp(const L& lhs, const R& rhs) { |
| 200 using Math = M<typename UnderlyingType<L>::type, | 193 using Math = typename MathWrapper<M, L, R>::math; |
| 201 typename UnderlyingType<R>::type, void>; | |
| 202 T result = 0; | 194 T result = 0; |
| 203 bool is_valid = | 195 bool is_valid = |
| 204 Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && | 196 Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && |
| 205 Math::Op(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); | 197 Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); |
| 206 return CheckedNumeric<T>(result, is_valid); | 198 return CheckedNumeric<T>(result, is_valid); |
| 207 }; | 199 }; |
| 208 | 200 |
| 209 // Assignment arithmetic operations. | 201 // Assignment arithmetic operations. |
| 210 template <template <typename, typename, typename> class M, typename R> | 202 template <template <typename, typename, typename> class M, typename R> |
| 211 CheckedNumeric& MathOp(const R& rhs) { | 203 CheckedNumeric& MathOp(const R& rhs) { |
| 212 using Math = M<T, typename UnderlyingType<R>::type, void>; | 204 using Math = typename MathWrapper<M, T, R>::math; |
| 213 T result = 0; // Using T as the destination saves a range check. | 205 T result = 0; // Using T as the destination saves a range check. |
| 214 bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) && | 206 bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) && |
| 215 Math::Op(state_.value(), Wrapper<R>::value(rhs), &result); | 207 Math::Do(state_.value(), Wrapper<R>::value(rhs), &result); |
| 216 *this = CheckedNumeric<T>(result, is_valid); | 208 *this = CheckedNumeric<T>(result, is_valid); |
| 217 return *this; | 209 return *this; |
| 218 }; | 210 }; |
| 219 | 211 |
| 220 private: | 212 private: |
| 221 CheckedNumericState<T> state_; | 213 CheckedNumericState<T> state_; |
| 222 | 214 |
| 223 template <typename Src> | 215 template <typename Src> |
| 224 constexpr CheckedNumeric(Src value, bool is_valid) | 216 constexpr CheckedNumeric(Src value, bool is_valid) |
| 225 : state_(value, is_valid) {} | 217 : state_(value, is_valid) {} |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 236 struct Wrapper<CheckedNumeric<Src>> { | 228 struct Wrapper<CheckedNumeric<Src>> { |
| 237 static constexpr bool is_valid(const CheckedNumeric<Src>& v) { | 229 static constexpr bool is_valid(const CheckedNumeric<Src>& v) { |
| 238 return v.IsValid(); | 230 return v.IsValid(); |
| 239 } | 231 } |
| 240 static constexpr Src value(const CheckedNumeric<Src>& v) { | 232 static constexpr Src value(const CheckedNumeric<Src>& v) { |
| 241 return v.state_.value(); | 233 return v.state_.value(); |
| 242 } | 234 } |
| 243 }; | 235 }; |
| 244 }; | 236 }; |
| 245 | 237 |
| 238 // These variadic templates work out the return types. | |
| 239 // TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support. | |
| 240 template <template <typename, typename, typename> class M, | |
| 241 typename L, | |
| 242 typename R, | |
| 243 typename... Args> | |
| 244 struct ResultType; | |
| 245 | |
| 246 template <template <typename, typename, typename> class M, | |
| 247 typename L, | |
| 248 typename R> | |
| 249 struct ResultType<M, L, R> { | |
| 250 using type = typename MathWrapper<M, L, R>::type; | |
| 251 }; | |
| 252 | |
| 253 template <template <typename, typename, typename> class M, | |
| 254 typename L, | |
| 255 typename R, | |
| 256 typename... Args> | |
| 257 struct ResultType { | |
| 258 // The typedef was required here because MSVC fails to compile with "using". | |
|
Tom Sepez
2016/11/23 16:52:06
Let's get Bruce to weigh in on this one - at least
jschuh
2016/11/23 19:18:00
I've learned that any complex type resolution with
| |
| 259 typedef | |
| 260 typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type | |
| 261 type; | |
| 262 }; | |
| 263 | |
| 264 // These implement the variadic wrapper for the math operations. | |
| 265 template <template <typename, typename, typename> class M, | |
| 266 typename L, | |
| 267 typename R> | |
| 268 CheckedNumeric<typename MathWrapper<M, L, R>::type> ChkMathOp(const L lhs, | |
| 269 const R rhs) { | |
| 270 using Math = typename MathWrapper<M, L, R>::math; | |
| 271 return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs, | |
| 272 rhs); | |
| 273 }; | |
| 274 | |
| 275 template <template <typename, typename, typename> class M, | |
| 276 typename L, | |
| 277 typename R, | |
| 278 typename... Args> | |
| 279 CheckedNumeric<typename ResultType<M, L, R, Args...>::type> | |
| 280 ChkMathOp(const L lhs, const R rhs, const Args... args) { | |
| 281 auto tmp = ChkMathOp<M>(lhs, rhs); | |
| 282 return tmp.IsValid() ? ChkMathOp<M>(tmp, args...) | |
| 283 : decltype(ChkMathOp<M>(tmp, args...))(tmp); | |
| 284 }; | |
| 285 | |
| 246 // This is just boilerplate for the standard arithmetic operator overloads. | 286 // This is just boilerplate for the standard arithmetic operator overloads. |
| 247 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. | 287 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. |
| 248 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ | 288 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ |
| 249 /* Binary arithmetic operator for all CheckedNumeric operations. */ \ | 289 /* Binary arithmetic operator for all CheckedNumeric operations. */ \ |
| 250 template <typename L, typename R, \ | 290 template <typename L, typename R, \ |
| 251 typename std::enable_if<UnderlyingType<L>::is_numeric && \ | 291 typename std::enable_if<IsCheckedOp<L, R>::value>::type* = \ |
| 252 UnderlyingType<R>::is_numeric && \ | 292 nullptr> \ |
| 253 (UnderlyingType<L>::is_checked || \ | 293 CheckedNumeric<typename MathWrapper<Checked##NAME##Op, L, R>::type> \ |
| 254 UnderlyingType<R>::is_checked)>::type* = \ | 294 operator OP(const L lhs, const R rhs) { \ |
| 255 nullptr> \ | 295 return decltype(lhs OP rhs)::template MathOp<Checked##NAME##Op>(lhs, rhs); \ |
| 256 CheckedNumeric< \ | 296 } \ |
| 257 typename Checked##NAME<typename UnderlyingType<L>::type, \ | 297 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ |
| 258 typename UnderlyingType<R>::type>::result_type> \ | 298 template <typename L> \ |
| 259 operator OP(const L lhs, const R rhs) { \ | 299 template <typename R> \ |
| 260 return decltype(lhs OP rhs)::template MathOp<Checked##NAME>(lhs, rhs); \ | 300 CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \ |
| 261 } \ | 301 return MathOp<Checked##NAME##Op>(rhs); \ |
| 262 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ | 302 } \ |
| 263 template <typename L> \ | 303 /* Variadic arithmetic functions that return CheckedNumeric. */ \ |
| 264 template <typename R> \ | 304 template <typename L, typename R, typename... Args> \ |
| 265 CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \ | 305 CheckedNumeric<typename ResultType<Checked##NAME##Op, L, R, Args...>::type> \ |
| 266 return MathOp<Checked##NAME>(rhs); \ | 306 Checked##NAME(const L lhs, const R rhs, const Args... args) { \ |
| 307 return ChkMathOp<Checked##NAME##Op, L, R, Args...>(lhs, rhs, args...); \ | |
| 267 } | 308 } |
| 268 | 309 |
| 269 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=) | 310 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=) |
| 270 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=) | 311 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=) |
| 271 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=) | 312 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=) |
| 272 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=) | 313 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=) |
| 273 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=) | 314 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=) |
| 274 BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=) | 315 BASE_NUMERIC_ARITHMETIC_OPERATORS(Lsh, <<, <<=) |
| 275 BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=) | 316 BASE_NUMERIC_ARITHMETIC_OPERATORS(Rsh, >>, >>=) |
| 276 | 317 |
| 277 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS | 318 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS |
| 278 | 319 |
| 279 } // namespace internal | 320 } // namespace internal |
| 280 | 321 |
| 281 using internal::CheckedNumeric; | 322 using internal::CheckedNumeric; |
| 323 using internal::CheckedAdd; | |
| 324 using internal::CheckedSub; | |
| 325 using internal::CheckedMul; | |
| 326 using internal::CheckedDiv; | |
| 327 using internal::CheckedMod; | |
| 328 using internal::CheckedLsh; | |
| 329 using internal::CheckedRsh; | |
| 282 | 330 |
| 283 } // namespace base | 331 } // namespace base |
| 284 | 332 |
| 285 #endif // BASE_NUMERICS_SAFE_MATH_H_ | 333 #endif // BASE_NUMERICS_SAFE_MATH_H_ |
| OLD | NEW |