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