• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CheckedNumeric()62     CheckedNumeric() {}
63 
64     // Copy constructor.
65     template <typename Src>
CheckedNumeric(const CheckedNumeric<Src> & rhs)66     CheckedNumeric(const CheckedNumeric<Src> &rhs) : state_(rhs.ValueUnsafe(), rhs.validity())
67     {}
68 
69     template <typename Src>
CheckedNumeric(Src value,RangeConstraint validity)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>
CheckedNumeric(Src value)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>
CheckedNumeric(StrictNumeric<Src> value)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.
IsValid()90     bool IsValid() const { return validity() == RANGE_VALID; }
91 
92     // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid and is within the
93     // range supported by the destination type. Returns true if successful and false otherwise.
94     template <typename Dst>
AssignIfValid(Dst * result)95     constexpr bool AssignIfValid(Dst *result) const
96     {
97         return IsValid() ? ((*result = static_cast<Dst>(state_.value())), true) : false;
98     }
99 
100     // ValueOrDie() The primary accessor for the underlying value. If the current
101     // state is not valid it will CHECK and crash.
ValueOrDie()102     T ValueOrDie() const
103     {
104         CHECK(IsValid());
105         return state_.value();
106     }
107 
108     // ValueOrDefault(T default_value) A convenience method that returns the
109     // current value if the state is valid, and the supplied default_value for
110     // any other state.
ValueOrDefault(T default_value)111     T ValueOrDefault(T default_value) const { return IsValid() ? state_.value() : default_value; }
112 
113     // ValueFloating() - Since floating point values include their validity state,
114     // we provide an easy method for extracting them directly, without a risk of
115     // crashing on a CHECK.
ValueFloating()116     T ValueFloating() const
117     {
118         static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float.");
119         return CheckedNumeric<T>::cast(*this).ValueUnsafe();
120     }
121 
122     // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for
123     // tests and to avoid a big matrix of friend operator overloads. But the
124     // values it returns are likely to change in the future.
125     // Returns: current validity state (i.e. valid, overflow, underflow, nan).
126     // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
127     // saturation/wrapping so we can expose this state consistently and implement
128     // saturated arithmetic.
validity()129     RangeConstraint validity() const { return state_.validity(); }
130 
131     // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now
132     // for tests and to avoid a big matrix of friend operator overloads. But the
133     // values it returns are likely to change in the future.
134     // Returns: the raw numeric value, regardless of the current state.
135     // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
136     // saturation/wrapping so we can expose this state consistently and implement
137     // saturated arithmetic.
ValueUnsafe()138     T ValueUnsafe() const { return state_.value(); }
139 
140     // Prototypes for the supported arithmetic operator overloads.
141     template <typename Src>
142     CheckedNumeric &operator+=(Src rhs);
143     template <typename Src>
144     CheckedNumeric &operator-=(Src rhs);
145     template <typename Src>
146     CheckedNumeric &operator*=(Src rhs);
147     template <typename Src>
148     CheckedNumeric &operator/=(Src rhs);
149     template <typename Src>
150     CheckedNumeric &operator%=(Src rhs);
151 
152     CheckedNumeric operator-() const
153     {
154         RangeConstraint validity;
155         T value = CheckedNeg(state_.value(), &validity);
156         // Negation is always valid for floating point.
157         if (std::numeric_limits<T>::is_iec559)
158             return CheckedNumeric<T>(value);
159 
160         validity = GetRangeConstraint(state_.validity() | validity);
161         return CheckedNumeric<T>(value, validity);
162     }
163 
Abs()164     CheckedNumeric Abs() const
165     {
166         RangeConstraint validity;
167         T value = CheckedAbs(state_.value(), &validity);
168         // Absolute value is always valid for floating point.
169         if (std::numeric_limits<T>::is_iec559)
170             return CheckedNumeric<T>(value);
171 
172         validity = GetRangeConstraint(state_.validity() | validity);
173         return CheckedNumeric<T>(value, validity);
174     }
175 
176     // This function is available only for integral types. It returns an unsigned
177     // integer of the same width as the source type, containing the absolute value
178     // of the source, and properly handling signed min.
UnsignedAbs()179     CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const
180     {
181         return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
182             CheckedUnsignedAbs(state_.value()), state_.validity());
183     }
184 
185     CheckedNumeric &operator++()
186     {
187         *this += 1;
188         return *this;
189     }
190 
191     CheckedNumeric operator++(int)
192     {
193         CheckedNumeric value = *this;
194         *this += 1;
195         return value;
196     }
197 
198     CheckedNumeric &operator--()
199     {
200         *this -= 1;
201         return *this;
202     }
203 
204     CheckedNumeric operator--(int)
205     {
206         CheckedNumeric value = *this;
207         *this -= 1;
208         return value;
209     }
210 
211     // These static methods behave like a convenience cast operator targeting
212     // the desired CheckedNumeric type. As an optimization, a reference is
213     // returned when Src is the same type as T.
214     template <typename Src>
215     static CheckedNumeric<T> cast(
216         Src u,
217         typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0)
218     {
219         return u;
220     }
221 
222     template <typename Src>
223     static CheckedNumeric<T> cast(
224         const CheckedNumeric<Src> &u,
225         typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0)
226     {
227         return u;
228     }
229 
cast(const CheckedNumeric<T> & u)230     static const CheckedNumeric<T> &cast(const CheckedNumeric<T> &u) { return u; }
231 
232   private:
233     template <typename NumericType>
234     struct UnderlyingType
235     {
236         using type = NumericType;
237     };
238 
239     template <typename NumericType>
240     struct UnderlyingType<CheckedNumeric<NumericType>>
241     {
242         using type = NumericType;
243     };
244 
245     CheckedNumericState<T> state_;
246 };
247 
248 // This is the boilerplate for the standard arithmetic operator overloads. A
249 // macro isn't the prettiest solution, but it beats rewriting these five times.
250 // Some details worth noting are:
251 //  * We apply the standard arithmetic promotions.
252 //  * We skip range checks for floating points.
253 //  * We skip range checks for destination integers with sufficient range.
254 // TODO(jschuh): extract these out into templates.
255 #define ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP)                              \
256     /* Binary arithmetic operator for CheckedNumerics of the same type. */                         \
257     template <typename T>                                                                          \
258     CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP(                             \
259         const CheckedNumeric<T> &lhs, const CheckedNumeric<T> &rhs)                                \
260     {                                                                                              \
261         typedef typename ArithmeticPromotion<T>::type Promotion;                                   \
262         /* Floating point always takes the fast path */                                            \
263         if (std::numeric_limits<T>::is_iec559)                                                     \
264             return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe());                      \
265         if (IsIntegerArithmeticSafe<Promotion, T, T>::value)                                       \
266             return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(),               \
267                                              GetRangeConstraint(rhs.validity() | lhs.validity())); \
268         RangeConstraint validity = RANGE_VALID;                                                    \
269         T result =                                                                                 \
270             static_cast<T>(Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()),                \
271                                          static_cast<Promotion>(rhs.ValueUnsafe()), &validity));   \
272         return CheckedNumeric<Promotion>(                                                          \
273             result, GetRangeConstraint(validity | lhs.validity() | rhs.validity()));               \
274     }                                                                                              \
275     /* Assignment arithmetic operator implementation from CheckedNumeric. */                       \
276     template <typename T>                                                                          \
277     template <typename Src>                                                                        \
278     CheckedNumeric<T> &CheckedNumeric<T>::operator COMPOUND_OP(Src rhs)                            \
279     {                                                                                              \
280         *this = CheckedNumeric<T>::cast(*this)                                                     \
281             OP CheckedNumeric<typename UnderlyingType<Src>::type>::cast(rhs);                      \
282         return *this;                                                                              \
283     }                                                                                              \
284     /* Binary arithmetic operator for CheckedNumeric of different type. */                         \
285     template <typename T, typename Src>                                                            \
286     CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP(                        \
287         const CheckedNumeric<Src> &lhs, const CheckedNumeric<T> &rhs)                              \
288     {                                                                                              \
289         typedef typename ArithmeticPromotion<T, Src>::type Promotion;                              \
290         if (IsIntegerArithmeticSafe<Promotion, T, Src>::value)                                     \
291             return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(),               \
292                                              GetRangeConstraint(rhs.validity() | lhs.validity())); \
293         return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs);       \
294     }                                                                                              \
295     /* Binary arithmetic operator for left CheckedNumeric and right numeric. */                    \
296     template <typename T, typename Src,                                                            \
297               typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr>           \
298     CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP(                        \
299         const CheckedNumeric<T> &lhs, Src rhs)                                                     \
300     {                                                                                              \
301         typedef typename ArithmeticPromotion<T, Src>::type Promotion;                              \
302         if (IsIntegerArithmeticSafe<Promotion, T, Src>::value)                                     \
303             return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, lhs.validity());            \
304         return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs);       \
305     }                                                                                              \
306     /* Binary arithmetic operator for left numeric and right CheckedNumeric. */                    \
307     template <typename T, typename Src,                                                            \
308               typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr>           \
309     CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP(                        \
310         Src lhs, const CheckedNumeric<T> &rhs)                                                     \
311     {                                                                                              \
312         typedef typename ArithmeticPromotion<T, Src>::type Promotion;                              \
313         if (IsIntegerArithmeticSafe<Promotion, T, Src>::value)                                     \
314             return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), rhs.validity());            \
315         return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs);       \
316     }
317 
318 ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
319 ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
320 ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
321 ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
322 ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
323 
324 #undef ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS
325 
326 }  // namespace internal
327 
328 using internal::CheckedNumeric;
329 
330 }  // namespace base
331 
332 }  // namespace angle
333 
334 #endif  // ANGLEBASE_NUMERICS_SAFE_MATH_H_
335