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 |
3 | // found in the LICENSE file. |
4 | |
5 | #ifndef ANGLEBASE_NUMERICS_SAFE_MATH_H_ |
6 | #define ANGLEBASE_NUMERICS_SAFE_MATH_H_ |
7 | |
8 | #include <stddef.h> |
9 | |
10 | #include <limits> |
11 | #include <type_traits> |
12 | |
13 | #include "anglebase/logging.h" |
14 | #include "anglebase/numerics/safe_math_impl.h" |
15 | |
16 | namespace angle |
17 | { |
18 | |
19 | namespace base |
20 | { |
21 | |
22 | namespace internal |
23 | { |
24 | |
25 | // CheckedNumeric implements all the logic and operators for detecting integer |
26 | // boundary conditions such as overflow, underflow, and invalid conversions. |
27 | // The CheckedNumeric type implicitly converts from floating point and integer |
28 | // data types, and contains overloads for basic arithmetic operations (i.e.: +, |
29 | // -, *, /, %). |
30 | // |
31 | // The following methods convert from CheckedNumeric to standard numeric values: |
32 | // IsValid() - Returns true if the underlying numeric value is valid (i.e. has |
33 | // has not wrapped and is not the result of an invalid conversion). |
34 | // ValueOrDie() - Returns the underlying value. If the state is not valid this |
35 | // call will crash on a CHECK. |
36 | // ValueOrDefault() - Returns the current value, or the supplied default if the |
37 | // state is not valid. |
38 | // ValueFloating() - Returns the underlying floating point value (valid only |
39 | // only for floating point CheckedNumeric types). |
40 | // |
41 | // Bitwise operations are explicitly not supported, because correct |
42 | // handling of some cases (e.g. sign manipulation) is ambiguous. Comparison |
43 | // operations are explicitly not supported because they could result in a crash |
44 | // on a CHECK condition. You should use patterns like the following for these |
45 | // operations: |
46 | // Bitwise operation: |
47 | // CheckedNumeric<int> checked_int = untrusted_input_value; |
48 | // int x = checked_int.ValueOrDefault(0) | kFlagValues; |
49 | // Comparison: |
50 | // CheckedNumeric<size_t> checked_size = untrusted_input_value; |
51 | // checked_size += HEADER LENGTH; |
52 | // if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) |
53 | // Do stuff... |
54 | template <typename T> |
55 | class CheckedNumeric |
56 | { |
57 | static_assert(std::is_arithmetic<T>::value, "CheckedNumeric<T>: T must be a numeric type." ); |
58 | |
59 | public: |
60 | typedef T type; |
61 | |
62 | CheckedNumeric() {} |
63 | |
64 | // Copy constructor. |
65 | template <typename Src> |
66 | CheckedNumeric(const CheckedNumeric<Src> &rhs) : state_(rhs.ValueUnsafe(), rhs.validity()) |
67 | {} |
68 | |
69 | template <typename Src> |
70 | CheckedNumeric(Src value, RangeConstraint validity) : state_(value, validity) |
71 | {} |
72 | |
73 | // This is not an explicit constructor because we implicitly upgrade regular |
74 | // numerics to CheckedNumerics to make them easier to use. |
75 | template <typename Src> |
76 | CheckedNumeric(Src value) // NOLINT(runtime/explicit) |
77 | : state_(value) |
78 | { |
79 | static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric." ); |
80 | } |
81 | |
82 | // This is not an explicit constructor because we want a seamless conversion |
83 | // from StrictNumeric types. |
84 | template <typename Src> |
85 | CheckedNumeric(StrictNumeric<Src> value) // NOLINT(runtime/explicit) |
86 | : state_(static_cast<Src>(value)) |
87 | {} |
88 | |
89 | // IsValid() is the public API to test if a CheckedNumeric is currently valid. |
90 | bool IsValid() const { return validity() == RANGE_VALID; } |
91 | |
92 | // ValueOrDie() The primary accessor for the underlying value. If the current |
93 | // state is not valid it will CHECK and crash. |
94 | T ValueOrDie() const |
95 | { |
96 | CHECK(IsValid()); |
97 | return state_.value(); |
98 | } |
99 | |
100 | // ValueOrDefault(T default_value) A convenience method that returns the |
101 | // current value if the state is valid, and the supplied default_value for |
102 | // any other state. |
103 | T ValueOrDefault(T default_value) const { return IsValid() ? state_.value() : default_value; } |
104 | |
105 | // ValueFloating() - Since floating point values include their validity state, |
106 | // we provide an easy method for extracting them directly, without a risk of |
107 | // crashing on a CHECK. |
108 | T ValueFloating() const |
109 | { |
110 | static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float." ); |
111 | return CheckedNumeric<T>::cast(*this).ValueUnsafe(); |
112 | } |
113 | |
114 | // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for |
115 | // tests and to avoid a big matrix of friend operator overloads. But the |
116 | // values it returns are likely to change in the future. |
117 | // Returns: current validity state (i.e. valid, overflow, underflow, nan). |
118 | // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for |
119 | // saturation/wrapping so we can expose this state consistently and implement |
120 | // saturated arithmetic. |
121 | RangeConstraint validity() const { return state_.validity(); } |
122 | |
123 | // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now |
124 | // for tests and to avoid a big matrix of friend operator overloads. But the |
125 | // values it returns are likely to change in the future. |
126 | // Returns: the raw numeric value, regardless of the current state. |
127 | // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for |
128 | // saturation/wrapping so we can expose this state consistently and implement |
129 | // saturated arithmetic. |
130 | T ValueUnsafe() const { return state_.value(); } |
131 | |
132 | // Prototypes for the supported arithmetic operator overloads. |
133 | template <typename Src> |
134 | CheckedNumeric &operator+=(Src rhs); |
135 | template <typename Src> |
136 | CheckedNumeric &operator-=(Src rhs); |
137 | template <typename Src> |
138 | CheckedNumeric &operator*=(Src rhs); |
139 | template <typename Src> |
140 | CheckedNumeric &operator/=(Src rhs); |
141 | template <typename Src> |
142 | CheckedNumeric &operator%=(Src rhs); |
143 | |
144 | CheckedNumeric operator-() const |
145 | { |
146 | RangeConstraint validity; |
147 | T value = CheckedNeg(state_.value(), &validity); |
148 | // Negation is always valid for floating point. |
149 | if (std::numeric_limits<T>::is_iec559) |
150 | return CheckedNumeric<T>(value); |
151 | |
152 | validity = GetRangeConstraint(state_.validity() | validity); |
153 | return CheckedNumeric<T>(value, validity); |
154 | } |
155 | |
156 | CheckedNumeric Abs() const |
157 | { |
158 | RangeConstraint validity; |
159 | T value = CheckedAbs(state_.value(), &validity); |
160 | // Absolute value is always valid for floating point. |
161 | if (std::numeric_limits<T>::is_iec559) |
162 | return CheckedNumeric<T>(value); |
163 | |
164 | validity = GetRangeConstraint(state_.validity() | validity); |
165 | return CheckedNumeric<T>(value, validity); |
166 | } |
167 | |
168 | // This function is available only for integral types. It returns an unsigned |
169 | // integer of the same width as the source type, containing the absolute value |
170 | // of the source, and properly handling signed min. |
171 | CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const |
172 | { |
173 | return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>( |
174 | CheckedUnsignedAbs(state_.value()), state_.validity()); |
175 | } |
176 | |
177 | CheckedNumeric &operator++() |
178 | { |
179 | *this += 1; |
180 | return *this; |
181 | } |
182 | |
183 | CheckedNumeric operator++(int) |
184 | { |
185 | CheckedNumeric value = *this; |
186 | *this += 1; |
187 | return value; |
188 | } |
189 | |
190 | CheckedNumeric &operator--() |
191 | { |
192 | *this -= 1; |
193 | return *this; |
194 | } |
195 | |
196 | CheckedNumeric operator--(int) |
197 | { |
198 | CheckedNumeric value = *this; |
199 | *this -= 1; |
200 | return value; |
201 | } |
202 | |
203 | // These static methods behave like a convenience cast operator targeting |
204 | // the desired CheckedNumeric type. As an optimization, a reference is |
205 | // returned when Src is the same type as T. |
206 | template <typename Src> |
207 | static CheckedNumeric<T> cast( |
208 | Src u, |
209 | typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0) |
210 | { |
211 | return u; |
212 | } |
213 | |
214 | template <typename Src> |
215 | static CheckedNumeric<T> cast( |
216 | const CheckedNumeric<Src> &u, |
217 | typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0) |
218 | { |
219 | return u; |
220 | } |
221 | |
222 | static const CheckedNumeric<T> &cast(const CheckedNumeric<T> &u) { return u; } |
223 | |
224 | private: |
225 | template <typename NumericType> |
226 | struct UnderlyingType |
227 | { |
228 | using type = NumericType; |
229 | }; |
230 | |
231 | template <typename NumericType> |
232 | struct UnderlyingType<CheckedNumeric<NumericType>> |
233 | { |
234 | using type = NumericType; |
235 | }; |
236 | |
237 | CheckedNumericState<T> state_; |
238 | }; |
239 | |
240 | // This is the boilerplate for the standard arithmetic operator overloads. A |
241 | // macro isn't the prettiest solution, but it beats rewriting these five times. |
242 | // Some details worth noting are: |
243 | // * We apply the standard arithmetic promotions. |
244 | // * We skip range checks for floating points. |
245 | // * We skip range checks for destination integers with sufficient range. |
246 | // TODO(jschuh): extract these out into templates. |
247 | #define ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ |
248 | /* Binary arithmetic operator for CheckedNumerics of the same type. */ \ |
249 | template <typename T> \ |
250 | CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \ |
251 | const CheckedNumeric<T> &lhs, const CheckedNumeric<T> &rhs) \ |
252 | { \ |
253 | typedef typename ArithmeticPromotion<T>::type Promotion; \ |
254 | /* Floating point always takes the fast path */ \ |
255 | if (std::numeric_limits<T>::is_iec559) \ |
256 | return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \ |
257 | if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \ |
258 | return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ |
259 | GetRangeConstraint(rhs.validity() | lhs.validity())); \ |
260 | RangeConstraint validity = RANGE_VALID; \ |
261 | T result = \ |
262 | static_cast<T>(Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \ |
263 | static_cast<Promotion>(rhs.ValueUnsafe()), &validity)); \ |
264 | return CheckedNumeric<Promotion>( \ |
265 | result, GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \ |
266 | } \ |
267 | /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ |
268 | template <typename T> \ |
269 | template <typename Src> \ |
270 | CheckedNumeric<T> &CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) \ |
271 | { \ |
272 | *this = CheckedNumeric<T>::cast(*this) \ |
273 | OP CheckedNumeric<typename UnderlyingType<Src>::type>::cast(rhs); \ |
274 | return *this; \ |
275 | } \ |
276 | /* Binary arithmetic operator for CheckedNumeric of different type. */ \ |
277 | template <typename T, typename Src> \ |
278 | CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ |
279 | const CheckedNumeric<Src> &lhs, const CheckedNumeric<T> &rhs) \ |
280 | { \ |
281 | typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ |
282 | if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ |
283 | return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ |
284 | GetRangeConstraint(rhs.validity() | lhs.validity())); \ |
285 | return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \ |
286 | } \ |
287 | /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \ |
288 | template <typename T, typename Src, \ |
289 | typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr> \ |
290 | CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ |
291 | const CheckedNumeric<T> &lhs, Src rhs) \ |
292 | { \ |
293 | typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ |
294 | if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ |
295 | return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, lhs.validity()); \ |
296 | return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \ |
297 | } \ |
298 | /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \ |
299 | template <typename T, typename Src, \ |
300 | typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr> \ |
301 | CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ |
302 | Src lhs, const CheckedNumeric<T> &rhs) \ |
303 | { \ |
304 | typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ |
305 | if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ |
306 | return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), rhs.validity()); \ |
307 | return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \ |
308 | } |
309 | |
310 | ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=) |
311 | ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=) |
312 | ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=) |
313 | ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=) |
314 | ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=) |
315 | |
316 | #undef ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS |
317 | |
318 | } // namespace internal |
319 | |
320 | using internal::CheckedNumeric; |
321 | |
322 | } // namespace base |
323 | |
324 | } // namespace angle |
325 | |
326 | #endif // ANGLEBASE_NUMERICS_SAFE_MATH_H_ |
327 | |