| // Copyright 2024 Google LLC | |
| // SPDX-License-Identifier: Apache-2.0 | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef HIGHWAY_HWY_BIT_SET_H_ | |
| #define HIGHWAY_HWY_BIT_SET_H_ | |
| // Various BitSet for 64, up to 4096, or any number of bits. | |
| #include <stddef.h> | |
| #include <atomic> | |
| #include "third_party/highway/hwy/base.h" | |
| namespace hwy { | |
| // 64-bit specialization of `std::bitset`, which lacks `Foreach`. | |
| class BitSet64 { | |
| public: | |
| constexpr size_t MaxSize() const { return 64; } | |
| // No harm if `i` is already set. | |
| void Set(size_t i) { | |
| HWY_DASSERT(i < 64); | |
| bits_ |= (1ULL << i); | |
| HWY_DASSERT(Get(i)); | |
| } | |
| // Equivalent to Set(i) for i in [0, 64) where (bits >> i) & 1. This does | |
| // not clear any existing bits. | |
| void SetNonzeroBitsFrom64(uint64_t bits) { bits_ |= bits; } | |
| void Clear(size_t i) { | |
| HWY_DASSERT(i < 64); | |
| bits_ &= ~(1ULL << i); | |
| } | |
| bool Get(size_t i) const { | |
| HWY_DASSERT(i < 64); | |
| return (bits_ & (1ULL << i)) != 0; | |
| } | |
| // Returns true if Get(i) would return true for any i in [0, 64). | |
| bool Any() const { return bits_ != 0; } | |
| // Returns true if Get(i) would return true for all i in [0, 64). | |
| bool All() const { return bits_ == ~uint64_t{0}; } | |
| // Returns lowest i such that `Get(i)`. Caller must first ensure `Any()`! | |
| size_t First() const { | |
| HWY_DASSERT(Any()); | |
| return Num0BitsBelowLS1Bit_Nonzero64(bits_); | |
| } | |
| // Returns lowest i such that `!Get(i)`. Caller must first ensure `!All()`! | |
| size_t First0() const { | |
| HWY_DASSERT(!All()); | |
| return Num0BitsBelowLS1Bit_Nonzero64(~bits_); | |
| } | |
| // Returns uint64_t(Get(i)) << i for i in [0, 64). | |
| uint64_t Get64() const { return bits_; } | |
| // Calls `func(i)` for each `i` in the set. It is safe for `func` to modify | |
| // the set, but the current Foreach call is unaffected. | |
| template <class Func> | |
| void Foreach(const Func& func) const { | |
| uint64_t remaining_bits = bits_; | |
| while (remaining_bits != 0) { | |
| const size_t i = Num0BitsBelowLS1Bit_Nonzero64(remaining_bits); | |
| remaining_bits &= remaining_bits - 1; // clear LSB | |
| func(i); | |
| } | |
| } | |
| size_t Count() const { return PopCount(bits_); } | |
| private: | |
| uint64_t bits_ = 0; | |
| }; | |
| // Any number of bits, flat array. | |
| template <size_t kMaxSize> | |
| class BitSet { | |
| static_assert(kMaxSize != 0, "BitSet requires non-zero size"); | |
| public: | |
| constexpr size_t MaxSize() const { return kMaxSize; } | |
| // No harm if `i` is already set. | |
| void Set(size_t i) { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| bits_[idx].Set(mod); | |
| } | |
| void Clear(size_t i) { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| bits_[idx].Clear(mod); | |
| HWY_DASSERT(!Get(i)); | |
| } | |
| bool Get(size_t i) const { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| return bits_[idx].Get(mod); | |
| } | |
| // Returns true if Get(i) would return true for any i in [0, kMaxSize). | |
| bool Any() const { | |
| for (const BitSet64& bits : bits_) { | |
| if (bits.Any()) return true; | |
| } | |
| return false; | |
| } | |
| // Returns true if Get(i) would return true for all i in [0, kMaxSize). | |
| bool All() const { | |
| for (size_t idx = 0; idx < kNum64 - 1; ++idx) { | |
| if (!bits_[idx].All()) return false; | |
| } | |
| constexpr size_t kRemainder = kMaxSize % 64; | |
| if (kRemainder == 0) { | |
| return bits_[kNum64 - 1].All(); | |
| } | |
| return bits_[kNum64 - 1].Count() == kRemainder; | |
| } | |
| // Returns lowest i such that `Get(i)`. Caller must first ensure `Any()`! | |
| size_t First() const { | |
| HWY_DASSERT(Any()); | |
| for (size_t idx = 0;; ++idx) { | |
| HWY_DASSERT(idx < kNum64); | |
| if (bits_[idx].Any()) return idx * 64 + bits_[idx].First(); | |
| } | |
| } | |
| // Returns lowest i such that `!Get(i)`. Caller must first ensure `All()`! | |
| size_t First0() const { | |
| HWY_DASSERT(!All()); | |
| for (size_t idx = 0;; ++idx) { | |
| HWY_DASSERT(idx < kNum64); | |
| if (!bits_[idx].All()) { | |
| const size_t first0 = idx * 64 + bits_[idx].First0(); | |
| HWY_DASSERT(first0 < kMaxSize); | |
| return first0; | |
| } | |
| } | |
| } | |
| // Calls `func(i)` for each `i` in the set. It is safe for `func` to modify | |
| // the set, but the current Foreach call is only affected if changing one of | |
| // the not yet visited BitSet64. | |
| template <class Func> | |
| void Foreach(const Func& func) const { | |
| for (size_t idx = 0; idx < kNum64; ++idx) { | |
| bits_[idx].Foreach([idx, &func](size_t mod) { func(idx * 64 + mod); }); | |
| } | |
| } | |
| size_t Count() const { | |
| size_t total = 0; | |
| for (const BitSet64& bits : bits_) { | |
| total += bits.Count(); | |
| } | |
| return total; | |
| } | |
| private: | |
| static constexpr size_t kNum64 = DivCeil(kMaxSize, size_t{64}); | |
| BitSet64 bits_[kNum64]; | |
| }; | |
| // Any number of bits, flat array, atomic updates to the u64. | |
| template <size_t kMaxSize> | |
| class AtomicBitSet { | |
| static_assert(kMaxSize != 0, "AtomicBitSet requires non-zero size"); | |
| // Bits may signal something to other threads, hence relaxed is insufficient. | |
| // Acq/Rel ensures a happens-before relationship. | |
| static constexpr auto kAcq = std::memory_order_acquire; | |
| static constexpr auto kRel = std::memory_order_release; | |
| public: | |
| constexpr size_t MaxSize() const { return kMaxSize; } | |
| // No harm if `i` is already set. | |
| void Set(size_t i) { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| bits_[idx].fetch_or(1ULL << mod, kRel); | |
| } | |
| void Clear(size_t i) { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| bits_[idx].fetch_and(~(1ULL << mod), kRel); | |
| HWY_DASSERT(!Get(i)); | |
| } | |
| bool Get(size_t i) const { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| return ((bits_[idx].load(kAcq) & (1ULL << mod))) != 0; | |
| } | |
| // Returns true if Get(i) would return true for any i in [0, kMaxSize). | |
| bool Any() const { | |
| for (const std::atomic<uint64_t>& bits : bits_) { | |
| if (bits.load(kAcq)) return true; | |
| } | |
| return false; | |
| } | |
| // Returns true if Get(i) would return true for all i in [0, kMaxSize). | |
| bool All() const { | |
| for (size_t idx = 0; idx < kNum64 - 1; ++idx) { | |
| if (bits_[idx].load(kAcq) != ~uint64_t{0}) return false; | |
| } | |
| constexpr size_t kRemainder = kMaxSize % 64; | |
| const uint64_t last_bits = bits_[kNum64 - 1].load(kAcq); | |
| if (kRemainder == 0) { | |
| return last_bits == ~uint64_t{0}; | |
| } | |
| return PopCount(last_bits) == kRemainder; | |
| } | |
| // Returns lowest i such that `Get(i)`. Caller must first ensure `Any()`! | |
| size_t First() const { | |
| HWY_DASSERT(Any()); | |
| for (size_t idx = 0;; ++idx) { | |
| HWY_DASSERT(idx < kNum64); | |
| const uint64_t bits = bits_[idx].load(kAcq); | |
| if (bits != 0) { | |
| return idx * 64 + Num0BitsBelowLS1Bit_Nonzero64(bits); | |
| } | |
| } | |
| } | |
| // Returns lowest i such that `!Get(i)`. Caller must first ensure `!All()`! | |
| size_t First0() const { | |
| HWY_DASSERT(!All()); | |
| for (size_t idx = 0;; ++idx) { | |
| HWY_DASSERT(idx < kNum64); | |
| const uint64_t inv_bits = ~bits_[idx].load(kAcq); | |
| if (inv_bits != 0) { | |
| const size_t first0 = | |
| idx * 64 + Num0BitsBelowLS1Bit_Nonzero64(inv_bits); | |
| HWY_DASSERT(first0 < kMaxSize); | |
| return first0; | |
| } | |
| } | |
| } | |
| // Calls `func(i)` for each `i` in the set. It is safe for `func` to modify | |
| // the set, but the current Foreach call is only affected if changing one of | |
| // the not yet visited uint64_t. | |
| template <class Func> | |
| void Foreach(const Func& func) const { | |
| for (size_t idx = 0; idx < kNum64; ++idx) { | |
| uint64_t remaining_bits = bits_[idx].load(kAcq); | |
| while (remaining_bits != 0) { | |
| const size_t i = Num0BitsBelowLS1Bit_Nonzero64(remaining_bits); | |
| remaining_bits &= remaining_bits - 1; // clear LSB | |
| func(idx * 64 + i); | |
| } | |
| } | |
| } | |
| size_t Count() const { | |
| size_t total = 0; | |
| for (const std::atomic<uint64_t>& bits : bits_) { | |
| total += PopCount(bits.load(kAcq)); | |
| } | |
| return total; | |
| } | |
| private: | |
| static constexpr size_t kNum64 = DivCeil(kMaxSize, size_t{64}); | |
| std::atomic<uint64_t> bits_[kNum64] = {}; | |
| }; | |
| // Two-level bitset for up to `kMaxSize` <= 4096 values. The iterators | |
| // (`Any/First/Foreach/Count`) are more efficient than `BitSet` for sparse sets. | |
| // This comes at the cost of slightly slower mutators (`Set/Clear`). | |
| template <size_t kMaxSize = 4096> | |
| class BitSet4096 { | |
| static_assert(kMaxSize != 0, "BitSet4096 requires non-zero size"); | |
| public: | |
| constexpr size_t MaxSize() const { return kMaxSize; } | |
| // No harm if `i` is already set. | |
| void Set(size_t i) { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| bits_[idx].Set(mod); | |
| nonzero_.Set(idx); | |
| HWY_DASSERT(Get(i)); | |
| } | |
| // Equivalent to Set(i) for i in [0, 64) where (bits >> i) & 1. This does | |
| // not clear any existing bits. | |
| void SetNonzeroBitsFrom64(uint64_t bits) { | |
| bits_[0].SetNonzeroBitsFrom64(bits); | |
| if (bits) nonzero_.Set(0); | |
| } | |
| void Clear(size_t i) { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| bits_[idx].Clear(mod); | |
| if (!bits_[idx].Any()) { | |
| nonzero_.Clear(idx); | |
| } | |
| HWY_DASSERT(!Get(i)); | |
| } | |
| bool Get(size_t i) const { | |
| HWY_DASSERT(i < kMaxSize); | |
| const size_t idx = i / 64; | |
| const size_t mod = i % 64; | |
| return bits_[idx].Get(mod); | |
| } | |
| // Returns true if `Get(i)` would return true for any i in [0, kMaxSize). | |
| bool Any() const { return nonzero_.Any(); } | |
| // Returns true if `Get(i)` would return true for all i in [0, kMaxSize). | |
| bool All() const { | |
| // Do not check `nonzero_.All()` - that only works if `kMaxSize` is 4096. | |
| if (nonzero_.Count() != kNum64) return false; | |
| return Count() == kMaxSize; | |
| } | |
| // Returns lowest i such that `Get(i)`. Caller must first ensure `Any()`! | |
| size_t First() const { | |
| HWY_DASSERT(Any()); | |
| const size_t idx = nonzero_.First(); | |
| return idx * 64 + bits_[idx].First(); | |
| } | |
| // Returns lowest i such that `!Get(i)`. Caller must first ensure `!All()`! | |
| size_t First0() const { | |
| HWY_DASSERT(!All()); | |
| // It is likely not worthwhile to have a separate `BitSet64` for `not_all_`, | |
| // hence iterate over all u64. | |
| for (size_t idx = 0;; ++idx) { | |
| HWY_DASSERT(idx < kNum64); | |
| if (!bits_[idx].All()) { | |
| const size_t first0 = idx * 64 + bits_[idx].First0(); | |
| HWY_DASSERT(first0 < kMaxSize); | |
| return first0; | |
| } | |
| } | |
| } | |
| // Returns uint64_t(Get(i)) << i for i in [0, 64). | |
| uint64_t Get64() const { return bits_[0].Get64(); } | |
| // Calls `func(i)` for each `i` in the set. It is safe for `func` to modify | |
| // the set, but the current Foreach call is only affected if changing one of | |
| // the not yet visited BitSet64 for which Any() is true. | |
| template <class Func> | |
| void Foreach(const Func& func) const { | |
| nonzero_.Foreach([&func, this](size_t idx) { | |
| bits_[idx].Foreach([idx, &func](size_t mod) { func(idx * 64 + mod); }); | |
| }); | |
| } | |
| size_t Count() const { | |
| size_t total = 0; | |
| nonzero_.Foreach( | |
| [&total, this](size_t idx) { total += bits_[idx].Count(); }); | |
| return total; | |
| } | |
| private: | |
| static_assert(kMaxSize <= 64 * 64, "One BitSet64 insufficient"); | |
| static constexpr size_t kNum64 = DivCeil(kMaxSize, size_t{64}); | |
| BitSet64 nonzero_; | |
| BitSet64 bits_[kNum64]; | |
| }; | |
| } // namespace hwy | |
| #endif // HIGHWAY_HWY_BIT_SET_H_ |