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

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

Issue 2472933003: Simplify Checked arithmetic functions in base/numerics (Closed)
Patch Set: typo 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') | 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>
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 "CheckedNumeric<T>: T must be a numeric type."); 52 "CheckedNumeric<T>: T must be a numeric type.");
53 53
54 public: 54 public:
55 typedef T type; 55 typedef T type;
56 56
57 CheckedNumeric() {} 57 CheckedNumeric() {}
58 58
59 // Copy constructor. 59 // Copy constructor.
60 template <typename Src> 60 template <typename Src>
61 CheckedNumeric(const CheckedNumeric<Src>& rhs) 61 CheckedNumeric(const CheckedNumeric<Src>& rhs)
62 : state_(rhs.ValueUnsafe(), rhs.validity()) {} 62 : state_(rhs.ValueUnsafe(), rhs.IsValid()) {}
63 63
64 template <typename Src> 64 template <typename Src>
65 CheckedNumeric(Src value, RangeConstraint validity) 65 CheckedNumeric(Src value, bool is_valid) : state_(value, is_valid) {}
66 : state_(value, validity) {}
67 66
68 // This is not an explicit constructor because we implicitly upgrade regular 67 // This is not an explicit constructor because we implicitly upgrade regular
69 // numerics to CheckedNumerics to make them easier to use. 68 // numerics to CheckedNumerics to make them easier to use.
70 template <typename Src> 69 template <typename Src>
71 CheckedNumeric(Src value) // NOLINT(runtime/explicit) 70 CheckedNumeric(Src value) // NOLINT(runtime/explicit)
72 : state_(value) { 71 : state_(value) {
73 static_assert(std::numeric_limits<Src>::is_specialized, 72 static_assert(std::numeric_limits<Src>::is_specialized,
74 "Argument must be numeric."); 73 "Argument must be numeric.");
75 } 74 }
76 75
77 // This is not an explicit constructor because we want a seamless conversion 76 // This is not an explicit constructor because we want a seamless conversion
78 // from StrictNumeric types. 77 // from StrictNumeric types.
79 template <typename Src> 78 template <typename Src>
80 CheckedNumeric(StrictNumeric<Src> value) // NOLINT(runtime/explicit) 79 CheckedNumeric(StrictNumeric<Src> value) // NOLINT(runtime/explicit)
81 : state_(static_cast<Src>(value)) { 80 : state_(static_cast<Src>(value)) {
82 } 81 }
83 82
84 // IsValid() is the public API to test if a CheckedNumeric is currently valid. 83 // IsValid() is the public API to test if a CheckedNumeric is currently valid.
85 bool IsValid() const { return validity() == RANGE_VALID; } 84 bool IsValid() const { return state_.is_valid(); }
86 85
87 // ValueOrDie() The primary accessor for the underlying value. If the current 86 // ValueOrDie() The primary accessor for the underlying value. If the current
88 // state is not valid it will CHECK and crash. 87 // state is not valid it will CHECK and crash.
89 T ValueOrDie() const { 88 T ValueOrDie() const {
90 CHECK(IsValid()); 89 CHECK(IsValid());
91 return state_.value(); 90 return state_.value();
92 } 91 }
93 92
94 // ValueOrDefault(T default_value) A convenience method that returns the 93 // ValueOrDefault(T default_value) A convenience method that returns the
95 // current value if the state is valid, and the supplied default_value for 94 // current value if the state is valid, and the supplied default_value for
96 // any other state. 95 // any other state.
97 T ValueOrDefault(T default_value) const { 96 T ValueOrDefault(T default_value) const {
98 return IsValid() ? state_.value() : default_value; 97 return IsValid() ? state_.value() : default_value;
99 } 98 }
100 99
101 // ValueFloating() - Since floating point values include their validity state, 100 // ValueFloating() - Since floating point values include their validity state,
102 // we provide an easy method for extracting them directly, without a risk of 101 // we provide an easy method for extracting them directly, without a risk of
103 // crashing on a CHECK. 102 // crashing on a CHECK.
104 T ValueFloating() const { 103 T ValueFloating() const {
105 static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float."); 104 static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float.");
106 return CheckedNumeric<T>::cast(*this).ValueUnsafe(); 105 return CheckedNumeric<T>::cast(*this).ValueUnsafe();
107 } 106 }
108 107
109 // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for
110 // tests and to avoid a big matrix of friend operator overloads. But the
111 // values it returns are likely to change in the future.
112 // Returns: current validity state (i.e. valid, overflow, underflow, nan).
113 // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
114 // saturation/wrapping so we can expose this state consistently and implement
115 // saturated arithmetic.
116 RangeConstraint validity() const { return state_.validity(); }
117
118 // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now 108 // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now
119 // for tests and to avoid a big matrix of friend operator overloads. But the 109 // for tests and to avoid a big matrix of friend operator overloads. But the
120 // values it returns are likely to change in the future. 110 // values it returns are unintuitive and likely to change in the future.
121 // Returns: the raw numeric value, regardless of the current state. 111 // Returns: the raw numeric value, regardless of the current state.
122 // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
123 // saturation/wrapping so we can expose this state consistently and implement
124 // saturated arithmetic.
125 T ValueUnsafe() const { return state_.value(); } 112 T ValueUnsafe() const { return state_.value(); }
126 113
127 // Prototypes for the supported arithmetic operator overloads. 114 // Prototypes for the supported arithmetic operator overloads.
128 template <typename Src> CheckedNumeric& operator+=(Src rhs); 115 template <typename Src> CheckedNumeric& operator+=(Src rhs);
129 template <typename Src> CheckedNumeric& operator-=(Src rhs); 116 template <typename Src> CheckedNumeric& operator-=(Src rhs);
130 template <typename Src> CheckedNumeric& operator*=(Src rhs); 117 template <typename Src> CheckedNumeric& operator*=(Src rhs);
131 template <typename Src> CheckedNumeric& operator/=(Src rhs); 118 template <typename Src> CheckedNumeric& operator/=(Src rhs);
132 template <typename Src> CheckedNumeric& operator%=(Src rhs); 119 template <typename Src> CheckedNumeric& operator%=(Src rhs);
133 120
134 CheckedNumeric operator-() const { 121 CheckedNumeric operator-() const {
135 RangeConstraint validity; 122 bool is_valid;
136 T value = CheckedNeg(state_.value(), &validity); 123 T value = CheckedNeg(state_.value(), &is_valid);
137 // Negation is always valid for floating point. 124 // Negation is always valid for floating point.
138 if (std::numeric_limits<T>::is_iec559) 125 if (std::numeric_limits<T>::is_iec559)
139 return CheckedNumeric<T>(value); 126 return CheckedNumeric<T>(value);
140 127
141 validity = GetRangeConstraint(state_.validity() | validity); 128 is_valid &= state_.is_valid();
142 return CheckedNumeric<T>(value, validity); 129 return CheckedNumeric<T>(value, is_valid);
143 } 130 }
144 131
145 CheckedNumeric Abs() const { 132 CheckedNumeric Abs() const {
146 RangeConstraint validity; 133 bool is_valid;
147 T value = CheckedAbs(state_.value(), &validity); 134 T value = CheckedAbs(state_.value(), &is_valid);
148 // Absolute value is always valid for floating point. 135 // Absolute value is always valid for floating point.
149 if (std::numeric_limits<T>::is_iec559) 136 if (std::numeric_limits<T>::is_iec559)
150 return CheckedNumeric<T>(value); 137 return CheckedNumeric<T>(value);
151 138
152 validity = GetRangeConstraint(state_.validity() | validity); 139 is_valid &= state_.is_valid();
153 return CheckedNumeric<T>(value, validity); 140 return CheckedNumeric<T>(value, is_valid);
154 } 141 }
155 142
156 // This function is available only for integral types. It returns an unsigned 143 // This function is available only for integral types. It returns an unsigned
157 // integer of the same width as the source type, containing the absolute value 144 // integer of the same width as the source type, containing the absolute value
158 // of the source, and properly handling signed min. 145 // of the source, and properly handling signed min.
159 CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const { 146 CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const {
160 return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>( 147 return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
161 CheckedUnsignedAbs(state_.value()), state_.validity()); 148 CheckedUnsignedAbs(state_.value()), state_.is_valid());
162 } 149 }
163 150
164 CheckedNumeric& operator++() { 151 CheckedNumeric& operator++() {
165 *this += 1; 152 *this += 1;
166 return *this; 153 return *this;
167 } 154 }
168 155
169 CheckedNumeric operator++(int) { 156 CheckedNumeric operator++(int) {
170 CheckedNumeric value = *this; 157 CheckedNumeric value = *this;
171 *this += 1; 158 *this += 1;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 CheckedNumericState<T> state_; 204 CheckedNumericState<T> state_;
218 }; 205 };
219 206
220 // This is the boilerplate for the standard arithmetic operator overloads. A 207 // This is the boilerplate for the standard arithmetic operator overloads. A
221 // macro isn't the prettiest solution, but it beats rewriting these five times. 208 // macro isn't the prettiest solution, but it beats rewriting these five times.
222 // Some details worth noting are: 209 // Some details worth noting are:
223 // * We apply the standard arithmetic promotions. 210 // * We apply the standard arithmetic promotions.
224 // * We skip range checks for floating points. 211 // * We skip range checks for floating points.
225 // * We skip range checks for destination integers with sufficient range. 212 // * We skip range checks for destination integers with sufficient range.
226 // TODO(jschuh): extract these out into templates. 213 // TODO(jschuh): extract these out into templates.
227 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ 214 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
228 /* Binary arithmetic operator for CheckedNumerics of the same type. */ \ 215 /* Binary arithmetic operator for CheckedNumerics of the same type. */ \
229 template <typename T> \ 216 template <typename T> \
230 CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \ 217 CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \
231 const CheckedNumeric<T>& lhs, const CheckedNumeric<T>& rhs) { \ 218 const CheckedNumeric<T>& lhs, const CheckedNumeric<T>& rhs) { \
232 typedef typename ArithmeticPromotion<T>::type Promotion; \ 219 typedef typename ArithmeticPromotion<T>::type Promotion; \
233 /* Floating point always takes the fast path */ \ 220 /* Floating point always takes the fast path */ \
234 if (std::numeric_limits<T>::is_iec559) \ 221 if (std::numeric_limits<T>::is_iec559) \
235 return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \ 222 return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \
236 if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \ 223 if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \
237 return CheckedNumeric<Promotion>( \ 224 return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
238 lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ 225 rhs.IsValid() && lhs.IsValid()); \
239 GetRangeConstraint(rhs.validity() | lhs.validity())); \ 226 bool is_valid = true; \
240 RangeConstraint validity = RANGE_VALID; \ 227 T result = static_cast<T>( \
241 T result = static_cast<T>( \ 228 Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \
242 Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \ 229 static_cast<Promotion>(rhs.ValueUnsafe()), &is_valid)); \
243 static_cast<Promotion>(rhs.ValueUnsafe()), &validity)); \ 230 return CheckedNumeric<Promotion>( \
244 return CheckedNumeric<Promotion>( \ 231 result, is_valid && lhs.IsValid() && rhs.IsValid()); \
245 result, \ 232 } \
246 GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \ 233 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
247 } \ 234 template <typename T> \
248 /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ 235 template <typename Src> \
249 template <typename T> \ 236 CheckedNumeric<T>& CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) { \
250 template <typename Src> \ 237 *this = CheckedNumeric<T>::cast(*this) \
251 CheckedNumeric<T>& CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) { \ 238 OP CheckedNumeric<typename UnderlyingType<Src>::type>::cast(rhs); \
252 *this = CheckedNumeric<T>::cast(*this) \ 239 return *this; \
253 OP CheckedNumeric<typename UnderlyingType<Src>::type>::cast(rhs); \ 240 } \
254 return *this; \ 241 /* Binary arithmetic operator for CheckedNumeric of different type. */ \
255 } \ 242 template <typename T, typename Src> \
256 /* Binary arithmetic operator for CheckedNumeric of different type. */ \ 243 CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
257 template <typename T, typename Src> \ 244 const CheckedNumeric<Src>& lhs, const CheckedNumeric<T>& rhs) { \
258 CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ 245 typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
259 const CheckedNumeric<Src>& lhs, const CheckedNumeric<T>& rhs) { \ 246 if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
260 typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ 247 return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
261 if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ 248 rhs.IsValid() && lhs.IsValid()); \
262 return CheckedNumeric<Promotion>( \ 249 return CheckedNumeric<Promotion>::cast(lhs) \
263 lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ 250 OP CheckedNumeric<Promotion>::cast(rhs); \
264 GetRangeConstraint(rhs.validity() | lhs.validity())); \ 251 } \
265 return CheckedNumeric<Promotion>::cast(lhs) \ 252 /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \
266 OP CheckedNumeric<Promotion>::cast(rhs); \ 253 template <typename T, typename Src, \
267 } \ 254 typename std::enable_if<std::is_arithmetic<Src>::value>::type* = \
268 /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \ 255 nullptr> \
269 template <typename T, typename Src, \ 256 CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
270 typename std::enable_if<std::is_arithmetic<Src>::value>::type* = \ 257 const CheckedNumeric<T>& lhs, Src rhs) { \
271 nullptr> \ 258 typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
272 CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ 259 if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
273 const CheckedNumeric<T>& lhs, Src rhs) { \ 260 return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, \
274 typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ 261 lhs.IsValid()); \
275 if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ 262 return CheckedNumeric<Promotion>::cast(lhs) \
276 return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, \ 263 OP CheckedNumeric<Promotion>::cast(rhs); \
277 lhs.validity()); \ 264 } \
278 return CheckedNumeric<Promotion>::cast(lhs) \ 265 /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \
279 OP CheckedNumeric<Promotion>::cast(rhs); \ 266 template <typename T, typename Src, \
280 } \ 267 typename std::enable_if<std::is_arithmetic<Src>::value>::type* = \
281 /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \ 268 nullptr> \
282 template <typename T, typename Src, \ 269 CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
283 typename std::enable_if<std::is_arithmetic<Src>::value>::type* = \ 270 Src lhs, const CheckedNumeric<T>& rhs) { \
284 nullptr> \ 271 typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
285 CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ 272 if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
286 Src lhs, const CheckedNumeric<T>& rhs) { \ 273 return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), \
287 typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ 274 rhs.IsValid()); \
288 if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ 275 return CheckedNumeric<Promotion>::cast(lhs) \
289 return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), \ 276 OP CheckedNumeric<Promotion>::cast(rhs); \
290 rhs.validity()); \
291 return CheckedNumeric<Promotion>::cast(lhs) \
292 OP CheckedNumeric<Promotion>::cast(rhs); \
293 } 277 }
294 278
295 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, += ) 279 BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, += )
296 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -= ) 280 BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -= )
297 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *= ) 281 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *= )
298 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /= ) 282 BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /= )
299 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %= ) 283 BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %= )
300 284
301 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS 285 #undef BASE_NUMERIC_ARITHMETIC_OPERATORS
302 286
303 } // namespace internal 287 } // namespace internal
304 288
305 using internal::CheckedNumeric; 289 using internal::CheckedNumeric;
306 290
307 } // namespace base 291 } // namespace base
308 292
309 #endif // BASE_NUMERICS_SAFE_MATH_H_ 293 #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