Files
pvxs/src/bitmask.h
T
Michael Davidsaver c503eece31 spell check
2021-08-13 15:18:31 -07:00

266 lines
7.8 KiB
C++

/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvxs is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#ifndef PVXS_BITMASK_H
#define PVXS_BITMASK_H
#include <ostream>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <cstdint>
#include <pvxs/version.h>
namespace pvxs {
namespace detail {
// base type, defines operations which can be performed on an BitMask expression
template <typename Sub>
struct BitBase {
size_t size() const { return (*static_cast<const Sub*>(this)).size(); }
size_t wsize() const { return (*static_cast<const Sub*>(this)).wsize(); }
uint64_t word(size_t i) const { return (*static_cast<const Sub*>(this)).word(i); }
};
// unary negate
template <typename Inp>
struct BitNot : public BitBase<BitNot<Inp>>
{
const Inp& inp;
BitNot(const Inp& inp) :inp(inp) {}
size_t size() const { return inp.size(); }
size_t wsize() const { return inp.wsize(); }
uint64_t word(size_t i) const { return ~inp.word(i); }
};
template <typename Inp>
constexpr
BitNot<BitBase<Inp>>
operator!(const BitBase<Inp>& inp)
{
return BitNot<BitBase<Inp>>{inp};
}
// binary bitwise and
template <typename Lhs, typename Rhs>
struct BitAnd : public BitBase<BitAnd<Lhs,Rhs>>
{
const Lhs& lhs;
const Rhs& rhs;
BitAnd(const Lhs& lhs, const Rhs& rhs) :lhs(lhs), rhs(rhs) {}
size_t size() const { return std::min(lhs.size(), rhs.size()); }
size_t wsize() const { return std::min(lhs.wsize(), rhs.wsize()); }
uint64_t word(size_t i) const { return lhs.word(i) & rhs.word(i); }
};
template<typename Lhs, typename Rhs>
BitAnd<BitBase<Lhs>, BitBase<Rhs>>
operator &(const BitBase<Lhs>& lhs, const BitBase<Rhs>& rhs)
{
if(lhs.size()!=rhs.size())
throw std::logic_error("op size mis-match"); // this sucks, need to figure out a way to handle different size input
return BitAnd<BitBase<Lhs>, BitBase<Rhs>>{lhs, rhs};
}
// binary bitwise or
template <typename Lhs, typename Rhs>
struct BitOr : public BitBase<BitOr<Lhs,Rhs>>
{
const Lhs& lhs;
const Rhs& rhs;
BitOr(const Lhs& lhs, const Rhs& rhs) :lhs(lhs), rhs(rhs) {}
size_t size() const { return std::max(lhs.size(), rhs.size()); }
size_t wsize() const { return std::max(lhs.wsize(), rhs.wsize()); }
uint64_t word(size_t i) const { return lhs.word(i) | rhs.word(i); }
};
template<typename Lhs, typename Rhs>
BitOr<BitBase<Lhs>, BitBase<Rhs>>
operator |(const BitBase<Lhs>& lhs, const BitBase<Rhs>& rhs)
{
if(lhs.size()!=rhs.size())
throw std::logic_error("op size mis-match");
return BitOr<BitBase<Lhs>, BitBase<Rhs>>{lhs, rhs};
}
} // namespace detail
class BitMask : public detail::BitBase<BitMask> {
// bit 0 - lsb of word 0
// bit 63 - msb of word 0
// bit 64 - lsb of word 1
std::vector<uint64_t> _words;
// actual size in bits
// _words.size()*64u >= _size
uint16_t _size=0u;
public:
typedef bool value_type;
//! Empty mask with size()==0
BitMask() = default;
// movable, not copyable
BitMask(const BitMask&) = delete;
BitMask(BitMask&&) noexcept;
BitMask& operator=(const BitMask&) = delete;
BitMask& operator=(BitMask&&) noexcept;
~BitMask() = default;
//! cleared mask with size()==0
explicit BitMask(size_t nbits) {
resize(nbits);
}
//! Initialize certain bit numbers. ensure size()>=nbits.
PVXS_API
BitMask(std::initializer_list<size_t> bits, size_t nbits=0);
//! number of bits
inline size_t size() const { return _size; }
//! size()==0
inline bool empty() const { return _size==0u; }
//! number of storage words
inline size_t wsize() const { return _words.size(); }
//! storage word
inline uint64_t& word(size_t i) { return _words[i]; }
inline const uint64_t& word(size_t i) const { return _words[i]; }
PVXS_API
void resize(size_t bits);
//! Returns index of first set bit in range [start, size()] inclusive.
//! Returns size() if no bits are set.
PVXS_API
size_t findSet(size_t start=0u) const;
private:
template<typename BR>
class _BitRef {
friend class BitMask;
BR* _mask;
size_t _bit;
constexpr _BitRef(BR* mask, size_t bit) :_mask(mask), _bit(bit) {}
public:
operator bool() const {
return _mask->_words[_bit/64u]&(uint64_t(1)<<(_bit%64u));
}
_BitRef& operator=(bool v) {
auto& word = _mask->_words[_bit/64u];
if(v)
word |= uint64_t(1)<<(_bit%64u);
else
word &= ~(uint64_t(1)<<(_bit%64u));
return *this;
}
};
template<typename BR> friend class _BitRef;
public:
typedef _BitRef<BitMask> reference;
typedef _BitRef<const BitMask> const_reference;
reference operator[](size_t bit) { return _BitRef<BitMask>{this, bit}; }
const_reference operator[](size_t bit) const { return _BitRef<const BitMask>{this, bit}; }
private:
class _SetIter {
friend BitMask;
const BitMask* _mask = nullptr;
size_t _bit = 0u;
public:
constexpr _SetIter() = default;
constexpr _SetIter(const BitMask* mask, size_t bit) :_mask(mask), _bit(bit) {}
size_t operator*() const { return _bit; }
_SetIter& operator++() { _bit=_mask->findSet(_bit+1); return *this; }
_SetIter operator++(int) { _SetIter ret{*this}; _bit=_mask->findSet(_bit+1); return ret;}
bool operator==(const _SetIter& o) { return _bit==o._bit; }
bool operator!=(const _SetIter& o) { return _bit!=o._bit; }
};
class _OnlySet {
friend BitMask;
const BitMask* _mask;
size_t a, b;
constexpr explicit _OnlySet(const BitMask* mask, size_t a, size_t b) :_mask(mask), a(a), b(b) {}
public:
typedef _SetIter iterator;
iterator begin() const { return iterator{_mask, _mask->findSet(a)}; }
iterator end() const { return iterator{_mask, b}; }
};
public:
//! return object which can be iterated to find all set bits
_OnlySet onlySet() const { return _OnlySet{this, 0u, size()}; }
//! return object which can be iterated to find all set bits in range [a, b)
_OnlySet onlySet(size_t a, size_t b) const { return _OnlySet{this, a, b}; }
// all()
// evaluate expression
template<typename Inp>
BitMask(const detail::BitBase<Inp>& expr) {
resize(expr.size());
for(size_t i=0, N=expr.wsize(); i<N; i++)
_words[i] = expr.word(i);
}
// evaluate expression
template<typename Inp>
BitMask& operator=(const detail::BitBase<Inp>& expr) {
resize(expr.size());
for(size_t i=0, N=expr.wsize(); i<N; i++)
_words[i] = expr.word(i);
return *this;
}
// evaluate expression
template<typename Inp>
BitMask& operator|=(const detail::BitBase<Inp>& expr) {
resize(expr.size());
for(size_t i=0, N=expr.wsize(); i<N; i++)
_words[i] |= expr.word(i);
return *this;
}
// evaluate expression
template<typename Inp>
BitMask& operator&=(const detail::BitBase<Inp>& expr) {
resize(expr.size());
for(size_t i=0, N=expr.wsize(); i<N; i++)
_words[i] &= expr.word(i);
return *this;
}
friend
PVXS_API
bool operator==(const BitMask& lhs, const BitMask& rhs);
};
PVXS_API
std::ostream& operator<<(std::ostream& strm, const BitMask& mask);
PVXS_API
bool operator==(const BitMask& lhs, const BitMask& rhs);
inline bool operator!=(const BitMask& lhs, const BitMask& rhs) {
return !(lhs==rhs);
}
namespace impl {
struct Buffer;
PVXS_API
void to_wire(Buffer& buf, const BitMask& mask);
PVXS_API
void from_wire(Buffer& buf, BitMask& mask);
}
} // namespace pvxs
#endif // PVXS_BITMASK_H