add BitMask

This commit is contained in:
Michael Davidsaver
2019-12-10 15:21:57 -08:00
parent 801d295c1f
commit 8fca41b683
5 changed files with 487 additions and 0 deletions
+2
View File
@@ -38,6 +38,7 @@ INC += pvxs/versionNum.h
INC += pvxs/log.h
INC += pvxs/unittest.h
INC += pvxs/util.h
INC += pvxs/bitmask.h
INC += pvxs/sharedArray.h
INC += pvxs/data.h
INC += pvxs/server.h
@@ -47,6 +48,7 @@ LIBRARY = pvxs
LIB_SRCS += log.cpp
LIB_SRCS += unittest.cpp
LIB_SRCS += util.cpp
LIB_SRCS += bitmask.cpp
LIB_SRCS += data.cpp
LIB_SRCS += evhelper.cpp
LIB_SRCS += udp_collector.cpp
+105
View File
@@ -0,0 +1,105 @@
/**
* 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.
*/
#include <algorithm>
#include <pvxs/bitmask.h>
namespace pvxs {
BitMask::BitMask(BitMask&& o) noexcept
:_words(std::move(o._words))
,_size(o._size)
{
o._size = 0u;
}
BitMask& BitMask::operator=(BitMask&& o) noexcept
{
_words = std::move(o._words);
_size = o._size;
o._size = 0u;
return *this;
}
BitMask::BitMask(std::initializer_list<size_t> bits, size_t nbits)
{
if(bits.size()>0u) {
auto it_max = std::max_element(bits.begin(), bits.end());
resize(std::max(nbits, 1u+*it_max));
for(auto bit : bits) {
(*this)[bit] = true;
}
} else {
resize(nbits);
}
}
void BitMask::resize(size_t bits) {
// round up to multiple of 64
size_t storebits = ((bits-1u)|0x3f)+1u;
_words.resize(storebits/64u, 0u);
_size = bits;
}
size_t BitMask::findSet(size_t start) const
{
while(start < _size) {
size_t word = start/64u,
bit = start%64u;
// first see if we can skip to next word
uint64_t mask = ~((1ull<<bit)-1); // mask of bit and higher
uint64_t masked = _words[word]&mask;
if(masked==0u) {
start = (word+1u)*64u;
continue;
}
// the answer is in range [bit, 64)
// count consecutive "trailing" zeros.
// http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightParallel
masked &= -masked; // and with two's complement. neat. clears all except the bit we care about
// now a binary search
// we know masked is non-zero, and can start from 63
//bit = 64u;
//if(masked) bit--;
bit = 63u;
if(masked&0x00000000ffffffffull) bit -= 32u;
if(masked&0x0000ffff0000ffffull) bit -= 16u;
if(masked&0x00ff00ff00ff00ffull) bit -= 8u;
if(masked&0x0f0f0f0f0f0f0f0full) bit -= 4u;
if(masked&0x3333333333333333ull) bit -= 2u; // 0xb0011 repeated
if(masked&0x5555555555555555ull) bit -= 1u; // 0xb0101 repeated
return (word*64u) | bit;
}
return _size;
}
//BitMask& BitMask::operator&=(const BitMask& o) {}
//BitMask& BitMask::operator|=(const BitMask& o) {}
//BitMask& BitMask::operator^=(const BitMask& o) {}
std::ostream& operator<<(std::ostream& strm, const BitMask& mask)
{
strm.put('{');
bool first = true;
for(auto bit : mask.onlySet()) {
if(first) first = false;
else strm<<", ";
strm<<bit;
}
strm.put('}');
return strm;
}
} // namespace pvxs
+241
View File
@@ -0,0 +1,241 @@
/**
* 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 <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
uint8_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 _words.empty(); }
//! 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;
constexpr explicit _OnlySet(const BitMask* mask) :_mask(mask) {}
public:
typedef _SetIter iterator;
iterator begin() const { return iterator{_mask, _mask->findSet(0u)}; }
iterator end() const { return iterator{_mask, _mask->size()}; }
};
public:
//! return object which can be iterated to find all set bits
_OnlySet onlySet() const { return _OnlySet{this}; }
// all()
// evaulate 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);
}
// evaulate 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;
}
// evaulate 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;
}
// evaulate 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;
}
};
PVXS_API
std::ostream& operator<<(std::ostream& strm, const BitMask& mask);
} // namespace pvxs
#endif // PVXS_BITMASK_H
+4
View File
@@ -26,6 +26,10 @@ TESTPROD += testshared
testshared_SRCS += testshared.cpp
TESTS += testshared
TESTPROD += testbitmask
testbitmask_SRCS += testbitmask.cpp
TESTS += testbitmask
TESTPROD += testxcode
testxcode_SRCS += testxcode.cpp
TESTS += testxcode
+135
View File
@@ -0,0 +1,135 @@
/**
* 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.
*/
#include <testMain.h>
#include <epicsUnitTest.h>
#include <pvxs/unittest.h>
#include <pvxs/bitmask.h>
#include "utilpvt.h"
using namespace pvxs;
namespace {
void testEmpty()
{
testDiag("%s", __func__);
BitMask empty;
testOk1(!!empty.empty());
testEq(empty.size(), 0u);
testEq(empty.wsize(), 0u);
testEq(empty.findSet(0u), 0u);
testEq(std::string(SB()<<empty), "{}");
}
void testBasic1()
{
testDiag("%s", __func__);
BitMask M({1, 5, 3, 7}); // 0b10101010
testOk1(!M.empty());
testEq(M.size(), 8u);
testEq(M.wsize(), 1u);
testEq(M.findSet(0u), 1u);
testEq(M.findSet(1u), 1u);
testEq(M.findSet(2u), 3u);
testEq(M.findSet(3u), 3u);
testEq(M.findSet(M.size()), M.size());
testEq(std::string(SB()<<M), "{1, 3, 5, 7}"); // tests M.onlySet()
}
void testBasic2()
{
testDiag("%s", __func__);
BitMask M({6, 0, 4, 2}); // 0b01010101
testOk1(!M.empty());
testEq(M.size(), 7u);
testEq(M.wsize(), 1u);
testEq(M.findSet(0u), 0u);
testEq(M.findSet(1u), 2u);
testEq(M.findSet(2u), 2u);
testEq(M.findSet(3u), 4u);
testEq(M.findSet(M.size()), M.size());
testEq(std::string(SB()<<M), "{0, 2, 4, 6}");
}
void testBasic3()
{
testDiag("%s", __func__);
BitMask M({63, 64, 67});
testOk1(!M.empty());
testEq(M.size(), 68u);
testEq(M.wsize(), 2u);
testEq(M.findSet(0u), 63u);
testEq(M.findSet(62u), 63u);
testEq(M.findSet(63u), 63u);
testEq(M.findSet(64u), 64u);
testEq(M.findSet(65u), 67u);
testEq(M.findSet(M.size()), M.size());
testEq(std::string(SB()<<M), "{63, 64, 67}");
}
void testOp()
{
testDiag("%s", __func__);
BitMask M({ 1, 2, 4, 5}, 6u);
testOk1(!M.empty());
testEq(M.size(), 6u);
testEq(M.wsize(), 1u);
testOk1(!M[0]);
testOk1(!!M[1]);
M[0] = true;
M[1] = false;
testOk1(!!M[0]);
testOk1(!M[1]);
}
void testExpr()
{
testDiag("%s", __func__);
BitMask A({ 1, 2, 4, 5}, 6u);
BitMask B({0, 2, 3, 4 }, 6u);
BitMask C({ 5}, 6u);
BitMask Not(!A);
BitMask Or(A | B);
BitMask And(A & B);
BitMask Complex(C | (A & B));
testEq(std::string(SB()<<Not), "{0, 3}");
testEq(std::string(SB()<<Or), "{0, 1, 2, 3, 4, 5}");
testEq(std::string(SB()<<And), "{2, 4}");
testEq(std::string(SB()<<Complex), "{2, 4, 5}");
}
} // namespace
MAIN(testbitmask)
{
testPlan(44);
testEmpty();
testBasic1();
testBasic2();
testBasic3();
testOp();
testExpr();
cleanup_for_valgrind();
return testDone();
}