/* bitSet.cpp */ /** * Copyright - See the COPYRIGHT that is included with this distribution. * EPICS pvData is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ /** * @author mes */ #include #include #include #include #define epicsExportSharedSymbols #include #include #include /* * BitSets are packed into arrays of "words." Currently a word is * a long, which consists of 64 bits, requiring 6 address bits. * The choice of word size is determined purely by performance concerns. */ #define ADDRESS_BITS_PER_WORD 6u #define BITS_PER_WORD (1u << ADDRESS_BITS_PER_WORD) #define BYTES_PER_WORD sizeof(uint64) #define BIT_INDEX_MASK (BITS_PER_WORD - 1u) /** Used to shift left or right for a partial word mask */ #define WORD_MASK ~((uint64)0) // index of work containing this bit #define WORD_INDEX(bitn) ((bitn)>>ADDRESS_BITS_PER_WORD) // bit offset within word #define WORD_OFFSET(bitn) ((bitn)&BIT_INDEX_MASK) // the words vector should be size()d as small as posible, // so the last word should always have a bit set when the set is not empty #define CHECK_POST() assert(words.empty() || words.back()!=0) namespace epics { namespace pvData { BitSet::shared_pointer BitSet::create(uint32 nbits) { return BitSet::shared_pointer(new BitSet(nbits)); } BitSet::BitSet() {} BitSet::BitSet(uint32 nbits) { words.reserve((nbits == 0) ? 1 : WORD_INDEX(nbits-1) + 1); } BitSet::~BitSet() {} void BitSet::recalculateWordsInUse() { // step back from the end to find the first non-zero element size_t nsize = words.size(); for(; nsize; nsize--) { if(words[nsize-1]) break; } words.resize(nsize); CHECK_POST(); } void BitSet::ensureCapacity(uint32 wordsRequired) { words.resize(std::max(words.size(), (size_t)wordsRequired), 0); } void BitSet::expandTo(uint32 wordIndex) { ensureCapacity(wordIndex+1); } void BitSet::flip(uint32 bitIndex) { uint32 wordIdx = WORD_INDEX(bitIndex); expandTo(wordIdx); words[wordIdx] ^= (((uint64)1) << WORD_OFFSET(bitIndex)); recalculateWordsInUse(); } void BitSet::set(uint32 bitIndex) { uint32 wordIdx = WORD_INDEX(bitIndex); expandTo(wordIdx); words[wordIdx] |= (((uint64)1) << WORD_OFFSET(bitIndex)); } void BitSet::clear(uint32 bitIndex) { uint32 wordIdx = WORD_INDEX(bitIndex); if (wordIdx >= words.size()) return; words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex)); recalculateWordsInUse(); } void BitSet::set(uint32 bitIndex, bool value) { if (value) set(bitIndex); else clear(bitIndex); } bool BitSet::get(uint32 bitIndex) const { uint32 wordIdx = WORD_INDEX(bitIndex); return ((wordIdx < words.size()) && ((words[wordIdx] & (((uint64)1) << WORD_OFFSET(bitIndex))) != 0)); } void BitSet::clear() { words.clear(); } uint32 BitSet::numberOfTrailingZeros(uint64 i) { // HD, Figure 5-14 uint32 x, y; if (i == 0) return 64; uint32 n = 63; y = (uint32)i; if (y != 0) { n = n -32; x = y; } else x = (uint32)(i>>32); y = x <<16; if (y != 0) { n = n -16; x = y; } y = x << 8; if (y != 0) { n = n - 8; x = y; } y = x << 4; if (y != 0) { n = n - 4; x = y; } y = x << 2; if (y != 0) { n = n - 2; x = y; } return n - ((x << 1) >> 31); } uint32 BitSet::bitCount(uint64 i) { // HD, Figure 5-14 i = i - ((i >> 1) & 0x5555555555555555LL); i = (i & 0x3333333333333333LL) + ((i >> 2) & 0x3333333333333333LL); i = (i + (i >> 4)) & 0x0f0f0f0f0f0f0f0fLL; i = i + (i >> 8); i = i + (i >> 16); i = i + (i >> 32); return (uint32)(i & 0x7f); } int32 BitSet::nextSetBit(uint32 fromIndex) const { uint32 u = WORD_INDEX(fromIndex); if (u >= words.size()) return -1; uint64 word = words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD)); while (true) { if (word != 0) return (u * BITS_PER_WORD) + numberOfTrailingZeros(word); if (++u == words.size()) return -1; word = words[u]; } } int32 BitSet::nextClearBit(uint32 fromIndex) const { // Neither spec nor implementation handle bitsets of maximal length. uint32 u = WORD_INDEX(fromIndex); if (u >= words.size()) return fromIndex; uint64 word = ~words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD)); while (true) { if (word != 0) return (u * BITS_PER_WORD) + numberOfTrailingZeros(word); if (++u == words.size()) return words.size() * BITS_PER_WORD; word = ~words[u]; } } bool BitSet::isEmpty() const { return words.empty(); } uint32 BitSet::cardinality() const { uint32 sum = 0; for (uint32 i = 0; i < words.size(); i++) sum += bitCount(words[i]); return sum; } uint32 BitSet::size() const { return words.size() * BITS_PER_WORD; } BitSet& BitSet::operator&=(const BitSet& set) { // Check for self-assignment! if (this == &set) return *this; // the result length will be <= the shorter of the two inputs words.resize(std::min(words.size(), set.words.size()), 0); for(size_t i=0, e=words.size(); i>= 8) len++; SerializeHelper::writeSize(len, buffer, flusher); flusher->ensureBuffer(len); n = len / 8; for (uint32 i = 0; i < n; i++) buffer->putLong(words[i]); if (n < words.size()) for (uint64 x = words[words.size() - 1]; x != 0; x >>= 8) buffer->putByte((int8) (x & 0xff)); } void BitSet::deserialize(ByteBuffer* buffer, DeserializableControl* control) { uint32 bytes = static_cast(SerializeHelper::readSize(buffer, control)); // in bytes size_t wordsInUse = (bytes + 7) / BYTES_PER_WORD; words.resize(wordsInUse); if (wordsInUse == 0) return; control->ensureData(bytes); uint32 i = 0; uint32 longs = bytes / 8; while (i < longs) words[i++] = buffer->getLong(); for (uint32 j = i; j < wordsInUse; j++) words[j] = 0; for (uint32 remaining = (bytes - longs * 8), j = 0; j < remaining; j++) words[i] |= (buffer->getByte() & 0xffLL) << (8 * j); recalculateWordsInUse(); // Sender shouldn't add extra zero bytes, but don't fail it it does } epicsShareExtern std::ostream& operator<<(std::ostream& o, const BitSet& b) { o << '{'; int32 i = b.nextSetBit(0); if (i != -1) { o << i; for (i = b.nextSetBit(i+1); i >= 0; i = b.nextSetBit(i+1)) { int32 endOfRun = b.nextClearBit(i); do { o << ", " << i; } while (++i < endOfRun); } } o << '}'; return o; } }};