/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_BASE_BIT_HPP #define MPT_BASE_BIT_HPP #include "mpt/base/detect.hpp" #include "mpt/base/integer.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/macros.hpp" #if MPT_CXX_AT_LEAST(20) #include #else // !C++20 #include #include #endif // C++20 #include #include #if MPT_CXX_BEFORE(20) #include #endif // !C++20 namespace mpt { inline namespace MPT_INLINE_NS { #if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(14, 0, 0) using std::bit_cast; #else // !C++20 // C++2a compatible bit_cast. // Not implementing constexpr because this is not easily possible pre C++20. template MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::is_trivially_copyable::value && std::is_trivially_copyable::value, Tdst>::type bit_cast(const Tsrc & src) noexcept { Tdst dst{}; std::memcpy(&dst, &src, sizeof(Tdst)); return dst; } #endif // C++20 #if MPT_CXX_AT_LEAST(20) using std::endian; static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); constexpr mpt::endian get_endian() noexcept { return mpt::endian::native; } constexpr bool endian_is_little() noexcept { return get_endian() == mpt::endian::little; } constexpr bool endian_is_big() noexcept { return get_endian() == mpt::endian::big; } constexpr bool endian_is_weird() noexcept { return !endian_is_little() && !endian_is_big(); } #else // !C++20 #if !MPT_COMPILER_GENERIC #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define MPT_PLATFORM_BIG_ENDIAN #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define MPT_PLATFORM_LITTLE_ENDIAN #endif #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__) #if __ORDER_BIG_ENDIAN__ != __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define MPT_PLATFORM_BIG_ENDIAN #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define MPT_PLATFORM_LITTLE_ENDIAN #endif #endif #endif // fallback: #if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN) #if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \ || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \ || (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN)) #define MPT_PLATFORM_BIG_ENDIAN #elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \ || (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \ || (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN)) #define MPT_PLATFORM_LITTLE_ENDIAN #elif defined(__hpux) || defined(__hppa) \ || defined(_MIPSEB) \ || defined(__s390__) #define MPT_PLATFORM_BIG_ENDIAN #elif defined(__i386__) || defined(_M_IX86) \ || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ || defined(__bfin__) #define MPT_PLATFORM_LITTLE_ENDIAN #endif #endif #endif // !MPT_COMPILER_GENERIC enum class endian { little = 0x78563412u, big = 0x12345678u, weird = 1u, #if MPT_COMPILER_GENERIC native = 0u, #elif defined(MPT_PLATFORM_LITTLE_ENDIAN) native = little, #elif defined(MPT_PLATFORM_BIG_ENDIAN) native = big, #else native = 0u, #endif }; static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); MPT_FORCEINLINE mpt::endian endian_probe() noexcept { using endian_probe_type = uint32; static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; const std::array probe{ {std::byte{0x12}, std::byte{0x34}, std::byte{0x56}, std::byte{0x78}} }; const endian_probe_type test = mpt::bit_cast(probe); mpt::endian result = mpt::endian::native; switch (test) { case endian_probe_big: result = mpt::endian::big; break; case endian_probe_little: result = mpt::endian::little; break; default: result = mpt::endian::weird; break; } return result; } MPT_FORCEINLINE mpt::endian get_endian() noexcept { #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable : 6285) // false-positive: ( || ) is always a non-zero constant. #endif // MPT_COMPILER_MSVC if constexpr ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else { return mpt::endian_probe(); } #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC } MPT_FORCEINLINE bool endian_is_little() noexcept { return get_endian() == mpt::endian::little; } MPT_FORCEINLINE bool endian_is_big() noexcept { return get_endian() == mpt::endian::big; } MPT_FORCEINLINE bool endian_is_weird() noexcept { return !endian_is_little() && !endian_is_big(); } #endif // C++20 #if MPT_CXX_AT_LEAST(20) && MPT_MSVC_AT_LEAST(2022, 1) && !MPT_CLANG_BEFORE(12, 0, 0) // Disabled for VS2022.0 because of // // / with fix already queued // (). using std::bit_ceil; using std::bit_floor; using std::bit_width; using std::countl_one; using std::countl_zero; using std::countr_one; using std::countr_zero; using std::has_single_bit; using std::popcount; using std::rotl; using std::rotr; #else // !C++20 // C++20 header. // Note that we do not use SFINAE here but instead rely on static_assert. template constexpr int popcount(T val) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int result = 0; while (val > 0) { if (val & 0x1) { result++; } val >>= 1; } return result; } template constexpr bool has_single_bit(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); return mpt::popcount(x) == 1; } template constexpr T bit_ceil(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); T result = 1; while (result < x) { T newresult = result << 1; if (newresult < result) { return 0; } result = newresult; } return result; } template constexpr T bit_floor(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); if (x == 0) { return 0; } T result = 1; do { T newresult = result << 1; if (newresult < result) { return result; } result = newresult; } while (result <= x); return result >> 1; } template constexpr T bit_width(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); T result = 0; while (x > 0) { x >>= 1; result += 1; } return result; } template constexpr int countl_zero(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = std::numeric_limits::digits - 1; bit >= 0; --bit) { if ((x & (1u << bit)) == 0u) { count++; } else { break; } } return count; } template constexpr int countl_one(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = std::numeric_limits::digits - 1; bit >= 0; --bit) { if ((x & (1u << bit)) != 0u) { count++; } else { break; } } return count; } template constexpr int countr_zero(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = 0; bit < std::numeric_limits::digits; ++bit) { if ((x & (1u << bit)) == 0u) { count++; } else { break; } } return count; } template constexpr int countr_one(T x) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); int count = 0; for (int bit = 0; bit < std::numeric_limits::digits; ++bit) { if ((x & (1u << bit)) != 0u) { count++; } else { break; } } return count; } template constexpr T rotl_impl(T x, int r) noexcept { auto N = std::numeric_limits::digits; return (x >> (N - r)) | (x << r); } template constexpr T rotr_impl(T x, int r) noexcept { auto N = std::numeric_limits::digits; return (x << (N - r)) | (x >> r); } template constexpr T rotl(T x, int s) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); auto N = std::numeric_limits::digits; auto r = s % N; return (s < 0) ? mpt::rotr_impl(x, -s) : ((x >> (N - r)) | (x << r)); } template constexpr T rotr(T x, int s) noexcept { static_assert(std::numeric_limits::is_integer); static_assert(std::is_unsigned::value); auto N = std::numeric_limits::digits; auto r = s % N; return (s < 0) ? mpt::rotl_impl(x, -s) : ((x << (N - r)) | (x >> r)); } #endif // C++20 template constexpr int lower_bound_entropy_bits(T x_) { typename std::make_unsigned::type x = static_cast::type>(x_); return mpt::bit_width(x) == static_cast::type>(mpt::popcount(x)) ? mpt::bit_width(x) : mpt::bit_width(x) - 1; } template constexpr bool is_mask(T x) { static_assert(std::is_integral::value); typedef typename std::make_unsigned::type unsigned_T; unsigned_T ux = static_cast(x); unsigned_T mask = 0; for (std::size_t bits = 0; bits <= (sizeof(unsigned_T) * 8); ++bits) { mask = (mask << 1) | 1u; if (ux == mask) { return true; } } return false; } } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_BASE_BIT_HPP