1 // 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 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
7
8 #include <stdint.h>
9
10 #include <limits>
11 #include <type_traits>
12
13 #if defined(__GNUC__) || defined(__clang__)
14 #define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
15 #define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
16 #else
17 #define BASE_NUMERICS_LIKELY(x) (x)
18 #define BASE_NUMERICS_UNLIKELY(x) (x)
19 #endif
20
21 namespace base {
22 namespace internal {
23
24 // The std library doesn't provide a binary max_exponent for integers, however
25 // we can compute an analog using std::numeric_limits<>::digits.
26 template <typename NumericType>
27 struct MaxExponent {
28 static const int value = std::is_floating_point<NumericType>::value
29 ? std::numeric_limits<NumericType>::max_exponent
30 : std::numeric_limits<NumericType>::digits + 1;
31 };
32
33 // The number of bits (including the sign) in an integer. Eliminates sizeof
34 // hacks.
35 template <typename NumericType>
36 struct IntegerBitsPlusSign {
37 static const int value = std::numeric_limits<NumericType>::digits +
38 std::is_signed<NumericType>::value;
39 };
40
41 // Helper templates for integer manipulations.
42
43 template <typename Integer>
44 struct PositionOfSignBit {
45 static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
46 };
47
48 // Determines if a numeric value is negative without throwing compiler
49 // warnings on: unsigned(value) < 0.
50 template <typename T,
51 typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
IsValueNegative(T value)52 constexpr bool IsValueNegative(T value) {
53 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
54 return value < 0;
55 }
56
57 template <typename T,
58 typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
IsValueNegative(T)59 constexpr bool IsValueNegative(T) {
60 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
61 return false;
62 }
63
64 // This performs a fast negation, returning a signed value. It works on unsigned
65 // arguments, but probably doesn't do what you want for any unsigned value
66 // larger than max / 2 + 1 (i.e. signed min cast to unsigned).
67 template <typename T>
ConditionalNegate(T x,bool is_negative)68 constexpr typename std::make_signed<T>::type ConditionalNegate(
69 T x,
70 bool is_negative) {
71 static_assert(std::is_integral<T>::value, "Type must be integral");
72 using SignedT = typename std::make_signed<T>::type;
73 using UnsignedT = typename std::make_unsigned<T>::type;
74 return static_cast<SignedT>(
75 (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
76 }
77
78 // This performs a safe, absolute value via unsigned overflow.
79 template <typename T>
SafeUnsignedAbs(T value)80 constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
81 static_assert(std::is_integral<T>::value, "Type must be integral");
82 using UnsignedT = typename std::make_unsigned<T>::type;
83 return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
84 : static_cast<UnsignedT>(value);
85 }
86
87 // This allows us to switch paths on known compile-time constants.
88 #if defined(__clang__) || defined(__GNUC__)
CanDetectCompileTimeConstant()89 constexpr bool CanDetectCompileTimeConstant() {
90 return true;
91 }
92 template <typename T>
IsCompileTimeConstant(const T v)93 constexpr bool IsCompileTimeConstant(const T v) {
94 return __builtin_constant_p(v);
95 }
96 #else
CanDetectCompileTimeConstant()97 constexpr bool CanDetectCompileTimeConstant() {
98 return false;
99 }
100 template <typename T>
IsCompileTimeConstant(const T)101 constexpr bool IsCompileTimeConstant(const T) {
102 return false;
103 }
104 #endif
105 template <typename T>
MustTreatAsConstexpr(const T v)106 constexpr bool MustTreatAsConstexpr(const T v) {
107 // Either we can't detect a compile-time constant, and must always use the
108 // constexpr path, or we know we have a compile-time constant.
109 return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
110 }
111
112 // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
113 // Also used in a constexpr template to trigger a compilation failure on
114 // an error condition.
115 struct CheckOnFailure {
116 template <typename T>
HandleFailureCheckOnFailure117 static T HandleFailure() {
118 #if defined(_MSC_VER)
119 __debugbreak();
120 #elif defined(__GNUC__) || defined(__clang__)
121 __builtin_trap();
122 #else
123 ((void)(*(volatile char*)0 = 0));
124 #endif
125 return T();
126 }
127 };
128
129 enum IntegerRepresentation {
130 INTEGER_REPRESENTATION_UNSIGNED,
131 INTEGER_REPRESENTATION_SIGNED
132 };
133
134 // A range for a given nunmeric Src type is contained for a given numeric Dst
135 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
136 // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
137 // We implement this as template specializations rather than simple static
138 // comparisons to ensure type correctness in our comparisons.
139 enum NumericRangeRepresentation {
140 NUMERIC_RANGE_NOT_CONTAINED,
141 NUMERIC_RANGE_CONTAINED
142 };
143
144 // Helper templates to statically determine if our destination type can contain
145 // maximum and minimum values represented by the source type.
146
147 template <typename Dst,
148 typename Src,
149 IntegerRepresentation DstSign = std::is_signed<Dst>::value
150 ? INTEGER_REPRESENTATION_SIGNED
151 : INTEGER_REPRESENTATION_UNSIGNED,
152 IntegerRepresentation SrcSign = std::is_signed<Src>::value
153 ? INTEGER_REPRESENTATION_SIGNED
154 : INTEGER_REPRESENTATION_UNSIGNED>
155 struct StaticDstRangeRelationToSrcRange;
156
157 // Same sign: Dst is guaranteed to contain Src only if its range is equal or
158 // larger.
159 template <typename Dst, typename Src, IntegerRepresentation Sign>
160 struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
161 static const NumericRangeRepresentation value =
162 MaxExponent<Dst>::value >= MaxExponent<Src>::value
163 ? NUMERIC_RANGE_CONTAINED
164 : NUMERIC_RANGE_NOT_CONTAINED;
165 };
166
167 // Unsigned to signed: Dst is guaranteed to contain source only if its range is
168 // larger.
169 template <typename Dst, typename Src>
170 struct StaticDstRangeRelationToSrcRange<Dst,
171 Src,
172 INTEGER_REPRESENTATION_SIGNED,
173 INTEGER_REPRESENTATION_UNSIGNED> {
174 static const NumericRangeRepresentation value =
175 MaxExponent<Dst>::value > MaxExponent<Src>::value
176 ? NUMERIC_RANGE_CONTAINED
177 : NUMERIC_RANGE_NOT_CONTAINED;
178 };
179
180 // Signed to unsigned: Dst cannot be statically determined to contain Src.
181 template <typename Dst, typename Src>
182 struct StaticDstRangeRelationToSrcRange<Dst,
183 Src,
184 INTEGER_REPRESENTATION_UNSIGNED,
185 INTEGER_REPRESENTATION_SIGNED> {
186 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
187 };
188
189 // This class wraps the range constraints as separate booleans so the compiler
190 // can identify constants and eliminate unused code paths.
191 class RangeCheck {
192 public:
193 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
194 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
195 constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
196 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
197 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
198 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
199 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
200 constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
201 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
202 constexpr bool operator==(const RangeCheck rhs) const {
203 return is_underflow_ == rhs.is_underflow_ &&
204 is_overflow_ == rhs.is_overflow_;
205 }
206 constexpr bool operator!=(const RangeCheck rhs) const {
207 return !(*this == rhs);
208 }
209
210 private:
211 // Do not change the order of these member variables. The integral conversion
212 // optimization depends on this exact order.
213 const bool is_underflow_;
214 const bool is_overflow_;
215 };
216
217 // The following helper template addresses a corner case in range checks for
218 // conversion from a floating-point type to an integral type of smaller range
219 // but larger precision (e.g. float -> unsigned). The problem is as follows:
220 // 1. Integral maximum is always one less than a power of two, so it must be
221 // truncated to fit the mantissa of the floating point. The direction of
222 // rounding is implementation defined, but by default it's always IEEE
223 // floats, which round to nearest and thus result in a value of larger
224 // magnitude than the integral value.
225 // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
226 // // is 4294967295u.
227 // 2. If the floating point value is equal to the promoted integral maximum
228 // value, a range check will erroneously pass.
229 // Example: (4294967296f <= 4294967295u) // This is true due to a precision
230 // // loss in rounding up to float.
231 // 3. When the floating point value is then converted to an integral, the
232 // resulting value is out of range for the target integral type and
233 // thus is implementation defined.
234 // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
235 // To fix this bug we manually truncate the maximum value when the destination
236 // type is an integral of larger precision than the source floating-point type,
237 // such that the resulting maximum is represented exactly as a floating point.
238 template <typename Dst, typename Src, template <typename> class Bounds>
239 struct NarrowingRange {
240 using SrcLimits = std::numeric_limits<Src>;
241 using DstLimits = typename std::numeric_limits<Dst>;
242
243 // Computes the mask required to make an accurate comparison between types.
244 static const int kShift =
245 (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
246 SrcLimits::digits < DstLimits::digits)
247 ? (DstLimits::digits - SrcLimits::digits)
248 : 0;
249 template <
250 typename T,
251 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
252
253 // Masks out the integer bits that are beyond the precision of the
254 // intermediate type used for comparison.
255 static constexpr T Adjust(T value) {
256 static_assert(std::is_same<T, Dst>::value, "");
257 static_assert(kShift < DstLimits::digits, "");
258 return static_cast<T>(
259 ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
260 IsValueNegative(value)));
261 }
262
263 template <typename T,
264 typename std::enable_if<std::is_floating_point<T>::value>::type* =
265 nullptr>
266 static constexpr T Adjust(T value) {
267 static_assert(std::is_same<T, Dst>::value, "");
268 static_assert(kShift == 0, "");
269 return value;
270 }
271
272 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
273 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
274 };
275
276 template <typename Dst,
277 typename Src,
278 template <typename> class Bounds,
279 IntegerRepresentation DstSign = std::is_signed<Dst>::value
280 ? INTEGER_REPRESENTATION_SIGNED
281 : INTEGER_REPRESENTATION_UNSIGNED,
282 IntegerRepresentation SrcSign = std::is_signed<Src>::value
283 ? INTEGER_REPRESENTATION_SIGNED
284 : INTEGER_REPRESENTATION_UNSIGNED,
285 NumericRangeRepresentation DstRange =
286 StaticDstRangeRelationToSrcRange<Dst, Src>::value>
287 struct DstRangeRelationToSrcRangeImpl;
288
289 // The following templates are for ranges that must be verified at runtime. We
290 // split it into checks based on signedness to avoid confusing casts and
291 // compiler warnings on signed an unsigned comparisons.
292
293 // Same sign narrowing: The range is contained for normal limits.
294 template <typename Dst,
295 typename Src,
296 template <typename> class Bounds,
297 IntegerRepresentation DstSign,
298 IntegerRepresentation SrcSign>
299 struct DstRangeRelationToSrcRangeImpl<Dst,
300 Src,
301 Bounds,
302 DstSign,
303 SrcSign,
304 NUMERIC_RANGE_CONTAINED> {
305 static constexpr RangeCheck Check(Src value) {
306 using SrcLimits = std::numeric_limits<Src>;
307 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
308 return RangeCheck(
309 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
310 static_cast<Dst>(value) >= DstLimits::lowest(),
311 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
312 static_cast<Dst>(value) <= DstLimits::max());
313 }
314 };
315
316 // Signed to signed narrowing: Both the upper and lower boundaries may be
317 // exceeded for standard limits.
318 template <typename Dst, typename Src, template <typename> class Bounds>
319 struct DstRangeRelationToSrcRangeImpl<Dst,
320 Src,
321 Bounds,
322 INTEGER_REPRESENTATION_SIGNED,
323 INTEGER_REPRESENTATION_SIGNED,
324 NUMERIC_RANGE_NOT_CONTAINED> {
325 static constexpr RangeCheck Check(Src value) {
326 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
327 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
328 }
329 };
330
331 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
332 // standard limits.
333 template <typename Dst, typename Src, template <typename> class Bounds>
334 struct DstRangeRelationToSrcRangeImpl<Dst,
335 Src,
336 Bounds,
337 INTEGER_REPRESENTATION_UNSIGNED,
338 INTEGER_REPRESENTATION_UNSIGNED,
339 NUMERIC_RANGE_NOT_CONTAINED> {
340 static constexpr RangeCheck Check(Src value) {
341 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
342 return RangeCheck(
343 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
344 value <= DstLimits::max());
345 }
346 };
347
348 // Unsigned to signed: Only the upper bound can be exceeded for standard limits.
349 template <typename Dst, typename Src, template <typename> class Bounds>
350 struct DstRangeRelationToSrcRangeImpl<Dst,
351 Src,
352 Bounds,
353 INTEGER_REPRESENTATION_SIGNED,
354 INTEGER_REPRESENTATION_UNSIGNED,
355 NUMERIC_RANGE_NOT_CONTAINED> {
356 static constexpr RangeCheck Check(Src value) {
357 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
358 using Promotion = decltype(Src() + Dst());
359 return RangeCheck(DstLimits::lowest() <= Dst(0) ||
360 static_cast<Promotion>(value) >=
361 static_cast<Promotion>(DstLimits::lowest()),
362 static_cast<Promotion>(value) <=
363 static_cast<Promotion>(DstLimits::max()));
364 }
365 };
366
367 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
368 // and any negative value exceeds the lower boundary for standard limits.
369 template <typename Dst, typename Src, template <typename> class Bounds>
370 struct DstRangeRelationToSrcRangeImpl<Dst,
371 Src,
372 Bounds,
373 INTEGER_REPRESENTATION_UNSIGNED,
374 INTEGER_REPRESENTATION_SIGNED,
375 NUMERIC_RANGE_NOT_CONTAINED> {
376 static constexpr RangeCheck Check(Src value) {
377 using SrcLimits = std::numeric_limits<Src>;
378 using DstLimits = NarrowingRange<Dst, Src, Bounds>;
379 using Promotion = decltype(Src() + Dst());
380 return RangeCheck(
381 value >= Src(0) && (DstLimits::lowest() == 0 ||
382 static_cast<Dst>(value) >= DstLimits::lowest()),
383 static_cast<Promotion>(SrcLimits::max()) <=
384 static_cast<Promotion>(DstLimits::max()) ||
385 static_cast<Promotion>(value) <=
386 static_cast<Promotion>(DstLimits::max()));
387 }
388 };
389
390 // Simple wrapper for statically checking if a type's range is contained.
391 template <typename Dst, typename Src>
392 struct IsTypeInRangeForNumericType {
393 static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
394 NUMERIC_RANGE_CONTAINED;
395 };
396
397 template <typename Dst,
398 template <typename> class Bounds = std::numeric_limits,
399 typename Src>
400 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
401 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
402 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
403 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
404 return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
405 }
406
407 // Integer promotion templates used by the portable checked integer arithmetic.
408 template <size_t Size, bool IsSigned>
409 struct IntegerForDigitsAndSign;
410
411 #define INTEGER_FOR_DIGITS_AND_SIGN(I) \
412 template <> \
413 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
414 std::is_signed<I>::value> { \
415 using type = I; \
416 }
417
418 INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
419 INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
420 INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
421 INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
422 INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
423 INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
424 INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
425 INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
426 #undef INTEGER_FOR_DIGITS_AND_SIGN
427
428 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
429 // support 128-bit math, then the ArithmeticPromotion template below will need
430 // to be updated (or more likely replaced with a decltype expression).
431 static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
432 "Max integer size not supported for this toolchain.");
433
434 template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
435 struct TwiceWiderInteger {
436 using type =
437 typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
438 IsSigned>::type;
439 };
440
441 enum ArithmeticPromotionCategory {
442 LEFT_PROMOTION, // Use the type of the left-hand argument.
443 RIGHT_PROMOTION // Use the type of the right-hand argument.
444 };
445
446 // Determines the type that can represent the largest positive value.
447 template <typename Lhs,
448 typename Rhs,
449 ArithmeticPromotionCategory Promotion =
450 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
451 ? LEFT_PROMOTION
452 : RIGHT_PROMOTION>
453 struct MaxExponentPromotion;
454
455 template <typename Lhs, typename Rhs>
456 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
457 using type = Lhs;
458 };
459
460 template <typename Lhs, typename Rhs>
461 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
462 using type = Rhs;
463 };
464
465 // Determines the type that can represent the lowest arithmetic value.
466 template <typename Lhs,
467 typename Rhs,
468 ArithmeticPromotionCategory Promotion =
469 std::is_signed<Lhs>::value
470 ? (std::is_signed<Rhs>::value
471 ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
472 ? LEFT_PROMOTION
473 : RIGHT_PROMOTION)
474 : LEFT_PROMOTION)
475 : (std::is_signed<Rhs>::value
476 ? RIGHT_PROMOTION
477 : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
478 ? LEFT_PROMOTION
479 : RIGHT_PROMOTION))>
480 struct LowestValuePromotion;
481
482 template <typename Lhs, typename Rhs>
483 struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
484 using type = Lhs;
485 };
486
487 template <typename Lhs, typename Rhs>
488 struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
489 using type = Rhs;
490 };
491
492 // Determines the type that is best able to represent an arithmetic result.
493 template <
494 typename Lhs,
495 typename Rhs = Lhs,
496 bool is_intmax_type =
497 std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
498 IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
499 value == IntegerBitsPlusSign<intmax_t>::value,
500 bool is_max_exponent =
501 StaticDstRangeRelationToSrcRange<
502 typename MaxExponentPromotion<Lhs, Rhs>::type,
503 Lhs>::value ==
504 NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
505 typename MaxExponentPromotion<Lhs, Rhs>::type,
506 Rhs>::value == NUMERIC_RANGE_CONTAINED>
507 struct BigEnoughPromotion;
508
509 // The side with the max exponent is big enough.
510 template <typename Lhs, typename Rhs, bool is_intmax_type>
511 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
512 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
513 static const bool is_contained = true;
514 };
515
516 // We can use a twice wider type to fit.
517 template <typename Lhs, typename Rhs>
518 struct BigEnoughPromotion<Lhs, Rhs, false, false> {
519 using type =
520 typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
521 std::is_signed<Lhs>::value ||
522 std::is_signed<Rhs>::value>::type;
523 static const bool is_contained = true;
524 };
525
526 // No type is large enough.
527 template <typename Lhs, typename Rhs>
528 struct BigEnoughPromotion<Lhs, Rhs, true, false> {
529 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
530 static const bool is_contained = false;
531 };
532
533 // We can statically check if operations on the provided types can wrap, so we
534 // can skip the checked operations if they're not needed. So, for an integer we
535 // care if the destination type preserves the sign and is twice the width of
536 // the source.
537 template <typename T, typename Lhs, typename Rhs = Lhs>
538 struct IsIntegerArithmeticSafe {
539 static const bool value =
540 !std::is_floating_point<T>::value &&
541 !std::is_floating_point<Lhs>::value &&
542 !std::is_floating_point<Rhs>::value &&
543 std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
544 IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
545 std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
546 IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
547 };
548
549 // Promotes to a type that can represent any possible result of a binary
550 // arithmetic operation with the source types.
551 template <typename Lhs,
552 typename Rhs,
553 bool is_promotion_possible = IsIntegerArithmeticSafe<
554 typename std::conditional<std::is_signed<Lhs>::value ||
555 std::is_signed<Rhs>::value,
556 intmax_t,
557 uintmax_t>::type,
558 typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
559 struct FastIntegerArithmeticPromotion;
560
561 template <typename Lhs, typename Rhs>
562 struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
563 using type =
564 typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
565 std::is_signed<Lhs>::value ||
566 std::is_signed<Rhs>::value>::type;
567 static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
568 static const bool is_contained = true;
569 };
570
571 template <typename Lhs, typename Rhs>
572 struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
573 using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
574 static const bool is_contained = false;
575 };
576
577 // Extracts the underlying type from an enum.
578 template <typename T, bool is_enum = std::is_enum<T>::value>
579 struct ArithmeticOrUnderlyingEnum;
580
581 template <typename T>
582 struct ArithmeticOrUnderlyingEnum<T, true> {
583 using type = typename std::underlying_type<T>::type;
584 static const bool value = std::is_arithmetic<type>::value;
585 };
586
587 template <typename T>
588 struct ArithmeticOrUnderlyingEnum<T, false> {
589 using type = T;
590 static const bool value = std::is_arithmetic<type>::value;
591 };
592
593 // The following are helper templates used in the CheckedNumeric class.
594 template <typename T>
595 class CheckedNumeric;
596
597 template <typename T>
598 class ClampedNumeric;
599
600 template <typename T>
601 class StrictNumeric;
602
603 // Used to treat CheckedNumeric and arithmetic underlying types the same.
604 template <typename T>
605 struct UnderlyingType {
606 using type = typename ArithmeticOrUnderlyingEnum<T>::type;
607 static const bool is_numeric = std::is_arithmetic<type>::value;
608 static const bool is_checked = false;
609 static const bool is_clamped = false;
610 static const bool is_strict = false;
611 };
612
613 template <typename T>
614 struct UnderlyingType<CheckedNumeric<T>> {
615 using type = T;
616 static const bool is_numeric = true;
617 static const bool is_checked = true;
618 static const bool is_clamped = false;
619 static const bool is_strict = false;
620 };
621
622 template <typename T>
623 struct UnderlyingType<ClampedNumeric<T>> {
624 using type = T;
625 static const bool is_numeric = true;
626 static const bool is_checked = false;
627 static const bool is_clamped = true;
628 static const bool is_strict = false;
629 };
630
631 template <typename T>
632 struct UnderlyingType<StrictNumeric<T>> {
633 using type = T;
634 static const bool is_numeric = true;
635 static const bool is_checked = false;
636 static const bool is_clamped = false;
637 static const bool is_strict = true;
638 };
639
640 template <typename L, typename R>
641 struct IsCheckedOp {
642 static const bool value =
643 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
644 (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
645 };
646
647 template <typename L, typename R>
648 struct IsClampedOp {
649 static const bool value =
650 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
651 (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
652 !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
653 };
654
655 template <typename L, typename R>
656 struct IsStrictOp {
657 static const bool value =
658 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
659 (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
660 !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
661 !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
662 };
663
664 // as_signed<> returns the supplied integral value (or integral castable
665 // Numeric template) cast as a signed integral of equivalent precision.
666 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
667 template <typename Src>
668 constexpr typename std::make_signed<
669 typename base::internal::UnderlyingType<Src>::type>::type
670 as_signed(const Src value) {
671 static_assert(std::is_integral<decltype(as_signed(value))>::value,
672 "Argument must be a signed or unsigned integer type.");
673 return static_cast<decltype(as_signed(value))>(value);
674 }
675
676 // as_unsigned<> returns the supplied integral value (or integral castable
677 // Numeric template) cast as an unsigned integral of equivalent precision.
678 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
679 template <typename Src>
680 constexpr typename std::make_unsigned<
681 typename base::internal::UnderlyingType<Src>::type>::type
682 as_unsigned(const Src value) {
683 static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
684 "Argument must be a signed or unsigned integer type.");
685 return static_cast<decltype(as_unsigned(value))>(value);
686 }
687
688 template <typename L, typename R>
689 constexpr bool IsLessImpl(const L lhs,
690 const R rhs,
691 const RangeCheck l_range,
692 const RangeCheck r_range) {
693 return l_range.IsUnderflow() || r_range.IsOverflow() ||
694 (l_range == r_range &&
695 static_cast<decltype(lhs + rhs)>(lhs) <
696 static_cast<decltype(lhs + rhs)>(rhs));
697 }
698
699 template <typename L, typename R>
700 struct IsLess {
701 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
702 "Types must be numeric.");
703 static constexpr bool Test(const L lhs, const R rhs) {
704 return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
705 DstRangeRelationToSrcRange<L>(rhs));
706 }
707 };
708
709 template <typename L, typename R>
710 constexpr bool IsLessOrEqualImpl(const L lhs,
711 const R rhs,
712 const RangeCheck l_range,
713 const RangeCheck r_range) {
714 return l_range.IsUnderflow() || r_range.IsOverflow() ||
715 (l_range == r_range &&
716 static_cast<decltype(lhs + rhs)>(lhs) <=
717 static_cast<decltype(lhs + rhs)>(rhs));
718 }
719
720 template <typename L, typename R>
721 struct IsLessOrEqual {
722 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
723 "Types must be numeric.");
724 static constexpr bool Test(const L lhs, const R rhs) {
725 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
726 DstRangeRelationToSrcRange<L>(rhs));
727 }
728 };
729
730 template <typename L, typename R>
731 constexpr bool IsGreaterImpl(const L lhs,
732 const R rhs,
733 const RangeCheck l_range,
734 const RangeCheck r_range) {
735 return l_range.IsOverflow() || r_range.IsUnderflow() ||
736 (l_range == r_range &&
737 static_cast<decltype(lhs + rhs)>(lhs) >
738 static_cast<decltype(lhs + rhs)>(rhs));
739 }
740
741 template <typename L, typename R>
742 struct IsGreater {
743 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
744 "Types must be numeric.");
745 static constexpr bool Test(const L lhs, const R rhs) {
746 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
747 DstRangeRelationToSrcRange<L>(rhs));
748 }
749 };
750
751 template <typename L, typename R>
752 constexpr bool IsGreaterOrEqualImpl(const L lhs,
753 const R rhs,
754 const RangeCheck l_range,
755 const RangeCheck r_range) {
756 return l_range.IsOverflow() || r_range.IsUnderflow() ||
757 (l_range == r_range &&
758 static_cast<decltype(lhs + rhs)>(lhs) >=
759 static_cast<decltype(lhs + rhs)>(rhs));
760 }
761
762 template <typename L, typename R>
763 struct IsGreaterOrEqual {
764 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
765 "Types must be numeric.");
766 static constexpr bool Test(const L lhs, const R rhs) {
767 return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
768 DstRangeRelationToSrcRange<L>(rhs));
769 }
770 };
771
772 template <typename L, typename R>
773 struct IsEqual {
774 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
775 "Types must be numeric.");
776 static constexpr bool Test(const L lhs, const R rhs) {
777 return DstRangeRelationToSrcRange<R>(lhs) ==
778 DstRangeRelationToSrcRange<L>(rhs) &&
779 static_cast<decltype(lhs + rhs)>(lhs) ==
780 static_cast<decltype(lhs + rhs)>(rhs);
781 }
782 };
783
784 template <typename L, typename R>
785 struct IsNotEqual {
786 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
787 "Types must be numeric.");
788 static constexpr bool Test(const L lhs, const R rhs) {
789 return DstRangeRelationToSrcRange<R>(lhs) !=
790 DstRangeRelationToSrcRange<L>(rhs) ||
791 static_cast<decltype(lhs + rhs)>(lhs) !=
792 static_cast<decltype(lhs + rhs)>(rhs);
793 }
794 };
795
796 // These perform the actual math operations on the CheckedNumerics.
797 // Binary arithmetic operations.
798 template <template <typename, typename> class C, typename L, typename R>
799 constexpr bool SafeCompare(const L lhs, const R rhs) {
800 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
801 "Types must be numeric.");
802 using Promotion = BigEnoughPromotion<L, R>;
803 using BigType = typename Promotion::type;
804 return Promotion::is_contained
805 // Force to a larger type for speed if both are contained.
806 ? C<BigType, BigType>::Test(
807 static_cast<BigType>(static_cast<L>(lhs)),
808 static_cast<BigType>(static_cast<R>(rhs)))
809 // Let the template functions figure it out for mixed types.
810 : C<L, R>::Test(lhs, rhs);
811 }
812
813 template <typename Dst, typename Src>
814 constexpr bool IsMaxInRangeForNumericType() {
815 return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
816 std::numeric_limits<Src>::max());
817 }
818
819 template <typename Dst, typename Src>
820 constexpr bool IsMinInRangeForNumericType() {
821 return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
822 std::numeric_limits<Src>::lowest());
823 }
824
825 template <typename Dst, typename Src>
826 constexpr Dst CommonMax() {
827 return !IsMaxInRangeForNumericType<Dst, Src>()
828 ? Dst(std::numeric_limits<Dst>::max())
829 : Dst(std::numeric_limits<Src>::max());
830 }
831
832 template <typename Dst, typename Src>
833 constexpr Dst CommonMin() {
834 return !IsMinInRangeForNumericType<Dst, Src>()
835 ? Dst(std::numeric_limits<Dst>::lowest())
836 : Dst(std::numeric_limits<Src>::lowest());
837 }
838
839 // This is a wrapper to generate return the max or min for a supplied type.
840 // If the argument is false, the returned value is the maximum. If true the
841 // returned value is the minimum.
842 template <typename Dst, typename Src = Dst>
843 constexpr Dst CommonMaxOrMin(bool is_min) {
844 return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
845 }
846
847 } // namespace internal
848 } // namespace base
849
850 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
851