chromium/third_party/openscreen/src/util/saturate_cast.h

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UTIL_SATURATE_CAST_H_
#define UTIL_SATURATE_CAST_H_

#include <cmath>
#include <limits>
#include <type_traits>

namespace openscreen {

// Case 0: When To and From are the same type, saturate_cast<> is pass-through.
template <typename To, typename From>
constexpr std::enable_if_t<
    std::is_same<std::remove_cv<To>, std::remove_cv<From>>::value,
    To>
saturate_cast(From from) {}

// Because of the way C++ signed versus unsigned comparison works (i.e., the
// type promotion strategy employed), extra care must be taken to range-check
// the input value. For example, if the current architecture is 32-bits, then
// any int32_t compared with a uint32_t will NOT promote to a int64_t↔int64_t
// comparison. Instead, it will become a uint32_t↔uint32_t comparison (!),
// which will sometimes produce invalid results.

// Case 1: "From" and "To" are either both signed, or are both unsigned. In
// this case, the smaller of the two types will be promoted to match the
// larger's size, and a valid comparison will be made.
template <typename To, typename From>
constexpr std::enable_if_t<
    std::is_integral<From>::value && std::is_integral<To>::value &&
        (std::is_signed<From>::value == std::is_signed<To>::value),
    To>
saturate_cast(From from) {}

// Case 2: "From" is signed, but "To" is unsigned.
template <typename To, typename From>
constexpr std::enable_if_t<
    std::is_integral<From>::value && std::is_integral<To>::value &&
        std::is_signed<From>::value && !std::is_signed<To>::value,
    To>
saturate_cast(From from) {}

// Case 3: "From" is unsigned, but "To" is signed.
template <typename To, typename From>
constexpr std::enable_if_t<
    std::is_integral<From>::value && std::is_integral<To>::value &&
        !std::is_signed<From>::value && std::is_signed<To>::value,
    To>
saturate_cast(From from) {}

// Case 4: "From" is a floating-point type, and "To" is an integer type (signed
// or unsigned). The result is truncated, per the usual C++ float-to-int
// conversion rules.
template <typename To, typename From>
constexpr std::enable_if_t<std::is_floating_point<From>::value &&
                               std::is_integral<To>::value,
                           To>
saturate_cast(From from) {}

// Like saturate_cast<>, but rounds to the nearest integer instead of
// truncating.
template <typename To, typename From>
constexpr std::enable_if_t<std::is_floating_point<From>::value &&
                               std::is_integral<To>::value,
                           To>
rounded_saturate_cast(From from) {}

}  // namespace openscreen

#endif  // UTIL_SATURATE_CAST_H_