blob: 433860c58ba0c1808f7df99f669b969ed1326132 [file] [log] [blame]
Scott Graham66962112018-06-08 12:42:08 -07001// Copyright 2017 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_NUMERICS_CHECKED_MATH_H_
6#define BASE_NUMERICS_CHECKED_MATH_H_
7
8#include <stddef.h>
9
10#include <limits>
11#include <type_traits>
12
13#include "base/numerics/checked_math_impl.h"
14
15namespace base {
16namespace internal {
17
18template <typename T>
19class CheckedNumeric {
20 static_assert(std::is_arithmetic<T>::value,
21 "CheckedNumeric<T>: T must be a numeric type.");
22
23 public:
24 using type = T;
25
26 constexpr CheckedNumeric() = default;
27
28 // Copy constructor.
29 template <typename Src>
30 constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
31 : state_(rhs.state_.value(), rhs.IsValid()) {}
32
33 template <typename Src>
34 friend class CheckedNumeric;
35
36 // This is not an explicit constructor because we implicitly upgrade regular
37 // numerics to CheckedNumerics to make them easier to use.
38 template <typename Src>
39 constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
40 : state_(value) {
41 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
42 }
43
44 // This is not an explicit constructor because we want a seamless conversion
45 // from StrictNumeric types.
46 template <typename Src>
47 constexpr CheckedNumeric(
48 StrictNumeric<Src> value) // NOLINT(runtime/explicit)
49 : state_(static_cast<Src>(value)) {}
50
51 // IsValid() - The public API to test if a CheckedNumeric is currently valid.
52 // A range checked destination type can be supplied using the Dst template
53 // parameter.
54 template <typename Dst = T>
55 constexpr bool IsValid() const {
56 return state_.is_valid() &&
57 IsValueInRangeForNumericType<Dst>(state_.value());
58 }
59
60 // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
61 // and is within the range supported by the destination type. Returns true if
62 // successful and false otherwise.
63 template <typename Dst>
64#if defined(__clang__) || defined(__GNUC__)
65 __attribute__((warn_unused_result))
66#elif defined(_MSC_VER)
67 _Check_return_
68#endif
69 constexpr bool
70 AssignIfValid(Dst* result) const {
71 return BASE_NUMERICS_LIKELY(IsValid<Dst>())
72 ? ((*result = static_cast<Dst>(state_.value())), true)
73 : false;
74 }
75
76 // ValueOrDie() - The primary accessor for the underlying value. If the
77 // current state is not valid it will CHECK and crash.
78 // A range checked destination type can be supplied using the Dst template
79 // parameter, which will trigger a CHECK if the value is not in bounds for
80 // the destination.
81 // The CHECK behavior can be overridden by supplying a handler as a
82 // template parameter, for test code, etc. However, the handler cannot access
83 // the underlying value, and it is not available through other means.
84 template <typename Dst = T, class CheckHandler = CheckOnFailure>
85 constexpr StrictNumeric<Dst> ValueOrDie() const {
86 return BASE_NUMERICS_LIKELY(IsValid<Dst>())
87 ? static_cast<Dst>(state_.value())
88 : CheckHandler::template HandleFailure<Dst>();
89 }
90
91 // ValueOrDefault(T default_value) - A convenience method that returns the
92 // current value if the state is valid, and the supplied default_value for
93 // any other state.
94 // A range checked destination type can be supplied using the Dst template
95 // parameter. WARNING: This function may fail to compile or CHECK at runtime
96 // if the supplied default_value is not within range of the destination type.
97 template <typename Dst = T, typename Src>
98 constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
99 return BASE_NUMERICS_LIKELY(IsValid<Dst>())
100 ? static_cast<Dst>(state_.value())
101 : checked_cast<Dst>(default_value);
102 }
103
104 // Returns a checked numeric of the specified type, cast from the current
105 // CheckedNumeric. If the current state is invalid or the destination cannot
106 // represent the result then the returned CheckedNumeric will be invalid.
107 template <typename Dst>
108 constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
109 return *this;
110 }
111
112 // This friend method is available solely for providing more detailed logging
113 // in the the tests. Do not implement it in production code, because the
114 // underlying values may change at any time.
115 template <typename U>
116 friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
117
118 // Prototypes for the supported arithmetic operator overloads.
119 template <typename Src>
120 constexpr CheckedNumeric& operator+=(const Src rhs);
121 template <typename Src>
122 constexpr CheckedNumeric& operator-=(const Src rhs);
123 template <typename Src>
124 constexpr CheckedNumeric& operator*=(const Src rhs);
125 template <typename Src>
126 constexpr CheckedNumeric& operator/=(const Src rhs);
127 template <typename Src>
128 constexpr CheckedNumeric& operator%=(const Src rhs);
129 template <typename Src>
130 constexpr CheckedNumeric& operator<<=(const Src rhs);
131 template <typename Src>
132 constexpr CheckedNumeric& operator>>=(const Src rhs);
133 template <typename Src>
134 constexpr CheckedNumeric& operator&=(const Src rhs);
135 template <typename Src>
136 constexpr CheckedNumeric& operator|=(const Src rhs);
137 template <typename Src>
138 constexpr CheckedNumeric& operator^=(const Src rhs);
139
140 constexpr CheckedNumeric operator-() const {
141 // The negation of two's complement int min is int min, so we simply
142 // check for that in the constexpr case.
143 // We use an optimized code path for a known run-time variable.
144 return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
145 std::is_floating_point<T>::value
146 ? CheckedNumeric<T>(
147 NegateWrapper(state_.value()),
148 IsValid() && (!std::is_signed<T>::value ||
149 std::is_floating_point<T>::value ||
150 NegateWrapper(state_.value()) !=
151 std::numeric_limits<T>::lowest()))
152 : FastRuntimeNegate();
153 }
154
155 constexpr CheckedNumeric operator~() const {
156 return CheckedNumeric<decltype(InvertWrapper(T()))>(
157 InvertWrapper(state_.value()), IsValid());
158 }
159
160 constexpr CheckedNumeric Abs() const {
161 return !IsValueNegative(state_.value()) ? *this : -*this;
162 }
163
164 template <typename U>
165 constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
166 const U rhs) const {
167 using R = typename UnderlyingType<U>::type;
168 using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
169 // TODO(jschuh): This can be converted to the MathOp version and remain
170 // constexpr once we have C++14 support.
171 return CheckedNumeric<result_type>(
172 static_cast<result_type>(
173 IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
174 ? state_.value()
175 : Wrapper<U>::value(rhs)),
176 state_.is_valid() && Wrapper<U>::is_valid(rhs));
177 }
178
179 template <typename U>
180 constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
181 const U rhs) const {
182 using R = typename UnderlyingType<U>::type;
183 using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
184 // TODO(jschuh): This can be converted to the MathOp version and remain
185 // constexpr once we have C++14 support.
186 return CheckedNumeric<result_type>(
187 static_cast<result_type>(
188 IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
189 ? state_.value()
190 : Wrapper<U>::value(rhs)),
191 state_.is_valid() && Wrapper<U>::is_valid(rhs));
192 }
193
194 // This function is available only for integral types. It returns an unsigned
195 // integer of the same width as the source type, containing the absolute value
196 // of the source, and properly handling signed min.
197 constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
198 UnsignedAbs() const {
199 return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
200 SafeUnsignedAbs(state_.value()), state_.is_valid());
201 }
202
203 constexpr CheckedNumeric& operator++() {
204 *this += 1;
205 return *this;
206 }
207
208 constexpr CheckedNumeric operator++(int) {
209 CheckedNumeric value = *this;
210 *this += 1;
211 return value;
212 }
213
214 constexpr CheckedNumeric& operator--() {
215 *this -= 1;
216 return *this;
217 }
218
219 constexpr CheckedNumeric operator--(int) {
220 CheckedNumeric value = *this;
221 *this -= 1;
222 return value;
223 }
224
225 // These perform the actual math operations on the CheckedNumerics.
226 // Binary arithmetic operations.
227 template <template <typename, typename, typename> class M,
228 typename L,
229 typename R>
230 static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
231 using Math = typename MathWrapper<M, L, R>::math;
232 T result = 0;
233 bool is_valid =
234 Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
235 Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
236 return CheckedNumeric<T>(result, is_valid);
237 }
238
239 // Assignment arithmetic operations.
240 template <template <typename, typename, typename> class M, typename R>
241 constexpr CheckedNumeric& MathOp(const R rhs) {
242 using Math = typename MathWrapper<M, T, R>::math;
243 T result = 0; // Using T as the destination saves a range check.
244 bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
245 Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
246 *this = CheckedNumeric<T>(result, is_valid);
247 return *this;
248 }
249
250 private:
251 CheckedNumericState<T> state_;
252
253 CheckedNumeric FastRuntimeNegate() const {
254 T result;
255 bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
256 return CheckedNumeric<T>(result, IsValid() && success);
257 }
258
259 template <typename Src>
260 constexpr CheckedNumeric(Src value, bool is_valid)
261 : state_(value, is_valid) {}
262
263 // These wrappers allow us to handle state the same way for both
264 // CheckedNumeric and POD arithmetic types.
265 template <typename Src>
266 struct Wrapper {
267 static constexpr bool is_valid(Src) { return true; }
268 static constexpr Src value(Src value) { return value; }
269 };
270
271 template <typename Src>
272 struct Wrapper<CheckedNumeric<Src>> {
273 static constexpr bool is_valid(const CheckedNumeric<Src> v) {
274 return v.IsValid();
275 }
276 static constexpr Src value(const CheckedNumeric<Src> v) {
277 return v.state_.value();
278 }
279 };
280
281 template <typename Src>
282 struct Wrapper<StrictNumeric<Src>> {
283 static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
284 static constexpr Src value(const StrictNumeric<Src> v) {
285 return static_cast<Src>(v);
286 }
287 };
288};
289
290// Convenience functions to avoid the ugly template disambiguator syntax.
291template <typename Dst, typename Src>
292constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
293 return value.template IsValid<Dst>();
294}
295
296template <typename Dst, typename Src>
297constexpr StrictNumeric<Dst> ValueOrDieForType(
298 const CheckedNumeric<Src> value) {
299 return value.template ValueOrDie<Dst>();
300}
301
302template <typename Dst, typename Src, typename Default>
303constexpr StrictNumeric<Dst> ValueOrDefaultForType(
304 const CheckedNumeric<Src> value,
305 const Default default_value) {
306 return value.template ValueOrDefault<Dst>(default_value);
307}
308
309// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
310// or CheckedNumericType.
311template <typename T>
312constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
313 const T value) {
314 return value;
315}
316
317// These implement the variadic wrapper for the math operations.
318template <template <typename, typename, typename> class M,
319 typename L,
320 typename R>
321constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
322 const L lhs,
323 const R rhs) {
324 using Math = typename MathWrapper<M, L, R>::math;
325 return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
326 rhs);
327}
328
329// General purpose wrapper template for arithmetic operations.
330template <template <typename, typename, typename> class M,
331 typename L,
332 typename R,
333 typename... Args>
334constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
335CheckMathOp(const L lhs, const R rhs, const Args... args) {
336 return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
337}
338
339BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
340BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
341BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
342BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
343BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
344BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
345BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
346BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
347BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
348BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
349BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
350BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
351
352// These are some extra StrictNumeric operators to support simple pointer
353// arithmetic with our result types. Since wrapping on a pointer is always
354// bad, we trigger the CHECK condition here.
355template <typename L, typename R>
356L* operator+(L* lhs, const StrictNumeric<R> rhs) {
357 uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
358 CheckMul(sizeof(L), static_cast<R>(rhs)))
359 .template ValueOrDie<uintptr_t>();
360 return reinterpret_cast<L*>(result);
361}
362
363template <typename L, typename R>
364L* operator-(L* lhs, const StrictNumeric<R> rhs) {
365 uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
366 CheckMul(sizeof(L), static_cast<R>(rhs)))
367 .template ValueOrDie<uintptr_t>();
368 return reinterpret_cast<L*>(result);
369}
370
371} // namespace internal
372
Scott Graham76a8dc72018-06-18 13:37:29 -0700373using internal::CheckAdd;
374using internal::CheckAnd;
375using internal::CheckDiv;
Scott Graham66962112018-06-08 12:42:08 -0700376using internal::CheckedNumeric;
Scott Graham76a8dc72018-06-18 13:37:29 -0700377using internal::CheckLsh;
Scott Graham66962112018-06-08 12:42:08 -0700378using internal::CheckMax;
379using internal::CheckMin;
Scott Graham66962112018-06-08 12:42:08 -0700380using internal::CheckMod;
Scott Graham76a8dc72018-06-18 13:37:29 -0700381using internal::CheckMul;
Scott Graham66962112018-06-08 12:42:08 -0700382using internal::CheckOr;
Scott Graham76a8dc72018-06-18 13:37:29 -0700383using internal::CheckRsh;
384using internal::CheckSub;
Scott Graham66962112018-06-08 12:42:08 -0700385using internal::CheckXor;
Scott Graham76a8dc72018-06-18 13:37:29 -0700386using internal::IsValidForType;
387using internal::MakeCheckedNum;
388using internal::ValueOrDefaultForType;
389using internal::ValueOrDieForType;
Scott Graham66962112018-06-08 12:42:08 -0700390
391} // namespace base
392
393#endif // BASE_NUMERICS_CHECKED_MATH_H_