mirror of https://gitee.com/bigwinds/arangodb
338 lines
9.6 KiB
C++
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
|