#pragma once #include #include #include #include #include #include #include #include #if __has_include() #include #endif // NOLINTBEGIN(modernize-use-constraints) namespace stdx { inline namespace v1 { // endian #if __cpp_lib_endian < 201907L // NOLINTNEXTLINE(performance-enum-size) enum struct endian : underlying_type_t { little = __ORDER_LITTLE_ENDIAN__, big = __ORDER_BIG_ENDIAN__, native = __BYTE_ORDER__ }; #else using endian = std::endian; #endif // bit_cast #if __cpp_lib_bit_cast < 201806L template [[nodiscard]] constexpr auto bit_cast(From &&from) noexcept -> To { return __builtin_bit_cast(To, from); } #else using std::bit_cast; #endif // byteswap #if __cpp_lib_byteswap < 202110L template [[nodiscard]] constexpr auto byteswap(T x) noexcept -> std::enable_if_t, T> { if constexpr (sizeof(T) == sizeof(std::uint16_t)) { return __builtin_bswap16(x); } else if constexpr (sizeof(T) == sizeof(std::uint32_t)) { return __builtin_bswap32(x); } else if constexpr (sizeof(T) == sizeof(std::uint64_t)) { return __builtin_bswap64(x); } else { return x; } } #else using std::byteswap; #endif // bit ops #if __cpp_lib_bitops < 201907L template [[nodiscard]] constexpr auto countl_zero(T x) noexcept -> std::enable_if_t, int> { if (x == 0) { return std::numeric_limits::digits; } if constexpr (sizeof(T) == sizeof(unsigned int)) { return __builtin_clz(x); } else if constexpr (sizeof(T) == sizeof(unsigned long)) { // NOLINT(google-runtime-int) return __builtin_clzl(x); } else if constexpr ( sizeof(T) == sizeof(unsigned long long)) { // NOLINT(google-runtime-int) return __builtin_clzll(x); } else { return __builtin_clzll(x) + std::numeric_limits::digits - std::numeric_limits< unsigned long long>::digits; // NOLINT(google-runtime-int) } } template [[nodiscard]] constexpr auto countr_zero(T x) noexcept -> std::enable_if_t, int> { if (x == 0) { return std::numeric_limits::digits; } if constexpr (sizeof(T) == sizeof(unsigned int)) { return __builtin_ctz(x); } else if constexpr (sizeof(T) == sizeof(unsigned long)) { // NOLINT(google-runtime-int) return __builtin_ctzl(x); } else { return __builtin_ctzll(x); } } template [[nodiscard]] constexpr auto countl_one(T x) noexcept -> std::enable_if_t, int> { return countl_zero(T(~x)); } template [[nodiscard]] constexpr auto countr_one(T x) noexcept -> std::enable_if_t, int> { return countr_zero(T(~x)); } template [[nodiscard]] constexpr auto popcount(T x) noexcept -> std::enable_if_t, int> { if constexpr (sizeof(T) <= sizeof(unsigned int)) { return __builtin_popcount(x); } else if constexpr (sizeof(T) <= sizeof(unsigned long)) { // NOLINT(google-runtime-int) return __builtin_popcountl(x); } else { return __builtin_popcountll(x); } } namespace detail { template [[nodiscard]] constexpr auto rotl(T x, T s) noexcept -> std::enable_if_t, T> { #ifdef __clang__ if constexpr (sizeof(T) == sizeof(std::uint8_t)) { return __builtin_rotateleft8(x, s); } else if constexpr (sizeof(T) == sizeof(std::uint16_t)) { return __builtin_rotateleft16(x, s); } else if constexpr (sizeof(T) == sizeof(std::uint32_t)) { return __builtin_rotateleft32(x, s); } else { return __builtin_rotateleft64(x, s); } #else return static_cast((x << s) | (x >> (std::numeric_limits::digits - s))); #endif } template [[nodiscard]] constexpr auto rotr(T x, T s) noexcept -> std::enable_if_t, T> { #ifdef __clang__ if constexpr (sizeof(T) == sizeof(std::uint8_t)) { return __builtin_rotateright8(x, s); } else if constexpr (sizeof(T) == sizeof(std::uint16_t)) { return __builtin_rotateright16(x, s); } else if constexpr (sizeof(T) == sizeof(std::uint32_t)) { return __builtin_rotateright32(x, s); } else { return __builtin_rotateright64(x, s); } #else return static_cast((x >> s) | (x << (std::numeric_limits::digits - s))); #endif } } // namespace detail template [[nodiscard]] constexpr auto rotl(T x, int s) noexcept -> std::enable_if_t, T> { if (s == 0) { return x; } if (s < 0) { return detail::rotr(x, T(-s)); } return detail::rotl(x, T(s)); } template [[nodiscard]] constexpr auto rotr(T x, int s) noexcept -> std::enable_if_t, T> { if (s == 0) { return x; } if (s < 0) { return detail::rotl(x, T(-s)); } return detail::rotr(x, T(s)); } #else using std::countl_one; using std::countl_zero; using std::countr_one; using std::countr_zero; using std::popcount; using std::rotl; using std::rotr; #endif // pow2 #if __cpp_lib_int_pow2 < 202002L template [[nodiscard]] constexpr auto has_single_bit(T x) noexcept -> std::enable_if_t, bool> { return x and not(x & (x - 1)); } template [[nodiscard]] constexpr auto bit_width(T x) noexcept -> std::enable_if_t, int> { return std::numeric_limits::digits - countl_zero(x); } template [[nodiscard]] constexpr auto bit_ceil(T x) noexcept -> std::enable_if_t, T> { if (x <= 1U) { return 1U; } return T(1U << bit_width(x)); } template [[nodiscard]] constexpr auto bit_floor(T x) noexcept -> std::enable_if_t, T> { if (x == 0) { return x; } return T(1U << (bit_width(x) - 1)); } #else using std::bit_ceil; using std::bit_floor; using std::bit_width; using std::has_single_bit; #endif template [[nodiscard]] constexpr auto to_le(T x) noexcept -> std::enable_if_t, T> { if constexpr (stdx::endian::native == stdx::endian::big) { return byteswap(x); } else { return x; } } template [[nodiscard]] constexpr auto to_be(T x) noexcept -> std::enable_if_t, T> { if constexpr (stdx::endian::native == stdx::endian::little) { return byteswap(x); } else { return x; } } template [[nodiscard]] constexpr auto from_le(T x) noexcept -> std::enable_if_t, T> { if constexpr (stdx::endian::native == stdx::endian::big) { return byteswap(x); } else { return x; } } template [[nodiscard]] constexpr auto from_be(T x) noexcept -> std::enable_if_t, T> { if constexpr (stdx::endian::native == stdx::endian::little) { return byteswap(x); } else { return x; } } template constexpr auto bit_pack = [](auto... args) { static_assert(stdx::always_false_v, "bit_pack is undefined for those types"); }; template <> constexpr inline auto bit_pack = [](std::uint8_t hi, std::uint8_t lo) -> std::uint16_t { return static_cast((static_cast(hi) << 8u) | lo); }; template <> constexpr inline auto bit_pack = stdx::overload{[](std::uint16_t hi, std::uint16_t lo) -> std::uint32_t { return (static_cast(hi) << 16u) | lo; }, [](std::uint8_t b0, std::uint8_t b1, std::uint8_t b2, std::uint8_t b3) -> std::uint32_t { return (static_cast(b0) << 24u) | (static_cast(b1) << 16u) | (static_cast(b2) << 8u) | b3; }}; template <> constexpr inline auto bit_pack = stdx::overload{[](std::uint32_t hi, std::uint32_t lo) -> std::uint64_t { return (static_cast(hi) << 32u) | lo; }, [](std::uint16_t w0, std::uint16_t w1, std::uint16_t w2, std::uint16_t w3) -> std::uint64_t { return (static_cast(w0) << 48u) | (static_cast(w1) << 32u) | (static_cast(w2) << 16u) | w3; }, [](std::uint8_t b0, std::uint8_t b1, std::uint8_t b2, std::uint8_t b3, std::uint8_t b4, std::uint8_t b5, std::uint8_t b6, std::uint8_t b7) -> std::uint64_t { return (static_cast(b0) << 56u) | (static_cast(b1) << 48u) | (static_cast(b2) << 40u) | (static_cast(b3) << 32u) | (static_cast(b4) << 24u) | (static_cast(b5) << 16u) | (static_cast(b6) << 8u) | b7; }}; template constexpr auto bit_unpack(From arg) { static_assert(unsigned_integral and unsigned_integral, "bit_unpack is undefined for those types"); constexpr auto sz = sized{1}.template in(); auto r = bit_cast>(to_be(arg)); for (auto &elem : r) { elem = from_be(elem); } return r; } namespace detail { template constexpr auto mask_bits() -> std::enable_if_t::digits, T> { if constexpr (Bit == std::numeric_limits::digits) { return std::numeric_limits::max(); } else { return static_cast(T{1} << Bit) - T{1}; } } template constexpr auto mask_bits(std::size_t Bit) -> T { if (Bit == std::numeric_limits::digits) { return std::numeric_limits::max(); } return static_cast(T{1} << Bit) - T{1}; } } // namespace detail template ::digits - 1, std::size_t Lsb = 0> [[nodiscard]] constexpr auto bit_mask() noexcept -> std::enable_if_t and Msb >= Lsb, T> { static_assert(Msb < std::numeric_limits::digits); return detail::mask_bits() - detail::mask_bits(); } template [[nodiscard]] constexpr auto bit_mask(std::size_t Msb, std::size_t Lsb = 0) noexcept -> std::enable_if_t, T> { return detail::mask_bits(Msb + 1) - detail::mask_bits(Lsb); } template constexpr auto bit_size() -> std::size_t { return sizeof(T) * CHAR_BIT; } template CONSTEVAL auto smallest_uint() { if constexpr (N <= std::numeric_limits::digits) { return std::uint8_t{}; } else if constexpr (N <= std::numeric_limits::digits) { return std::uint16_t{}; } else if constexpr (N <= std::numeric_limits::digits) { return std::uint32_t{}; } else { return std::uint64_t{}; } } template using smallest_uint_t = decltype(smallest_uint()); } // namespace v1 } // namespace stdx // NOLINTEND(modernize-use-constraints)