Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
1785 lines
59 KiB
C++
1785 lines
59 KiB
C++
/// @file dpf/bit_array.hpp
|
|
/// @brief Packed bit arrays, static and dynamic, with bit proxies and iterators.
|
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
|
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
|
/// see [LICENSE.md](@ref license) for details.
|
|
|
|
#ifndef LIBDPF_INCLUDE_DPF_BIT_ARRAY_HPP__
|
|
#define LIBDPF_INCLUDE_DPF_BIT_ARRAY_HPP__
|
|
|
|
#include <cstddef>
|
|
#include <cmath>
|
|
#include <type_traits>
|
|
#include <memory>
|
|
#include <new>
|
|
#include <utility>
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
#include <functional>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <array>
|
|
#include <ostream>
|
|
#include <cassert>
|
|
|
|
#include "hedley/hedley.h"
|
|
#include "portable-snippets/exact-int/exact-int.h"
|
|
#include "portable-snippets/builtin/builtin.h"
|
|
#include "portable-snippets/endian/endian.h"
|
|
|
|
#include "dpf/utils.hpp"
|
|
#include "dpf/bit.hpp"
|
|
#include "dpf/aligned_allocator.hpp"
|
|
|
|
namespace dpf
|
|
{
|
|
|
|
namespace detail
|
|
{
|
|
|
|
template <typename Word>
|
|
HEDLEY_NO_THROW
|
|
constexpr void check_one_bit(Word mask) noexcept
|
|
{
|
|
(void)mask;
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
if (__builtin_is_constant_evaluated()) return;
|
|
#endif
|
|
assert(mask != Word{0}
|
|
&& (mask & static_cast<Word>(mask - Word{1})) == Word{0});
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
template <typename ConcreteBitArrayT, typename WordT>
|
|
class setbit_index_iterable; // forward declaration
|
|
|
|
template <typename ConcreteBitArrayT, typename WordT>
|
|
class bit_iterator_base; // forward reference
|
|
|
|
template <typename ConcreteBitArrayT, typename WordT>
|
|
class bit_iterator; // forward reference
|
|
|
|
template <typename ConcreteBitArrayT, typename WordT>
|
|
class const_bit_iterator; // forward reference
|
|
|
|
/// @brief a base class for classes representing a sequence of bits
|
|
/// @details A `bit_array` represents a sequence of bits. The underlying
|
|
/// storage is an array of integers of type `dpf::bit_array::word_type`.
|
|
/// @tparam ConcreteBitArrayT concrete bit array type
|
|
/// @tparam WordT word used to pack bits
|
|
template <typename ConcreteBitArrayT, typename WordT = psnip_uint64_t>
|
|
class bit_array_base
|
|
{
|
|
private:
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_PURE
|
|
HEDLEY_NO_THROW
|
|
constexpr auto derived_from_this() noexcept
|
|
{
|
|
return static_cast<ConcreteBitArrayT *>(this);
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_PURE
|
|
HEDLEY_NO_THROW
|
|
constexpr auto derived_from_this() const noexcept
|
|
{
|
|
return static_cast<const ConcreteBitArrayT *>(this);
|
|
}
|
|
public:
|
|
class bit_reference; // forward reference
|
|
/// @brief `dpf::bit`
|
|
using value_type = dpf::bit;
|
|
/// @brief proxy class representing a reference to a single bit.
|
|
using reference = bit_array_base::bit_reference;
|
|
/// @brief `dpf::bit`
|
|
using const_reference = dpf::bit;
|
|
/// @brief a random access iterator to `value_type`.
|
|
/// @note convertible to `const_bit_iterator`
|
|
using iterator = bit_iterator<ConcreteBitArrayT, WordT>;
|
|
/// @brief a random access iterator to `const value_type`
|
|
using const_iterator = const_bit_iterator<ConcreteBitArrayT, WordT>;
|
|
/// @brief a type that simulates pointer-to-`value_type` behavior,
|
|
/// identical to `iterator`.
|
|
using pointer = iterator;
|
|
/// @brief a type that simulates pointer-to-`const value_type` behavior,
|
|
/// identical to `const_iterator`.
|
|
using const_pointer = const_iterator;
|
|
/// @brief a signed integral type, identical to
|
|
/// `std::iterator_traits<iterator>::difference_type`.
|
|
using difference_type = std::ptrdiff_t;
|
|
static_assert(std::is_integral_v<difference_type>
|
|
&& std::is_signed_v<difference_type>);
|
|
/// @brief an unsigned integral type that can represent any non-negative
|
|
/// value of `difference_type`.
|
|
using size_type = std::size_t;
|
|
static_assert(std::is_integral_v<size_type>
|
|
&& std::is_unsigned_v<size_type>);
|
|
/// @brief an unsigned integral type used for the internal representation
|
|
/// of bits.
|
|
using word_type = WordT;
|
|
static_assert(std::is_integral_v<word_type>
|
|
&& std::is_unsigned_v<word_type>);
|
|
/// @brief pointer to `word_type`
|
|
using word_pointer = std::add_pointer_t<word_type>;
|
|
/// @brief const pointer to `word_type`
|
|
using const_word_pointer = std::add_pointer_t<std::add_const_t<word_type>>;
|
|
/// @brief the number of `dpf::bit`s represented by each `word_type`.
|
|
/// @note guaranteed (via `static_assert`) to at most `64` bits
|
|
using child_type = ConcreteBitArrayT;
|
|
static constexpr size_type bits_per_word = utils::bitlength_of<word_type>();
|
|
static constexpr size_type lg_bits_per_word = std::log2(bits_per_word);
|
|
static_assert(bits_per_word <= 64, "bits_per_word greater than 64");
|
|
|
|
/// @brief default copy constructor
|
|
inline constexpr bit_array_base(const bit_array_base &) = default;
|
|
|
|
/// @brief default move constructor
|
|
HEDLEY_NO_THROW
|
|
inline constexpr bit_array_base(bit_array_base &&) noexcept = default;
|
|
|
|
/// @brief default destructor
|
|
~bit_array_base() = default;
|
|
|
|
/// @brief default copy assignment
|
|
/// @return `*this`
|
|
inline constexpr
|
|
bit_array_base & operator=(const bit_array_base &) = default;
|
|
|
|
/// @brief defaulted move assignment
|
|
/// @return `*this`
|
|
HEDLEY_NO_THROW
|
|
inline constexpr
|
|
bit_array_base & operator=(bit_array_base &&) noexcept = default;
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr const_word_pointer data() const noexcept
|
|
{
|
|
return derived_from_this()->data();
|
|
}
|
|
|
|
/// @brief direct access into the underlying data array (w/o bounds
|
|
/// checking)
|
|
/// @param pos the array element to access
|
|
/// @note Does not perform bounds checking; behaviour is undefined if
|
|
/// `pos` is out of bounds
|
|
/// @return `data()[pos]`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr word_type data(size_type pos) const noexcept
|
|
{
|
|
return data()[pos];
|
|
}
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr word_pointer data() noexcept { return derived_from_this()->data(); }
|
|
|
|
/// @brief direct access into the underlying data array (w/o bounds
|
|
/// checking)
|
|
/// @param pos the array element to access
|
|
/// @note Does not perform bounds checking; behaviour is undefined if
|
|
/// `pos` is out of bounds
|
|
/// @return `data()[pos]`
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr word_type & data(size_type pos) noexcept
|
|
{
|
|
return data()[pos];
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr size_type data_length() const noexcept { return derived_from_this()->data_length(); }
|
|
|
|
/// @brief access specified bit (w/o bounds checking)
|
|
/// @{
|
|
/// @details accesses the bit at position `pos`
|
|
/// @param pos the 0-based position of the bit to return (least
|
|
/// significant to most significant)
|
|
/// @note Unlike `test` and `at`, does not throw exceptions: the behavior
|
|
/// is undefined if `pos` is out of bounds
|
|
/// @return an object of type `dpf::bit_array_base::reference`, which
|
|
/// allows writing to the requested bit
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
inline constexpr reference operator[](size_type pos) noexcept
|
|
{
|
|
return reference{&data(pos / bits_per_word),
|
|
static_cast<word_type>(word_type(1) << (pos % bits_per_word))};
|
|
}
|
|
|
|
/// @details accesses the bit at position `pos`
|
|
/// @param pos the 0-based position of the bit to return (least
|
|
/// significant to most significant)
|
|
/// @return the value of the requested bit
|
|
/// @note Unlike `test` and `at`, does not throw exceptions: the behavior
|
|
/// is undefined if `pos` is out of bounds
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
inline constexpr const_reference operator[](size_type pos) const noexcept
|
|
{
|
|
const word_type tmp = data(pos / bits_per_word);
|
|
const word_type mask = static_cast<word_type>(word_type(1) << (pos % bits_per_word));
|
|
return (tmp & mask) ? dpf::bit::one : dpf::bit::zero;
|
|
}
|
|
/// @}
|
|
|
|
/// @brief access specified bit with bounds checking
|
|
/// @{
|
|
/// @details accesses the bit at position `pos`
|
|
/// @param pos the 0-based position of the bit to return (least
|
|
/// significant to most significant)
|
|
/// @throws std::out_of_range if `pos` does not correspond to a valid
|
|
/// position within the `bit_array_base`
|
|
/// @return an object of type `dpf::bit_array_base::reference`, which
|
|
/// allows writing to the requested bit
|
|
/// @complexity `O(1)`
|
|
constexpr reference at(size_type pos)
|
|
{
|
|
utils::constexpr_maybe_throw<std::out_of_range>(
|
|
!(pos < size()),
|
|
"pos is out of range");
|
|
return this->operator[](pos);
|
|
}
|
|
|
|
/// @details accesses the bit at position `pos`
|
|
/// @param pos the 0-based position of the bit to return (least
|
|
/// significant to most significant)
|
|
/// @return the value of the requested bit
|
|
/// @throws std::out_of_range if `pos` does not correspond to a valid
|
|
/// position within the `bit_array_base`
|
|
/// @complexity `O(1)`
|
|
constexpr const_reference at(size_type pos) const
|
|
{
|
|
utils::constexpr_maybe_throw<std::out_of_range>(
|
|
!(pos < size()),
|
|
"pos is out of range");
|
|
return this->operator[](pos);
|
|
}
|
|
/// @}
|
|
|
|
/// @brief returns an iterator to the first bit
|
|
/// @{
|
|
/// @return iterator to the first element
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
constexpr iterator begin() noexcept
|
|
{
|
|
auto *p = data();
|
|
if (p == nullptr) return iterator{};
|
|
return iterator{p, word_type(1)};
|
|
}
|
|
/// @return iterator to the first element
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator begin() const noexcept
|
|
{
|
|
auto *p = data();
|
|
if (p == nullptr) return const_iterator{};
|
|
return const_iterator{p, word_type(1)};
|
|
}
|
|
/// @return iterator to the first element
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator cbegin() const noexcept
|
|
{
|
|
return begin();
|
|
}
|
|
/// @}
|
|
|
|
/// @brief returns an iterator to the end (one past the last bit)
|
|
/// @{
|
|
/// @return iterator to the element following the last element
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
constexpr iterator end() noexcept
|
|
{
|
|
auto *p = data();
|
|
if (p == nullptr) return iterator{};
|
|
return iterator{p + (size() >> lg_bits_per_word),
|
|
static_cast<word_type>(word_type(1) << (size() % bits_per_word))};
|
|
}
|
|
/// @return iterator to the element following the last element
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator end() const noexcept
|
|
{
|
|
auto *p = data();
|
|
if (p == nullptr) return const_iterator{};
|
|
return const_iterator{p + (size() >> lg_bits_per_word),
|
|
static_cast<word_type>(word_type(1) << (size() % bits_per_word))};
|
|
}
|
|
/// @return iterator to the element following the last element
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator cend() const noexcept
|
|
{
|
|
return end();
|
|
}
|
|
/// @}
|
|
|
|
/// @brief checks if the specified bit is set to `true`
|
|
/// @param pos the 0-based position of the bit to return (least
|
|
/// significant to most significant)
|
|
/// @return `true` if the requested bit is set, `false` otherwise
|
|
/// @complexity `O(1)`
|
|
bool test(size_type pos) const
|
|
{
|
|
return at(pos);
|
|
}
|
|
|
|
/// @brief checks if all, any or none of the bits are set to `true`
|
|
/// @{
|
|
/// @details checks if all bits are set to `true`
|
|
/// @return `true` if all of the bits are set to `true`, otherwise `false`
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
bool all() const noexcept
|
|
{
|
|
if (size() == 0) return true;
|
|
const size_type n = data_length();
|
|
const size_type rem = size() % bits_per_word;
|
|
const word_type *p = data();
|
|
const size_type full = (rem == 0) ? n : n - 1;
|
|
const word_type ones = static_cast<word_type>(~word_type{0});
|
|
for (size_type i = 0; i < full; ++i)
|
|
if (p[i] != ones) return false;
|
|
if (rem != 0)
|
|
{
|
|
const word_type m = low_bits_mask(rem);
|
|
if (static_cast<word_type>(p[n - 1] & m) != m) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// @details checks if all bits in a range are set to `true`
|
|
/// @tparam Iterator an iterator type
|
|
/// @param first,last the range of elements under consideration
|
|
/// @return `true` if all of the bits in the given range are set to `true`, otherwise `false`
|
|
/// @complexity `O(last-first)`
|
|
template <typename Iterator>
|
|
HEDLEY_NO_THROW
|
|
bool all(Iterator first, Iterator last) const noexcept
|
|
{
|
|
bool ok = true;
|
|
for_each_span(first, last, [&](word_type bits, word_type relevant)
|
|
{
|
|
if (bits != relevant) ok = false;
|
|
return ok;
|
|
});
|
|
return ok;
|
|
}
|
|
|
|
/// @details checks if any bits are set to `true`
|
|
/// @return `true` if any of the bits are set to `true`, otherwise `false`
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
bool any() const noexcept
|
|
{
|
|
const size_type n = data_length();
|
|
if (n == 0 || size() == 0) return false;
|
|
const size_type rem = size() % bits_per_word;
|
|
const word_type *p = data();
|
|
const size_type full = (rem == 0) ? n : n - 1;
|
|
for (size_type i = 0; i < full; ++i)
|
|
if (p[i] != word_type{0}) return true;
|
|
if (rem != 0)
|
|
return static_cast<word_type>(p[n - 1] & low_bits_mask(rem)) != word_type{0};
|
|
return false;
|
|
}
|
|
|
|
/// @details checks if any bits in a range are set to `true`
|
|
/// @tparam Iterator an iterator type
|
|
/// @param first,last the range of elements under consideration
|
|
/// @return `true` if any of the bits in the given range are set to `true`, otherwise `false`
|
|
/// @complexity `O(last-first)`
|
|
template <typename Iterator>
|
|
HEDLEY_NO_THROW
|
|
bool any(Iterator first, Iterator last) const noexcept
|
|
{
|
|
bool found = false;
|
|
for_each_span(first, last, [&](word_type bits, word_type)
|
|
{
|
|
if (bits != word_type{0}) found = true;
|
|
return !found;
|
|
});
|
|
return found;
|
|
}
|
|
|
|
/// @details checks if none of the bits are set to `true`
|
|
/// @return `true` if none of the bits are set to `true`, otherwise `false`
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
bool none() const noexcept
|
|
{
|
|
return !any();
|
|
}
|
|
|
|
/// @details checks if none bits in a range are set to `true`
|
|
/// @tparam Iterator an iterator type
|
|
/// @param first,last the range of elements under consideration
|
|
/// @return `true` if none of the bits in the given range are set to `true`, otherwise `false`
|
|
/// @complexity `O(last-first)`
|
|
template <typename Iterator>
|
|
HEDLEY_NO_THROW
|
|
bool none(Iterator first, Iterator last) const noexcept
|
|
{
|
|
return !any(first, last);
|
|
}
|
|
/// @}
|
|
|
|
/// @brief returns the number of bits set to `true`
|
|
/// @{
|
|
/// @details counts the number of bits that are set to `true`
|
|
/// @return the number of bits set to `true`
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
size_type count() const noexcept
|
|
{
|
|
const size_type n = data_length();
|
|
if (n == 0 || size() == 0) return 0;
|
|
const size_type rem = size() % bits_per_word;
|
|
const word_type *p = data();
|
|
const size_type full = (rem == 0) ? n : n - 1;
|
|
size_type sum = 0;
|
|
for (size_type i = 0; i < full; ++i)
|
|
sum += pop(p[i]);
|
|
if (rem != 0)
|
|
sum += pop(static_cast<word_type>(p[n - 1] & low_bits_mask(rem)));
|
|
return sum;
|
|
}
|
|
/// @details counts the number of bits in a range that are set to `true`
|
|
/// @tparam Iterator an iterator type
|
|
/// @param first,last the range of elements under consideration
|
|
/// @return the number of bits in the given range that are set to `true`
|
|
/// @complexity `O(last-first)`
|
|
template <typename Iterator>
|
|
HEDLEY_NO_THROW
|
|
size_type count(Iterator first, Iterator last) const noexcept
|
|
{
|
|
size_type sum = 0;
|
|
for_each_span(first, last, [&](word_type bits, word_type)
|
|
{
|
|
sum += pop(bits);
|
|
return true;
|
|
});
|
|
return sum;
|
|
}
|
|
/// @}
|
|
|
|
/// @brief returns the parity of all stored bits
|
|
/// @{
|
|
/// @details counts the parity of all stored bits
|
|
/// @return the parity of all stored bits
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
size_type parity() const noexcept
|
|
{
|
|
const size_type n = data_length();
|
|
if (n == 0 || size() == 0) return 0;
|
|
const size_type rem = size() % bits_per_word;
|
|
const word_type *p = data();
|
|
const size_type full = (rem == 0) ? n : n - 1;
|
|
word_type x = word_type{0};
|
|
for (size_type i = 0; i < full; ++i)
|
|
x ^= p[i];
|
|
if (rem != 0)
|
|
x ^= static_cast<word_type>(p[n - 1] & low_bits_mask(rem));
|
|
return utils::parity(x);
|
|
}
|
|
|
|
/// @details counts the parity of bits in a range
|
|
/// @tparam Iterator an iterator type
|
|
/// @param first,last the range of elements under consideration
|
|
/// @return the parity of all bits in the given range
|
|
/// @complexity `O(last-first)`
|
|
template <typename Iterator>
|
|
HEDLEY_NO_THROW
|
|
size_type parity(Iterator first, Iterator last) const noexcept
|
|
{
|
|
word_type x = word_type{0};
|
|
for_each_span(first, last, [&](word_type bits, word_type)
|
|
{
|
|
x ^= bits;
|
|
return true;
|
|
});
|
|
return utils::parity(x);
|
|
}
|
|
/// @}
|
|
|
|
/// @brief returns the number of bits
|
|
/// @return number of bits that the `bit_array_base` holds
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_PURE
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr size_type size() const noexcept
|
|
{
|
|
return derived_from_this()->size();
|
|
}
|
|
|
|
/// @brief sets bits to `true` or given value
|
|
/// @{
|
|
/// @brief sets all bits to `true`
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
constexpr void set() noexcept
|
|
{
|
|
const size_type n = data_length();
|
|
if (n == 0) return;
|
|
const size_type rem = size() % bits_per_word;
|
|
word_type *p = data();
|
|
const word_type ones = static_cast<word_type>(~word_type{0});
|
|
if (rem == 0)
|
|
{
|
|
for (size_type i = 0; i < n; ++i) p[i] = ones;
|
|
return;
|
|
}
|
|
for (size_type i = 0; i + 1 < n; ++i) p[i] = ones;
|
|
p[n - 1] = low_bits_mask(rem);
|
|
}
|
|
/// @brief sets the bit at position `pos` to the value `value`
|
|
/// @param pos the 0-based position of the bit to set (least significant
|
|
/// to most significant)
|
|
/// @param value the value to set the bit to
|
|
/// @throws std::out_of_range if `pos` does not correspond to a valid
|
|
/// position within the `bit_array_base`
|
|
/// @complexity `O(1)`
|
|
constexpr void set(size_type pos, bool value = true)
|
|
{
|
|
at(pos) = value;
|
|
}
|
|
|
|
/// @brief sets the bit at position `pos` to the value `value`
|
|
/// @param pos the 0-based position of the bit to set (least significant
|
|
/// to most significant)
|
|
/// @param value the value to set the bit to
|
|
/// @throws std::out_of_range if `pos` does not correspond to a valid
|
|
/// position within the `bit_array_base`
|
|
/// @complexity `O(1)`
|
|
constexpr void unchecked_set(size_type pos, bool value = true)
|
|
{
|
|
this->operator[](pos) = value;
|
|
}
|
|
/// @}
|
|
|
|
/// @brief sets bits to `false`
|
|
/// @{
|
|
/// @brief sets all bits to `false'
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
constexpr void unset() noexcept
|
|
{
|
|
word_type *p = data();
|
|
const size_type n = data_length();
|
|
for (size_type i = 0; i < n; ++i) p[i] = word_type{0};
|
|
}
|
|
/// @brief sets the bit at position `pos` to `false`
|
|
/// @param pos the 0-based position of the bit to unset (least
|
|
/// significant to most significant)
|
|
/// @throws std::out_of_range if `pos` does not correspond to a valid
|
|
/// position within the `bit_array_base`
|
|
/// @complexity `O(1)`
|
|
constexpr void unset(size_type pos)
|
|
{
|
|
at(pos) = dpf::bit::zero;
|
|
}
|
|
/// @}
|
|
|
|
constexpr void unchecked_unset(size_type pos)
|
|
{
|
|
this->operator[](pos) = dpf::bit::zero;
|
|
}
|
|
|
|
/// @brief toggles the values of bits
|
|
/// @{
|
|
/// @brief flips all bits (like `operator~`, but in-place)
|
|
/// @complexity `O(size())`
|
|
HEDLEY_NO_THROW
|
|
constexpr void flip() noexcept
|
|
{
|
|
const size_type n = data_length();
|
|
if (n == 0) return;
|
|
word_type *p = data();
|
|
for (size_type i = 0; i < n; ++i)
|
|
p[i] = static_cast<word_type>(~p[i]);
|
|
const size_type rem = size() % bits_per_word;
|
|
if (rem != 0)
|
|
p[n - 1] &= low_bits_mask(rem);
|
|
}
|
|
/// @brief flips the bit at the position `pos`
|
|
/// @param pos the 0-based position of the bit to flip (least
|
|
/// significant to most significant)
|
|
/// @throws std::out_of_range if `pos` does not correspond to a valid
|
|
/// position within the `bit_array_base`
|
|
/// @complexity `O(1)`
|
|
constexpr void flip(size_type pos)
|
|
{
|
|
at(pos).flip();
|
|
}
|
|
/// @}
|
|
|
|
/// @brief returns a string representation of the data
|
|
/// @details converts the contents of the `bit_array_base` to a string. Uses
|
|
/// `zero` to represent bits with value `false` and `one` to
|
|
/// represent bits with value `true`. The resulting string
|
|
/// contains `size()` characters with the first character
|
|
/// corresponding to the last `(size()-1th)` bit and the last
|
|
/// character corresponding tot he first `(0th)` bit.
|
|
/// @tparam CharT character type
|
|
/// @tparam Traits character traits
|
|
/// @tparam Allocator allocator type
|
|
/// @param zero character to use to represent `false`/`0` (default: ``CharT('0')``)
|
|
/// @param one character to use to represent `true`/`1` (default: ``CharT('1')``)
|
|
/// @return the converted string
|
|
/// @throws May throw `std::bad_alloc` from the `std::string` constructor.
|
|
/// @complexity `O(size())`
|
|
template <typename CharT = char,
|
|
typename Traits = std::char_traits<CharT>,
|
|
typename Allocator = std::allocator<CharT>>
|
|
std::basic_string<CharT, Traits, Allocator> to_string(
|
|
CharT zero = CharT('0'),
|
|
CharT one = CharT('1')) const
|
|
{
|
|
const size_type n = this->size();
|
|
std::basic_string<CharT, Traits, Allocator> s(n, zero);
|
|
for (size_type i = 0; i < n; ++i)
|
|
{
|
|
if (this->operator[](n - 1 - i))
|
|
s[i] = one;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/// @brief proxy class representing a reference to a bit
|
|
/// @details This class is used as a proxy object to allow users to
|
|
/// interact with individual bits of a ``bit_array_base`, since
|
|
/// standard C++ types have insufficient precision to specify
|
|
/// individual bits.
|
|
/// @note The primary use of ``bit_array_base::bit_reference`` is to
|
|
/// provide an l-value that can be returned from
|
|
/// `bit_array_base::operator[]`.
|
|
class bit_reference
|
|
{
|
|
public:
|
|
using word_type = bit_array_base::word_type;
|
|
using word_pointer = bit_array_base::word_pointer;
|
|
|
|
/// @brief (deleted) default c'tor
|
|
inline bit_reference() = delete;
|
|
/// @brief copy c'tor
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr bit_reference(const bit_reference &) noexcept = default;
|
|
/// @brief move c'tor
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr bit_reference(bit_reference &&) noexcept = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference & operator=(bool b) noexcept
|
|
{
|
|
this->assign(b);
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference & operator=(const bit_reference & b) noexcept
|
|
{
|
|
this->assign(static_cast<bool>(b));
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference & operator=(bit_reference && b) noexcept
|
|
{
|
|
this->assign(static_cast<bool>(b));
|
|
return *this;
|
|
}
|
|
|
|
~bit_reference() = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr operator bool() const noexcept
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
detail::check_one_bit(mask_);
|
|
return !(!(*word_ptr_ & mask_));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr operator int() const noexcept
|
|
{
|
|
return int{static_cast<bool>(*this)};
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr operator dpf::bit() const noexcept
|
|
{
|
|
return dpf::bit{static_cast<bool>(*this)};
|
|
}
|
|
|
|
/// @brief XOR. Exact match so `bit_reference - bit_reference` is not
|
|
/// ambiguous with integer subtraction of the proxy.
|
|
/// @param lhs the left-hand operand
|
|
/// @param rhs the right-hand operand
|
|
/// @return XOR
|
|
friend constexpr dpf::bit operator-(bit_reference lhs, bit_reference rhs) noexcept
|
|
{
|
|
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
friend constexpr dpf::bit operator+(bit_reference lhs, bit_reference rhs) noexcept
|
|
{
|
|
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
|
|
}
|
|
|
|
/// @brief performs binary AND, OR, XOR and NOT
|
|
/// @{
|
|
/// @details sets `*this` to the result of binary AND on `*this` and `b`
|
|
/// @param b the other bit
|
|
/// @return `*this`
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference & operator&=(bool b) noexcept
|
|
{
|
|
if (b == false) *word_ptr_ &= ~this->mask_;
|
|
return *this;
|
|
}
|
|
|
|
/// @details sets `*this` to the result of binary OR on `*this` and `b`
|
|
/// @param b the other bit
|
|
/// @return `*this`
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference & operator|=(bool b) noexcept
|
|
{
|
|
if (b == true) *word_ptr_ |= this->mask_;
|
|
return *this;
|
|
}
|
|
|
|
/// @details sets `*this` to the result of binary XOR on `*this` and `b`
|
|
/// @param b the other bit
|
|
/// @return `*this`
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference & operator^=(bool b) noexcept
|
|
{
|
|
if (b == true) *word_ptr_ ^= this->mask_;
|
|
return *this;
|
|
}
|
|
|
|
/// @details returns a temporary copy of `*this` with its value flipped (binary NOT)
|
|
/// @return the flipped bit
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr dpf::bit operator~() const noexcept
|
|
{
|
|
return dpf::bit{!static_cast<bool>(*this)};
|
|
}
|
|
/// @}
|
|
|
|
/// @brief sets to the referenced bit to 1
|
|
/// @return `*this`
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference& set() noexcept
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
detail::check_one_bit(mask_);
|
|
*word_ptr_ |= mask_;
|
|
return *this;
|
|
}
|
|
|
|
/// @brief unsets the referenced bit to 0
|
|
/// @return `*this`
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference& unset() noexcept
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
detail::check_one_bit(mask_);
|
|
*word_ptr_ &= static_cast<word_type>(~mask_);
|
|
return *this;
|
|
}
|
|
|
|
/// @brief assigns `b ? 1 : 0` to the referenced bit
|
|
/// @param b the `b`
|
|
/// @return `*this`
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference& assign(bool b) noexcept
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
detail::check_one_bit(mask_);
|
|
*word_ptr_ = b ? (*word_ptr_ | mask_)
|
|
: (*word_ptr_ & static_cast<word_type>(~mask_));
|
|
return *this;
|
|
}
|
|
|
|
/// @brief flips the referenced bit
|
|
/// @return `*this`
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference& flip() noexcept
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
detail::check_one_bit(mask_);
|
|
*word_ptr_ ^= mask_;
|
|
return *this;
|
|
}
|
|
|
|
/// @brief Exchange the bits named by two proxies, including temporaries
|
|
/// returned from `operator[]` and `operator*`.
|
|
/// @param a the `a`
|
|
/// @param b the `b`
|
|
HEDLEY_NO_THROW
|
|
friend constexpr void swap(bit_reference a, bit_reference b) noexcept
|
|
{
|
|
const bool tmp = static_cast<bool>(a);
|
|
a = static_cast<bool>(b);
|
|
b = tmp;
|
|
}
|
|
private:
|
|
word_pointer word_ptr_; //< pointer to word holding the bit.
|
|
word_type mask_; //< bitmask for referenced bit within `*word_ptr`.
|
|
|
|
/// @brief private c'tor.
|
|
/// @param word_ptr pointer to the word holding the actual bit.
|
|
/// @param mask bitmask indicating referenced bit of `*word_ptr`.
|
|
/// @note `word_ptr` must not be `nullptr`.
|
|
/// @note `mask` must be `1ull << b` for some `0 <= b < 64`.
|
|
/// @note accessible only to `dpf::bit_array_base` and its iterators.
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_NON_NULL()
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr bit_reference(word_pointer word_ptr, word_type mask) noexcept
|
|
: word_ptr_{word_ptr},
|
|
mask_{mask}
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
detail::check_one_bit(mask_);
|
|
}
|
|
|
|
template <typename CharT,
|
|
typename Traits>
|
|
friend std::basic_ostream<CharT, Traits> &
|
|
operator<<(std::basic_ostream<CharT, Traits> & os, bit_reference bit)
|
|
{
|
|
return os << dpf::to_string<CharT, Traits>(bit);
|
|
}
|
|
|
|
friend class bit_array_base; //< access to c'tor
|
|
friend class bit_iterator_base<ConcreteBitArrayT, WordT>; //< access to c'tor
|
|
friend class bit_iterator<ConcreteBitArrayT, WordT>; //< access to c'tor
|
|
friend class const_bit_iterator<ConcreteBitArrayT, WordT>; //< access to c'tor
|
|
}; // class bit_array_base::bit_reference
|
|
|
|
protected:
|
|
/// @brief constructs a `bit_array_base`
|
|
inline constexpr bit_array_base() = default;
|
|
/// @brief an all-`1`s sentinel word marking the end of the data
|
|
/// @note `sentinel` exists to assist `setbit_index_iterator` in deciding
|
|
/// if it has hit the end of the data array
|
|
static constexpr word_type sentinel = ~word_type(0);
|
|
|
|
/// @brief Low `n` bits set. `n == 0` yields 0. `n >= bits_per_word` yields all ones.
|
|
/// @param n the `n`
|
|
/// @return Low `n` bits set
|
|
HEDLEY_NO_THROW
|
|
static constexpr word_type low_bits_mask(size_type n) noexcept
|
|
{
|
|
if (n == 0) return word_type{0};
|
|
if (n >= bits_per_word) return static_cast<word_type>(~word_type{0});
|
|
return static_cast<word_type>(
|
|
static_cast<word_type>(~word_type{0}) >> (bits_per_word - n));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
static constexpr size_type pop(word_type w) noexcept
|
|
{
|
|
return static_cast<size_type>(utils::popcount(w));
|
|
}
|
|
|
|
/// @brief Bits strictly below the single set bit in `mask`.
|
|
/// @param mask the bit mask
|
|
/// @return Bits strictly below the single set bit in `mask`
|
|
HEDLEY_NO_THROW
|
|
static constexpr word_type bits_below(word_type mask) noexcept
|
|
{
|
|
return static_cast<word_type>(mask - word_type{1});
|
|
}
|
|
|
|
/// @brief Bits at and above the single set bit in `mask`.
|
|
/// @param mask the bit mask
|
|
/// @return Bits at and above the single set bit in `mask`
|
|
HEDLEY_NO_THROW
|
|
static constexpr word_type bits_at_and_above(word_type mask) noexcept
|
|
{
|
|
return static_cast<word_type>(~bits_below(mask));
|
|
}
|
|
|
|
/// @brief Invoke `fn(masked_bits, relevant_mask)` for each limb touched by
|
|
/// `[first, last)`. Does not dereference a one-past-the-end word.
|
|
/// `fn` returns false to stop early.
|
|
/// @tparam Iterator iterator type
|
|
/// @tparam Fn fn
|
|
/// @param first the first element of the range
|
|
/// @param last the past-the-end element of the range
|
|
/// @param fn the `fn`
|
|
template <typename Iterator, typename Fn>
|
|
void for_each_span(Iterator first, Iterator last, Fn fn) const
|
|
{
|
|
if (first == last) return;
|
|
|
|
const word_type *base = data();
|
|
const word_type *limit = base + data_length();
|
|
const word_type *fw = first.word_ptr_;
|
|
const word_type *lw = last.word_ptr_;
|
|
const word_type fm = first.mask_;
|
|
const word_type lm = last.mask_;
|
|
|
|
auto contained = [base, limit](const word_type *p)
|
|
{
|
|
return p >= base && p < limit;
|
|
};
|
|
auto emit = [&](const word_type *p, word_type relevant) -> bool
|
|
{
|
|
if (!contained(p) || relevant == word_type{0}) return true;
|
|
return fn(static_cast<word_type>(*p & relevant), relevant);
|
|
};
|
|
|
|
if (fw == lw)
|
|
{
|
|
const word_type span = static_cast<word_type>(
|
|
bits_below(lm) & bits_at_and_above(fm));
|
|
emit(fw, span);
|
|
return;
|
|
}
|
|
|
|
if (!emit(fw, bits_at_and_above(fm))) return;
|
|
|
|
const word_type *mid = fw + 1;
|
|
const word_type *mid_end = (lw < limit) ? lw : limit;
|
|
if (mid < base) mid = base;
|
|
const word_type ones = static_cast<word_type>(~word_type{0});
|
|
for (const word_type *p = mid; p < mid_end; ++p)
|
|
{
|
|
if (!fn(*p, ones)) return;
|
|
}
|
|
emit(lw, bits_below(lm));
|
|
}
|
|
}; // class bit_array_base
|
|
|
|
/// @brief a base class provided to simplify the definition of
|
|
/// `bit_iterator` and `const_bit_iterator`
|
|
/// @tparam ConcreteBitArrayT concrete bit array type
|
|
/// @tparam WordT word used to pack bits
|
|
template <typename ConcreteBitArrayT,
|
|
typename WordT>
|
|
class bit_iterator_base
|
|
{
|
|
public:
|
|
using difference_type = std::ptrdiff_t;
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
using word_type = typename bit_array_base<ConcreteBitArrayT, WordT>::word_type;
|
|
using word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::word_pointer;
|
|
static constexpr auto bits_per_word = bit_array_base<ConcreteBitArrayT, WordT>::bits_per_word;
|
|
|
|
inline constexpr bool operator==(const bit_iterator_base & rhs) const
|
|
{
|
|
return (word_ptr_ == rhs.word_ptr_) && (mask_ == rhs.mask_);
|
|
}
|
|
|
|
inline constexpr bool operator<(const bit_iterator_base & rhs) const
|
|
{
|
|
return word_ptr_ < rhs.word_ptr_ ||
|
|
(word_ptr_ == rhs.word_ptr_ && mask_ < rhs.mask_);
|
|
}
|
|
|
|
inline constexpr bool operator!=(const bit_iterator_base & rhs) const
|
|
{
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
inline constexpr bool operator>(const bit_iterator_base & rhs) const
|
|
{
|
|
return rhs < *this;
|
|
}
|
|
|
|
inline constexpr bool operator<=(const bit_iterator_base & rhs) const
|
|
{
|
|
return !(rhs < *this);
|
|
}
|
|
|
|
inline constexpr bool operator>=(const bit_iterator_base & rhs) const
|
|
{
|
|
return !(*this < rhs);
|
|
}
|
|
|
|
protected:
|
|
/// @brief bitmask for the least-significant bit of a word
|
|
static constexpr word_type lsb = word_type(1);
|
|
|
|
/// @brief bitmask for the most-significant bit of a word
|
|
static constexpr word_type msb = ~(~word_type(0) >> 1);
|
|
|
|
/// @brief pointer to the word containing the current iteration bit
|
|
word_pointer word_ptr_;
|
|
/// @brief mask into `*word_ptr_` to obtain current iteration bit
|
|
word_type mask_;
|
|
|
|
/// @brief Singular iterator. Comparable, not dereferenceable.
|
|
HEDLEY_NO_THROW
|
|
inline constexpr bit_iterator_base() noexcept
|
|
: word_ptr_{nullptr},
|
|
mask_{lsb}
|
|
{ }
|
|
|
|
/// @brief constructs a `bit_iterator_base`
|
|
/// @{
|
|
/// @brief constructs a `bit_iterator_base`
|
|
/// @param word_ptr pointer to the word containing the first iteration
|
|
/// bit
|
|
/// @note `word_ptr` must be dereferencable (not `nullptr`)
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_NON_NULL()
|
|
inline constexpr explicit bit_iterator_base(word_pointer word_ptr) noexcept
|
|
: word_ptr_{word_ptr},
|
|
mask_{lsb}
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
}
|
|
|
|
/// @brief constructs a `bit_iterator_base`
|
|
/// @param word_ptr pointer to the word containing the first iteration
|
|
/// bit
|
|
/// @param mask bitmask to obtain `*word_ptr` first iteration bit from
|
|
/// `word_ptr`
|
|
/// @note `word_ptr` must be dereferencable (not `nullptr`)
|
|
/// @note `mask` must have exactly one bit set
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_NON_NULL()
|
|
inline constexpr bit_iterator_base(word_pointer word_ptr, word_type mask) noexcept
|
|
: word_ptr_{word_ptr},
|
|
mask_{mask}
|
|
{
|
|
assert(word_ptr_ != nullptr);
|
|
detail::check_one_bit(mask_);
|
|
}
|
|
/// @}
|
|
|
|
/// @brief increments the iterator by one bit
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
void constexpr increment() noexcept
|
|
{
|
|
if (HEDLEY_UNLIKELY(!(mask_ <<= 1)))
|
|
{
|
|
mask_ = lsb;
|
|
++word_ptr_;
|
|
}
|
|
}
|
|
|
|
/// @brief decrements the iterator by one bit
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
void constexpr decrement() noexcept
|
|
{
|
|
if (HEDLEY_UNLIKELY(!(mask_ >>= 1)))
|
|
{
|
|
mask_ = msb;
|
|
--word_ptr_;
|
|
}
|
|
}
|
|
|
|
/// @brief increments the iterator by specified number of bits
|
|
/// @param amt the number of bits to increment by
|
|
HEDLEY_NO_THROW
|
|
inline constexpr void increment_by(difference_type amt) noexcept
|
|
{
|
|
if (amt == 0) return;
|
|
|
|
// Stay in signed arithmetic. `ctz` returns `size_t`, and dividing a
|
|
// negative `ptrdiff_t` by that unsigned width walks the pointer the
|
|
// wrong direction.
|
|
const auto bpw = static_cast<difference_type>(bits_per_word);
|
|
const auto bit = static_cast<difference_type>(utils::ctz(mask_));
|
|
difference_type offset = amt + bit;
|
|
difference_type words = offset / bpw;
|
|
difference_type rem = offset % bpw;
|
|
if (rem < 0)
|
|
{
|
|
rem += bpw;
|
|
--words;
|
|
}
|
|
word_ptr_ += words;
|
|
mask_ = static_cast<word_type>(static_cast<word_type>(1) << rem);
|
|
}
|
|
|
|
/// @brief decrements the iterator by specified number of bits
|
|
/// @param amt the number of bits to decrement by
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
void constexpr decrement_by(difference_type amt) noexcept
|
|
{
|
|
increment_by(-amt);
|
|
}
|
|
|
|
friend constexpr difference_type operator-(const bit_iterator_base & lhs,
|
|
const bit_iterator_base & rhs) noexcept
|
|
{
|
|
const auto bpw = static_cast<difference_type>(
|
|
bit_iterator_base<ConcreteBitArrayT, WordT>::bits_per_word);
|
|
const auto lb = static_cast<difference_type>(utils::ctz(lhs.mask_));
|
|
const auto rb = static_cast<difference_type>(utils::ctz(rhs.mask_));
|
|
return bpw * (lhs.word_ptr_ - rhs.word_ptr_) + (lb - rb);
|
|
}
|
|
|
|
friend class setbit_index_iterable<ConcreteBitArrayT, WordT>;
|
|
}; // class bit_iterator_base
|
|
|
|
template <typename ConcreteBitArrayT,
|
|
typename WordT>
|
|
class bit_iterator final
|
|
: public bit_iterator_base<ConcreteBitArrayT, WordT>
|
|
{
|
|
private:
|
|
using base = bit_iterator_base<ConcreteBitArrayT, WordT>;
|
|
public:
|
|
using difference_type = std::ptrdiff_t;
|
|
using value_type = typename bit_array_base<ConcreteBitArrayT, WordT>::value_type;
|
|
using reference = typename bit_array_base<ConcreteBitArrayT, WordT>::reference;
|
|
using const_reference = typename bit_array_base<ConcreteBitArrayT>::const_reference;
|
|
using pointer = std::add_pointer_t<reference>;
|
|
using iterator = bit_iterator<ConcreteBitArrayT, WordT>;
|
|
using word_type = typename bit_array_base<ConcreteBitArrayT, WordT>::word_type;
|
|
using word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::word_pointer;
|
|
|
|
/// @brief Singular iterator. Comparable, not dereferenceable.
|
|
HEDLEY_NO_THROW
|
|
constexpr bit_iterator() noexcept = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
inline constexpr
|
|
bit_iterator(const bit_iterator &) noexcept = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
inline constexpr bit_iterator(bit_iterator &&) noexcept = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_NON_NULL()
|
|
inline constexpr explicit bit_iterator(word_pointer word_ptr) noexcept
|
|
: base{word_ptr} { }
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_NON_NULL()
|
|
inline constexpr explicit bit_iterator(word_pointer word_ptr,
|
|
word_type mask) noexcept
|
|
: base{word_ptr, mask} { }
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
bit_iterator & operator=(const bit_iterator &) noexcept = default;
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
bit_iterator & operator=(bit_iterator &&) noexcept = default;
|
|
|
|
~bit_iterator() = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr reference operator*() const noexcept
|
|
{
|
|
return reference{base::word_ptr_, base::mask_};
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr iterator & operator++() noexcept
|
|
{
|
|
base::increment();
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr iterator operator++(int) noexcept
|
|
{
|
|
iterator tmp = *this;
|
|
base::increment();
|
|
return tmp;
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr iterator & operator--() noexcept
|
|
{
|
|
base::decrement();
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr iterator operator--(int) noexcept
|
|
{
|
|
iterator tmp = *this;
|
|
base::decrement();
|
|
return tmp;
|
|
}
|
|
|
|
inline constexpr iterator & operator+=(difference_type amt)
|
|
{
|
|
base::increment_by(amt);
|
|
return *this;
|
|
}
|
|
|
|
inline constexpr iterator & operator-=(difference_type amt)
|
|
{
|
|
base::decrement_by(amt);
|
|
return *this;
|
|
}
|
|
|
|
inline constexpr iterator operator+(difference_type amt) const
|
|
{
|
|
iterator tmp = *this;
|
|
return tmp += amt;
|
|
}
|
|
|
|
inline constexpr iterator operator-(difference_type amt) const
|
|
{
|
|
iterator tmp = *this;
|
|
return tmp -= amt;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
friend constexpr iterator operator+(difference_type amt, iterator it) noexcept
|
|
{
|
|
return it + amt;
|
|
}
|
|
|
|
inline constexpr reference operator[](difference_type i)
|
|
{
|
|
return *(*this + i);
|
|
}
|
|
|
|
inline constexpr const_reference operator[](difference_type i) const
|
|
{
|
|
return *(*this + i);
|
|
}
|
|
|
|
friend class bit_array_base<ConcreteBitArrayT, WordT>;
|
|
friend class const_bit_iterator<ConcreteBitArrayT, WordT>;
|
|
}; // class bit_iterator
|
|
|
|
template <typename ConcreteBitArrayT,
|
|
typename WordT>
|
|
class const_bit_iterator final
|
|
: public bit_iterator_base<ConcreteBitArrayT, WordT>
|
|
{
|
|
private:
|
|
using base = bit_iterator_base<ConcreteBitArrayT, WordT>;
|
|
public:
|
|
using difference_type = std::ptrdiff_t;
|
|
using value_type = typename bit_array_base<ConcreteBitArrayT, WordT>::value_type;
|
|
using reference = typename bit_array_base<ConcreteBitArrayT, WordT>::const_reference;
|
|
using const_reference = typename bit_array_base<ConcreteBitArrayT, WordT>::const_reference;
|
|
using pointer = std::add_pointer_t<reference>;
|
|
using iterator = const_bit_iterator<ConcreteBitArrayT, WordT>;
|
|
using const_iterator = const_bit_iterator<ConcreteBitArrayT, WordT>;
|
|
using word_type = typename bit_array_base<ConcreteBitArrayT, WordT>::word_type;
|
|
using word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::word_pointer;
|
|
using const_word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::const_word_pointer;
|
|
|
|
/// @brief Singular iterator. Comparable, not dereferenceable.
|
|
HEDLEY_NO_THROW
|
|
constexpr const_bit_iterator() noexcept = default;
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
const_bit_iterator(const const_bit_iterator &) noexcept = default;
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
const_bit_iterator(const_bit_iterator &&) noexcept = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_NON_NULL()
|
|
inline constexpr explicit const_bit_iterator(const_word_pointer word_ptr) noexcept
|
|
: base{word_ptr} { }
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_NON_NULL()
|
|
inline constexpr explicit const_bit_iterator(const_word_pointer word_ptr,
|
|
word_type mask) noexcept
|
|
: base{const_cast<word_pointer>(word_ptr), mask} { }
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
const_bit_iterator & operator=(const const_bit_iterator &) noexcept
|
|
= default;
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
const_bit_iterator & operator=(const_bit_iterator &&) noexcept
|
|
= default;
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr
|
|
explicit const_bit_iterator(const bit_iterator<ConcreteBitArrayT, WordT> & to_copy) noexcept
|
|
: base{to_copy.word_ptr_, to_copy.mask_} {}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr
|
|
explicit const_bit_iterator(bit_iterator<ConcreteBitArrayT, WordT> && to_copy) noexcept
|
|
: base{to_copy.word_ptr_, to_copy.mask_} {}
|
|
|
|
~const_bit_iterator() = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr const_reference operator*() const noexcept
|
|
{
|
|
using bitref = typename bit_array_base<ConcreteBitArrayT, WordT>::bit_reference;
|
|
return bitref{base::word_ptr_, base::mask_};
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator & operator++() noexcept
|
|
{
|
|
base::increment();
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator operator++(int) noexcept
|
|
{
|
|
const_iterator tmp = *this;
|
|
base::increment();
|
|
return tmp;
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator & operator--() noexcept
|
|
{
|
|
base::decrement();
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr const_iterator operator--(int) noexcept
|
|
{
|
|
const_iterator tmp = *this;
|
|
base::decrement();
|
|
return tmp;
|
|
}
|
|
|
|
inline constexpr
|
|
const_iterator & operator+=(difference_type amt) noexcept
|
|
{
|
|
base::increment_by(amt);
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
inline constexpr
|
|
const_iterator & operator-=(difference_type amt) noexcept
|
|
{
|
|
base::decrement_by(amt);
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
inline constexpr
|
|
const_iterator operator+(difference_type amt) const noexcept
|
|
{
|
|
const_iterator tmp = *this;
|
|
return tmp += amt;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
inline constexpr
|
|
const_iterator operator-(difference_type amt) const noexcept
|
|
{
|
|
const_iterator tmp = *this;
|
|
return tmp -= amt;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
friend constexpr const_iterator operator+(difference_type amt,
|
|
const_iterator it) noexcept
|
|
{
|
|
return it + amt;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
inline constexpr
|
|
const_reference operator[](difference_type i) const noexcept
|
|
{
|
|
return *(*this + i);
|
|
}
|
|
|
|
friend class bit_array_base<ConcreteBitArrayT, WordT>;
|
|
}; // class bit_array_base::const_bit_iterator
|
|
|
|
template <std::size_t Nbits, typename WordT = psnip_uint64_t>
|
|
class alignas(utils::max_align_v) static_bit_array final
|
|
: public bit_array_base<static_bit_array<Nbits, WordT>, WordT>
|
|
{
|
|
private:
|
|
using base = bit_array_base<static_bit_array<Nbits, WordT>, WordT>;
|
|
public:
|
|
using size_type = typename base::size_type;
|
|
using word_pointer = typename base::word_pointer;
|
|
using const_word_pointer = typename base::const_word_pointer;
|
|
using word_type = typename base::word_type;
|
|
static constexpr auto bits_per_word = base::bits_per_word;
|
|
private:
|
|
/// @brief the number of `word_type`s are being used to represent the
|
|
/// `size()` bits
|
|
static constexpr size_type data_length_ = utils::quotient_ceiling(Nbits, bits_per_word);
|
|
public:
|
|
HEDLEY_NO_THROW
|
|
constexpr static_bit_array(static_bit_array &&) noexcept = default;
|
|
HEDLEY_NO_THROW
|
|
constexpr static_bit_array(const static_bit_array &) noexcept = default;
|
|
~static_bit_array() = default;
|
|
HEDLEY_NO_THROW
|
|
constexpr static_bit_array & operator=(static_bit_array &&) noexcept = default;
|
|
HEDLEY_NO_THROW
|
|
constexpr static_bit_array & operator=(const static_bit_array &) noexcept = default;
|
|
/// @brief constructs a zeroed `static_bit_array` that holds `Nbits` bits
|
|
inline constexpr static_bit_array()
|
|
: data_{}
|
|
{
|
|
data_[data_length_] = base::sentinel;
|
|
}
|
|
|
|
/// @brief constructs a `static_bit_array` from the low bits of `val`
|
|
/// @param val the `val`
|
|
inline constexpr explicit static_bit_array(std::size_t val)
|
|
: data_{}
|
|
{
|
|
if constexpr (data_length_ > 0)
|
|
{
|
|
data_[0] = static_cast<word_type>(val);
|
|
// The partial limb is the last word. Word 0 is partial only when
|
|
// the whole array fits in one word.
|
|
if constexpr (data_length_ == 1 && Nbits % bits_per_word != 0)
|
|
data_[0] &= base::low_bits_mask(Nbits % bits_per_word);
|
|
}
|
|
data_[data_length_] = base::sentinel;
|
|
}
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr word_pointer data() noexcept
|
|
{
|
|
return static_cast<word_pointer>(__builtin_assume_aligned(std::data(data_), utils::max_align_v));
|
|
}
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
constexpr const_word_pointer data() const noexcept
|
|
{
|
|
return static_cast<const_word_pointer>(__builtin_assume_aligned(std::data(data_), utils::max_align_v));
|
|
}
|
|
|
|
/// @brief length of the underlying data array
|
|
/// @return the number of elements in the underlying array (excluding a
|
|
/// non-data sentinel element)
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr size_type data_length() const noexcept
|
|
{
|
|
return data_length_;
|
|
}
|
|
|
|
/// @brief returns the number of bits
|
|
/// @return number of bits that the `static_bit_array` holds
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_PURE
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr size_type size() const noexcept
|
|
{
|
|
return Nbits;
|
|
}
|
|
|
|
private:
|
|
alignas(utils::max_align_v) std::array<word_type, data_length_+1> data_;
|
|
};
|
|
|
|
template <typename WordT = psnip_uint64_t>
|
|
class dynamic_bit_array
|
|
: public bit_array_base<dynamic_bit_array<WordT>, WordT>
|
|
{
|
|
private:
|
|
using base = bit_array_base<dynamic_bit_array<WordT>, WordT>;
|
|
public:
|
|
using size_type = typename base::size_type;
|
|
using word_pointer = typename base::word_pointer;
|
|
using const_word_pointer = typename base::const_word_pointer;
|
|
using word_type = typename base::word_type;
|
|
static constexpr auto bits_per_word = base::bits_per_word;
|
|
private:
|
|
using allocator = aligned_allocator<word_type, utils::max_align_v>;
|
|
using unique_ptr = typename allocator::unique_ptr;
|
|
public:
|
|
/// @brief constructs a zeroed `dynamic_bit_array` that holds `nbits` bits
|
|
/// @param nbits the width in bits
|
|
/// @param alloc the `alloc`
|
|
/// @throws std::bad_alloc if allocating storage fails
|
|
inline explicit dynamic_bit_array(std::size_t nbits,
|
|
allocator alloc = allocator{})
|
|
: num_bits_{nbits},
|
|
data_length_{utils::quotient_ceiling(nbits, bits_per_word)},
|
|
data_{alloc.allocate_unique_ptr(data_length_+1)}
|
|
{
|
|
if (HEDLEY_UNLIKELY(data_ == nullptr)) throw std::bad_alloc{};
|
|
std::fill_n(data_.get(), data_length_, word_type{0});
|
|
data_[data_length_] = base::sentinel;
|
|
}
|
|
|
|
dynamic_bit_array(const dynamic_bit_array & other)
|
|
: num_bits_{other.num_bits_},
|
|
data_length_{other.data_length_},
|
|
data_{other.data_ ? allocator{}.allocate_unique_ptr(other.data_length_ + 1)
|
|
: unique_ptr{}}
|
|
{
|
|
if (data_)
|
|
std::copy_n(other.data_.get(), data_length_ + 1, data_.get());
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
dynamic_bit_array(dynamic_bit_array && other) noexcept
|
|
: num_bits_{std::exchange(other.num_bits_, 0)},
|
|
data_length_{std::exchange(other.data_length_, 0)},
|
|
data_{std::move(other.data_)}
|
|
{ }
|
|
|
|
dynamic_bit_array & operator=(const dynamic_bit_array & other)
|
|
{
|
|
if (this != &other)
|
|
{
|
|
dynamic_bit_array tmp(other);
|
|
*this = std::move(tmp);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
dynamic_bit_array & operator=(dynamic_bit_array && other) noexcept
|
|
{
|
|
if (this != &other)
|
|
{
|
|
wipe();
|
|
data_ = std::move(other.data_);
|
|
num_bits_ = std::exchange(other.num_bits_, 0);
|
|
data_length_ = std::exchange(other.data_length_, 0);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
~dynamic_bit_array() noexcept
|
|
{
|
|
wipe();
|
|
}
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
word_pointer data() noexcept
|
|
{
|
|
auto *raw = data_.get();
|
|
if (raw == nullptr) return nullptr;
|
|
return static_cast<word_pointer>(__builtin_assume_aligned(raw, utils::max_align_v));
|
|
}
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @param i the `i`
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
word_type & data(std::size_t i) noexcept
|
|
{
|
|
return this->data()[i];
|
|
}
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
const_word_pointer data() const noexcept
|
|
{
|
|
auto *raw = data_.get();
|
|
if (raw == nullptr) return nullptr;
|
|
return static_cast<const_word_pointer>(__builtin_assume_aligned(raw, utils::max_align_v));
|
|
}
|
|
|
|
/// @brief direct access to the underlying data array
|
|
/// @param i the `i`
|
|
/// @return a pointer to the start of the data array
|
|
HEDLEY_ALWAYS_INLINE
|
|
HEDLEY_NO_THROW
|
|
const word_type & data(std::size_t i) const noexcept
|
|
{
|
|
return this->data()[i];
|
|
}
|
|
|
|
/// @brief length of the underlying data array
|
|
/// @return the number of elements in the underlying array (excluding a
|
|
/// non-data sentinel element)
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr size_type data_length() const noexcept
|
|
{
|
|
return data_length_;
|
|
}
|
|
|
|
/// @brief returns the number of bits
|
|
/// @return number of bits that the `dynamic_bit_array` holds
|
|
/// @complexity `O(1)`
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_PURE
|
|
HEDLEY_ALWAYS_INLINE
|
|
constexpr size_type size() const noexcept
|
|
{
|
|
return num_bits_;
|
|
}
|
|
|
|
private:
|
|
/// @brief Store zeros through `volatile` so the wipe is not deleted as a dead store.
|
|
HEDLEY_NO_THROW
|
|
void wipe() noexcept
|
|
{
|
|
if (!data_) return;
|
|
volatile word_type *p = data_.get();
|
|
for (size_type i = 0; i < data_length_ + 1; ++i)
|
|
p[i] = word_type{0};
|
|
}
|
|
|
|
size_type num_bits_;
|
|
/// @brief the number of `word_type`s are being used to represent the
|
|
/// `size()` bits
|
|
size_type data_length_;
|
|
unique_ptr data_;
|
|
};
|
|
|
|
/// @brief Exchanges the bits named by two `dynamic_bit_array` proxies.
|
|
/// @tparam WordT word used to pack bits
|
|
/// @param lhs the left-hand operand
|
|
/// @param rhs the right-hand operand
|
|
template <typename WordT>
|
|
HEDLEY_NO_THROW
|
|
inline constexpr void swap(typename dynamic_bit_array<WordT>::reference lhs,
|
|
typename dynamic_bit_array<WordT>::reference rhs) noexcept
|
|
{
|
|
bool tmp = lhs;
|
|
lhs = static_cast<bool>(rhs);
|
|
rhs = tmp;
|
|
}
|
|
|
|
/// @brief Exchanges the bits named by two `static_bit_array` proxies.
|
|
/// @tparam Nbits width in bits
|
|
/// @tparam WordT word used to pack bits
|
|
/// @param lhs the left-hand operand
|
|
/// @param rhs the right-hand operand
|
|
template <std::size_t Nbits,
|
|
typename WordT>
|
|
HEDLEY_NO_THROW
|
|
inline constexpr void swap(typename static_bit_array<Nbits, WordT>::reference lhs,
|
|
typename static_bit_array<Nbits, WordT>::reference rhs) noexcept
|
|
{
|
|
bool tmp = lhs;
|
|
lhs = static_cast<bool>(rhs);
|
|
rhs = tmp;
|
|
}
|
|
|
|
namespace utils
|
|
{
|
|
|
|
template <typename ChildT,
|
|
typename WordT>
|
|
struct is_bit_array<bit_array_base<ChildT, WordT>> : std::true_type {};
|
|
|
|
template <std::size_t Nbits,
|
|
typename WordT>
|
|
struct is_bit_array<static_bit_array<Nbits, WordT>> : std::true_type {};
|
|
|
|
template <typename WordT>
|
|
struct is_bit_array<dynamic_bit_array<WordT>> : std::true_type {};
|
|
|
|
} // namespace utils
|
|
|
|
} // namespace dpf
|
|
|
|
namespace std
|
|
{
|
|
|
|
template <typename ConcreteBitArrayT,
|
|
typename WordT>
|
|
struct iterator_traits<dpf::bit_iterator<ConcreteBitArrayT, WordT>>
|
|
{
|
|
private:
|
|
using type = dpf::bit_iterator<ConcreteBitArrayT, WordT>;
|
|
public:
|
|
using iterator_category = typename type::iterator_category;
|
|
using difference_type = typename type::difference_type;
|
|
using value_type = typename type::value_type;
|
|
using reference = typename type::reference;
|
|
using const_reference = typename type::const_reference;
|
|
using pointer = typename type::pointer;
|
|
};
|
|
|
|
template <typename ConcreteBitArrayT,
|
|
typename WordT>
|
|
struct iterator_traits<dpf::const_bit_iterator<ConcreteBitArrayT, WordT>>
|
|
{
|
|
private:
|
|
using type = dpf::const_bit_iterator<ConcreteBitArrayT, WordT>;
|
|
public:
|
|
using iterator_category = typename type::iterator_category;
|
|
using difference_type = typename type::difference_type;
|
|
using value_type = typename type::value_type;
|
|
using reference = typename type::reference;
|
|
using const_reference = typename type::const_reference;
|
|
using pointer = typename type::pointer;
|
|
};
|
|
|
|
} // namespace std
|
|
|
|
#endif // LIBDPF_INCLUDE_DPF_BIT_ARRAY_HPP__
|