🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.7 KiB

  1. #ifndef AL_BIT_H
  2. #define AL_BIT_H
  3. #include <cstdint>
  4. #include <limits>
  5. #include <type_traits>
  6. #if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64))
  7. #include <intrin.h>
  8. #endif
  9. namespace al {
  10. #ifdef __BYTE_ORDER__
  11. enum class endian {
  12. little = __ORDER_LITTLE_ENDIAN__,
  13. big = __ORDER_BIG_ENDIAN__,
  14. native = __BYTE_ORDER__
  15. };
  16. #else
  17. /* This doesn't support mixed-endian. */
  18. namespace detail_ {
  19. constexpr bool IsLittleEndian() noexcept
  20. {
  21. static_assert(sizeof(char) < sizeof(int), "char is too big");
  22. constexpr int test_val{1};
  23. return static_cast<const char&>(test_val) ? true : false;
  24. }
  25. } // namespace detail_
  26. enum class endian {
  27. big = 0,
  28. little = 1,
  29. native = detail_::IsLittleEndian() ? little : big
  30. };
  31. #endif
  32. /* Define popcount (population count/count 1 bits) and countr_zero (count
  33. * trailing zero bits, starting from the lsb) methods, for various integer
  34. * types.
  35. */
  36. #ifdef __GNUC__
  37. namespace detail_ {
  38. inline int popcount(unsigned long long val) noexcept { return __builtin_popcountll(val); }
  39. inline int popcount(unsigned long val) noexcept { return __builtin_popcountl(val); }
  40. inline int popcount(unsigned int val) noexcept { return __builtin_popcount(val); }
  41. inline int countr_zero(unsigned long long val) noexcept { return __builtin_ctzll(val); }
  42. inline int countr_zero(unsigned long val) noexcept { return __builtin_ctzl(val); }
  43. inline int countr_zero(unsigned int val) noexcept { return __builtin_ctz(val); }
  44. } // namespace detail_
  45. template<typename T>
  46. inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
  47. int> popcount(T v) noexcept { return detail_::popcount(v); }
  48. template<typename T>
  49. inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
  50. int> countr_zero(T val) noexcept
  51. { return val ? detail_::countr_zero(val) : std::numeric_limits<T>::digits; }
  52. #else
  53. /* There be black magics here. The popcount method is derived from
  54. * https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
  55. * while the ctz-utilizing-popcount algorithm is shown here
  56. * http://www.hackersdelight.org/hdcodetxt/ntz.c.txt
  57. * as the ntz2 variant. These likely aren't the most efficient methods, but
  58. * they're good enough if the GCC built-ins aren't available.
  59. */
  60. namespace detail_ {
  61. template<typename T, size_t = std::numeric_limits<T>::digits>
  62. struct fast_utype { };
  63. template<typename T>
  64. struct fast_utype<T,8> { using type = std::uint_fast8_t; };
  65. template<typename T>
  66. struct fast_utype<T,16> { using type = std::uint_fast16_t; };
  67. template<typename T>
  68. struct fast_utype<T,32> { using type = std::uint_fast32_t; };
  69. template<typename T>
  70. struct fast_utype<T,64> { using type = std::uint_fast64_t; };
  71. template<typename T>
  72. constexpr T repbits(unsigned char bits) noexcept
  73. {
  74. T ret{bits};
  75. for(size_t i{1};i < sizeof(T);++i)
  76. ret = (ret<<8) | bits;
  77. return ret;
  78. }
  79. } // namespace detail_
  80. template<typename T>
  81. constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
  82. int> popcount(T val) noexcept
  83. {
  84. using fast_type = typename detail_::fast_utype<T>::type;
  85. constexpr fast_type b01010101{detail_::repbits<fast_type>(0x55)};
  86. constexpr fast_type b00110011{detail_::repbits<fast_type>(0x33)};
  87. constexpr fast_type b00001111{detail_::repbits<fast_type>(0x0f)};
  88. constexpr fast_type b00000001{detail_::repbits<fast_type>(0x01)};
  89. fast_type v{fast_type{val} - ((fast_type{val} >> 1) & b01010101)};
  90. v = (v & b00110011) + ((v >> 2) & b00110011);
  91. v = (v + (v >> 4)) & b00001111;
  92. return static_cast<int>(((v * b00000001) >> ((sizeof(T)-1)*8)) & 0xff);
  93. }
  94. #ifdef _WIN32
  95. template<typename T>
  96. inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value
  97. && std::numeric_limits<T>::digits <= 32,
  98. int> countr_zero(T v)
  99. {
  100. unsigned long idx{std::numeric_limits<T>::digits};
  101. _BitScanForward(&idx, static_cast<uint32_t>(v));
  102. return static_cast<int>(idx);
  103. }
  104. template<typename T>
  105. inline std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value
  106. && 32 < std::numeric_limits<T>::digits && std::numeric_limits<T>::digits <= 64,
  107. int> countr_zero(T v)
  108. {
  109. unsigned long idx{std::numeric_limits<T>::digits};
  110. #ifdef _WIN64
  111. _BitScanForward64(&idx, v);
  112. #else
  113. if(!_BitScanForward(&idx, static_cast<uint32_t>(v)))
  114. {
  115. if(_BitScanForward(&idx, static_cast<uint32_t>(v>>32)))
  116. idx += 32;
  117. }
  118. #endif /* _WIN64 */
  119. return static_cast<int>(idx);
  120. }
  121. #else
  122. template<typename T>
  123. constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
  124. int> countr_zero(T value)
  125. { return popcount(static_cast<T>(~value & (value - 1))); }
  126. #endif
  127. #endif
  128. } // namespace al
  129. #endif /* AL_BIT_H */