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
16namespace angle
17{
18
19namespace base
20{
21
22namespace 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...
54template <typename T>
55class 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
310ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
311ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
312ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
313ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
314ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
315
316#undef ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS
317
318} // namespace internal
319
320using internal::CheckedNumeric;
321
322} // namespace base
323
324} // namespace angle
325
326#endif // ANGLEBASE_NUMERICS_SAFE_MATH_H_
327