Bitset.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #pragma once
  2. #include <c10/macros/Macros.h>
  3. #include <c10/util/C++17.h>
  4. #include <c10/util/Optional.h>
  5. #if defined(_MSC_VER)
  6. #include <intrin.h>
  7. #endif
  8. namespace c10 {
  9. namespace utils {
  10. /**
  11. * This is a simple bitset class with sizeof(long long int) bits.
  12. * You can set bits, unset bits, query bits by index,
  13. * and query for the first set bit.
  14. * Before using this class, please also take a look at std::bitset,
  15. * which has more functionality and is more generic. It is probably
  16. * a better fit for your use case. The sole reason for c10::utils::bitset
  17. * to exist is that std::bitset misses a find_first_set() method.
  18. */
  19. struct bitset final {
  20. private:
  21. #if defined(_MSC_VER)
  22. // MSVCs _BitScanForward64 expects int64_t
  23. using bitset_type = int64_t;
  24. #else
  25. // POSIX ffsll expects long long int
  26. using bitset_type = long long int;
  27. #endif
  28. public:
  29. static constexpr size_t NUM_BITS() {
  30. return 8 * sizeof(bitset_type);
  31. }
  32. constexpr bitset() noexcept = default;
  33. constexpr bitset(const bitset&) noexcept = default;
  34. constexpr bitset(bitset&&) noexcept = default;
  35. // there is an issure for gcc 5.3.0 when define default function as constexpr
  36. // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68754.
  37. bitset& operator=(const bitset&) noexcept = default;
  38. bitset& operator=(bitset&&) noexcept = default;
  39. constexpr void set(size_t index) noexcept {
  40. bitset_ |= (static_cast<long long int>(1) << index);
  41. }
  42. constexpr void unset(size_t index) noexcept {
  43. bitset_ &= ~(static_cast<long long int>(1) << index);
  44. }
  45. constexpr bool get(size_t index) const noexcept {
  46. return bitset_ & (static_cast<long long int>(1) << index);
  47. }
  48. constexpr bool is_entirely_unset() const noexcept {
  49. return 0 == bitset_;
  50. }
  51. // Call the given functor with the index of each bit that is set
  52. template <class Func>
  53. void for_each_set_bit(Func&& func) const {
  54. bitset cur = *this;
  55. size_t index = cur.find_first_set();
  56. while (0 != index) {
  57. // -1 because find_first_set() is not one-indexed.
  58. index -= 1;
  59. func(index);
  60. cur.unset(index);
  61. index = cur.find_first_set();
  62. }
  63. }
  64. private:
  65. // Return the index of the first set bit. The returned index is one-indexed
  66. // (i.e. if the very first bit is set, this function returns '1'), and a
  67. // return of '0' means that there was no bit set.
  68. size_t find_first_set() const {
  69. #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
  70. unsigned long result;
  71. bool has_bits_set = (0 != _BitScanForward64(&result, bitset_));
  72. if (!has_bits_set) {
  73. return 0;
  74. }
  75. return result + 1;
  76. #elif defined(_MSC_VER) && defined(_M_IX86)
  77. unsigned long result;
  78. if (static_cast<uint32_t>(bitset_) != 0) {
  79. bool has_bits_set =
  80. (0 != _BitScanForward(&result, static_cast<uint32_t>(bitset_)));
  81. if (!has_bits_set) {
  82. return 0;
  83. }
  84. return result + 1;
  85. } else {
  86. bool has_bits_set =
  87. (0 != _BitScanForward(&result, static_cast<uint32_t>(bitset_ >> 32)));
  88. if (!has_bits_set) {
  89. return 32;
  90. }
  91. return result + 33;
  92. }
  93. #else
  94. return __builtin_ffsll(bitset_);
  95. #endif
  96. }
  97. friend bool operator==(bitset lhs, bitset rhs) noexcept {
  98. return lhs.bitset_ == rhs.bitset_;
  99. }
  100. bitset_type bitset_{0};
  101. };
  102. inline bool operator!=(bitset lhs, bitset rhs) noexcept {
  103. return !(lhs == rhs);
  104. }
  105. } // namespace utils
  106. } // namespace c10