diff options
Diffstat (limited to 'common/albit.h')
-rw-r--r-- | common/albit.h | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/common/albit.h b/common/albit.h new file mode 100644 index 00000000..ad596208 --- /dev/null +++ b/common/albit.h @@ -0,0 +1,155 @@ +#ifndef AL_BIT_H +#define AL_BIT_H + +#include <cstdint> +#include <limits> +#include <type_traits> +#if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64)) +#include <intrin.h> +#endif + +namespace al { + +#ifdef __BYTE_ORDER__ +enum class endian { + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__, + native = __BYTE_ORDER__ +}; + +#else + +/* This doesn't support mixed-endian. */ +namespace detail_ { +constexpr bool IsLittleEndian() noexcept +{ + static_assert(sizeof(char) < sizeof(int), "char is too big"); + + constexpr int test_val{1}; + return static_cast<const char&>(test_val) ? true : false; +} +} // namespace detail_ + +enum class endian { + big = 0, + little = 1, + native = detail_::IsLittleEndian() ? little : big +}; +#endif + + +/* Define popcount (population count/count 1 bits) and countr_zero (count + * trailing zero bits, starting from the lsb) methods, for various integer + * types. + */ +#ifdef __GNUC__ + +namespace detail_ { + inline int popcount(unsigned long long val) noexcept { return __builtin_popcountll(val); } + inline int popcount(unsigned long val) noexcept { return __builtin_popcountl(val); } + inline int popcount(unsigned int val) noexcept { return __builtin_popcount(val); } + + inline int countr_zero(unsigned long long val) noexcept { return __builtin_ctzll(val); } + inline int countr_zero(unsigned long val) noexcept { return __builtin_ctzl(val); } + inline int countr_zero(unsigned int val) noexcept { return __builtin_ctz(val); } +} // namespace detail_ + +template<typename T> +inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, +int> popcount(T v) noexcept { return detail_::popcount(v); } + +template<typename T> +inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, +int> countr_zero(T val) noexcept +{ return val ? detail_::countr_zero(val) : std::numeric_limits<T>::digits; } + +#else + +/* There be black magics here. The popcount method is derived from + * https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + * while the ctz-utilizing-popcount algorithm is shown here + * http://www.hackersdelight.org/hdcodetxt/ntz.c.txt + * as the ntz2 variant. These likely aren't the most efficient methods, but + * they're good enough if the GCC built-ins aren't available. + */ +namespace detail_ { + template<typename T, size_t = std::numeric_limits<T>::digits> + struct fast_utype { }; + template<typename T> + struct fast_utype<T,8> { using type = std::uint_fast8_t; }; + template<typename T> + struct fast_utype<T,16> { using type = std::uint_fast16_t; }; + template<typename T> + struct fast_utype<T,32> { using type = std::uint_fast32_t; }; + template<typename T> + struct fast_utype<T,64> { using type = std::uint_fast64_t; }; + + template<typename T> + constexpr T repbits(unsigned char bits) noexcept + { + T ret{bits}; + for(size_t i{1};i < sizeof(T);++i) + ret = (ret<<8) | bits; + return ret; + } +} // namespace detail_ + +template<typename T> +constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, +int> popcount(T val) noexcept +{ + using fast_type = typename detail_::fast_utype<T>::type; + constexpr fast_type b01010101{detail_::repbits<fast_type>(0x55)}; + constexpr fast_type b00110011{detail_::repbits<fast_type>(0x33)}; + constexpr fast_type b00001111{detail_::repbits<fast_type>(0x0f)}; + constexpr fast_type b00000001{detail_::repbits<fast_type>(0x01)}; + + fast_type v{fast_type{val} - ((fast_type{val} >> 1) & b01010101)}; + v = (v & b00110011) + ((v >> 2) & b00110011); + v = (v + (v >> 4)) & b00001111; + return static_cast<int>(((v * b00000001) >> ((sizeof(T)-1)*8)) & 0xff); +} + +#ifdef _WIN32 + +template<typename T> +inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value + && std::numeric_limits<T>::digits <= 32, +int> countr_zero(T v) +{ + unsigned long idx{std::numeric_limits<T>::digits}; + _BitScanForward(&idx, static_cast<uint32_t>(v)); + return static_cast<int>(idx); +} + +template<typename T> +inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value + && 32 < std::numeric_limits<T>::digits && std::numeric_limits<T>::digits <= 64, +int> countr_zero(T v) +{ + unsigned long idx{std::numeric_limits<T>::digits}; +#ifdef _WIN64 + _BitScanForward64(&idx, v); +#else + if(!_BitScanForward(&idx, static_cast<uint32_t>(v))) + { + if(_BitScanForward(&idx, static_cast<uint32_t>(v>>32))) + idx += 32; + } +#endif /* _WIN64 */ + return static_cast<int>(idx); +} + +#else + +template<typename T> +constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, +int> countr_zero(T value) +{ return popcount(static_cast<T>(~value & (value - 1))); } + +#endif +#endif + +} // namespace al + +#endif /* AL_BIT_H */ |