| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| #ifndef FFL_UTILITY_H_ |
| #define FFL_UTILITY_H_ |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <limits> |
| #include <type_traits> |
| |
| namespace ffl { |
| |
| static_assert(-1 == ~0, "FFL requires a two's complement architecture!"); |
| |
| // Type tag used to disambiguate single-argument template constructors. |
| struct Init {}; |
| |
| // Type representing a zero-based bit ordinal. |
| template <size_t Ordinal> |
| struct Bit {}; |
| |
| // Type representing resolution in terms of fractional bits. |
| template <size_t FractionalBits> |
| struct Resolution {}; |
| |
| // Typed constant representing the bit position around which to round. |
| template <size_t Place> |
| constexpr auto ToPlace = Bit<Place>{}; |
| |
| // Traits type the determines the precision of the given integer type. |
| template <typename T> |
| struct IntegerPrecisionType { |
| static_assert(sizeof(T) != sizeof(T), "T must be a standard integer type!"); |
| }; |
| |
| // The precision of signed values does not include the signed bit. |
| template <> |
| struct IntegerPrecisionType<int8_t> { |
| static constexpr size_t value = 7; |
| }; |
| template <> |
| struct IntegerPrecisionType<int16_t> { |
| static constexpr size_t value = 15; |
| }; |
| template <> |
| struct IntegerPrecisionType<int32_t> { |
| static constexpr size_t value = 31; |
| }; |
| template <> |
| struct IntegerPrecisionType<int64_t> { |
| static constexpr size_t value = 63; |
| }; |
| |
| // The precision of unsigned values covers the full range. |
| template <> |
| struct IntegerPrecisionType<uint8_t> { |
| static constexpr size_t value = 8; |
| }; |
| template <> |
| struct IntegerPrecisionType<uint16_t> { |
| static constexpr size_t value = 16; |
| }; |
| template <> |
| struct IntegerPrecisionType<uint32_t> { |
| static constexpr size_t value = 32; |
| }; |
| template <> |
| struct IntegerPrecisionType<uint64_t> { |
| static constexpr size_t value = 64; |
| }; |
| |
| template <typename T> |
| static constexpr size_t IntegerPrecision = IntegerPrecisionType<T>::value; |
| |
| // Trait type to determine the best-fitting integer for a given sign and |
| // precision in bits. |
| template <bool Signed, size_t Precision, typename = void> |
| struct BestFittingType; |
| |
| // Signed values require space for the signed bit, precision covers the positive |
| // range. |
| template <size_t Precision> |
| struct BestFittingType<true, Precision, std::enable_if_t<(Precision < 8)>> { |
| using Type = int8_t; |
| }; |
| template <size_t Precision> |
| struct BestFittingType<true, Precision, std::enable_if_t<(Precision >= 8 && Precision < 16)>> { |
| using Type = int16_t; |
| }; |
| template <size_t Precision> |
| struct BestFittingType<true, Precision, std::enable_if_t<(Precision >= 16 && Precision < 32)>> { |
| using Type = int32_t; |
| }; |
| template <size_t Precision> |
| struct BestFittingType<true, Precision, std::enable_if_t<(Precision >= 32)>> { |
| using Type = int64_t; |
| }; |
| |
| // Unsigned values do not have a signed bit, precision covers the entire range. |
| template <size_t Precision> |
| struct BestFittingType<false, Precision, std::enable_if_t<(Precision <= 8)>> { |
| using Type = uint8_t; |
| }; |
| template <size_t Precision> |
| struct BestFittingType<false, Precision, std::enable_if_t<(Precision > 8 && Precision <= 16)>> { |
| using Type = uint16_t; |
| }; |
| template <size_t Precision> |
| struct BestFittingType<false, Precision, std::enable_if_t<(Precision > 16 && Precision <= 32)>> { |
| using Type = uint32_t; |
| }; |
| template <size_t Precision> |
| struct BestFittingType<false, Precision, std::enable_if_t<(Precision > 32)>> { |
| using Type = uint64_t; |
| }; |
| |
| template <bool Signed, size_t Precision> |
| using BestFitting = typename BestFittingType<Signed, Precision>::Type; |
| |
| // Changes the signedness of Integer to match the signedness of Reference, |
| // preserving the original size of Integer. |
| template <typename Reference, typename Integer> |
| using MatchSignedOrUnsigned = |
| std::conditional_t<std::is_signed_v<Reference>, std::make_signed_t<Integer>, |
| std::make_unsigned_t<Integer>>; |
| |
| // Clamps the given Integer value to the range of Result. Optimized for all |
| // combinations of sizes and signedness. |
| template <typename Result, typename Integer, |
| std::enable_if_t<std::is_integral_v<Result> && std::is_integral_v<Integer> && |
| std::is_unsigned_v<Result> && std::is_signed_v<Integer>, |
| int> = 0> |
| constexpr Result ClampCast(Integer value) { |
| if (value <= 0) { |
| return 0; |
| } |
| if constexpr (sizeof(Result) < sizeof(Integer)) { |
| constexpr auto kMax = std::numeric_limits<Result>::max(); |
| if (value > static_cast<Integer>(kMax)) { |
| return kMax; |
| } |
| } |
| return static_cast<Result>(value); |
| } |
| template <typename Result, typename Integer, |
| std::enable_if_t<std::is_integral_v<Result> && std::is_integral_v<Integer> && |
| std::is_unsigned_v<Result> && std::is_unsigned_v<Integer>, |
| int> = 1> |
| constexpr Result ClampCast(Integer value) { |
| if constexpr (sizeof(Result) < sizeof(Integer)) { |
| constexpr auto kMax = std::numeric_limits<Result>::max(); |
| if (value > kMax) { |
| return kMax; |
| } |
| } |
| return static_cast<Result>(value); |
| } |
| template <typename Result, typename Integer, |
| std::enable_if_t<std::is_integral_v<Result> && std::is_integral_v<Integer> && |
| std::is_signed_v<Result> && std::is_unsigned_v<Integer>, |
| int> = 2> |
| constexpr Result ClampCast(Integer value) { |
| if constexpr (sizeof(Result) <= sizeof(Integer)) { |
| constexpr auto kMax = std::numeric_limits<Result>::max(); |
| if (value > static_cast<Integer>(kMax)) { |
| return kMax; |
| } |
| } |
| return static_cast<Result>(value); |
| } |
| template <typename Result, typename Integer, |
| std::enable_if_t<std::is_integral_v<Result> && std::is_integral_v<Integer> && |
| std::is_signed_v<Result> && std::is_signed_v<Integer>, |
| int> = 3> |
| constexpr Result ClampCast(Integer value) { |
| if constexpr (sizeof(Result) < sizeof(Integer)) { |
| constexpr auto kMin = std::numeric_limits<Result>::min(); |
| constexpr auto kMax = std::numeric_limits<Result>::max(); |
| if (value < kMin) { |
| return kMin; |
| } else if (value > kMax) { |
| return kMax; |
| } |
| } |
| return static_cast<Result>(value); |
| } |
| |
| } // namespace ffl |
| |
| #endif // FFL_UTILITY_H_ |