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

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

Issue 2516153002: Move the remaining CheckedNumeric logic out of the macro (Closed)
Patch Set: nit Created 4 years, 1 month 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_math_impl.h » ('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>
(...skipping 28 matching lines...) Expand all
39 // on a CHECK condition. You should use patterns like the following for these 39 // on a CHECK condition. You should use patterns like the following for these
40 // operations: 40 // operations:
41 // Bitwise operation: 41 // Bitwise operation:
42 // CheckedNumeric<int> checked_int = untrusted_input_value; 42 // CheckedNumeric<int> checked_int = untrusted_input_value;
43 // int x = checked_int.ValueOrDefault(0) | kFlagValues; 43 // int x = checked_int.ValueOrDefault(0) | kFlagValues;
44 // Comparison: 44 // Comparison:
45 // CheckedNumeric<size_t> checked_size = untrusted_input_value; 45 // CheckedNumeric<size_t> checked_size = untrusted_input_value;
46 // checked_size += HEADER LENGTH; 46 // checked_size += HEADER LENGTH;
47 // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) 47 // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
48 // Do stuff... 48 // Do stuff...
49
50 template <typename T>
51 class CheckedNumeric;
52
53 // Used to treat CheckedNumeric and arithmetic underlying types the same.
54 template <typename T>
55 struct UnderlyingType {
56 using type = T;
57 static const bool is_numeric = std::is_arithmetic<T>::value;
58 static const bool is_checked = false;
59 };
60
61 template <typename T>
62 struct UnderlyingType<CheckedNumeric<T>> {
63 using type = T;
64 static const bool is_numeric = true;
65 static const bool is_checked = true;
66 };
67
49 template <typename T> 68 template <typename T>
50 class CheckedNumeric { 69 class CheckedNumeric {
51 static_assert(std::is_arithmetic<T>::value, 70 static_assert(std::is_arithmetic<T>::value,
52 "CheckedNumeric<T>: T must be a numeric type."); 71 "CheckedNumeric<T>: T must be a numeric type.");
53 72
54 public: 73 public:
55 typedef T type; 74 typedef T type;
56 75
57 CheckedNumeric() {} 76 CheckedNumeric() {}
58 77
59 // Copy constructor. 78 // Copy constructor.
60 template <typename Src> 79 template <typename Src>
61 CheckedNumeric(const CheckedNumeric<Src>& rhs) 80 CheckedNumeric(const CheckedNumeric<Src>& rhs)
62 : state_(rhs.ValueUnsafe(), rhs.IsValid()) {} 81 : state_(rhs.state_.value(), rhs.IsValid()) {}
63 82
64 template <typename Src> 83 template <typename Src>
65 CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) {} 84 friend class CheckedNumeric;
66 85
67 // This is not an explicit constructor because we implicitly upgrade regular 86 // This is not an explicit constructor because we implicitly upgrade regular
68 // numerics to CheckedNumerics to make them easier to use. 87 // numerics to CheckedNumerics to make them easier to use.
69 template <typename Src> 88 template <typename Src>
70 CheckedNumeric(Src value) // NOLINT(runtime/explicit) 89 CheckedNumeric(Src value) // NOLINT(runtime/explicit)
71 : state_(value) { 90 : state_(value) {
72 static_assert(std::numeric_limits<Src>::is_specialized, 91 static_assert(std::numeric_limits<Src>::is_specialized,
73 "Argument must be numeric."); 92 "Argument must be numeric.");
74 } 93 }
75 94
(...skipping 19 matching lines...) Expand all
95 // any other state. 114 // any other state.
96 T ValueOrDefault(T default_value) const { 115 T ValueOrDefault(T default_value) const {
97 return IsValid() ? state_.value() : default_value; 116 return IsValid() ? state_.value() : default_value;
98 } 117 }
99 118
100 // ValueFloating() - Since floating point values include their validity state, 119 // ValueFloating() - Since floating point values include their validity state,
101 // we provide an easy method for extracting them directly, without a risk of 120 // we provide an easy method for extracting them directly, without a risk of
102 // crashing on a CHECK. 121 // crashing on a CHECK.
103 T ValueFloating() const { 122 T ValueFloating() const {
104 static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float."); 123 static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float.");
105 return CheckedNumeric<T>::cast(*this).ValueUnsafe(); 124 return state_.value();
106 } 125 }
107 126
108 // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now 127 // This friend method is available solely for providing more detailed logging
109 // for tests and to avoid a big matrix of friend operator overloads. But the 128 // in the the tests. Do not implement it in production code, because the
110 // values it returns are unintuitive and likely to change in the future. 129 // underlying values may change at any time.
111 // Returns: the raw numeric value, regardless of the current state. 130 template <typename U>
112 T ValueUnsafe() const { return state_.value(); } 131 friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
113 132
114 // Prototypes for the supported arithmetic operator overloads. 133 // Prototypes for the supported arithmetic operator overloads.
115 template <typename Src> 134 template <typename Src>
116 CheckedNumeric& operator+=(Src rhs); 135 CheckedNumeric& operator+=(const Src rhs);
117 template <typename Src> 136 template <typename Src>
118 CheckedNumeric& operator-=(Src rhs); 137 CheckedNumeric& operator-=(const Src rhs);
119 template <typename Src> 138 template <typename Src>
120 CheckedNumeric& operator*=(Src rhs); 139 CheckedNumeric& operator*=(const Src rhs);
121 template <typename Src> 140 template <typename Src>
122 CheckedNumeric& operator/=(Src rhs); 141 CheckedNumeric& operator/=(const Src rhs);
123 template <typename Src> 142 template <typename Src>
124 CheckedNumeric& operator%=(Src rhs); 143 CheckedNumeric& operator%=(const Src rhs);
125 template <typename Src> 144 template <typename Src>
126 CheckedNumeric& operator<<=(Src rhs); 145 CheckedNumeric& operator<<=(const Src rhs);
127 template <typename Src> 146 template <typename Src>
128 CheckedNumeric& operator>>=(Src rhs); 147 CheckedNumeric& operator>>=(const Src rhs);
129 148
130 CheckedNumeric operator-() const { 149 CheckedNumeric operator-() const {
131 // Negation is always valid for floating point. 150 // Negation is always valid for floating point.
132 T value = 0; 151 T value = 0;
133 bool is_valid = (std::numeric_limits<T>::is_iec559 || IsValid()) && 152 bool is_valid = (std::numeric_limits<T>::is_iec559 || IsValid()) &&
134 CheckedNeg(state_.value(), &value); 153 CheckedNeg(state_.value(), &value);
135 return CheckedNumeric<T>(value, is_valid); 154 return CheckedNumeric<T>(value, is_valid);
136 } 155 }
137 156
138 CheckedNumeric Abs() const { 157 CheckedNumeric Abs() const {
(...skipping 27 matching lines...) Expand all
166 *this -= 1; 185 *this -= 1;
167 return *this; 186 return *this;
168 } 187 }
169 188
170 CheckedNumeric operator--(int) { 189 CheckedNumeric operator--(int) {
171 CheckedNumeric value = *this; 190 CheckedNumeric value = *this;
172 *this -= 1; 191 *this -= 1;
173 return value; 192 return value;
174 } 193 }
175 194
176 // These static methods behave like a convenience cast operator targeting 195 // These perform the actual math operations on the CheckedNumerics.
177 // the desired CheckedNumeric type. As an optimization, a reference is 196 // Binary arithmetic operations.
178 // returned when Src is the same type as T. 197 template <template <typename, typename, typename> class M,
179 template <typename Src> 198 typename L,
180 static CheckedNumeric<T> cast( 199 typename R>
181 Src u, 200 static CheckedNumeric MathOp(const L& lhs, const R& rhs) {
182 typename std::enable_if<std::numeric_limits<Src>::is_specialized, 201 using Math = M<typename UnderlyingType<L>::type,
183 int>::type = 0) { 202 typename UnderlyingType<R>::type, void>;
184 return u; 203 T result = 0;
185 } 204 bool is_valid =
205 Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
206 Math::Op(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
207 return CheckedNumeric<T>(result, is_valid);
208 };
186 209
187 template <typename Src> 210 // Assignment arithmetic operations.
188 static CheckedNumeric<T> cast( 211 template <template <typename, typename, typename> class M, typename R>
189 const CheckedNumeric<Src>& u, 212 CheckedNumeric& MathOp(const R& rhs) {
190 typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0) { 213 using Math = M<T, typename UnderlyingType<R>::type, void>;
191 return u; 214 T result = 0; // Using T as the destination saves a range check.
192 } 215 bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
193 216 Math::Op(state_.value(), Wrapper<R>::value(rhs), &result);
194 static const CheckedNumeric<T>& cast(const CheckedNumeric<T>& u) { return u; } 217 *this = CheckedNumeric<T>(result, is_valid);
218 return *this;
219 };
195 220
196 private: 221 private:
197 CheckedNumericState<T> state_; 222 CheckedNumericState<T> state_;
223
224 template <typename Src>
225 CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) {}
226
227 // These wrappers allow us to handle state the same way for both
228 // CheckedNumeric and POD arithmetic types.
229 template <typename Src>
230 struct Wrapper {
231 static bool is_valid(Src) { return true; }
232 static Src value(Src value) { return value; }
233 };
234
235 template <typename Src>
236 struct Wrapper<CheckedNumeric<Src>> {
237 static bool is_valid(const CheckedNumeric<Src>& v) { return v.IsValid(); }
238 static Src value(const CheckedNumeric<Src>& v) { return v.state_.value(); }
239 };
198 }; 240 };
199 241
200 // This is the boilerplate for the standard arithmetic operator overloads. A 242 // This is just boilerplate for the standard arithmetic operator overloads.
201 // macro isn't the prettiest solution, but it beats rewriting these five times. 243 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
202 // Some details worth noting are: 244 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
203 // * We apply the standard arithmetic promotions. 245 /* Binary arithmetic operator for all CheckedNumeric operations. */ \
204 // * We skip range checks for floating points. 246 template <typename L, typename R, \
205 // * We skip range checks for destination integers with sufficient range. 247 typename std::enable_if<UnderlyingType<L>::is_numeric && \
206 // TODO(jschuh): extract these out into templates. 248 UnderlyingType<R>::is_numeric && \
207 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP, PROMOTION) \ 249 (UnderlyingType<L>::is_checked || \
208 /* Binary arithmetic operator for CheckedNumerics of the same type. */ \ 250 UnderlyingType<R>::is_checked)>::type* = \
209 template <typename L, typename R> \ 251 nullptr> \
210 CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \ 252 CheckedNumeric< \
211 operator OP(const CheckedNumeric<L>& lhs, const CheckedNumeric<R>& rhs) { \ 253 typename Checked##NAME<typename UnderlyingType<L>::type, \
212 using P = typename ArithmeticPromotion<PROMOTION, L, R>::type; \ 254 typename UnderlyingType<R>::type>::result_type> \
213 if (!rhs.IsValid() || !lhs.IsValid()) \ 255 operator OP(const L lhs, const R rhs) { \
214 return CheckedNumeric<P>(0, false); \ 256 return decltype(lhs OP rhs)::template MathOp<Checked##NAME>(lhs, rhs); \
215 /* Floating point always takes the fast path */ \
216 if (std::is_floating_point<L>::value || std::is_floating_point<R>::value) \
217 return CheckedNumeric<P>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \
218 P result = 0; \
219 bool is_valid = \
220 Checked##NAME(lhs.ValueUnsafe(), rhs.ValueUnsafe(), &result); \
221 return CheckedNumeric<P>(result, is_valid); \
222 } \ 257 } \
223 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ 258 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
224 template <typename L> \ 259 template <typename L> \
225 template <typename R> \ 260 template <typename R> \
226 CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(R rhs) { \ 261 CheckedNumeric<L>& CheckedNumeric<L>::operator COMPOUND_OP(const R rhs) { \
227 *this = *this OP rhs; \ 262 return MathOp<Checked##NAME>(rhs); \
228 return *this; \
229 } \
230 /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \
231 template <typename L, typename R, \
232 typename std::enable_if<std::is_arithmetic<R>::value>::type* = \
233 nullptr> \
234 CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \
235 operator OP(const CheckedNumeric<L>& lhs, R rhs) { \
236 return lhs OP CheckedNumeric<R>(rhs); \
237 } \
238 /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \
239 template <typename L, typename R, \
240 typename std::enable_if<std::is_arithmetic<L>::value>::type* = \
241 nullptr> \
242 CheckedNumeric<typename ArithmeticPromotion<PROMOTION, L, R>::type> \
243 operator OP(L lhs, const CheckedNumeric<R>& rhs) { \
244 return CheckedNumeric<L>(lhs) OP rhs; \
245 } 263 }
246 264
247 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=, MAX_EXPONENT_PROMOTION) 265 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
248 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=, MAX_EXPONENT_PROMOTION) 266 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
249 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=, MAX_EXPONENT_PROMOTION) 267 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
250 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=, MAX_EXPONENT_PROMOTION) 268 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
251 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=, MAX_EXPONENT_PROMOTION) 269 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
252 BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=, LEFT_PROMOTION) 270 BASE_NUMERIC_ARITHMETIC_OPERATORS(LeftShift, <<, <<=)
253 BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=, LEFT_PROMOTION) 271 BASE_NUMERIC_ARITHMETIC_OPERATORS(RightShift, >>, >>=)
254 272
255 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS 273 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS
256 274
257 } // namespace internal 275 } // namespace internal
258 276
259 using internal::CheckedNumeric; 277 using internal::CheckedNumeric;
260 278
261 } // namespace base 279 } // namespace base
262 280
263 #endif // BASE_NUMERICS_SAFE_MATH_H_ 281 #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_math_impl.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698