Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: base/numerics/safe_math.h

Issue 2522073004: Add variadic helper functions for CheckedNumeric math operations (Closed)
Patch Set: test fixes Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | base/numerics/safe_math_impl.h » ('j') | base/numerics/safe_numerics_unittest.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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_
OLDNEW
« no previous file with comments | « no previous file | base/numerics/safe_math_impl.h » ('j') | base/numerics/safe_numerics_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698