vectorscan/src/util/bitfield.h

434 lines
12 KiB
C++

/*
* Copyright (c) 2015-2018, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* \brief Fast bitset class with find_first and find_next operations.
*/
#ifndef BITFIELD_H
#define BITFIELD_H
#include "ue2common.h"
#include "popcount.h"
#include "util/bitutils.h"
#include "util/hash.h"
#include <array>
#include <cassert>
#include <boost/dynamic_bitset.hpp>
namespace ue2 {
/**
* \brief Templated bitset class with find_first and find_next operations.
*
* This is a simple (but hopefully fast) class to replace our use of
* std::bitset<>.
*
* Note: underlying storage is allocated as an array of 64-bit blocks. All
* mutating operations MUST ensure that the trailer (the bits between
* requested_size and the end of the array) is filled with zeroes; there's a
* clear_trailer member function for this.
*/
template<size_t requested_size>
class bitfield {
public:
/// Empty constructor, zero initializes all bits.
bitfield() : bits{{0}} {
assert(none());
}
explicit bitfield(const boost::dynamic_bitset<> &a) : bits{{0}} {
assert(a.size() == requested_size);
assert(none());
for (auto i = a.find_first(); i != a.npos; i = a.find_next(i)) {
set(i);
}
}
/// Complete bitset equality.
bool operator==(const bitfield &a) const {
return bits == a.bits;
}
/// Inequality.
bool operator!=(const bitfield &a) const {
return bits != a.bits;
}
/// Ordering.
bool operator<(const bitfield &a) const {
return bits < a.bits;
}
/// Set all bits.
void setall() {
std::fill(bits.begin(), bits.end(), all_ones);
clear_trailer();
}
/// Set all bits (alias for bitset::setall, to match dynamic_bitset).
void set() {
setall();
}
/// Clear all bits.
void clear() {
std::fill(bits.begin(), bits.end(), 0);
}
/// Clear all bits (alias for bitset::clear).
void reset() {
clear();
}
/// Clear bit N.
void clear(size_t n) {
assert(n < size());
bits[getword(n)] &= ~maskbit(n);
}
/// Set bit N.
void set(size_t n) {
assert(n < size());
bits[getword(n)] |= maskbit(n);
}
/// Test bit N.
bool test(size_t n) const {
assert(n < size());
return bits[getword(n)] & maskbit(n);
}
/// Flip bit N.
void flip(size_t n) {
assert(n < size());
bits[getword(n)] ^= maskbit(n);
}
/// Flip all bits.
void flip() {
for (size_t i = 0; i < size(); i++) {
flip(i);
}
clear_trailer();
}
/// Switch on the bit in the range [from, to], inclusive.
void set_range(size_t from, size_t to) {
assert(from <= to);
assert(to < requested_size);
if (from / block_size == to / block_size) {
// Small case, our indices are in the same block.
block_type block = all_ones << (from % block_size);
if (to % block_size != block_size - 1) {
block &= maskbit(to + 1) - 1;
}
bits[from / block_size] |= block;
return;
}
// Large case, work in block units. Write a partial mask, then a
// run of all-ones blocks, then a partial mask at the end.
size_t i = from;
if (i % block_size) {
block_type block = all_ones << (i % block_size);
bits[i / block_size] |= block;
i = ROUNDUP_N(i, block_size);
}
for (; i + block_size <= to + 1; i += block_size) {
bits[i / block_size] = all_ones;
}
if (i <= to) {
assert(to - i + 1 < block_size);
bits[i / block_size] |= (maskbit(to + 1) - 1);
}
}
/// Returns total number of bits.
static constexpr size_t size() {
return requested_size;
}
/// Returns number of bits set on.
size_t count() const {
static_assert(block_size == 64, "adjust popcount for block_type");
size_t sum = 0;
size_t i = 0;
for (; i + 4 <= num_blocks; i += 4) {
sum += popcount64x4(&bits[i]);
}
for (; i < num_blocks; i++) {
sum += popcount64(bits[i]);
}
assert(sum <= size());
return sum;
}
/// Are no bits set?
bool none() const {
// cppcheck-suppress useStlAlgorithm
// XXX maybe do this one..
for (const auto &e : bits) {
if (e != 0) {
return false;
}
}
return true;
}
/// Is any bit set?
bool any() const {
return !none();
}
/// Are all bits set?
bool all() const {
for (size_t i = 0; i < bits.size() - 1; i++) {
if (bits[i] != all_ones) {
return false;
}
}
size_t rem = requested_size % block_size;
block_type exp = rem ? ((block_type{1} << rem) - 1) : all_ones;
return *bits.rbegin() == exp;
}
/// Returns first bit set, or bitfield::npos if none set.
size_t find_first() const {
for (size_t i = 0; i < bits.size(); i++) {
if (bits[i] != 0) {
return (i * block_size) + word_ctz(i);
}
}
return npos;
}
// Returns last bit set, or bitfield::npos if none set.
size_t find_last() const {
for (int i = bits.size() - 1; i >= 0; i--) {
if (bits[i]) {
static_assert(block_size == 64, "adjust clz for block_type");
return (i * block_size) + block_size - 1 - clz64(bits[i]);
}
}
return npos;
}
/// Returns next bit set, or bitfield::npos if none set after 'last'.
size_t find_next(size_t last) const {
if (last >= size()) {
return npos;
}
// check current word.
size_t i = getword(last);
block_type lastword = bits[i];
if ((last % block_size) != (block_size - 1)) {
lastword &= (all_ones << ((last % block_size) + 1));
if (lastword) {
static_assert(block_size == 64, "adjust ctz for block_type");
return (i * block_size) + ctz64(lastword);
}
}
// check the rest.
for (i++; i < bits.size(); i++) {
if (bits[i]) {
return (i * block_size) + word_ctz(i);
}
}
return npos;
}
size_t find_nth(size_t n) const {
assert(n < npos);
static_assert(block_size == 64, "adjust for block_type");
size_t sum = 0;
for (size_t i = 0; i < bits.size(); i++) {
block_type block = bits[i];
size_t aftersum = sum + popcount64(block);
if (aftersum > n) { // Block contains the nth bit.
for (; sum < n; sum++) {
assert(block);
block &= (block - 1);
}
assert(block);
size_t bit = (i * block_size) + ctz64(block);
assert(test(bit));
return bit;
}
sum = aftersum;
}
assert(count() < n + 1);
return npos;
}
/// Bitwise OR.
bitfield operator|(const bitfield &a) const {
bitfield b = a;
b |= *this;
return b;
}
/// Bitwise OR-equals.
void operator|=(const bitfield &a) {
size_t i = 0;
for (; i + 4 <= num_blocks; i += 4) {
bits[i] |= a.bits[i];
bits[i + 1] |= a.bits[i + 1];
bits[i + 2] |= a.bits[i + 2];
bits[i + 3] |= a.bits[i + 3];
}
for (; i < num_blocks; i++) {
bits[i] |= a.bits[i];
}
}
/// Bitwise AND.
bitfield operator&(const bitfield &a) const {
bitfield b = a;
b &= *this;
return b;
}
/// Bitwise AND-equals.
void operator&=(const bitfield &a) {
size_t i = 0;
for (; i + 4 <= num_blocks; i += 4) {
bits[i] &= a.bits[i];
bits[i + 1] &= a.bits[i + 1];
bits[i + 2] &= a.bits[i + 2];
bits[i + 3] &= a.bits[i + 3];
}
for (; i < num_blocks; i++) {
bits[i] &= a.bits[i];
}
}
/// Bitwise XOR.
bitfield operator^(bitfield a) const {
a ^= *this;
return a;
}
/// Bitwise XOR-equals.
void operator^=(bitfield a) {
size_t i = 0;
for (; i + 4 <= num_blocks; i += 4) {
bits[i] ^= a.bits[i];
bits[i + 1] ^= a.bits[i + 1];
bits[i + 2] ^= a.bits[i + 2];
bits[i + 3] ^= a.bits[i + 3];
}
for (; i < num_blocks; i++) {
bits[i] ^= a.bits[i];
}
}
/// Bitwise complement.
bitfield operator~(void) const {
bitfield cr(*this);
cr.flip();
return cr;
}
/// Simple hash.
size_t hash() const {
return ue2_hasher()(bits);
}
/// Sentinel value meaning "no more bits", used by find_first and
/// find_next.
static constexpr size_t npos = requested_size;
private:
/// Underlying block type.
using block_type = u64a;
/// A block filled with on bits.
static constexpr block_type all_ones = ~block_type{0};
/// Size of a block.
static constexpr size_t block_size = sizeof(block_type) * 8;
static size_t getword(size_t n) {
return n / block_size;
}
static block_type maskbit(size_t n) {
return (block_type{1} << (n % block_size));
}
size_t word_ctz(size_t n) const {
static_assert(block_size == 64, "adjust ctz call for block type");
return ctz64(bits[n]);
}
/// Ensures that bits between our requested size and the end of storage are
/// zero.
void clear_trailer() {
size_t final_bits = requested_size % block_size;
if (final_bits) {
bits.back() &= ((block_type{1} << final_bits) - 1);
}
}
/// Size of storage array of blocks.
static constexpr size_t num_blocks =
(requested_size + block_size - 1) / block_size;
/// Underlying storage.
std::array<block_type, num_blocks> bits;
};
} // namespace ue2
namespace std {
template<size_t requested_size>
struct hash<ue2::bitfield<requested_size>> {
size_t operator()(const ue2::bitfield<requested_size> &b) const {
return b.hash();
}
};
} // namespace std
#endif // BITFIELD_H