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 #ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_
6 #define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
7
8 // IWYU pragma: private, include "base/numerics/checked_math.h"
9
10 #include <stdint.h>
11
12 #include <cmath>
13 #include <concepts>
14 #include <limits>
15 #include <type_traits>
16
17 #include "base/numerics/safe_conversions.h"
18 #include "base/numerics/safe_math_shared_impl.h" // IWYU pragma: export
19
20 namespace base {
21 namespace internal {
22
23 template <typename T>
CheckedAddImpl(T x,T y,T * result)24 constexpr bool CheckedAddImpl(T x, T y, T* result) {
25 static_assert(std::is_integral_v<T>, "Type must be integral");
26 // Since the value of x+y is undefined if we have a signed type, we compute
27 // it using the unsigned type of the same size.
28 using UnsignedDst = typename std::make_unsigned<T>::type;
29 using SignedDst = typename std::make_signed<T>::type;
30 const UnsignedDst ux = static_cast<UnsignedDst>(x);
31 const UnsignedDst uy = static_cast<UnsignedDst>(y);
32 const UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
33 // Addition is valid if the sign of (x + y) is equal to either that of x or
34 // that of y.
35 if (std::is_signed_v<T>
36 ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) < 0
37 : uresult < uy) { // Unsigned is either valid or underflow.
38 return false;
39 }
40 *result = static_cast<T>(uresult);
41 return true;
42 }
43
44 template <typename T, typename U>
45 struct CheckedAddOp {};
46
47 template <typename T, typename U>
48 requires(std::integral<T> && std::integral<U>)
49 struct CheckedAddOp<T, U> {
50 using result_type = MaxExponentPromotion<T, U>;
51 template <typename V>
52 static constexpr bool Do(T x, U y, V* result) {
53 if constexpr (CheckedAddFastOp<T, U>::is_supported)
54 return CheckedAddFastOp<T, U>::Do(x, y, result);
55
56 // Double the underlying type up to a full machine word.
57 using FastPromotion = FastIntegerArithmeticPromotion<T, U>;
58 using Promotion =
59 std::conditional_t<(kIntegerBitsPlusSign<FastPromotion> >
60 kIntegerBitsPlusSign<intptr_t>),
61 BigEnoughPromotion<T, U>, FastPromotion>;
62 // Fail if either operand is out of range for the promoted type.
63 // TODO(jschuh): This could be made to work for a broader range of values.
64 if (!IsValueInRangeForNumericType<Promotion>(x) ||
65 !IsValueInRangeForNumericType<Promotion>(y)) [[unlikely]] {
66 return false;
67 }
68
69 Promotion presult = {};
70 bool is_valid = true;
71 if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
72 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
73 } else {
74 is_valid = CheckedAddImpl(static_cast<Promotion>(x),
75 static_cast<Promotion>(y), &presult);
76 }
77 if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
78 return false;
79 *result = static_cast<V>(presult);
80 return true;
81 }
82 };
83
84 template <typename T>
85 constexpr bool CheckedSubImpl(T x, T y, T* result) {
86 static_assert(std::is_integral_v<T>, "Type must be integral");
87 // Since the value of x+y is undefined if we have a signed type, we compute
88 // it using the unsigned type of the same size.
89 using UnsignedDst = typename std::make_unsigned<T>::type;
90 using SignedDst = typename std::make_signed<T>::type;
91 const UnsignedDst ux = static_cast<UnsignedDst>(x);
92 const UnsignedDst uy = static_cast<UnsignedDst>(y);
93 const UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
94 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
95 // the same sign.
96 if (std::is_signed_v<T>
97 ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) < 0
98 : x < y) {
99 return false;
100 }
101 *result = static_cast<T>(uresult);
102 return true;
103 }
104
105 template <typename T, typename U>
106 struct CheckedSubOp {};
107
108 template <typename T, typename U>
109 requires(std::integral<T> && std::integral<U>)
110 struct CheckedSubOp<T, U> {
111 using result_type = MaxExponentPromotion<T, U>;
112 template <typename V>
113 static constexpr bool Do(T x, U y, V* result) {
114 if constexpr (CheckedSubFastOp<T, U>::is_supported)
115 return CheckedSubFastOp<T, U>::Do(x, y, result);
116
117 // Double the underlying type up to a full machine word.
118 using FastPromotion = FastIntegerArithmeticPromotion<T, U>;
119 using Promotion =
120 std::conditional_t<(kIntegerBitsPlusSign<FastPromotion> >
121 kIntegerBitsPlusSign<intptr_t>),
122 BigEnoughPromotion<T, U>, FastPromotion>;
123 // Fail if either operand is out of range for the promoted type.
124 // TODO(jschuh): This could be made to work for a broader range of values.
125 if (!IsValueInRangeForNumericType<Promotion>(x) ||
126 !IsValueInRangeForNumericType<Promotion>(y)) [[unlikely]] {
127 return false;
128 }
129
130 Promotion presult = {};
131 bool is_valid = true;
132 if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
133 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
134 } else {
135 is_valid = CheckedSubImpl(static_cast<Promotion>(x),
136 static_cast<Promotion>(y), &presult);
137 }
138 if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
139 return false;
140 *result = static_cast<V>(presult);
141 return true;
142 }
143 };
144
145 template <typename T>
146 constexpr bool CheckedMulImpl(T x, T y, T* result) {
147 static_assert(std::is_integral_v<T>, "Type must be integral");
148 // Since the value of x*y is potentially undefined if we have a signed type,
149 // we compute it using the unsigned type of the same size.
150 using UnsignedDst = typename std::make_unsigned<T>::type;
151 using SignedDst = typename std::make_signed<T>::type;
152 const UnsignedDst ux = SafeUnsignedAbs(x);
153 const UnsignedDst uy = SafeUnsignedAbs(y);
154 const UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
155 const bool is_negative =
156 std::is_signed_v<T> && static_cast<SignedDst>(x ^ y) < 0;
157 // We have a fast out for unsigned identity or zero on the second operand.
158 // After that it's an unsigned overflow check on the absolute value, with
159 // a +1 bound for a negative result.
160 if (uy > UnsignedDst(!std::is_signed_v<T> || is_negative) &&
161 ux > (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy) {
162 return false;
163 }
164 *result = static_cast<T>(is_negative ? 0 - uresult : uresult);
165 return true;
166 }
167
168 template <typename T, typename U>
169 struct CheckedMulOp {};
170
171 template <typename T, typename U>
172 requires(std::integral<T> && std::integral<U>)
173 struct CheckedMulOp<T, U> {
174 using result_type = MaxExponentPromotion<T, U>;
175 template <typename V>
176 static constexpr bool Do(T x, U y, V* result) {
177 if constexpr (CheckedMulFastOp<T, U>::is_supported)
178 return CheckedMulFastOp<T, U>::Do(x, y, result);
179
180 using Promotion = FastIntegerArithmeticPromotion<T, U>;
181 // Verify the destination type can hold the result (always true for 0).
182 if ((!IsValueInRangeForNumericType<Promotion>(x) ||
183 !IsValueInRangeForNumericType<Promotion>(y)) &&
184 x && y) [[unlikely]] {
185 return false;
186 }
187
188 Promotion presult = {};
189 bool is_valid = true;
190 if constexpr (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
191 // The fast op may be available with the promoted type.
192 // The casts here are safe because of the "value in range" conditional
193 // above.
194 is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(
195 static_cast<Promotion>(x), static_cast<Promotion>(y), &presult);
196 } else if constexpr (kIsIntegerArithmeticSafe<Promotion, T, U>) {
197 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
198 } else {
199 is_valid = CheckedMulImpl(static_cast<Promotion>(x),
200 static_cast<Promotion>(y), &presult);
201 }
202 if (!is_valid || !IsValueInRangeForNumericType<V>(presult))
203 return false;
204 *result = static_cast<V>(presult);
205 return true;
206 }
207 };
208
209 // Division just requires a check for a zero denominator or an invalid negation
210 // on signed min/-1.
211 template <typename T, typename U>
212 struct CheckedDivOp {};
213
214 template <typename T, typename U>
215 requires(std::integral<T> && std::integral<U>)
216 struct CheckedDivOp<T, U> {
217 using result_type = MaxExponentPromotion<T, U>;
218 template <typename V>
219 static constexpr bool Do(T x, U y, V* result) {
220 if (!y) [[unlikely]] {
221 return false;
222 }
223
224 // The overflow check can be compiled away if we don't have the exact
225 // combination of types needed to trigger this case.
226 using Promotion = BigEnoughPromotion<T, U>;
227 if (std::is_signed_v<T> && std::is_signed_v<U> &&
228 kIsTypeInRangeForNumericType<T, Promotion> &&
229 static_cast<Promotion>(x) == std::numeric_limits<Promotion>::lowest() &&
230 y == static_cast<U>(-1)) [[unlikely]] {
231 return false;
232 }
233
234 // This branch always compiles away if the above branch wasn't removed.
235 if ((!IsValueInRangeForNumericType<Promotion>(x) ||
236 !IsValueInRangeForNumericType<Promotion>(y)) &&
237 x) [[unlikely]] {
238 return false;
239 }
240
241 const Promotion presult = Promotion(x) / Promotion(y);
242 if (!IsValueInRangeForNumericType<V>(presult))
243 return false;
244 *result = static_cast<V>(presult);
245 return true;
246 }
247 };
248
249 template <typename T, typename U>
250 struct CheckedModOp {};
251
252 template <typename T, typename U>
253 requires(std::integral<T> && std::integral<U>)
254 struct CheckedModOp<T, U> {
255 using result_type = MaxExponentPromotion<T, U>;
256 template <typename V>
257 static constexpr bool Do(T x, U y, V* result) {
258 if (!y) [[unlikely]] {
259 return false;
260 }
261
262 using Promotion = BigEnoughPromotion<T, U>;
263 if (std::is_signed_v<T> && std::is_signed_v<U> &&
264 kIsTypeInRangeForNumericType<T, Promotion> &&
265 static_cast<Promotion>(x) == std::numeric_limits<Promotion>::lowest() &&
266 y == static_cast<U>(-1)) [[unlikely]] {
267 *result = 0;
268 return true;
269 }
270
271 const Promotion presult =
272 static_cast<Promotion>(x) % static_cast<Promotion>(y);
273 if (!IsValueInRangeForNumericType<V>(presult))
274 return false;
275 *result = static_cast<Promotion>(presult);
276 return true;
277 }
278 };
279
280 template <typename T, typename U>
281 struct CheckedLshOp {};
282
283 // Left shift. Shifts less than 0 or greater than or equal to the number
284 // of bits in the promoted type are undefined. Shifts of negative values
285 // are undefined. Otherwise it is defined when the result fits.
286 template <typename T, typename U>
287 requires(std::integral<T> && std::integral<U>)
288 struct CheckedLshOp<T, U> {
289 using result_type = T;
290 template <typename V>
291 static constexpr bool Do(T x, U shift, V* result) {
292 // Disallow negative numbers and verify the shift is in bounds.
293 if (!IsValueNegative(x) &&
294 as_unsigned(shift) < as_unsigned(std::numeric_limits<T>::digits))
295 [[likely]] {
296 // Shift as unsigned to avoid undefined behavior.
297 *result = static_cast<V>(as_unsigned(x) << shift);
298 // If the shift can be reversed, we know it was valid.
299 return *result >> shift == x;
300 }
301
302 // Handle the legal corner-case of a full-width signed shift of zero.
303 if (!std::is_signed_v<T> || x ||
304 as_unsigned(shift) != as_unsigned(std::numeric_limits<T>::digits)) {
305 return false;
306 }
307 *result = 0;
308 return true;
309 }
310 };
311
312 template <typename T, typename U>
313 struct CheckedRshOp {};
314
315 // Right shift. Shifts less than 0 or greater than or equal to the number
316 // of bits in the promoted type are undefined. Otherwise, it is always defined,
317 // but a right shift of a negative value is implementation-dependent.
318 template <typename T, typename U>
319 requires(std::integral<T> && std::integral<U>)
320 struct CheckedRshOp<T, U> {
321 using result_type = T;
322 template <typename V>
323 static constexpr bool Do(T x, U shift, V* result) {
324 // Use sign conversion to push negative values out of range.
325 if (as_unsigned(shift) >= kIntegerBitsPlusSign<T>) [[unlikely]] {
326 return false;
327 }
328
329 const T tmp = x >> shift;
330 if (!IsValueInRangeForNumericType<V>(tmp))
331 return false;
332 *result = static_cast<V>(tmp);
333 return true;
334 }
335 };
336
337 template <typename T, typename U>
338 struct CheckedAndOp {};
339
340 // For simplicity we support only unsigned integer results.
341 template <typename T, typename U>
342 requires(std::integral<T> && std::integral<U>)
343 struct CheckedAndOp<T, U> {
344 using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
345 template <typename V>
346 static constexpr bool Do(T x, U y, V* result) {
347 const result_type tmp =
348 static_cast<result_type>(x) & static_cast<result_type>(y);
349 if (!IsValueInRangeForNumericType<V>(tmp))
350 return false;
351 *result = static_cast<V>(tmp);
352 return true;
353 }
354 };
355
356 template <typename T, typename U>
357 struct CheckedOrOp {};
358
359 // For simplicity we support only unsigned integers.
360 template <typename T, typename U>
361 requires(std::integral<T> && std::integral<U>)
362 struct CheckedOrOp<T, U> {
363 using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
364 template <typename V>
365 static constexpr bool Do(T x, U y, V* result) {
366 const result_type tmp =
367 static_cast<result_type>(x) | static_cast<result_type>(y);
368 if (!IsValueInRangeForNumericType<V>(tmp))
369 return false;
370 *result = static_cast<V>(tmp);
371 return true;
372 }
373 };
374
375 template <typename T, typename U>
376 struct CheckedXorOp {};
377
378 // For simplicity we support only unsigned integers.
379 template <typename T, typename U>
380 requires(std::integral<T> && std::integral<U>)
381 struct CheckedXorOp<T, U> {
382 using result_type = std::make_unsigned_t<MaxExponentPromotion<T, U>>;
383 template <typename V>
384 static constexpr bool Do(T x, U y, V* result) {
385 const result_type tmp =
386 static_cast<result_type>(x) ^ static_cast<result_type>(y);
387 if (!IsValueInRangeForNumericType<V>(tmp))
388 return false;
389 *result = static_cast<V>(tmp);
390 return true;
391 }
392 };
393
394 // Max doesn't really need to be implemented this way because it can't fail,
395 // but it makes the code much cleaner to use the MathOp wrappers.
396 template <typename T, typename U>
397 struct CheckedMaxOp {};
398
399 template <typename T, typename U>
400 requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
401 struct CheckedMaxOp<T, U> {
402 using result_type = MaxExponentPromotion<T, U>;
403 template <typename V>
404 static constexpr bool Do(T x, U y, V* result) {
405 const result_type tmp = IsGreater<T, U>::Test(x, y)
406 ? static_cast<result_type>(x)
407 : static_cast<result_type>(y);
408 if (!IsValueInRangeForNumericType<V>(tmp))
409 return false;
410 *result = static_cast<V>(tmp);
411 return true;
412 }
413 };
414
415 // Min doesn't really need to be implemented this way because it can't fail,
416 // but it makes the code much cleaner to use the MathOp wrappers.
417 template <typename T, typename U>
418 struct CheckedMinOp {};
419
420 template <typename T, typename U>
421 requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
422 struct CheckedMinOp<T, U> {
423 using result_type = LowestValuePromotion<T, U>;
424 template <typename V>
425 static constexpr bool Do(T x, U y, V* result) {
426 const result_type tmp = IsLess<T, U>::Test(x, y)
427 ? static_cast<result_type>(x)
428 : static_cast<result_type>(y);
429 if (!IsValueInRangeForNumericType<V>(tmp))
430 return false;
431 *result = static_cast<V>(tmp);
432 return true;
433 }
434 };
435
436 // This is just boilerplate that wraps the standard floating point arithmetic.
437 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
438 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
439 template <typename T, typename U> \
440 requires(std::is_floating_point_v<T> || std::is_floating_point_v<U>) \
441 struct Checked##NAME##Op<T, U> { \
442 using result_type = MaxExponentPromotion<T, U>; \
443 template <typename V> \
444 static constexpr bool Do(T x, U y, V* result) { \
445 const result_type presult = x OP y; \
446 if (!IsValueInRangeForNumericType<V>(presult)) \
447 return false; \
448 *result = static_cast<V>(presult); \
449 return true; \
450 } \
451 };
452
453 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
454 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
455 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
456 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
457
458 #undef BASE_FLOAT_ARITHMETIC_OPS
459
460 // Floats carry around their validity state with them, but integers do not. So,
461 // we wrap the underlying value in a specialization in order to hide that detail
462 // and expose an interface via accessors.
463 enum NumericRepresentation {
464 NUMERIC_INTEGER,
465 NUMERIC_FLOATING,
466 NUMERIC_UNKNOWN
467 };
468
469 template <typename NumericType>
470 struct GetNumericRepresentation {
471 static const NumericRepresentation value =
472 std::is_integral_v<NumericType>
473 ? NUMERIC_INTEGER
474 : (std::is_floating_point_v<NumericType> ? NUMERIC_FLOATING
475 : NUMERIC_UNKNOWN);
476 };
477
478 template <typename T,
479 NumericRepresentation type = GetNumericRepresentation<T>::value>
480 class CheckedNumericState {};
481
482 // Integrals require quite a bit of additional housekeeping to manage state.
483 template <typename T>
484 class CheckedNumericState<T, NUMERIC_INTEGER> {
485 public:
486 template <typename Src = int>
487 constexpr explicit CheckedNumericState(Src value = 0, bool is_valid = true)
488 : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
489 value_(WellDefinedConversionOrZero(value, is_valid_)) {
490 static_assert(std::is_arithmetic_v<Src>, "Argument must be numeric.");
491 }
492
493 template <typename Src>
494 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
495 : CheckedNumericState(rhs.value(), rhs.is_valid()) {}
496
497 constexpr bool is_valid() const { return is_valid_; }
498
499 constexpr T value() const { return value_; }
500
501 private:
502 // Ensures that a type conversion does not trigger undefined behavior.
503 template <typename Src>
504 static constexpr T WellDefinedConversionOrZero(Src value, bool is_valid) {
505 using SrcType = typename internal::UnderlyingType<Src>::type;
506 return (std::is_integral_v<SrcType> || is_valid) ? static_cast<T>(value)
507 : 0;
508 }
509
510 // is_valid_ precedes value_ because member initializers in the constructors
511 // are evaluated in field order, and is_valid_ must be read when initializing
512 // value_.
513 bool is_valid_;
514 T value_;
515 };
516
517 // Floating points maintain their own validity, but need translation wrappers.
518 template <typename T>
519 class CheckedNumericState<T, NUMERIC_FLOATING> {
520 public:
521 template <typename Src = double>
522 constexpr explicit CheckedNumericState(Src value = 0.0, bool is_valid = true)
523 : value_(WellDefinedConversionOrNaN(
524 value,
525 is_valid && IsValueInRangeForNumericType<T>(value))) {}
526
527 template <typename Src>
528 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
529 : CheckedNumericState(rhs.value(), rhs.is_valid()) {}
530
531 constexpr bool is_valid() const {
532 // Written this way because std::isfinite is not constexpr before C++23.
533 // TODO(C++23): Use `std::isfinite()` unconditionally.
534 return std::is_constant_evaluated()
535 ? value_ <= std::numeric_limits<T>::max() &&
536 value_ >= std::numeric_limits<T>::lowest()
537 : std::isfinite(value_);
538 }
539
540 constexpr T value() const { return value_; }
541
542 private:
543 // Ensures that a type conversion does not trigger undefined behavior.
544 template <typename Src>
545 static constexpr T WellDefinedConversionOrNaN(Src value, bool is_valid) {
546 using SrcType = typename internal::UnderlyingType<Src>::type;
547 return (kStaticDstRangeRelationToSrcRange<T, SrcType> ==
548 NumericRangeRepresentation::kContained ||
549 is_valid)
550 ? static_cast<T>(value)
551 : std::numeric_limits<T>::quiet_NaN();
552 }
553
554 T value_;
555 };
556
557 } // namespace internal
558 } // namespace base
559
560 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_
561