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

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

Issue 2522073004: Add variadic helper functions for CheckedNumeric math operations (Closed)
Patch Set: compile fix 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') | no next file with comments »
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 //
24 // You may also use one of the variadic convenience functions, which accept
25 // standard arithmetic or CheckedNumeric types, perform arithmetic operations,
26 // and return a CheckedNumeric result. The supported functions are:
27 // CheckAdd() - Addition.
28 // CheckSub() - Subtraction.
29 // CheckMul() - Multiplication.
30 // CheckDiv() - Division.
31 // CheckMod() - Modulous (integer only).
32 // CheckLsh() - Left integer shift (integer only).
33 // CheckRsh() - Right integer shift (integer only).
34 //
35 // The unary negation, increment, and decrement operators are supported, along
36 // with the following unary arithmetic methods, which return a new
37 // CheckedNumeric as a result of the operation:
38 // Abs() - Absolute value.
39 // UnsignedAbs() - Absolute value as an equival-width unsigned underlying type
40 // (valid for only integral types).
41 //
25 // The following methods convert from CheckedNumeric to standard numeric values: 42 // The following methods convert from CheckedNumeric to standard numeric values:
26 // IsValid() - Returns true if the underlying numeric value is valid (i.e. has 43 // 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). 44 // 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 45 // ValueOrDie() - Returns the underlying value. If the state is not valid this
29 // call will crash on a CHECK. 46 // call will crash on a CHECK.
30 // ValueOrDefault() - Returns the current value, or the supplied default if the 47 // ValueOrDefault() - Returns the current value, or the supplied default if the
31 // state is not valid. 48 // state is not valid (will not trigger a CHECK).
32 // ValueFloating() - Returns the underlying floating point value (valid only 49 // ValueFloating() - Returns the underlying floating point value (valid only
33 // only for floating point CheckedNumeric types). 50 // for floating point CheckedNumeric types; will not cause a CHECK).
51 //
52 // The following are general utility methods that are useful for converting
53 // between arithmetic types and CheckedNumeric types:
54 // CheckedNumeric::Cast<Dst>() - Instance method returning a CheckedNumeric
55 // derived from casting the current instance to a CheckedNumeric of
56 // the supplied destination type.
57 // CheckNum() - Creates a new CheckedNumeric from the underlying type of the
58 // supplied arithmetic or CheckedNumeric type.
34 // 59 //
35 // Bitwise operations are explicitly not supported, because correct 60 // Bitwise operations are explicitly not supported, because correct
36 // handling of some cases (e.g. sign manipulation) is ambiguous. Comparison 61 // handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
37 // operations are explicitly not supported because they could result in a crash 62 // 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 63 // on a CHECK condition. You should use patterns like the following for these
39 // operations: 64 // operations:
40 // Bitwise operation: 65 // Bitwise operation:
41 // CheckedNumeric<int> checked_int = untrusted_input_value; 66 // CheckedNumeric<int> checked_int = untrusted_input_value;
42 // int x = checked_int.ValueOrDefault(0) | kFlagValues; 67 // int x = checked_int.ValueOrDefault(0) | kFlagValues;
43 // Comparison: 68 // Comparison:
44 // CheckedNumeric<size_t> checked_size = untrusted_input_value; 69 // CheckedNumeric<size_t> checked_size = untrusted_input_value;
45 // checked_size += HEADER LENGTH; 70 // checked_size += HEADER LENGTH;
46 // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) 71 // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
47 // Do stuff... 72 // Do stuff...
48 73
49 template <typename T> 74 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 { 75 class CheckedNumeric {
69 static_assert(std::is_arithmetic<T>::value, 76 static_assert(std::is_arithmetic<T>::value,
70 "CheckedNumeric<T>: T must be a numeric type."); 77 "CheckedNumeric<T>: T must be a numeric type.");
71 78
72 public: 79 public:
73 typedef T type; 80 typedef T type;
74 81
75 constexpr CheckedNumeric() {} 82 constexpr CheckedNumeric() {}
76 83
77 // Copy constructor. 84 // Copy constructor.
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 } 122 }
116 123
117 // ValueFloating() - Since floating point values include their validity state, 124 // ValueFloating() - Since floating point values include their validity state,
118 // we provide an easy method for extracting them directly, without a risk of 125 // we provide an easy method for extracting them directly, without a risk of
119 // crashing on a CHECK. 126 // crashing on a CHECK.
120 constexpr T ValueFloating() const { 127 constexpr T ValueFloating() const {
121 static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float."); 128 static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float.");
122 return state_.value(); 129 return state_.value();
123 } 130 }
124 131
132 // Returns a checked numeric of the specified type, cast from the current
133 // CheckedNumeric. If the current state is invalid or the destination cannot
134 // represent the result then the returned CheckedNumeric will be invalid.
135 template <typename Dst>
136 constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
137 return *this;
138 }
139
125 // This friend method is available solely for providing more detailed logging 140 // This friend method is available solely for providing more detailed logging
126 // in the the tests. Do not implement it in production code, because the 141 // in the the tests. Do not implement it in production code, because the
127 // underlying values may change at any time. 142 // underlying values may change at any time.
128 template <typename U> 143 template <typename U>
129 friend U GetNumericValueForTest(const CheckedNumeric<U>& src); 144 friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
130 145
131 // Prototypes for the supported arithmetic operator overloads. 146 // Prototypes for the supported arithmetic operator overloads.
132 template <typename Src> 147 template <typename Src>
133 CheckedNumeric& operator+=(const Src rhs); 148 CheckedNumeric& operator+=(const Src rhs);
134 template <typename Src> 149 template <typename Src>
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 *this -= 1; 205 *this -= 1;
191 return value; 206 return value;
192 } 207 }
193 208
194 // These perform the actual math operations on the CheckedNumerics. 209 // These perform the actual math operations on the CheckedNumerics.
195 // Binary arithmetic operations. 210 // Binary arithmetic operations.
196 template <template <typename, typename, typename> class M, 211 template <template <typename, typename, typename> class M,
197 typename L, 212 typename L,
198 typename R> 213 typename R>
199 static CheckedNumeric MathOp(const L& lhs, const R& rhs) { 214 static CheckedNumeric MathOp(const L& lhs, const R& rhs) {
200 using Math = M<typename UnderlyingType<L>::type, 215 using Math = typename MathWrapper<M, L, R>::math;
201 typename UnderlyingType<R>::type, void>;
202 T result = 0; 216 T result = 0;
203 bool is_valid = 217 bool is_valid =
204 Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && 218 Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
205 Math::Op(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); 219 Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
206 return CheckedNumeric<T>(result, is_valid); 220 return CheckedNumeric<T>(result, is_valid);
207 }; 221 };
208 222
209 // Assignment arithmetic operations. 223 // Assignment arithmetic operations.
210 template <template <typename, typename, typename> class M, typename R> 224 template <template <typename, typename, typename> class M, typename R>
211 CheckedNumeric& MathOp(const R& rhs) { 225 CheckedNumeric& MathOp(const R& rhs) {
212 using Math = M<T, typename UnderlyingType<R>::type, void>; 226 using Math = typename MathWrapper<M, T, R>::math;
213 T result = 0; // Using T as the destination saves a range check. 227 T result = 0; // Using T as the destination saves a range check.
214 bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) && 228 bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
215 Math::Op(state_.value(), Wrapper<R>::value(rhs), &result); 229 Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
216 *this = CheckedNumeric<T>(result, is_valid); 230 *this = CheckedNumeric<T>(result, is_valid);
217 return *this; 231 return *this;
218 }; 232 };
219 233
220 private: 234 private:
221 CheckedNumericState<T> state_; 235 CheckedNumericState<T> state_;
222 236
223 template <typename Src> 237 template <typename Src>
224 constexpr CheckedNumeric(Src value, bool is_valid) 238 constexpr CheckedNumeric(Src value, bool is_valid)
225 : state_(value, is_valid) {} 239 : state_(value, is_valid) {}
(...skipping 10 matching lines...) Expand all
236 struct Wrapper<CheckedNumeric<Src>> { 250 struct Wrapper<CheckedNumeric<Src>> {
237 static constexpr bool is_valid(const CheckedNumeric<Src>& v) { 251 static constexpr bool is_valid(const CheckedNumeric<Src>& v) {
238 return v.IsValid(); 252 return v.IsValid();
239 } 253 }
240 static constexpr Src value(const CheckedNumeric<Src>& v) { 254 static constexpr Src value(const CheckedNumeric<Src>& v) {
241 return v.state_.value(); 255 return v.state_.value();
242 } 256 }
243 }; 257 };
244 }; 258 };
245 259
260 // These variadic templates work out the return types.
261 // TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
262 template <template <typename, typename, typename> class M,
263 typename L,
264 typename R,
265 typename... Args>
266 struct ResultType;
267
268 template <template <typename, typename, typename> class M,
269 typename L,
270 typename R>
271 struct ResultType<M, L, R> {
272 using type = typename MathWrapper<M, L, R>::type;
273 };
274
275 template <template <typename, typename, typename> class M,
276 typename L,
277 typename R,
278 typename... Args>
279 struct ResultType {
280 // The typedef was required here because MSVC fails to compile with "using".
281 typedef
282 typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type
283 type;
284 };
285
286 // Convience wrapper to return a new CheckedNumeric from the provided arithmetic
287 // or CheckedNumericType.
288 template <typename T>
289 CheckedNumeric<typename UnderlyingType<T>::type> CheckNum(T value) {
290 return value;
291 }
292
293 // These implement the variadic wrapper for the math operations.
294 template <template <typename, typename, typename> class M,
295 typename L,
296 typename R>
297 CheckedNumeric<typename MathWrapper<M, L, R>::type> ChkMathOp(const L lhs,
298 const R rhs) {
299 using Math = typename MathWrapper<M, L, R>::math;
300 return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
301 rhs);
302 };
303
304 template <template <typename, typename, typename> class M,
305 typename L,
306 typename R,
307 typename... Args>
308 CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
309 ChkMathOp(const L lhs, const R rhs, const Args... args) {
310 auto tmp = ChkMathOp<M>(lhs, rhs);
311 return tmp.IsValid() ? ChkMathOp<M>(tmp, args...)
312 : decltype(ChkMathOp<M>(tmp, args...))(tmp);
313 };
314
246 // This is just boilerplate for the standard arithmetic operator overloads. 315 // This is just boilerplate for the standard arithmetic operator overloads.
247 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. 316 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
248 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ 317 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
249 /* Binary arithmetic operator for all CheckedNumeric operations. */ \ 318 /* Binary arithmetic operator for all CheckedNumeric operations. */ \
250 template <typename L, typename R, \ 319 template <typename L, typename R, \
251 typename std::enable_if<UnderlyingType<L>::is_numeric && \ 320 typename std::enable_if<IsCheckedOp<L, R>::value>::type* = \
252 UnderlyingType<R>::is_numeric && \ 321 nullptr> \
253 (UnderlyingType<L>::is_checked || \ 322 CheckedNumeric<typename MathWrapper<Checked##NAME##Op, L, R>::type> \
254 UnderlyingType<R>::is_checked)>::type* = \ 323 operator OP(const L lhs, const R rhs) { \
255 nullptr> \ 324 return decltype(lhs OP rhs)::template MathOp<Checked##NAME##Op>(lhs, rhs); \
256 CheckedNumeric< \ 325 } \
257 typename Checked##NAME<typename UnderlyingType<L>::type, \ 326 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
258 typename UnderlyingType<R>::type>::result_type> \ 327 template <typename L> \
259 operator OP(const L lhs, const R rhs) { \ 328 template <typename R> \
260 return decltype(lhs OP rhs)::template MathOp<Checked##NAME>(lhs, rhs); \ 329 CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \
261 } \ 330 return MathOp<Checked##NAME##Op>(rhs); \
262 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ 331 } \
263 template <typename L> \ 332 /* Variadic arithmetic functions that return CheckedNumeric. */ \
264 template <typename R> \ 333 template <typename L, typename R, typename... Args> \
265 CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \ 334 CheckedNumeric<typename ResultType<Checked##NAME##Op, L, R, Args...>::type> \
266 return MathOp<Checked##NAME>(rhs); \ 335 Check##NAME(const L lhs, const R rhs, const Args... args) { \
336 return ChkMathOp<Checked##NAME##Op, L, R, Args...>(lhs, rhs, args...); \
267 } 337 }
268 338
269 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=) 339 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
270 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=) 340 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
271 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=) 341 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
272 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=) 342 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
273 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=) 343 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
274 BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=) 344 BASE_NUMERIC_ARITHMETIC_OPERATORS(Lsh, <<, <<=)
275 BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=) 345 BASE_NUMERIC_ARITHMETIC_OPERATORS(Rsh, >>, >>=)
276 346
277 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS 347 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS
278 348
279 } // namespace internal 349 } // namespace internal
280 350
281 using internal::CheckedNumeric; 351 using internal::CheckedNumeric;
352 using internal::CheckNum;
353 using internal::CheckAdd;
354 using internal::CheckSub;
355 using internal::CheckMul;
356 using internal::CheckDiv;
357 using internal::CheckMod;
358 using internal::CheckLsh;
359 using internal::CheckRsh;
282 360
283 } // namespace base 361 } // namespace base
284 362
285 #endif // BASE_NUMERICS_SAFE_MATH_H_ 363 #endif // BASE_NUMERICS_SAFE_MATH_H_
OLDNEW
« no previous file with comments | « no previous file | base/numerics/safe_math_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698