Commit 8d1d46c4 authored by Philipp Götze's avatar Philipp Götze
Browse files

🔨 Compacted Bitmap a bit

parent 432fa08d
......@@ -31,284 +31,270 @@ static constexpr uint8_t tab64[64] = {
29, 10, 13, 21, 56, 45, 25, 31, 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5};
/**
* Helper class to zero out the unused high-order bits in the highest word.
* Own bitmap implementation to get access to underlying words.
* (inspired by GCC bitset)
*
* @tparam EB extra bits to clean.
* @tparam NUM_BITS the number of bits to store.
*/
template <size_t EB>
struct Sanitize {
static void sanitize(BITMAP_WORD& val) noexcept {
val &= ~((~static_cast<BITMAP_WORD>(0)) << EB);
template <size_t NUM_BITS>
class Bitmap {
using Word = BITMAP_WORD;
static constexpr auto BITS_PER_WORD = sizeof(Word) * 8;
static constexpr auto NUM_WORDS = (NUM_BITS + BITS_PER_WORD - 1) / BITS_PER_WORD;
using WordArray = Word[NUM_WORDS];
WordArray words;
Word& getWord(size_t bitPos) noexcept { return words[bitPos / BITS_PER_WORD]; }
constexpr Word getWord(size_t bitPos) const noexcept { return words[bitPos / BITS_PER_WORD]; }
constexpr Word maskBit(size_t bitPos) const noexcept {
return (static_cast<Word>(1)) << (bitPos % BITS_PER_WORD);
}
};
template <>
struct Sanitize<0> {
static void sanitize(BITMAP_WORD) noexcept {}
};
void check(size_t bitpos) const {
if (bitpos >= NUM_BITS)
throw std::out_of_range("pos (which is " + std::to_string(bitpos) +
") >= NUM_BITS (which is " + std::to_string(NUM_BITS) + ')');
}
/**
* Own bitmap implementation to get access to underlying words.
* (inspired by GCC bitset)
*
* @tparam NUM_BITS the number of bits to store.
* Helper method to zero out the unused high-order bits in the highest word.
*/
template <size_t NUM_BITS>
class Bitmap {
using Word = BITMAP_WORD;
static constexpr auto BITS_PER_WORD = sizeof(Word) * 8;
static constexpr auto NUM_WORDS = (NUM_BITS + BITS_PER_WORD - 1) / BITS_PER_WORD;
using WordArray = Word[NUM_WORDS];
WordArray words;
void sanitize() noexcept {
words[NUM_WORDS - 1] &= ~((~static_cast<BITMAP_WORD>(0)) << (NUM_BITS % BITS_PER_WORD));
}
Word& getWord(size_t bitPos) noexcept { return words[bitPos / BITS_PER_WORD]; }
public:
/**
* "This encapsulates the concept of a single bit. An instance of this class is a proxy for
* an actual bit."
*/
class Bitref {
friend class Bitmap;
Word* wordPtr;
size_t bitPos;
constexpr Word getWord(size_t bitPos) const noexcept { return words[bitPos / BITS_PER_WORD]; }
Bitref();
constexpr Word maskBit(size_t bitPos) const noexcept {
return (static_cast<Word>(1)) << (bitPos % BITS_PER_WORD);
public:
Bitref(Bitmap& bm, size_t pos) noexcept {
wordPtr = &bm.getword(pos);
bitPos = pos % BITS_PER_WORD;
}
void check(size_t bitpos) const {
if (bitpos >= NUM_BITS)
throw std::out_of_range("pos (which is " + std::to_string(bitpos) +
") >= NUM_BITS (which is " + std::to_string(NUM_BITS) + ')');
}
~Bitref() noexcept {}
void sanitize() noexcept {
Sanitize<NUM_BITS % BITS_PER_WORD>::sanitize(words[NUM_WORDS - 1]);
Bitref& operator=(bool x) noexcept {
if (x)
*wordPtr |= maskBit(bitPos);
else
*wordPtr &= ~maskBit(bitPos);
return *this;
}
public:
/**
* "This encapsulates the concept of a single bit. An instance of this class is a proxy for
* an actual bit."
*/
class Bitref {
friend class Bitmap;
Word* wordPtr;
size_t bitPos;
Bitref();
public:
Bitref(Bitmap& bm, size_t pos) noexcept {
wordPtr = &bm.getword(pos);
bitPos = pos % BITS_PER_WORD;
}
~Bitref() noexcept {}
Bitref& operator=(const Bitref& other) noexcept {
if ((*(other.wordPtr) & Bitmap::maskbit(other.bitPos)))
*wordPtr |= Bitmap::maskbit(bitPos);
else
*wordPtr &= ~Bitmap::maskbit(bitPos);
return *this;
}
Bitref& operator=(bool x) noexcept {
if (x)
*wordPtr |= maskBit(bitPos);
else
*wordPtr &= ~maskBit(bitPos);
return *this;
}
bool operator~() const noexcept { return (*(wordPtr)&Bitmap::maskbit(bitPos)) == 0; }
Bitref& operator=(const Bitref& other) noexcept {
if ((*(other.wordPtr) & Bitmap::maskbit(other.bitPos)))
*wordPtr |= Bitmap::maskbit(bitPos);
else
*wordPtr &= ~Bitmap::maskbit(bitPos);
return *this;
}
operator bool() const noexcept { return (*(wordPtr)&Bitmap::maskbit(bitPos)) != 0; }
bool operator~() const noexcept { return (*(wordPtr)&Bitmap::maskbit(bitPos)) == 0; }
Bitref& flip() noexcept {
*wordPtr ^= Bitmap::maskbit(bitPos);
return *this;
}
}; /// end class Bitref
operator bool() const noexcept { return (*(wordPtr)&Bitmap::maskbit(bitPos)) != 0; }
friend class Bitref;
Bitref& flip() noexcept {
*wordPtr ^= Bitmap::maskbit(bitPos);
return *this;
}
}; /// end class Bitref
friend class Bitref;
/**
* Constructor for creating new array of words.
*/
constexpr Bitmap() noexcept : words() {}
/**
* Test if a bit at the given position is set.
*
* @param pos the bit position (index) to test.
* @return the true if set, false otherwise.
*/
constexpr bool test(size_t pos) const {
check(pos);
return getWord(pos) & maskBit(pos);
}
/**
* Constructor for creating new array of words.
*/
constexpr Bitmap() noexcept : words() {}
/**
* Array-indexing support.
*
* @param pos the bit position (index) to test.
* @return A bool for a const Bitmap and an instance of the proxy class Bitref for non-const
* Bitmap.
*/
constexpr bool operator[](size_t pos) const {
check(pos);
return getWord(pos) & maskBit(pos);
}
Bitref operator[](size_t pos) { return Bitref(*this, pos); }
/**
* Test if a bit at the given position is set.
*
* @param pos the bit position (index) to test.
* @return the true if set, false otherwise.
*/
constexpr bool test(size_t pos) const {
check(pos);
return getWord(pos) & maskBit(pos);
}
template <class CharType, class Traits, class Alloc>
void copyToString(std::basic_string<CharType, Traits, Alloc>&, CharType, CharType) const;
template <class CharType, class Traits, class Alloc>
void copyToString(std::basic_string<CharType, Traits, Alloc>& s) const {
copyToString(s, CharType('0'), CharType('1'));
}
/**
* Array-indexing support.
*
* @param pos the bit position (index) to test.
* @return A bool for a const Bitmap and an instance of the proxy class Bitref for non-const
* Bitmap.
*/
constexpr bool operator[](size_t pos) const {
check(pos);
return getWord(pos) & maskBit(pos);
}
Bitref operator[](size_t pos) { return Bitref(*this, pos); }
/**
* Determine the number of set bits.
*
* @return the counted true bits.
*/
size_t count() const noexcept {
size_t c = 0;
for (size_t i = 0; i < NUM_WORDS; ++i) {
/// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
auto w = words[i];
w = w - ((w >> 1) & (Word) ~(Word)0 / 3);
w = (w & (Word) ~(Word)0 / 15 * 3) + ((w >> 2) & (Word) ~(Word)0 / 15 * 3);
w = (w + (w >> 4)) & (Word) ~(Word)0 / 255 * 15;
c += (Word)(w * ((Word) ~(Word)0 / 255)) >> (sizeof(Word) - 1) * 8;
}
return c;
}
template <class CharType, class Traits, class Alloc>
void copyToString(std::basic_string<CharType, Traits, Alloc>&, CharType, CharType) const;
template <class CharType, class Traits, class Alloc>
void copyToString(std::basic_string<CharType, Traits, Alloc>& s) const {
copyToString(s, CharType('0'), CharType('1'));
}
/**
* Tests whether all the bits are set.
*
* @return true if all are set.
*/
bool all() const noexcept {
for (size_t i = 0; i < NUM_WORDS - 1; ++i)
if (words[i] != ~static_cast<Word>(0)) return false;
return words[NUM_WORDS - 1] ==
(~static_cast<Word>(0) >> (NUM_WORDS * BITS_PER_WORD - NUM_BITS));
}
/**
* Set all bits to its opposite value.
*
* @return a reference to the updated bitmap.
*/
Bitmap<NUM_BITS>& flip() noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) words[i] = ~words[i];
sanitize();
return *this;
/**
* Determine the number of set bits.
*
* @return the counted true bits.
*/
size_t count() const noexcept {
size_t c = 0;
for (size_t i = 0; i < NUM_WORDS; ++i) {
/// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
auto w = words[i];
w = w - ((w >> 1) & (Word) ~(Word)0 / 3);
w = (w & (Word) ~(Word)0 / 15 * 3) + ((w >> 2) & (Word) ~(Word)0 / 15 * 3);
w = (w + (w >> 4)) & (Word) ~(Word)0 / 255 * 15;
c += (Word)(w * ((Word) ~(Word)0 / 255)) >> (sizeof(Word) - 1) * 8;
}
return c;
}
/**
* Set all bits to true/1.
*
* @return a reference to the updated bitmap.
*/
Bitmap<NUM_BITS>& set() noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) words[i] = ~static_cast<Word>(0);
sanitize();
return *this;
}
/**
* Tests whether all the bits are set.
*
* @return true if all are set.
*/
bool all() const noexcept {
for (size_t i = 0; i < NUM_WORDS - 1; ++i)
if (words[i] != ~static_cast<Word>(0)) return false;
return words[NUM_WORDS - 1] ==
(~static_cast<Word>(0) >> (NUM_WORDS * BITS_PER_WORD - NUM_BITS));
}
/**
* Set all bits to its opposite value.
*
* @return a reference to the updated bitmap.
*/
Bitmap<NUM_BITS>& flip() noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) words[i] = ~words[i];
sanitize();
return *this;
}
/**
* Set bit at given position to value (default true/1).
*
* @param pos the bit position (index) to update.
* @param value either true or false, defaults to true.
* @return a reference to the updated bitmap.
*/
Bitmap<NUM_BITS>& set(size_t pos, bool value = true) {
check(pos);
if (value)
getWord(pos) |= maskBit(pos);
else
getWord(pos) &= ~maskBit(pos);
return *this;
}
/**
* Set all bits to true/1.
*
* @return a reference to the updated bitmap.
*/
Bitmap<NUM_BITS>& set() noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) words[i] = ~static_cast<Word>(0);
sanitize();
return *this;
}
/**
* Reset bit at given position.
*
* @param pos the bit position (index) to reset.
* @return a reference to the updated bitmap
*/
Bitmap<NUM_BITS>& reset(size_t pos) {
check(pos);
/**
* Set bit at given position to value (default true/1).
*
* @param pos the bit position (index) to update.
* @param value either true or false, defaults to true.
* @return a reference to the updated bitmap.
*/
Bitmap<NUM_BITS>& set(size_t pos, bool value = true) {
check(pos);
if (value)
getWord(pos) |= maskBit(pos);
else
getWord(pos) &= ~maskBit(pos);
return *this;
}
return *this;
}
/**
* Retrieve the underlying words of this bitmap.
*
* @return a const reference to the word array.
*/
const WordArray& getWords() const noexcept { return words; }
/**
* Find a free slot in the bitmap.
*
* @return the index of the free bit.
*/
auto getFreeZero() const noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) {
/// http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
const auto w = ~words[i]; ///< we need the complement here!
if (w != 0) {
/// count consecutive one bits using multiply and lookup
/// Applying deBruijn hash function + lookup
return 64 * i + tab64[((uint64_t)((w & -w) * deBruijnSeq)) >> 58];
}
}
/// Valid result is between 0 and 63; 64 means no free position
return 64 * NUM_WORDS;
}
/**
* Reset bit at given position.
*
* @param pos the bit position (index) to reset.
* @return a reference to the updated bitmap
*/
Bitmap<NUM_BITS>& reset(size_t pos) {
check(pos);
getWord(pos) &= ~maskBit(pos);
return *this;
}
/**
* Retrieve the underlying words of this bitmap.
*
* @return a const reference to the word array.
*/
const WordArray& getWords() const noexcept { return words; }
/**
* Find first set position in the bitmap.
*
* @return the index of the set bit.
*/
auto getFirstSet() const noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) {
const auto w = words[i];
if (w != 0) {
/// count consecutive zero bits using multiply and lookup
/// Applying deBruijn hash function + lookup
return 64 * i + tab64[((uint64_t)((w & -w) * deBruijnSeq)) >> 58];
}
/**
* Find a free slot in the bitmap.
*
* @return the index of the free bit.
*/
auto getFreeZero() const noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) {
/// http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
const auto w = ~words[i]; ///< we need the complement here!
if (w != 0) {
/// count consecutive one bits using multiply and lookup
/// Applying deBruijn hash function + lookup
return 64 * i + tab64[((uint64_t)((w & -w) * deBruijnSeq)) >> 58];
}
/// Valid result is between 0 and 63; 64 means no free position
return 64 * NUM_WORDS;
}
}; /// end class Bitmap
template <size_t NB>
template <class CharType, class Traits, class Alloc>
void Bitmap<NB>::copyToString(std::basic_string<CharType, Traits, Alloc>& s, CharType zero,
CharType one) const {
s.assign(NB, zero);
for (size_t i = NB; i > 0; --i)
if (getWord(i - 1) & maskBit(i - 1)) Traits::assign(s[NB - i], one);
/// Valid result is between 0 and 63; 64 means no free position
return 64 * NUM_WORDS;
}
/**
* For pretty binary printing.
* Find first set position in the bitmap.
*
* @return the index of the set bit.
*/
template <class CharType, class Traits, size_t NB>
std::basic_ostream<CharType, Traits>& operator<<(std::basic_ostream<CharType, Traits>& os,
const dbis::Bitmap<NB>& bm) {
std::basic_string<CharType, Traits> tmp;
const std::ctype<CharType>& ct = std::use_facet<std::ctype<CharType> >(os.getloc());
bm.copyToString(tmp, ct.widen('0'), ct.widen('1'));
return os << tmp;
auto getFirstSet() const noexcept {
for (size_t i = 0; i < NUM_WORDS; ++i) {
const auto w = words[i];
if (w != 0) {
/// count consecutive zero bits using multiply and lookup
/// Applying deBruijn hash function + lookup
return 64 * i + tab64[((uint64_t)((w & -w) * deBruijnSeq)) >> 58];
}
}
/// Valid result is between 0 and 63; 64 means no free position
return 64 * NUM_WORDS;
}
}; /// end class Bitmap
template <size_t NB>
template <class CharType, class Traits, class Alloc>
void Bitmap<NB>::copyToString(std::basic_string<CharType, Traits, Alloc>& s, CharType zero,
CharType one) const {
s.assign(NB, zero);
for (size_t i = NB; i > 0; --i)
if (getWord(i - 1) & maskBit(i - 1)) Traits::assign(s[NB - i], one);
}
} // namespace dbis
/**
* For pretty binary printing.
*/
template <class CharType, class Traits, size_t NB>
std::basic_ostream<CharType, Traits>& operator<<(std::basic_ostream<CharType, Traits>& os,
const dbis::Bitmap<NB>& bm) {
std::basic_string<CharType, Traits> tmp;
const std::ctype<CharType>& ct = std::use_facet<std::ctype<CharType> >(os.getloc());
bm.copyToString(tmp, ct.widen('0'), ct.widen('1'));
return os << tmp;
}
#endif /// DBIS_BITMAP_HPP
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment