1
0
Fork 0
arangodb/3rdParty/iresearch/core/utils/bitvector.hpp

338 lines
9.6 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2017 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov
////////////////////////////////////////////////////////////////////////////////
#ifndef IRESEARCH_BITVECTOR_H
#define IRESEARCH_BITVECTOR_H
#include "bitset.hpp"
NS_ROOT
////////////////////////////////////////////////////////////////////////////////
/// @brief a growable implementation of a bitset
////////////////////////////////////////////////////////////////////////////////
class bitvector final {
public:
typedef bitset::word_t word_t;
bitvector() = default;
explicit bitvector(size_t bits): size_(bits) { resize(bits); }
bitvector(const bitvector& other) { *this = other; }
bitvector(bitvector&& other) NOEXCEPT { *this = std::move(other); }
bool operator==(const bitvector& rhs) const NOEXCEPT {
if (this->size() != rhs.size()) {
return false;
}
return 0 == std::memcmp(this->begin(), rhs.begin(), this->size());
}
bool operator!=(const bitvector& rhs) const NOEXCEPT {
return !(*this == rhs);
}
operator const bitset&() const { return set_; }
bitvector& operator=(const bitvector& other) {
if (this != &other) {
if (set_.words() < other.set_.words()) {
bitset set(other.set_.words() * bits_required<word_t>());
set.memset(other.set_.begin(), other.set_.words() * sizeof(word_t));
set_ = std::move(set);
} else { // optimization, reuse existing container
set_.clear();
set_.memset(other.set_.begin(), other.set_.words() * sizeof(word_t));
}
size_ = other.size_;
}
return *this;
}
bitvector& operator=(bitvector&& other) NOEXCEPT {
if (this != &other) {
set_ = std::move(other.set_);
size_ = std::move(other.size_);
other.size_ = 0;
}
return *this;
}
bitvector& operator&=(const bitvector& other) {
if (this == &other || !other.size()) {
return *this; // nothing to do
}
reserve(other.size_);
size_ = std::max(size_, other.size_);
auto* data = const_cast<word_t*>(begin());
auto last_word = bitset::word(other.size() - 1); // -1 for bit offset
for (size_t i = 0; i < last_word; ++i) {
assert(i < set_.words() && i < other.set_.words());
*(data + i) &= *(other.data() + i);
}
// for the last word consider only those bits included in 'other.size()'
auto last_word_bits = other.size() % bits_required<word_t>();
const auto mask = (word_t(1) << last_word_bits) - 1; // set all bits that are not part of 'other.size()'
assert(last_word < set_.words() && last_word < other.set_.words());
*(data + last_word) &= (*(other.data() + last_word) & mask);
std::memset(data + last_word + 1, 0, (set_.words() - last_word - 1) * sizeof(word_t)); // unset tail words
return *this;
}
bitvector& operator|=(const bitvector& other) {
if (this == &other || !other.size()) {
return *this; // nothing to do
}
reserve(other.size_);
size_ = std::max(size_, other.size_);
auto* data = const_cast<word_t*>(begin());
auto last_word = bitset::word(other.size() - 1); // -1 for bit offset
for (size_t i = 0; i <= last_word; ++i) {
assert(i < set_.words() && i < other.set_.words());
*(data + i) |= *(other.data() + i);
}
return *this;
}
bitvector& operator^=(const bitvector& other) {
if (!other.size()) {
return *this; // nothing to do
}
reserve(other.size_);
size_ = std::max(size_, other.size_);
auto* data = const_cast<word_t*>(begin());
auto last_word = bitset::word(other.size() - 1); // -1 for bit offset
for (size_t i = 0; i < last_word; ++i) {
assert(i < set_.words() && i < other.set_.words());
*(data + i) ^= *(other.data() + i);
}
// for the last word consider only those bits included in 'other.size()'
auto last_word_bits = other.size() % bits_required<word_t>();
auto mask = ~word_t(0);
// clear trailing bits
if (last_word_bits) {
mask = ~(mask << last_word_bits); // unset all bits that are not part of 'other.size()'
}
assert(last_word < set_.words() && last_word < other.set_.words());
*(data + last_word) ^= (*(other.data() + last_word) & mask);
return *this;
}
bitvector& operator-=(const bitvector& other) {
if (!other.size()) {
return *this; // nothing to do
}
reserve(other.size_);
size_ = std::max(size_, other.size_);
auto* data = const_cast<word_t*>(begin());
auto last_word = bitset::word(other.size() - 1); // -1 for bit offset
for (size_t i = 0; i < last_word; ++i) {
assert(i < set_.words() && i < other.set_.words());
*(data + i) &= ~(*(other.data() + i));
}
// for the last word consider only those bits included in 'other.size()'
auto last_word_bits = other.size() % bits_required<word_t>();
auto mask = ~word_t(0);
// clear trailing bits
if (last_word_bits) {
mask = ~(mask << last_word_bits); // unset all bits that are not part of 'other.size()'
}
assert(last_word < set_.words() && last_word < other.set_.words());
*(data + last_word) &= ~(*(other.data() + last_word) & mask);
return *this;
}
bool all() const NOEXCEPT { return set_.count() == size(); }
bool any() const NOEXCEPT { return set_.any(); }
const word_t* begin() const NOEXCEPT { return set_.data(); }
size_t capacity() const NOEXCEPT { return set_.capacity(); }
void clear() NOEXCEPT {
set_.clear();
size_ = 0;
}
word_t count() const NOEXCEPT { return set_.count(); }
const word_t* data() const NOEXCEPT { return set_.data(); }
const word_t* end() const NOEXCEPT { return set_.end(); }
template<typename T>
void memset(const T& value) NOEXCEPT { memset(&value, sizeof(value)); }
void memset(const void* src, size_t size) NOEXCEPT {
auto bits = bits_required<uint8_t>() * size; // size is in bytes
reserve(bits);
std::memcpy(const_cast<word_t*>(begin()), src, std::min(size, set_.words() * sizeof(word_t)));
size_ = std::max(size_, bits);
}
bool none() const NOEXCEPT { return set_.none(); }
// reserve at least this many bits
void reserve(size_t bits) {
const auto words = bitset::word(bits - 1) + 1;
if (!bits || words <= set_.words()) {
return; // nothing to do
}
auto set = bitset(words * bits_required<word_t>());
if (data()) {
set.memset(begin(), set_.words() * sizeof(word_t)); // copy original
}
set_ = std::move(set);
}
void resize(size_t bits, bool preserve_capacity = false) {
const auto words = bitset::word(bits) + 1; // +1 for count instead of offset
size_ = bits;
if (words > set_.words()) {
reserve(bits);
return;
}
if (preserve_capacity) {
std::memset(const_cast<word_t*>(begin()) + words, 0, (set_.words() - words) * sizeof(word_t)); // clear trailing words
} else if (words < set_.words()) {
auto set = bitset(words * bits_required<word_t>());
set.memset(begin(), set.words() * sizeof(word_t)); // copy original
set_ = std::move(set);
}
auto last_word_bits = bits % bits_required<word_t>();
// clear trailing bits
if (last_word_bits) {
auto* last_word = begin() + words - 1;
const auto mask = ~(~word_t(0) << last_word_bits); // set all bits up to and including max bit
const_cast<word_t&>(*last_word) &= mask; // keep only bits up to max bit
}
}
void reset(size_t i, bool set) {
if (!set && bitset::word(i) >= set_.words()) {
size_ = std::max(size(), i + 1);
return; // nothing to do
}
resize(std::max(size(), i + 1), true); // ensure capacity
set_.reset(i, set);
}
void shrink_to_fit() {
auto words = set_.words();
if (!words) {
return; // nothing to do, no buffer
}
while (words && 0 == *(begin() + words - 1)) {
--words;
}
if (!words) {
auto set = bitset(); // create empty set
set_ = std::move(set);
size_ = 0;
return;
}
if (words != set_.words()) {
auto set = bitset(words * bits_required<word_t>());
set.memset(begin(), set.words() * sizeof(word_t)); // copy original
set_ = std::move(set);
}
size_ = (set_.words() * bits_required<word_t>())
- math::math_traits<word_t>::clz(*(begin() + set_.words() - 1))
;
}
void set(size_t i) { reset(i, true); }
size_t size() const NOEXCEPT { return size_; }
bool empty() const NOEXCEPT { return 0 == size_; }
bool test(size_t i) const NOEXCEPT {
return bitset::word(i) < set_.words() && set_.test(i);
}
void unset(size_t i) { reset(i, false); }
size_t words() const NOEXCEPT { return set_.words(); }
template<typename Visitor>
bool visit(Visitor visitor) const {
for (size_t i = 0; i < size_; ++i) {
if (set_.test(i) && !visitor(i)) {
return false;
}
}
return true;
}
private:
bitset set_;
size_t size_{}; // number of bits requested in a bitset
};
NS_END
#endif