blob: 4e42d456e4b33738675bb069eab7f0e65e6ba4d8 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2014 The Chromium Authors
[email protected]4efb2c32014-01-16 06:57:252// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
danakj0a448602015-03-10 00:31:165#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
6#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
[email protected]4efb2c32014-01-16 06:57:257
Jean-Philippe Gravelf81c63b2024-04-23 20:33:178// IWYU pragma: private, include "base/numerics/safe_conversions.h"
9
10#include <stddef.h>
avi9b6f42932015-12-26 22:15:1411#include <stdint.h>
12
Daniel Cheng7bc78112023-11-20 19:41:2713#include <concepts>
rickyzd2fe3e242016-05-10 13:47:3714#include <limits>
thomasanderson61500f22016-08-31 01:30:3715#include <type_traits>
Takuto Ikuta4d4c6852024-12-09 11:16:0216#include <utility>
rickyzd2fe3e242016-05-10 13:47:3717
Peter Kasting6d970742024-12-06 05:06:2718#include "base/numerics/integral_constant_like.h"
19
Peter Kasting134183e2024-11-13 01:22:3920namespace base::internal {
[email protected]4efb2c32014-01-16 06:57:2521
[email protected]5bfecbc2014-02-27 13:49:0422// The std library doesn't provide a binary max_exponent for integers, however
jschuh5030b00c2016-12-05 18:21:4823// we can compute an analog using std::numeric_limits<>::digits.
[email protected]5bfecbc2014-02-27 13:49:0424template <typename NumericType>
Peter Kastingabcf60a2024-11-13 01:24:3725inline constexpr int kMaxExponent =
26 std::is_floating_point_v<NumericType>
27 ? std::numeric_limits<NumericType>::max_exponent
28 : std::numeric_limits<NumericType>::digits + 1;
jschuh5030b00c2016-12-05 18:21:4829
30// The number of bits (including the sign) in an integer. Eliminates sizeof
31// hacks.
32template <typename NumericType>
Peter Kastingabcf60a2024-11-13 01:24:3733inline constexpr int kIntegerBitsPlusSign =
34 std::numeric_limits<NumericType>::digits + std::is_signed_v<NumericType>;
[email protected]4efb2c32014-01-16 06:57:2535
jschuh71b669a2016-12-17 01:13:3136// Determines if a numeric value is negative without throwing compiler
37// warnings on: unsigned(value) < 0.
Daniel Cheng7bc78112023-11-20 19:41:2738template <typename T>
Peter Kastingbaaa6ac02024-11-13 01:02:5139 requires(std::is_arithmetic_v<T>)
jschuh71b669a2016-12-17 01:13:3140constexpr bool IsValueNegative(T value) {
Peter Kastingbaaa6ac02024-11-13 01:02:5141 if constexpr (std::is_signed_v<T>) {
42 return value < 0;
43 } else {
44 return false;
45 }
jschuh71b669a2016-12-17 01:13:3146}
47
48// This performs a fast negation, returning a signed value. It works on unsigned
49// arguments, but probably doesn't do what you want for any unsigned value
50// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
51template <typename T>
Peter Kastingbaaa6ac02024-11-13 01:02:5152 requires std::is_integral_v<T>
53constexpr auto ConditionalNegate(T x, bool is_negative) {
Peter Kasting134183e2024-11-13 01:22:3954 using SignedT = std::make_signed_t<T>;
55 using UnsignedT = std::make_unsigned_t<T>;
Peter Kasting92964de2022-05-27 18:59:4056 return static_cast<SignedT>((static_cast<UnsignedT>(x) ^
57 static_cast<UnsignedT>(-SignedT(is_negative))) +
58 is_negative);
jschuh71b669a2016-12-17 01:13:3159}
60
jschuh33232e02017-01-03 20:33:3661// This performs a safe, absolute value via unsigned overflow.
jschuhba3c4f942016-12-10 14:03:5562template <typename T>
Peter Kastingbaaa6ac02024-11-13 01:02:5163 requires std::is_integral_v<T>
64constexpr auto SafeUnsignedAbs(T value) {
Peter Kasting134183e2024-11-13 01:22:3965 using UnsignedT = std::make_unsigned_t<T>;
Scott Grahamd08e9872020-01-22 18:24:4566 return IsValueNegative(value)
67 ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
68 : static_cast<UnsignedT>(value);
jschuhba3c4f942016-12-10 14:03:5569}
70
Justin Schuhe2eefe612022-01-13 22:48:5071// TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict
72// some accelerated runtime paths to release builds until this can be forced
73// with consteval support in C++20 or C++23.
74#if defined(NDEBUG)
Peter Kastingabcf60a2024-11-13 01:24:3775inline constexpr bool kEnableAsmCode = true;
Justin Schuha26da272017-07-13 23:04:3076#else
Peter Kastingabcf60a2024-11-13 01:24:3777inline constexpr bool kEnableAsmCode = false;
Justin Schuha26da272017-07-13 23:04:3078#endif
Justin Schuha26da272017-07-13 23:04:3079
Peter Boström54119652024-11-14 00:16:3880// Forces a crash, like a NOTREACHED(). Used for numeric boundary errors.
Justin Schuha26da272017-07-13 23:04:3081// Also used in a constexpr template to trigger a compilation failure on
82// an error condition.
83struct CheckOnFailure {
84 template <typename T>
85 static T HandleFailure() {
Justin Schuh680caa472017-08-29 02:44:0786#if defined(_MSC_VER)
87 __debugbreak();
88#elif defined(__GNUC__) || defined(__clang__)
Justin Schuha26da272017-07-13 23:04:3089 __builtin_trap();
90#else
91 ((void)(*(volatile char*)0 = 0));
92#endif
93 return T();
94 }
95};
96
Peter Kastingabcf60a2024-11-13 01:24:3797enum class IntegerRepresentation { kUnsigned, kSigned };
[email protected]4efb2c32014-01-16 06:57:2598
[email protected]5bfecbc2014-02-27 13:49:0499// A range for a given nunmeric Src type is contained for a given numeric Dst
100// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
jschuh5030b00c2016-12-05 18:21:48101// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
[email protected]5bfecbc2014-02-27 13:49:04102// We implement this as template specializations rather than simple static
103// comparisons to ensure type correctness in our comparisons.
Peter Kastingabcf60a2024-11-13 01:24:37104enum class NumericRangeRepresentation { kNotContained, kContained };
[email protected]4efb2c32014-01-16 06:57:25105
106// Helper templates to statically determine if our destination type can contain
[email protected]5bfecbc2014-02-27 13:49:04107// maximum and minimum values represented by the source type.
[email protected]4efb2c32014-01-16 06:57:25108
Peter Kastingabcf60a2024-11-13 01:24:37109// Default case, used for same sign: Dst is guaranteed to contain Src only if
110// its range is equal or larger.
jschuh5030b00c2016-12-05 18:21:48111template <typename Dst,
112 typename Src,
Peter Kastingabcf60a2024-11-13 01:24:37113 IntegerRepresentation DstSign =
114 std::is_signed_v<Dst> ? IntegerRepresentation::kSigned
115 : IntegerRepresentation::kUnsigned,
116 IntegerRepresentation SrcSign =
117 std::is_signed_v<Src> ? IntegerRepresentation::kSigned
118 : IntegerRepresentation::kUnsigned>
119inline constexpr auto kStaticDstRangeRelationToSrcRange =
120 kMaxExponent<Dst> >= kMaxExponent<Src>
121 ? NumericRangeRepresentation::kContained
122 : NumericRangeRepresentation::kNotContained;
[email protected]5bfecbc2014-02-27 13:49:04123
124// Unsigned to signed: Dst is guaranteed to contain source only if its range is
125// larger.
[email protected]4efb2c32014-01-16 06:57:25126template <typename Dst, typename Src>
Peter Kastingabcf60a2024-11-13 01:24:37127inline constexpr auto
128 kStaticDstRangeRelationToSrcRange<Dst,
129 Src,
130 IntegerRepresentation::kSigned,
131 IntegerRepresentation::kUnsigned> =
132 kMaxExponent<Dst> > kMaxExponent<Src>
133 ? NumericRangeRepresentation::kContained
134 : NumericRangeRepresentation::kNotContained;
[email protected]4efb2c32014-01-16 06:57:25135
[email protected]5bfecbc2014-02-27 13:49:04136// Signed to unsigned: Dst cannot be statically determined to contain Src.
[email protected]4efb2c32014-01-16 06:57:25137template <typename Dst, typename Src>
Peter Kastingabcf60a2024-11-13 01:24:37138inline constexpr auto
139 kStaticDstRangeRelationToSrcRange<Dst,
140 Src,
141 IntegerRepresentation::kUnsigned,
142 IntegerRepresentation::kSigned> =
143 NumericRangeRepresentation::kNotContained;
[email protected]4efb2c32014-01-16 06:57:25144
jschuhe3bd1f62016-12-20 05:11:30145// This class wraps the range constraints as separate booleans so the compiler
146// can identify constants and eliminate unused code paths.
147class RangeCheck {
148 public:
Peter Kasting134183e2024-11-13 01:22:39149 constexpr RangeCheck() = default;
jschuh082b2ba2017-01-06 11:40:54150 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
jschuhc8f03cd2017-01-05 03:40:51151 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
Peter Kasting134183e2024-11-13 01:22:39152
153 constexpr bool operator==(const RangeCheck& rhs) const = default;
154
jschuhe3bd1f62016-12-20 05:11:30155 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
156 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
157 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
158 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
jschuhc8f03cd2017-01-05 03:40:51159 constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
160 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
jschuhe3bd1f62016-12-20 05:11:30161
162 private:
jschuhc8f03cd2017-01-05 03:40:51163 // Do not change the order of these member variables. The integral conversion
164 // optimization depends on this exact order.
Peter Kasting134183e2024-11-13 01:22:39165 const bool is_underflow_ = false;
166 const bool is_overflow_ = false;
jschuhe3bd1f62016-12-20 05:11:30167};
[email protected]4efb2c32014-01-16 06:57:25168
jschuhfafe0712015-09-14 20:21:24169// The following helper template addresses a corner case in range checks for
170// conversion from a floating-point type to an integral type of smaller range
171// but larger precision (e.g. float -> unsigned). The problem is as follows:
172// 1. Integral maximum is always one less than a power of two, so it must be
173// truncated to fit the mantissa of the floating point. The direction of
174// rounding is implementation defined, but by default it's always IEEE
175// floats, which round to nearest and thus result in a value of larger
176// magnitude than the integral value.
177// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
178// // is 4294967295u.
179// 2. If the floating point value is equal to the promoted integral maximum
180// value, a range check will erroneously pass.
181// Example: (4294967296f <= 4294967295u) // This is true due to a precision
182// // loss in rounding up to float.
183// 3. When the floating point value is then converted to an integral, the
184// resulting value is out of range for the target integral type and
185// thus is implementation defined.
186// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
187// To fix this bug we manually truncate the maximum value when the destination
188// type is an integral of larger precision than the source floating-point type,
189// such that the resulting maximum is represented exactly as a floating point.
jschuhe3bd1f62016-12-20 05:11:30190template <typename Dst, typename Src, template <typename> class Bounds>
jschuhfafe0712015-09-14 20:21:24191struct NarrowingRange {
jschuhe3bd1f62016-12-20 05:11:30192 using SrcLimits = std::numeric_limits<Src>;
Peter Kastingbaaa6ac02024-11-13 01:02:51193 using DstLimits = std::numeric_limits<Dst>;
jschuhfafe0712015-09-14 20:21:24194
jschuh71b669a2016-12-17 01:13:31195 // Computes the mask required to make an accurate comparison between types.
Peter Kastingabcf60a2024-11-13 01:24:37196 static constexpr int kShift = (kMaxExponent<Src> > kMaxExponent<Dst> &&
197 SrcLimits::digits < DstLimits::digits)
198 ? (DstLimits::digits - SrcLimits::digits)
199 : 0;
jschuh71b669a2016-12-17 01:13:31200
Daniel Cheng7bc78112023-11-20 19:41:27201 template <typename T>
Peter Kastingbaaa6ac02024-11-13 01:02:51202 requires(std::same_as<T, Dst> &&
203 ((std::integral<T> && kShift < DstLimits::digits) ||
204 (std::floating_point<T> && kShift == 0)))
jschuh71b669a2016-12-17 01:13:31205 // Masks out the integer bits that are beyond the precision of the
206 // intermediate type used for comparison.
207 static constexpr T Adjust(T value) {
Peter Kastingbaaa6ac02024-11-13 01:02:51208 if constexpr (std::integral<T>) {
209 using UnsignedDst = typename std::make_unsigned_t<T>;
210 return static_cast<T>(
211 ConditionalNegate(SafeUnsignedAbs(value) &
212 ~((UnsignedDst{1} << kShift) - UnsignedDst{1}),
213 IsValueNegative(value)));
214 } else {
215 return value;
216 }
jschuh71b669a2016-12-17 01:13:31217 }
218
219 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
220 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
jschuhfafe0712015-09-14 20:21:24221};
222
[email protected]4efb2c32014-01-16 06:57:25223// The following templates are for ranges that must be verified at runtime. We
224// split it into checks based on signedness to avoid confusing casts and
225// compiler warnings on signed an unsigned comparisons.
226
Peter Kastingabcf60a2024-11-13 01:24:37227// Default case, used for same sign narrowing: The range is contained for normal
228// limits.
[email protected]5bfecbc2014-02-27 13:49:04229template <typename Dst,
230 typename Src,
Yuki Shiinoea1c6232022-04-14 11:05:01231 template <typename>
232 class Bounds,
Peter Kastingabcf60a2024-11-13 01:24:37233 IntegerRepresentation DstSign =
234 std::is_signed_v<Dst> ? IntegerRepresentation::kSigned
235 : IntegerRepresentation::kUnsigned,
236 IntegerRepresentation SrcSign =
237 std::is_signed_v<Src> ? IntegerRepresentation::kSigned
238 : IntegerRepresentation::kUnsigned,
239 NumericRangeRepresentation DstRange =
240 kStaticDstRangeRelationToSrcRange<Dst, Src>>
241struct DstRangeRelationToSrcRangeImpl {
jschuhe3bd1f62016-12-20 05:11:30242 static constexpr RangeCheck Check(Src value) {
243 using SrcLimits = std::numeric_limits<Src>;
244 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
245 return RangeCheck(
jschuhe3bd1f62016-12-20 05:11:30246 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
jschuh082b2ba2017-01-06 11:40:54247 static_cast<Dst>(value) >= DstLimits::lowest(),
248 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
249 static_cast<Dst>(value) <= DstLimits::max());
jschuhe3bd1f62016-12-20 05:11:30250 }
[email protected]5bfecbc2014-02-27 13:49:04251};
252
253// Signed to signed narrowing: Both the upper and lower boundaries may be
jschuhe3bd1f62016-12-20 05:11:30254// exceeded for standard limits.
255template <typename Dst, typename Src, template <typename> class Bounds>
Peter Kastingabcf60a2024-11-13 01:24:37256struct DstRangeRelationToSrcRangeImpl<
257 Dst,
258 Src,
259 Bounds,
260 IntegerRepresentation::kSigned,
261 IntegerRepresentation::kSigned,
262 NumericRangeRepresentation::kNotContained> {
jschuhe3bd1f62016-12-20 05:11:30263 static constexpr RangeCheck Check(Src value) {
264 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
jschuh082b2ba2017-01-06 11:40:54265 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
[email protected]4efb2c32014-01-16 06:57:25266 }
267};
268
jschuhe3bd1f62016-12-20 05:11:30269// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
270// standard limits.
271template <typename Dst, typename Src, template <typename> class Bounds>
Peter Kastingabcf60a2024-11-13 01:24:37272struct DstRangeRelationToSrcRangeImpl<
273 Dst,
274 Src,
275 Bounds,
276 IntegerRepresentation::kUnsigned,
277 IntegerRepresentation::kUnsigned,
278 NumericRangeRepresentation::kNotContained> {
jschuhe3bd1f62016-12-20 05:11:30279 static constexpr RangeCheck Check(Src value) {
280 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
281 return RangeCheck(
Peter Kasting5e7c9bf22024-11-13 01:25:22282 DstLimits::lowest() == Dst{0} || value >= DstLimits::lowest(),
jschuh082b2ba2017-01-06 11:40:54283 value <= DstLimits::max());
[email protected]4efb2c32014-01-16 06:57:25284 }
285};
286
jschuhe3bd1f62016-12-20 05:11:30287// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
288template <typename Dst, typename Src, template <typename> class Bounds>
Peter Kastingabcf60a2024-11-13 01:24:37289struct DstRangeRelationToSrcRangeImpl<
290 Dst,
291 Src,
292 Bounds,
293 IntegerRepresentation::kSigned,
294 IntegerRepresentation::kUnsigned,
295 NumericRangeRepresentation::kNotContained> {
jschuhe3bd1f62016-12-20 05:11:30296 static constexpr RangeCheck Check(Src value) {
297 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
298 using Promotion = decltype(Src() + Dst());
Peter Kasting5e7c9bf22024-11-13 01:25:22299 return RangeCheck(DstLimits::lowest() <= Dst{0} ||
jschuhe3bd1f62016-12-20 05:11:30300 static_cast<Promotion>(value) >=
jschuh082b2ba2017-01-06 11:40:54301 static_cast<Promotion>(DstLimits::lowest()),
302 static_cast<Promotion>(value) <=
303 static_cast<Promotion>(DstLimits::max()));
[email protected]4efb2c32014-01-16 06:57:25304 }
305};
306
[email protected]5bfecbc2014-02-27 13:49:04307// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
jschuhe3bd1f62016-12-20 05:11:30308// and any negative value exceeds the lower boundary for standard limits.
309template <typename Dst, typename Src, template <typename> class Bounds>
Peter Kastingabcf60a2024-11-13 01:24:37310struct DstRangeRelationToSrcRangeImpl<
311 Dst,
312 Src,
313 Bounds,
314 IntegerRepresentation::kUnsigned,
315 IntegerRepresentation::kSigned,
316 NumericRangeRepresentation::kNotContained> {
jschuhe3bd1f62016-12-20 05:11:30317 static constexpr RangeCheck Check(Src value) {
318 using SrcLimits = std::numeric_limits<Src>;
319 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
320 using Promotion = decltype(Src() + Dst());
Peter Kasting5e7c9bf22024-11-13 01:25:22321 bool ge_zero;
Ng Zhi An280ed262020-10-22 00:10:12322 // Converting floating-point to integer will discard fractional part, so
323 // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
Peter Kasting134183e2024-11-13 01:22:39324 if constexpr (std::is_floating_point_v<Src>) {
Peter Kasting5e7c9bf22024-11-13 01:25:22325 ge_zero = value > Src{-1};
Ng Zhi An280ed262020-10-22 00:10:12326 } else {
Peter Kasting5e7c9bf22024-11-13 01:25:22327 ge_zero = value >= Src{0};
Ng Zhi An280ed262020-10-22 00:10:12328 }
jschuhe3bd1f62016-12-20 05:11:30329 return RangeCheck(
Ng Zhi An280ed262020-10-22 00:10:12330 ge_zero && (DstLimits::lowest() == 0 ||
331 static_cast<Dst>(value) >= DstLimits::lowest()),
jschuhe3bd1f62016-12-20 05:11:30332 static_cast<Promotion>(SrcLimits::max()) <=
333 static_cast<Promotion>(DstLimits::max()) ||
334 static_cast<Promotion>(value) <=
jschuh082b2ba2017-01-06 11:40:54335 static_cast<Promotion>(DstLimits::max()));
[email protected]4efb2c32014-01-16 06:57:25336 }
337};
338
Justin Schuha26da272017-07-13 23:04:30339// Simple wrapper for statically checking if a type's range is contained.
340template <typename Dst, typename Src>
Peter Kastingabcf60a2024-11-13 01:24:37341inline constexpr bool kIsTypeInRangeForNumericType =
342 kStaticDstRangeRelationToSrcRange<Dst, Src> ==
343 NumericRangeRepresentation::kContained;
Justin Schuha26da272017-07-13 23:04:30344
jschuhe3bd1f62016-12-20 05:11:30345template <typename Dst,
346 template <typename> class Bounds = std::numeric_limits,
347 typename Src>
Peter Kastingbaaa6ac02024-11-13 01:02:51348 requires(std::is_arithmetic_v<Src> && std::is_arithmetic_v<Dst> &&
349 Bounds<Dst>::lowest() < Bounds<Dst>::max())
jschuhe3bd1f62016-12-20 05:11:30350constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
jschuhe3bd1f62016-12-20 05:11:30351 return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
[email protected]4efb2c32014-01-16 06:57:25352}
353
jschuh23a4b062016-12-02 02:55:08354// Integer promotion templates used by the portable checked integer arithmetic.
355template <size_t Size, bool IsSigned>
Peter Kastingabcf60a2024-11-13 01:24:37356struct IntegerForDigitsAndSignImpl;
jschuh5030b00c2016-12-05 18:21:48357
Peter Kastingabcf60a2024-11-13 01:24:37358#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
359 template <> \
360 struct IntegerForDigitsAndSignImpl<kIntegerBitsPlusSign<I>, \
361 std::is_signed_v<I>> { \
362 using type = I; \
jschuh5030b00c2016-12-05 18:21:48363 }
364
365INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
366INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
367INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
368INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
369INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
370INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
371INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
372INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
373#undef INTEGER_FOR_DIGITS_AND_SIGN
jschuh23a4b062016-12-02 02:55:08374
Peter Kastingabcf60a2024-11-13 01:24:37375template <size_t Size, bool IsSigned>
376using IntegerForDigitsAndSign =
377 IntegerForDigitsAndSignImpl<Size, IsSigned>::type;
378
jschuh23a4b062016-12-02 02:55:08379// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
380// support 128-bit math, then the ArithmeticPromotion template below will need
381// to be updated (or more likely replaced with a decltype expression).
Peter Kastingabcf60a2024-11-13 01:24:37382static_assert(kIntegerBitsPlusSign<intmax_t> == 64,
jschuh5030b00c2016-12-05 18:21:48383 "Max integer size not supported for this toolchain.");
jschuh23a4b062016-12-02 02:55:08384
Andrew Rayskiyc817c3cf2023-10-16 18:15:40385template <typename Integer, bool IsSigned = std::is_signed_v<Integer>>
Peter Kastingabcf60a2024-11-13 01:24:37386using TwiceWiderInteger =
387 IntegerForDigitsAndSign<kIntegerBitsPlusSign<Integer> * 2, IsSigned>;
jschuh23a4b062016-12-02 02:55:08388
jschuh711ac6a2016-12-04 07:17:48389// Determines the type that can represent the largest positive value.
jschuh23a4b062016-12-02 02:55:08390template <typename Lhs, typename Rhs>
Peter Kastingabcf60a2024-11-13 01:24:37391using MaxExponentPromotion =
392 std::conditional_t<(kMaxExponent<Lhs> > kMaxExponent<Rhs>), Lhs, Rhs>;
jschuh23a4b062016-12-02 02:55:08393
jschuh711ac6a2016-12-04 07:17:48394// Determines the type that can represent the lowest arithmetic value.
jschuh711ac6a2016-12-04 07:17:48395template <typename Lhs, typename Rhs>
Peter Kastingabcf60a2024-11-13 01:24:37396using LowestValuePromotion = std::conditional_t<
397 std::is_signed_v<Lhs>
398 ? (!std::is_signed_v<Rhs> || kMaxExponent<Lhs> > kMaxExponent<Rhs>)
399 : (!std::is_signed_v<Rhs> && kMaxExponent<Lhs> < kMaxExponent<Rhs>),
400 Lhs,
401 Rhs>;
jschuh711ac6a2016-12-04 07:17:48402
403// Determines the type that is best able to represent an arithmetic result.
jschuh23a4b062016-12-02 02:55:08404
Peter Kastingabcf60a2024-11-13 01:24:37405// Default case, used when the side with the max exponent is big enough.
406template <typename Lhs,
407 typename Rhs = Lhs,
408 bool is_intmax_type =
409 std::is_integral_v<MaxExponentPromotion<Lhs, Rhs>> &&
410 kIntegerBitsPlusSign<MaxExponentPromotion<Lhs, Rhs>> ==
411 kIntegerBitsPlusSign<intmax_t>,
412 bool is_max_exponent =
413 kStaticDstRangeRelationToSrcRange<MaxExponentPromotion<Lhs, Rhs>,
414 Lhs> ==
415 NumericRangeRepresentation::kContained &&
416 kStaticDstRangeRelationToSrcRange<MaxExponentPromotion<Lhs, Rhs>,
417 Rhs> ==
418 NumericRangeRepresentation::kContained>
419struct BigEnoughPromotionImpl {
420 using type = MaxExponentPromotion<Lhs, Rhs>;
421 static constexpr bool kContained = true;
jschuh23a4b062016-12-02 02:55:08422};
423
424// We can use a twice wider type to fit.
425template <typename Lhs, typename Rhs>
Peter Kastingabcf60a2024-11-13 01:24:37426struct BigEnoughPromotionImpl<Lhs, Rhs, false, false> {
jschuh5030b00c2016-12-05 18:21:48427 using type =
Peter Kastingabcf60a2024-11-13 01:24:37428 TwiceWiderInteger<MaxExponentPromotion<Lhs, Rhs>,
429 std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>;
430 static constexpr bool kContained = true;
jschuh23a4b062016-12-02 02:55:08431};
432
433// No type is large enough.
434template <typename Lhs, typename Rhs>
Peter Kastingabcf60a2024-11-13 01:24:37435struct BigEnoughPromotionImpl<Lhs, Rhs, true, false> {
436 using type = MaxExponentPromotion<Lhs, Rhs>;
437 static constexpr bool kContained = false;
jschuh23a4b062016-12-02 02:55:08438};
439
Peter Kastingabcf60a2024-11-13 01:24:37440template <typename Lhs, typename Rhs>
441using BigEnoughPromotion = BigEnoughPromotionImpl<Lhs, Rhs>::type;
442
443template <typename Lhs, typename Rhs>
444inline constexpr bool kIsBigEnoughPromotionContained =
445 BigEnoughPromotionImpl<Lhs, Rhs>::kContained;
446
jschuh23a4b062016-12-02 02:55:08447// We can statically check if operations on the provided types can wrap, so we
448// can skip the checked operations if they're not needed. So, for an integer we
449// care if the destination type preserves the sign and is twice the width of
450// the source.
jschuh33232e02017-01-03 20:33:36451template <typename T, typename Lhs, typename Rhs = Lhs>
Peter Kastingabcf60a2024-11-13 01:24:37452inline constexpr bool kIsIntegerArithmeticSafe =
453 !std::is_floating_point_v<T> && !std::is_floating_point_v<Lhs> &&
454 !std::is_floating_point_v<Rhs> &&
455 std::is_signed_v<T> >= std::is_signed_v<Lhs> &&
456 kIntegerBitsPlusSign<T> >=
457 (2 * kIntegerBitsPlusSign<Lhs>)&&std::is_signed_v<T> >=
458 std::is_signed_v<Rhs> &&
459 kIntegerBitsPlusSign<T> >= (2 * kIntegerBitsPlusSign<Rhs>);
jschuh23a4b062016-12-02 02:55:08460
jschuh33232e02017-01-03 20:33:36461// Promotes to a type that can represent any possible result of a binary
462// arithmetic operation with the source types.
Daniel Cheng7bc78112023-11-20 19:41:27463template <typename Lhs, typename Rhs>
Peter Kastingabcf60a2024-11-13 01:24:37464struct FastIntegerArithmeticPromotionImpl {
465 using type = BigEnoughPromotion<Lhs, Rhs>;
466 static constexpr bool kContained = false;
Daniel Cheng7bc78112023-11-20 19:41:27467};
jschuh33232e02017-01-03 20:33:36468
469template <typename Lhs, typename Rhs>
Peter Kastingabcf60a2024-11-13 01:24:37470 requires(kIsIntegerArithmeticSafe<
Daniel Cheng7bc78112023-11-20 19:41:27471 std::conditional_t<std::is_signed_v<Lhs> || std::is_signed_v<Rhs>,
472 intmax_t,
473 uintmax_t>,
Peter Kastingabcf60a2024-11-13 01:24:37474 MaxExponentPromotion<Lhs, Rhs>>)
475struct FastIntegerArithmeticPromotionImpl<Lhs, Rhs> {
jschuh33232e02017-01-03 20:33:36476 using type =
Peter Kastingabcf60a2024-11-13 01:24:37477 TwiceWiderInteger<MaxExponentPromotion<Lhs, Rhs>,
478 std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>;
479 static_assert(kIsIntegerArithmeticSafe<type, Lhs, Rhs>);
480 static constexpr bool kContained = true;
jschuh33232e02017-01-03 20:33:36481};
482
Peter Kastingabcf60a2024-11-13 01:24:37483template <typename Lhs, typename Rhs>
484using FastIntegerArithmeticPromotion =
485 FastIntegerArithmeticPromotionImpl<Lhs, Rhs>::type;
486
487template <typename Lhs, typename Rhs>
488inline constexpr bool kIsFastIntegerArithmeticPromotionContained =
489 FastIntegerArithmeticPromotionImpl<Lhs, Rhs>::kContained;
490
Peter Kasting6d970742024-12-06 05:06:27491template <typename T>
492struct ArithmeticOrIntegralConstant {
493 using type = T;
494};
495
496template <typename T>
497 requires IntegralConstantLike<T>
498struct ArithmeticOrIntegralConstant<T> {
499 using type = T::value_type;
500};
501
jschuh23a4b062016-12-02 02:55:08502// Extracts the underlying type from an enum.
jschuh23a4b062016-12-02 02:55:08503template <typename T>
Peter Kastingabcf60a2024-11-13 01:24:37504using ArithmeticOrUnderlyingEnum =
505 typename std::conditional_t<std::is_enum_v<T>,
506 std::underlying_type<T>,
Peter Kasting6d970742024-12-06 05:06:27507 ArithmeticOrIntegralConstant<T>>::type;
jschuh23a4b062016-12-02 02:55:08508
509// The following are helper templates used in the CheckedNumeric class.
510template <typename T>
Peter Kastingbaaa6ac02024-11-13 01:02:51511 requires std::is_arithmetic_v<T>
jschuh23a4b062016-12-02 02:55:08512class CheckedNumeric;
513
514template <typename T>
Peter Kastingbaaa6ac02024-11-13 01:02:51515 requires std::is_arithmetic_v<T>
jschuh6a2c67b92017-06-12 22:57:22516class ClampedNumeric;
517
518template <typename T>
Peter Kastingbaaa6ac02024-11-13 01:02:51519 requires std::is_arithmetic_v<T>
jschuh23a4b062016-12-02 02:55:08520class StrictNumeric;
521
522// Used to treat CheckedNumeric and arithmetic underlying types the same.
523template <typename T>
Peter Kastinga01d6b62024-12-05 20:19:59524inline constexpr bool kIsCheckedNumeric = false;
525template <typename T>
526inline constexpr bool kIsCheckedNumeric<CheckedNumeric<T>> = true;
527template <typename T>
528concept IsCheckedNumeric = kIsCheckedNumeric<T>;
529
530template <typename T>
531inline constexpr bool kIsClampedNumeric = false;
532template <typename T>
533inline constexpr bool kIsClampedNumeric<ClampedNumeric<T>> = true;
534template <typename T>
535concept IsClampedNumeric = kIsClampedNumeric<T>;
536
537template <typename T>
538inline constexpr bool kIsStrictNumeric = false;
539template <typename T>
540inline constexpr bool kIsStrictNumeric<StrictNumeric<T>> = true;
541template <typename T>
542concept IsStrictNumeric = kIsStrictNumeric<T>;
543
544template <typename T>
545struct UnderlyingTypeImpl {
Peter Kastingabcf60a2024-11-13 01:24:37546 using type = ArithmeticOrUnderlyingEnum<T>;
jschuh23a4b062016-12-02 02:55:08547};
Peter Kastinga01d6b62024-12-05 20:19:59548template <typename T>
549struct UnderlyingTypeImpl<CheckedNumeric<T>> {
550 using type = T;
551};
552template <typename T>
553struct UnderlyingTypeImpl<ClampedNumeric<T>> {
554 using type = T;
555};
556template <typename T>
557struct UnderlyingTypeImpl<StrictNumeric<T>> {
558 using type = T;
559};
560template <typename T>
561using UnderlyingType = UnderlyingTypeImpl<T>::type;
jschuh23a4b062016-12-02 02:55:08562
563template <typename T>
Peter Kastinga01d6b62024-12-05 20:19:59564inline constexpr bool kIsNumeric = std::is_arithmetic_v<UnderlyingType<T>>;
jschuh6a2c67b92017-06-12 22:57:22565template <typename T>
Peter Kastinga01d6b62024-12-05 20:19:59566 requires(IsCheckedNumeric<T> || IsClampedNumeric<T> || IsStrictNumeric<T>)
567inline constexpr bool kIsNumeric<T> = true;
jschuh23a4b062016-12-02 02:55:08568template <typename T>
Peter Kastinga01d6b62024-12-05 20:19:59569concept IsNumeric = kIsNumeric<T>;
jschuh23a4b062016-12-02 02:55:08570
571template <typename L, typename R>
Peter Kastinga01d6b62024-12-05 20:19:59572concept IsCheckedOp = (IsCheckedNumeric<L> && IsNumeric<R>) ||
573 (IsCheckedNumeric<R> && IsNumeric<L>);
jschuh23a4b062016-12-02 02:55:08574
575template <typename L, typename R>
Peter Kastinga01d6b62024-12-05 20:19:59576concept IsClampedOp =
577 !IsCheckedOp<L, R> && ((IsClampedNumeric<L> && IsNumeric<R>) ||
578 (IsClampedNumeric<R> && IsNumeric<L>));
jschuh6a2c67b92017-06-12 22:57:22579
580template <typename L, typename R>
Peter Kastinga01d6b62024-12-05 20:19:59581concept IsStrictOp = !IsCheckedOp<L, R> && !IsClampedOp<L, R> &&
582 ((IsStrictNumeric<L> && IsNumeric<R>) ||
583 (IsStrictNumeric<R> && IsNumeric<L>));
jschuh23a4b062016-12-02 02:55:08584
Justin Schuha26da272017-07-13 23:04:30585// as_signed<> returns the supplied integral value (or integral castable
586// Numeric template) cast as a signed integral of equivalent precision.
587// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
Peter Kastinga01d6b62024-12-05 20:19:59588template <typename Src, typename Dst = std::make_signed_t<UnderlyingType<Src>>>
Peter Kastingbaaa6ac02024-11-13 01:02:51589 requires std::integral<Dst>
590constexpr auto as_signed(Src value) {
591 return static_cast<Dst>(value);
Justin Schuha26da272017-07-13 23:04:30592}
593
594// as_unsigned<> returns the supplied integral value (or integral castable
595// Numeric template) cast as an unsigned integral of equivalent precision.
596// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
Peter Kastingbaaa6ac02024-11-13 01:02:51597template <typename Src,
Peter Kastinga01d6b62024-12-05 20:19:59598 typename Dst = std::make_unsigned_t<UnderlyingType<Src>>>
Peter Kastingbaaa6ac02024-11-13 01:02:51599 requires std::integral<Dst>
600constexpr auto as_unsigned(Src value) {
601 return static_cast<Dst>(value);
Justin Schuha26da272017-07-13 23:04:30602}
603
jschuh23a4b062016-12-02 02:55:08604template <typename L, typename R>
Peter Kastingbaaa6ac02024-11-13 01:02:51605 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
jschuh23a4b062016-12-02 02:55:08606struct IsLess {
Peter Kasting5e7c9bf22024-11-13 01:25:22607 using SumT = decltype(std::declval<L>() + std::declval<R>());
608 static constexpr bool Test(L lhs, R rhs) {
609 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
610 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
611 return l_range.IsUnderflow() || r_range.IsOverflow() ||
612 (l_range == r_range &&
613 static_cast<SumT>(lhs) < static_cast<SumT>(rhs));
jschuh23a4b062016-12-02 02:55:08614 }
615};
616
617template <typename L, typename R>
Peter Kastingbaaa6ac02024-11-13 01:02:51618 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
jschuh23a4b062016-12-02 02:55:08619struct IsLessOrEqual {
Peter Kasting5e7c9bf22024-11-13 01:25:22620 using SumT = decltype(std::declval<L>() + std::declval<R>());
621 static constexpr bool Test(L lhs, R rhs) {
622 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
623 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
624 return l_range.IsUnderflow() || r_range.IsOverflow() ||
625 (l_range == r_range &&
626 static_cast<SumT>(lhs) <= static_cast<SumT>(rhs));
jschuh23a4b062016-12-02 02:55:08627 }
628};
629
630template <typename L, typename R>
Peter Kastingbaaa6ac02024-11-13 01:02:51631 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
jschuh23a4b062016-12-02 02:55:08632struct IsGreater {
Peter Kasting5e7c9bf22024-11-13 01:25:22633 using SumT = decltype(std::declval<L>() + std::declval<R>());
634 static constexpr bool Test(L lhs, R rhs) {
635 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
636 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
637 return l_range.IsOverflow() || r_range.IsUnderflow() ||
638 (l_range == r_range &&
639 static_cast<SumT>(lhs) > static_cast<SumT>(rhs));
jschuh23a4b062016-12-02 02:55:08640 }
641};
642
643template <typename L, typename R>
Peter Kastingbaaa6ac02024-11-13 01:02:51644 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
jschuh23a4b062016-12-02 02:55:08645struct IsGreaterOrEqual {
Peter Kasting5e7c9bf22024-11-13 01:25:22646 using SumT = decltype(std::declval<L>() + std::declval<R>());
647 static constexpr bool Test(L lhs, R rhs) {
648 const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs);
649 const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs);
650 return l_range.IsOverflow() || r_range.IsUnderflow() ||
651 (l_range == r_range &&
652 static_cast<SumT>(lhs) >= static_cast<SumT>(rhs));
jschuh23a4b062016-12-02 02:55:08653 }
654};
655
656template <typename L, typename R>
Peter Kastingbaaa6ac02024-11-13 01:02:51657 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
jschuh23a4b062016-12-02 02:55:08658struct IsEqual {
Peter Kasting5e7c9bf22024-11-13 01:25:22659 using SumT = decltype(std::declval<L>() + std::declval<R>());
660 static constexpr bool Test(L lhs, R rhs) {
jschuh23a4b062016-12-02 02:55:08661 return DstRangeRelationToSrcRange<R>(lhs) ==
662 DstRangeRelationToSrcRange<L>(rhs) &&
Peter Kasting5e7c9bf22024-11-13 01:25:22663 static_cast<SumT>(lhs) == static_cast<SumT>(rhs);
jschuh23a4b062016-12-02 02:55:08664 }
665};
666
667template <typename L, typename R>
Peter Kastingbaaa6ac02024-11-13 01:02:51668 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
jschuh23a4b062016-12-02 02:55:08669struct IsNotEqual {
Peter Kasting5e7c9bf22024-11-13 01:25:22670 using SumT = decltype(std::declval<L>() + std::declval<R>());
671 static constexpr bool Test(L lhs, R rhs) {
jschuh23a4b062016-12-02 02:55:08672 return DstRangeRelationToSrcRange<R>(lhs) !=
673 DstRangeRelationToSrcRange<L>(rhs) ||
Peter Kasting5e7c9bf22024-11-13 01:25:22674 static_cast<SumT>(lhs) != static_cast<SumT>(rhs);
jschuh23a4b062016-12-02 02:55:08675 }
676};
677
678// These perform the actual math operations on the CheckedNumerics.
679// Binary arithmetic operations.
Peter Kasting5e7c9bf22024-11-13 01:25:22680template <template <typename, typename> typename C, typename L, typename R>
Peter Kastingbaaa6ac02024-11-13 01:02:51681 requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R>
Peter Kasting5e7c9bf22024-11-13 01:25:22682constexpr bool SafeCompare(L lhs, R rhs) {
Peter Kastingabcf60a2024-11-13 01:24:37683 using BigType = BigEnoughPromotion<L, R>;
684 return kIsBigEnoughPromotionContained<L, R>
jschuh23a4b062016-12-02 02:55:08685 // Force to a larger type for speed if both are contained.
Peter Kastingbaaa6ac02024-11-13 01:02:51686 ? C<BigType, BigType>::Test(static_cast<BigType>(lhs),
687 static_cast<BigType>(rhs))
jschuh23a4b062016-12-02 02:55:08688 // Let the template functions figure it out for mixed types.
689 : C<L, R>::Test(lhs, rhs);
jschuh6a2c67b92017-06-12 22:57:22690}
jschuh23a4b062016-12-02 02:55:08691
Justin Schuh5cc2abf2017-08-01 04:58:07692template <typename Dst, typename Src>
Peter Kastingabcf60a2024-11-13 01:24:37693inline constexpr bool kIsMaxInRangeForNumericType =
694 IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
695 std::numeric_limits<Src>::max());
Justin Schuh5cc2abf2017-08-01 04:58:07696
697template <typename Dst, typename Src>
Peter Kastingabcf60a2024-11-13 01:24:37698inline constexpr bool kIsMinInRangeForNumericType =
699 IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
700 std::numeric_limits<Src>::lowest());
Justin Schuh5cc2abf2017-08-01 04:58:07701
702template <typename Dst, typename Src>
Peter Kastingabcf60a2024-11-13 01:24:37703inline constexpr Dst kCommonMax =
704 kIsMaxInRangeForNumericType<Dst, Src>
705 ? static_cast<Dst>(std::numeric_limits<Src>::max())
706 : std::numeric_limits<Dst>::max();
Justin Schuh5cc2abf2017-08-01 04:58:07707
708template <typename Dst, typename Src>
Peter Kastingabcf60a2024-11-13 01:24:37709inline constexpr Dst kCommonMin =
710 kIsMinInRangeForNumericType<Dst, Src>
711 ? static_cast<Dst>(std::numeric_limits<Src>::lowest())
712 : std::numeric_limits<Dst>::lowest();
Justin Schuh5cc2abf2017-08-01 04:58:07713
714// This is a wrapper to generate return the max or min for a supplied type.
715// If the argument is false, the returned value is the maximum. If true the
716// returned value is the minimum.
717template <typename Dst, typename Src = Dst>
718constexpr Dst CommonMaxOrMin(bool is_min) {
Peter Kastingabcf60a2024-11-13 01:24:37719 return is_min ? kCommonMin<Dst, Src> : kCommonMax<Dst, Src>;
Justin Schuh5cc2abf2017-08-01 04:58:07720}
721
Peter Kasting134183e2024-11-13 01:22:39722} // namespace base::internal
[email protected]4efb2c32014-01-16 06:57:25723
danakj0a448602015-03-10 00:31:16724#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_