/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkBitSet_DEFINED #define SkBitSet_DEFINED #include "include/private/SkMalloc.h" #include "include/private/SkTemplates.h" #include "src/core/SkMathPriv.h" #include #include #include #include class SkBitSet { public: explicit SkBitSet(size_t size) : fSize(size) // May http://wg21.link/p0593 be accepted. , fChunks((Chunk*)sk_calloc_throw(NumChunksFor(fSize) * sizeof(Chunk))) {} SkBitSet(const SkBitSet&) = delete; SkBitSet& operator=(const SkBitSet&) = delete; SkBitSet(SkBitSet&& that) { *this = std::move(that); } SkBitSet& operator=(SkBitSet&& that) { if (this != &that) { this->fSize = that.fSize; this->fChunks = std::move(that.fChunks); that.fSize = 0; } return *this; } ~SkBitSet() = default; /** Set the value of the index-th bit to true. */ void set(size_t index) { SkASSERT(index < fSize); *this->chunkFor(index) |= ChunkMaskFor(index); } /** Sets every bit in the bitset to true. */ void set() { Chunk* chunks = fChunks.get(); const size_t numChunks = NumChunksFor(fSize); std::memset(chunks, 0xFF, sizeof(Chunk) * numChunks); } /** Set the value of the index-th bit to false. */ void reset(size_t index) { SkASSERT(index < fSize); *this->chunkFor(index) &= ~ChunkMaskFor(index); } /** Sets every bit in the bitset to false. */ void reset() { Chunk* chunks = fChunks.get(); const size_t numChunks = NumChunksFor(fSize); std::memset(chunks, 0, sizeof(Chunk) * numChunks); } bool test(size_t index) const { SkASSERT(index < fSize); return SkToBool(*this->chunkFor(index) & ChunkMaskFor(index)); } size_t size() const { return fSize; } // Calls f(size_t index) for each set index. template void forEachSetIndex(FN f) const { const Chunk* chunks = fChunks.get(); const size_t numChunks = NumChunksFor(fSize); for (size_t i = 0; i < numChunks; ++i) { if (Chunk chunk = chunks[i]) { // There are set bits const size_t index = i * kChunkBits; for (size_t j = 0; j < kChunkBits; ++j) { if (0x1 & (chunk >> j)) { f(index + j); } } } } } // Use std::optional when possible. class OptionalIndex { bool fHasValue; size_t fValue; public: OptionalIndex() : fHasValue(false) {} constexpr OptionalIndex(size_t index) : fHasValue(true), fValue(index) {} constexpr size_t* operator->() { return &fValue; } constexpr const size_t* operator->() const { return &fValue; } constexpr size_t& operator*() & { return fValue; } constexpr const size_t& operator*() const& { return fValue; } constexpr size_t&& operator*() && { return std::move(fValue); } constexpr const size_t&& operator*() const&& { return std::move(fValue); } constexpr explicit operator bool() const noexcept { return fHasValue; } constexpr bool has_value() const noexcept { return fHasValue; } constexpr size_t& value() & { return fValue; } constexpr const size_t& value() const & { return fValue; } constexpr size_t&& value() && { return std::move(fValue); } constexpr const size_t&& value() const && { return std::move(fValue); } template constexpr size_t value_or(U&& defaultValue) const& { return bool(*this) ? **this : static_cast(std::forward(defaultValue)); } template constexpr size_t value_or(U&& defaultValue) && { return bool(*this) ? std::move(**this) : static_cast(std::forward(defaultValue)); } }; // If any bits are set, returns the index of the first. OptionalIndex findFirst() { const Chunk* chunks = fChunks.get(); const size_t numChunks = NumChunksFor(fSize); for (size_t i = 0; i < numChunks; ++i) { if (Chunk chunk = chunks[i]) { // There are set bits static_assert(kChunkBits <= std::numeric_limits::digits, "SkCTZ"); const size_t bitIndex = i * kChunkBits + SkCTZ(chunk); return OptionalIndex(bitIndex); } } return OptionalIndex(); } // If any bits are not set, returns the index of the first. OptionalIndex findFirstUnset() { const Chunk* chunks = fChunks.get(); const size_t numChunks = NumChunksFor(fSize); for (size_t i = 0; i < numChunks; ++i) { if (Chunk chunk = ~chunks[i]) { // if there are unset bits ... static_assert(kChunkBits <= std::numeric_limits::digits, "SkCTZ"); const size_t bitIndex = i * kChunkBits + SkCTZ(chunk); if (bitIndex >= fSize) { break; } return OptionalIndex(bitIndex); } } return OptionalIndex(); } private: size_t fSize; using Chunk = uint32_t; static_assert(std::numeric_limits::radix == 2); static constexpr size_t kChunkBits = std::numeric_limits::digits; static_assert(kChunkBits == sizeof(Chunk)*CHAR_BIT, "SkBitSet must use every bit in a Chunk"); std::unique_ptr> fChunks; Chunk* chunkFor(size_t index) const { return fChunks.get() + (index / kChunkBits); } static constexpr Chunk ChunkMaskFor(size_t index) { return (Chunk)1 << (index & (kChunkBits-1)); } static constexpr size_t NumChunksFor(size_t size) { return (size + (kChunkBits-1)) / kChunkBits; } }; #endif