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