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