tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestCasting.cpp (17223B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/Casting.h"
      8 
      9 #include <stdint.h>
     10 #include <cstdint>
     11 #include <limits>
     12 #include <type_traits>
     13 #include <iostream>
     14 #include <tuple>
     15 #include <type_traits>
     16 
     17 using mozilla::AssertedCast;
     18 using mozilla::BitwiseCast;
     19 using mozilla::SaturatingCast;
     20 using mozilla::detail::IsInBounds;
     21 
     22 static const uint8_t floatMantissaBitsPlusOne = 24;
     23 static const uint8_t doubleMantissaBitsPlusOne = 53;
     24 
     25 template <typename Uint, typename Ulong, bool = (sizeof(Uint) == sizeof(Ulong))>
     26 struct UintUlongBitwiseCast;
     27 
     28 template <typename Uint, typename Ulong>
     29 struct UintUlongBitwiseCast<Uint, Ulong, true> {
     30  static void test() {
     31    MOZ_RELEASE_ASSERT(BitwiseCast<Ulong>(Uint(8675309)) == Ulong(8675309));
     32  }
     33 };
     34 
     35 template <typename Uint, typename Ulong>
     36 struct UintUlongBitwiseCast<Uint, Ulong, false> {
     37  static void test() {}
     38 };
     39 
     40 static void TestBitwiseCast() {
     41  MOZ_RELEASE_ASSERT(BitwiseCast<int>(int(8675309)) == int(8675309));
     42  UintUlongBitwiseCast<unsigned int, unsigned long>::test();
     43 }
     44 
     45 static void TestSameSize() {
     46  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(0))));
     47  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MIN))));
     48  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MAX))));
     49  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint16_t>(uint16_t(UINT16_MAX))));
     50  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int16_t>(uint16_t(0))));
     51  MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int16_t>(uint16_t(-1))));
     52  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(-1))));
     53  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint16_t>(int16_t(INT16_MAX))));
     54  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(INT16_MIN))));
     55  MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint32_t>(int32_t(INT32_MAX))));
     56  MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint32_t>(int32_t(INT32_MIN))));
     57 }
     58 
     59 static void TestToBiggerSize() {
     60  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(0))));
     61  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MIN))));
     62  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MAX))));
     63  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint32_t>(uint16_t(UINT16_MAX))));
     64  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(0))));
     65  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(-1))));
     66  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(-1))));
     67  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint32_t>(int16_t(INT16_MAX))));
     68  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(INT16_MIN))));
     69  MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint64_t>(int32_t(INT32_MAX))));
     70  MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint64_t>(int32_t(INT32_MIN))));
     71 }
     72 
     73 static void TestToSmallerSize() {
     74  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int8_t>(int16_t(0))));
     75  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MIN))));
     76  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MAX))));
     77  MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, uint8_t>(uint16_t(UINT16_MAX))));
     78  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int8_t>(uint16_t(0))));
     79  MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int8_t>(uint16_t(-1))));
     80  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(-1))));
     81  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MAX))));
     82  MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MIN))));
     83  MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MAX))));
     84  MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MIN))));
     85 
     86  // Boundary cases
     87  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) - 1)));
     88  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN))));
     89  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) + 1)));
     90  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) - 1)));
     91  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX))));
     92  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) + 1)));
     93 
     94  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(-1))));
     95  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(0))));
     96  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(1))));
     97  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) - 1)));
     98  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX))));
     99  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) + 1)));
    100 }
    101 
    102 template <typename In, typename Out>
    103 void checkBoundariesFloating(In aEpsilon = {}, Out aIntegerOffset = {}) {
    104  // Check the max value of the input float can't be represented as an integer.
    105  // This is true for all floating point and integer width.
    106  MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(std::numeric_limits<In>::max())));
    107  // Check that the max value of the integer, as a float, minus an offset that
    108  // depends on the magnitude, can be represented as an integer.
    109  MOZ_RELEASE_ASSERT((IsInBounds<In, Out>(
    110      static_cast<In>(std::numeric_limits<Out>::max() - aIntegerOffset))));
    111  // Check that the max value of the integer, plus a number that depends on the
    112  // magnitude of the number, can't be represented as this integer (because it
    113  // becomes too big).
    114  MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(
    115      aEpsilon + static_cast<In>(std::numeric_limits<Out>::max()))));
    116  if constexpr (std::is_signed_v<In>) {
    117    // Same for negative numbers.
    118    MOZ_RELEASE_ASSERT(
    119        (!IsInBounds<In, Out>(std::numeric_limits<In>::lowest())));
    120    MOZ_RELEASE_ASSERT((IsInBounds<In, Out>(
    121        static_cast<In>(std::numeric_limits<Out>::lowest()))));
    122    MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(
    123        static_cast<In>(std::numeric_limits<Out>::lowest()) - aEpsilon)));
    124  } else {
    125    // Check for negative floats and unsigned integer types.
    126    MOZ_RELEASE_ASSERT((!IsInBounds<In, Out>(static_cast<In>(-1))));
    127  }
    128 }
    129 
    130 void TestFloatConversion() {
    131  MOZ_RELEASE_ASSERT((!IsInBounds<uint64_t, float>(UINT64_MAX)));
    132  MOZ_RELEASE_ASSERT((!IsInBounds<uint32_t, float>(UINT32_MAX)));
    133  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, float>(UINT16_MAX)));
    134  MOZ_RELEASE_ASSERT((IsInBounds<uint8_t, float>(UINT8_MAX)));
    135 
    136  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, float>(INT64_MAX)));
    137  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, float>(INT64_MIN)));
    138  MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, float>(INT32_MAX)));
    139  MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, float>(INT32_MIN)));
    140  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, float>(INT16_MAX)));
    141  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, float>(INT16_MIN)));
    142  MOZ_RELEASE_ASSERT((IsInBounds<int8_t, float>(INT8_MAX)));
    143  MOZ_RELEASE_ASSERT((IsInBounds<int8_t, float>(INT8_MIN)));
    144 
    145  MOZ_RELEASE_ASSERT((!IsInBounds<uint64_t, double>(UINT64_MAX)));
    146  MOZ_RELEASE_ASSERT((IsInBounds<uint32_t, double>(UINT32_MAX)));
    147  MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, double>(UINT16_MAX)));
    148  MOZ_RELEASE_ASSERT((IsInBounds<uint8_t, double>(UINT8_MAX)));
    149 
    150  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, double>(INT64_MAX)));
    151  MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, double>(INT64_MIN)));
    152  MOZ_RELEASE_ASSERT((IsInBounds<int32_t, double>(INT32_MAX)));
    153  MOZ_RELEASE_ASSERT((IsInBounds<int32_t, double>(INT32_MIN)));
    154  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, double>(INT16_MAX)));
    155  MOZ_RELEASE_ASSERT((IsInBounds<int16_t, double>(INT16_MIN)));
    156  MOZ_RELEASE_ASSERT((IsInBounds<int8_t, double>(INT8_MAX)));
    157  MOZ_RELEASE_ASSERT((IsInBounds<int8_t, double>(INT8_MIN)));
    158 
    159  // Floor check
    160  MOZ_RELEASE_ASSERT((IsInBounds<float, uint64_t>(4.3)));
    161  MOZ_RELEASE_ASSERT((AssertedCast<uint64_t>(4.3f) == 4u));
    162  MOZ_RELEASE_ASSERT((IsInBounds<float, uint32_t>(4.3)));
    163  MOZ_RELEASE_ASSERT((AssertedCast<uint32_t>(4.3f) == 4u));
    164  MOZ_RELEASE_ASSERT((IsInBounds<float, uint16_t>(4.3)));
    165  MOZ_RELEASE_ASSERT((AssertedCast<uint16_t>(4.3f) == 4u));
    166  MOZ_RELEASE_ASSERT((IsInBounds<float, uint8_t>(4.3)));
    167  MOZ_RELEASE_ASSERT((AssertedCast<uint8_t>(4.3f) == 4u));
    168 
    169  MOZ_RELEASE_ASSERT((IsInBounds<float, int64_t>(4.3)));
    170  MOZ_RELEASE_ASSERT((AssertedCast<int64_t>(4.3f) == 4u));
    171  MOZ_RELEASE_ASSERT((IsInBounds<float, int32_t>(4.3)));
    172  MOZ_RELEASE_ASSERT((AssertedCast<int32_t>(4.3f) == 4u));
    173  MOZ_RELEASE_ASSERT((IsInBounds<float, int16_t>(4.3)));
    174  MOZ_RELEASE_ASSERT((AssertedCast<int16_t>(4.3f) == 4u));
    175  MOZ_RELEASE_ASSERT((IsInBounds<float, int8_t>(4.3)));
    176  MOZ_RELEASE_ASSERT((AssertedCast<int8_t>(4.3f) == 4u));
    177 
    178  MOZ_RELEASE_ASSERT((IsInBounds<float, int64_t>(-4.3)));
    179  MOZ_RELEASE_ASSERT((AssertedCast<int64_t>(-4.3f) == -4));
    180  MOZ_RELEASE_ASSERT((IsInBounds<float, int32_t>(-4.3)));
    181  MOZ_RELEASE_ASSERT((AssertedCast<int32_t>(-4.3f) == -4));
    182  MOZ_RELEASE_ASSERT((IsInBounds<float, int16_t>(-4.3)));
    183  MOZ_RELEASE_ASSERT((AssertedCast<int16_t>(-4.3f) == -4));
    184  MOZ_RELEASE_ASSERT((IsInBounds<float, int8_t>(-4.3)));
    185  MOZ_RELEASE_ASSERT((AssertedCast<int8_t>(-4.3f) == -4));
    186 
    187  // Bound check for float to unsigned integer conversion. The parameters are
    188  // espilons and offsets allowing to check boundaries, that depend on the
    189  // magnitude of the numbers.
    190  checkBoundariesFloating<double, uint64_t>(2049.);
    191  checkBoundariesFloating<double, uint32_t>(1.);
    192  checkBoundariesFloating<double, uint16_t>(1.);
    193  checkBoundariesFloating<double, uint8_t>(1.);
    194  // Large number because of the lack of precision of floats at this magnitude
    195  checkBoundariesFloating<float, uint64_t>(1.1e12f);
    196  checkBoundariesFloating<float, uint32_t>(1.f, 128u);
    197  checkBoundariesFloating<float, uint16_t>(1.f);
    198  checkBoundariesFloating<float, uint8_t>(1.f);
    199 
    200  checkBoundariesFloating<double, int64_t>(1025.);
    201  checkBoundariesFloating<double, int32_t>(1.);
    202  checkBoundariesFloating<double, int16_t>(1.);
    203  checkBoundariesFloating<double, int8_t>(1.);
    204  // Large number because of the lack of precision of floats at this magnitude
    205  checkBoundariesFloating<float, int64_t>(1.1e12f);
    206  checkBoundariesFloating<float, int32_t>(256.f, 64u);
    207  checkBoundariesFloating<float, int16_t>(1.f);
    208  checkBoundariesFloating<float, int8_t>(1.f);
    209 
    210  // Integer to floating point, boundary cases
    211  MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, float>(
    212      int64_t(std::pow(2, floatMantissaBitsPlusOne)) + 1)));
    213  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
    214      int64_t(std::pow(2, floatMantissaBitsPlusOne)))));
    215  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
    216      int64_t(std::pow(2, floatMantissaBitsPlusOne)) - 1)));
    217 
    218  MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, float>(
    219      int64_t(-std::pow(2, floatMantissaBitsPlusOne)) - 1)));
    220  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
    221      int64_t(-std::pow(2, floatMantissaBitsPlusOne)))));
    222  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, float>(
    223      int64_t(-std::pow(2, floatMantissaBitsPlusOne)) + 1)));
    224 
    225  MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(
    226      uint64_t(std::pow(2, doubleMantissaBitsPlusOne)) + 1)));
    227  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
    228      uint64_t(std::pow(2, doubleMantissaBitsPlusOne)))));
    229  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
    230      uint64_t(std::pow(2, doubleMantissaBitsPlusOne)) - 1)));
    231 
    232  MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(
    233      int64_t(-std::pow(2, doubleMantissaBitsPlusOne)) - 1)));
    234  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
    235      int64_t(-std::pow(2, doubleMantissaBitsPlusOne)))));
    236  MOZ_RELEASE_ASSERT((IsInBounds<int64_t, double>(
    237      int64_t(-std::pow(2, doubleMantissaBitsPlusOne)) + 1)));
    238 
    239  MOZ_RELEASE_ASSERT(!(IsInBounds<uint64_t, double>(UINT64_MAX)));
    240  MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(INT64_MAX)));
    241  MOZ_RELEASE_ASSERT(!(IsInBounds<int64_t, double>(INT64_MIN)));
    242 
    243  MOZ_RELEASE_ASSERT(
    244      !(IsInBounds<double, float>(std::numeric_limits<double>::max())));
    245  MOZ_RELEASE_ASSERT(
    246      !(IsInBounds<double, float>(-std::numeric_limits<double>::max())));
    247 }
    248 
    249 #define ASSERT_EQ(a, b)                                                     \
    250  if ((a) != (b)) {                                                         \
    251    std::cerr << __FILE__ << ":" << __LINE__ << " Actual: " << +(a) << ", " \
    252              << "expected: " << +(b) << std::endl;                         \
    253    MOZ_CRASH();                                                            \
    254  }
    255 
    256 #ifdef ENABLE_DEBUG_PRINT
    257 #  define DEBUG_PRINT(in, out) \
    258    std::cout << "\tIn: " << +in << ", " << "out: " << +out << std::endl;
    259 #else
    260 #  define DEBUG_PRINT(in, out)
    261 #endif
    262 
    263 template <typename In, typename Out>
    264 void TestTypePairImpl() {
    265  std::cout << __PRETTY_FUNCTION__ << std::endl;
    266  std::cout << std::fixed;
    267  // Test casting infinities to integer works
    268  if constexpr (std::is_floating_point_v<In> &&
    269                !std::is_floating_point_v<Out>) {
    270    Out v = SaturatingCast<Out>(std::numeric_limits<In>::infinity());
    271    ASSERT_EQ(v, std::numeric_limits<Out>::max());
    272    v = SaturatingCast<Out>(-std::numeric_limits<In>::infinity());
    273    ASSERT_EQ(v, std::numeric_limits<Out>::lowest());
    274  }
    275  // Saturation of a floating point value that is infinity is infinity
    276  if constexpr (std::is_floating_point_v<Out> && std::is_floating_point_v<In>) {
    277    In in = std::numeric_limits<In>::infinity();
    278    Out v = SaturatingCast<Out>(in);
    279    DEBUG_PRINT(in, v);
    280    ASSERT_EQ(v, std::numeric_limits<Out>::infinity());
    281    in = -std::numeric_limits<In>::infinity();
    282    v = SaturatingCast<In>(in);
    283    DEBUG_PRINT(in, v);
    284    ASSERT_EQ(v, -std::numeric_limits<Out>::infinity());
    285    return;
    286  } else {
    287    if constexpr (sizeof(In) > sizeof(Out) && std::is_integral_v<Out>) {
    288      // Test with a value just outside the range of the output type
    289      In in = static_cast<In>(std::numeric_limits<Out>::max()) + 1ull;
    290      Out v = SaturatingCast<Out>(in);
    291      DEBUG_PRINT(in, v);
    292      ASSERT_EQ(v, std::numeric_limits<Out>::max());
    293 
    294      if (std::is_signed_v<In>) {
    295        // Test with a value just below the range of the output type
    296        Out lowest = std::numeric_limits<Out>::lowest();
    297        in = static_cast<In>(lowest) - 1;
    298        v = SaturatingCast<Out>(in);
    299        DEBUG_PRINT(in, v);
    300        if constexpr (std::is_signed_v<In> && !std::is_signed_v<Out>) {
    301          ASSERT_EQ(v, 0);
    302        } else {
    303          ASSERT_EQ(v, std::numeric_limits<Out>::lowest());
    304        }
    305      }
    306    } else if constexpr (std::is_integral_v<In> && std::is_integral_v<Out> &&
    307                         sizeof(In) == sizeof(Out) && !std::is_signed_v<In> &&
    308                         std::is_signed_v<Out>) {
    309      // Test that max uintXX_t saturates to max intXX_t
    310      In in = static_cast<In>(std::numeric_limits<Out>::max()) + 1;
    311      Out v = SaturatingCast<Out>(in);
    312      DEBUG_PRINT(in, v);
    313      ASSERT_EQ(v, std::numeric_limits<Out>::max());
    314    }
    315 
    316    // SaturatingCast of zero is zero
    317    In in = static_cast<In>(0);
    318    Out v = SaturatingCast<Out>(in);
    319    DEBUG_PRINT(in, v);
    320    ASSERT_EQ(v, 0);
    321 
    322    if constexpr (sizeof(In) >= sizeof(Out) && std::is_signed_v<Out> &&
    323                  std::is_signed_v<In>) {
    324      // Test with a value within the range of the output type
    325      In in = static_cast<In>(std::numeric_limits<Out>::max() / 2);
    326      Out v = SaturatingCast<Out>(in);
    327      DEBUG_PRINT(in, v);
    328      ASSERT_EQ(v, in);
    329 
    330      // Test with a negative value within the range of the output type
    331      in = static_cast<In>(std::numeric_limits<Out>::lowest() / 2);
    332      v = SaturatingCast<Out>(in);
    333      DEBUG_PRINT(in, v);
    334      ASSERT_EQ(v, in);
    335    }
    336  }
    337 }
    338 
    339 template <typename In, typename Out>
    340 void TestTypePair() {
    341  constexpr bool fromFloat = std::is_floating_point_v<In>;
    342  constexpr bool toFloat = std::is_floating_point_v<Out>;
    343  // Don't test casting to the same type
    344  if constexpr (!std::is_same_v<In, Out>) {
    345    if constexpr ((fromFloat && !toFloat) || (!fromFloat && !toFloat)) {
    346      TestTypePairImpl<In, Out>();
    347    }
    348  }
    349 }
    350 
    351 template <typename T, typename... Ts>
    352 void for_each_type_pair(std::tuple<T, Ts...>) {
    353  (TestTypePair<T, Ts>(), ...);
    354  (TestTypePair<Ts, T>(), ...);
    355  if constexpr (sizeof...(Ts) > 1) {
    356    for_each_type_pair(std::tuple<Ts...>{});
    357  }
    358 }
    359 
    360 template <typename... Args>
    361 void TestSaturatingCastImpl() {
    362  for_each_type_pair(std::tuple<Args...>{});
    363 }
    364 
    365 template <typename T, typename... Ts>
    366 void TestFirstToOthers() {
    367  (TestTypePair<T, Ts>(), ...);
    368 }
    369 
    370 void TestSaturatingCast() {
    371  // Each integer type against every other
    372  TestSaturatingCastImpl<short, int, long, int8_t, uint8_t, int16_t, uint16_t,
    373                         int32_t, uint32_t, int64_t, uint64_t>();
    374 
    375  // Floating point types to every integer type
    376  TestFirstToOthers<float, short, int, long, int8_t, uint8_t, int16_t, uint16_t,
    377                    int32_t, uint32_t, int64_t, uint64_t>();
    378  TestFirstToOthers<double, short, int, long, int8_t, uint8_t, int16_t,
    379                    uint16_t, int32_t, uint32_t, int64_t, uint64_t>();
    380 }
    381 
    382 int main() {
    383  TestBitwiseCast();
    384 
    385  TestSameSize();
    386  TestToBiggerSize();
    387  TestToSmallerSize();
    388  TestFloatConversion();
    389  TestSaturatingCast();
    390 
    391  return 0;
    392 }