mirror of
https://github.com/VectorCamp/vectorscan.git
synced 2025-06-28 16:41:01 +03:00
434 lines
12 KiB
C++
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
|