add BitMask
This commit is contained in:
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user