diff --git a/include/flatmemory/details/operator.hpp b/include/flatmemory/details/operator.hpp
deleted file mode 100644
index dea56cb..0000000
--- a/include/flatmemory/details/operator.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2024 Dominik Drexler
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef FLATMEMORY_OPERATOR_HPP_
-#define FLATMEMORY_OPERATOR_HPP_
-
-namespace flatmemory
-{
-/**
- * Implementation class.
- *
- * Provide overload with your Tag.
- *
- * Define operations between Builder, View, ConstView
- */
-template
-class Operator
-{
-};
-}
-
-#endif
diff --git a/include/flatmemory/details/types/bitset.hpp b/include/flatmemory/details/types/bitset.hpp
index 90877b9..00b21cf 100644
--- a/include/flatmemory/details/types/bitset.hpp
+++ b/include/flatmemory/details/types/bitset.hpp
@@ -71,30 +71,40 @@ class Layout>
};
/**
- * Operator
+ * BitsetUtils
*/
-template
-class Operator>
+class BitsetUtils
{
public:
+ template
static constexpr std::size_t block_size = sizeof(Block) * 8;
// 000...
+ template
static constexpr Block block_zeroes = 0;
// 111...
+ template
static constexpr Block block_ones = Block(-1);
// 100...
- static constexpr Block block_msb_one = Block(1) << (block_size - 1);
+ template
+ static constexpr Block block_msb_one = Block(1) << (block_size - 1);
// 011...
- static constexpr Block block_msb_zero = block_ones & (~block_msb_one);
+ template
+ static constexpr Block block_msb_zero = block_ones&(~block_msb_one);
// 111...
static constexpr std::size_t no_position = std::size_t(-1);
/**
- * Helpers
+ * Operators
*/
- static int64_t find_last_relevant_block(const std::vector& blocks, bool default_bit_value);
+ template
+ requires HaveSameBlockType && HaveCompatibleTagType
+ static bool is_superseteq(const B1& lhs, const B2& rhs);
+
+ template
+ requires HaveSameBlockType && HaveCompatibleTagType
+ static bool are_disjoint(const B1& lhs, const B2& rhs);
/**
* Lookup
@@ -107,10 +117,14 @@ class Operator>
template
static size_t count(const B& bitset);
+ template
+ static int64_t find_last_relevant_block(const std::vector& blocks, bool default_bit_value);
+
/**
* Iterators
*/
+ template
class const_iterator
{
private:
@@ -149,10 +163,13 @@ class Operator>
* Math
*/
+ template
static size_t get_lsb_position(Block n) noexcept;
+ template
static size_t get_index(size_t position) noexcept;
+ template
static size_t get_offset(size_t position) noexcept;
};
@@ -177,14 +194,6 @@ bool operator!=(const B1& lhs, const B2& rhs);
template
Builder> operator~(const B& element);
-template
-requires HaveSameBlockType && HaveCompatibleTagType
-bool is_superseteq(const B1& lhs, const B2& rhs);
-
-template
-requires HaveSameBlockType && HaveCompatibleTagType
-bool are_disjoint(const B1& lhs, const B2& rhs);
-
/**
* Builder
*/
@@ -196,11 +205,10 @@ class Builder> : public IBuilder>>
using BlockType = Block;
using TagType = Tag;
- using BitsetOperator = Operator>;
using BitsetLayout = Layout>;
using BitsetView = View>;
using BitsetConstView = ConstView>;
- using const_iterator = typename BitsetOperator::const_iterator;
+ using const_iterator = typename BitsetUtils::const_iterator;
private:
bool m_default_bit_value;
@@ -291,6 +299,18 @@ class Builder> : public IBuilder>>
template
requires HasBlockType && HasCompatibleTagType Builder& operator-=(const Other& other);
+ /**
+ * Operators
+ */
+
+ template
+ requires HasBlockType && HasCompatibleTagType
+ bool is_superseteq(const B& other);
+
+ template
+ requires HasBlockType && HasCompatibleTagType
+ bool are_disjoint(const B& other);
+
/**
* Lookup
*/
@@ -339,10 +359,9 @@ class View>
using TagType = Tag;
using BitsetLayout = Layout>;
- using BitsetOperator = Operator>;
using BitsetView = View>;
using BitsetConstView = ConstView>;
- using const_iterator = typename BitsetOperator::const_iterator;
+ using const_iterator = typename BitsetUtils::const_iterator;
private:
uint8_t* m_buf;
@@ -358,6 +377,18 @@ class View>
/// @param buf
explicit View(uint8_t* buf);
+ /**
+ * Operators
+ */
+
+ template
+ requires HasBlockType && HasCompatibleTagType
+ bool is_superseteq(const B& other);
+
+ template
+ requires HasBlockType && HasCompatibleTagType
+ bool are_disjoint(const B& other);
+
/**
* Lookup
*/
@@ -402,10 +433,9 @@ class ConstView>
using TagType = Tag;
using BitsetLayout = Layout>;
- using BitsetOperator = Operator>;
using BitsetView = View>;
using BitsetConstView = ConstView>;
- using const_iterator = typename BitsetOperator::const_iterator;
+ using const_iterator = typename BitsetUtils::const_iterator;
private:
const uint8_t* m_buf;
@@ -427,6 +457,18 @@ class ConstView>
ConstView(const BitsetView& view);
+ /**
+ * Operators
+ */
+
+ template
+ requires HasBlockType && HasCompatibleTagType
+ bool is_superseteq(const B& other);
+
+ template
+ requires HasBlockType && HasCompatibleTagType
+ bool are_disjoint(const B& other);
+
/**
* Lookup
*/
@@ -482,30 +524,134 @@ constexpr void Layout>::print() const
/* Operator */
-template
-int64_t Operator>::find_last_relevant_block(const std::vector& blocks, bool default_bit_value)
+template
+requires HaveSameBlockType && HaveCompatibleTagType
+bool BitsetUtils::is_superseteq(const B1& lhs, const B2& rhs)
+{
+ // Fetch data
+ const auto& blocks = lhs.get_blocks();
+ bool default_bit_value = lhs.get_default_bit_value();
+ const auto& other_blocks = rhs.get_blocks();
+ bool other_default_bit_value = rhs.get_default_bit_value();
+
+ if (other_default_bit_value && !default_bit_value)
+ {
+ // blocks has finitely many and other blocks has infinitely many set bits.
+ // Hence blocks cannot be a superseteq of other_blocks.
+ return false;
+ }
+
+ std::size_t common_size = std::min(blocks.size(), other_blocks.size());
+
+ for (std::size_t index = 0; index < common_size; ++index)
+ {
+ if ((blocks[index] & other_blocks[index]) != other_blocks[index])
+ {
+ // There exists a set bit in other block that is not set in block.
+ return false;
+ }
+ }
+
+ if (other_blocks.size() <= blocks.size())
+ {
+ // blocks can only contain additional set bits
+ return true;
+ }
+
+ if (default_bit_value)
+ {
+ return true;
+ }
+
+ for (std::size_t index = common_size; index < other_blocks.size(); ++index)
+ {
+ if (other_blocks[index])
+ {
+ // other_block contains additional set bits
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template
+requires HaveSameBlockType && HaveCompatibleTagType
+bool BitsetUtils::are_disjoint(const B1& lhs, const B2& rhs)
+{
+ // Fetch data
+ const auto& blocks = lhs.get_blocks();
+ bool default_bit_value = lhs.get_default_bit_value();
+ const auto& other_blocks = rhs.get_blocks();
+ bool other_default_bit_value = rhs.get_default_bit_value();
+
+ if (default_bit_value && other_default_bit_value)
+ {
+ // blocks and other blocks have infinitely many set bits after finite sized explicit bitsets.
+ // Hence blocks and other_blocks cannot be disjoint.
+ return false;
+ }
+
+ std::size_t common_size = std::min(blocks.size(), other_blocks.size());
+
+ for (std::size_t index = 0; index < common_size; ++index)
+ {
+ if ((blocks[index] & other_blocks[index]) > 0)
+ {
+ // block and other_block have set bits in common
+ return false;
+ }
+ }
+
+ if (default_bit_value && !other_default_bit_value)
+ {
+ for (std::size_t index = common_size; index < other_blocks.size(); ++index)
+ {
+ if (other_blocks[index] > 0)
+ {
+ // other_blocks has a set bit in common with blocks in the infinite part.
+ return false;
+ }
+ }
+ }
+
+ if (!default_bit_value && other_default_bit_value)
+ {
+ for (std::size_t index = common_size; index < blocks.size(); ++index)
+ {
+ if (blocks[index] > 0)
+ {
+ // blocks has a set bit in common with other_blocks in the infinite part.
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+template
+int64_t BitsetUtils::find_last_relevant_block(const std::vector& blocks, bool default_bit_value)
{
int64_t last_relevant_block_index = static_cast(blocks.size()) - 1;
- for (; (last_relevant_block_index >= 0)
- && (blocks[last_relevant_block_index] == (default_bit_value ? Layout>::block_ones : Layout>::block_zeroes));
+ for (; (last_relevant_block_index >= 0) && (blocks[last_relevant_block_index] == (default_bit_value ? block_ones : block_zeroes) );
--last_relevant_block_index)
{
}
return last_relevant_block_index;
}
-template
template
-bool Operator>::get(const B& bitset, std::size_t position)
+bool BitsetUtils::get(const B& bitset, std::size_t position)
{
{
- const std::size_t index = get_index(position);
+ const std::size_t index = get_index(position);
const auto& blocks = bitset.get_blocks();
if (index < blocks.size())
{
- const std::size_t offset = get_offset(position);
- return (blocks[index] & (static_cast(1) << offset)) != 0;
+ const std::size_t offset = get_offset(position);
+ return (blocks[index] & (static_cast(1) << offset)) != 0;
}
else
{
@@ -514,9 +660,8 @@ bool Operator>::get(const B& bitset, std::size_t position)
}
}
-template
template
-size_t Operator>::count(const B& bitset)
+size_t BitsetUtils::count(const B& bitset)
{
auto count = (size_t) 0;
for (auto it = bitset.begin(); it != bitset.end(); ++it)
@@ -526,8 +671,8 @@ size_t Operator>::count(const B& bitset)
return count;
}
-template
-void Operator>::const_iterator::next_set_bit()
+template
+void BitsetUtils::const_iterator::next_set_bit()
{
assert(m_pos != m_end_pos);
do
@@ -535,13 +680,13 @@ void Operator>::const_iterator::next_set_bit()
// Advance position
++m_pos;
++m_bit_index;
- if (m_bit_index == block_size)
+ if (m_bit_index == block_size)
{
++m_block_index;
if (m_block_index == m_num_blocks)
{
// Reached end of blocks, set m_pos to end
- m_pos += block_size - 1;
+ m_pos += block_size - 1;
break;
}
m_bit_index = 0;
@@ -551,7 +696,7 @@ void Operator>::const_iterator::next_set_bit()
if (m_cur_block)
{
// If there are set bits in the current value
- const auto lsb_position = get_lsb_position(m_cur_block);
+ const auto lsb_position = get_lsb_position(m_cur_block);
m_bit_index += lsb_position;
m_pos += lsb_position;
// shift by + 1 to advance further
@@ -561,28 +706,28 @@ void Operator>::const_iterator::next_set_bit()
else
{
// Skip the remaining bits, point to last position in the current block
- m_pos += block_size - m_bit_index - 1;
+ m_pos += block_size - m_bit_index - 1;
++m_block_index;
m_bit_index = -1;
// Fetch next data block or zeroes
- m_cur_block = m_block_index < m_num_blocks ? m_blocks[m_block_index] : block_zeroes;
+ m_cur_block = m_block_index < m_num_blocks ? m_blocks[m_block_index] : block_zeroes;
}
} while (m_pos < m_end_pos);
}
-template
-Operator>::const_iterator::const_iterator() : m_blocks(nullptr), m_num_blocks(0), m_block_index(0), m_bit_index(-1)
+template
+BitsetUtils::const_iterator::const_iterator() : m_blocks(nullptr), m_num_blocks(0), m_block_index(0), m_bit_index(-1)
{
}
-template
-Operator>::const_iterator::const_iterator(bool default_bit_value, const Block* blocks, size_t num_blocks, bool begin) :
+template
+BitsetUtils::const_iterator::const_iterator(bool default_bit_value, const Block* blocks, size_t num_blocks, bool begin) :
m_blocks(blocks),
m_num_blocks(num_blocks),
m_block_index(0),
m_bit_index(-1),
- m_cur_block(num_blocks > 0 ? m_blocks[m_block_index] : block_zeroes),
- m_end_pos((m_num_blocks + 1) * block_size - 1),
+ m_cur_block(num_blocks > 0 ? m_blocks[m_block_index] : block_zeroes),
+ m_end_pos((m_num_blocks + 1) * block_size - 1),
m_pos(begin ? -1 : m_end_pos)
{
if (default_bit_value)
@@ -607,59 +752,59 @@ Operator>::const_iterator::const_iterator(bool default_bit_va
}
}
-template
-size_t Operator>::const_iterator::operator*() const
+template
+size_t BitsetUtils::const_iterator::const_iterator::operator*() const
{
// Do not allow interpreting end as position.
assert(m_pos < m_end_pos);
return m_pos;
}
-template
-Operator>::const_iterator& Operator>::const_iterator::operator++()
+template
+BitsetUtils::const_iterator& BitsetUtils::const_iterator::operator++()
{
next_set_bit();
return *this;
}
-template
-Operator>::const_iterator Operator>::const_iterator::operator++(int)
+template
+BitsetUtils::const_iterator BitsetUtils::const_iterator::operator++(int)
{
const_iterator tmp = *this;
++(*this);
return tmp;
}
-template
-bool Operator>::const_iterator::operator==(const const_iterator& other) const
+template
+bool BitsetUtils::const_iterator::const_iterator::operator==(const const_iterator& other) const
{
return m_pos == other.m_pos;
}
-template
-bool Operator>::const_iterator::operator!=(const const_iterator& other) const
+template
+bool BitsetUtils::const_iterator::const_iterator::operator!=(const const_iterator& other) const
{
return !(*this == other);
}
-template
-size_t Operator>::get_lsb_position(Block n) noexcept
+template
+size_t BitsetUtils::get_lsb_position(Block n) noexcept
{
assert(n != 0);
const Block v = n & (-n);
return std::bit_width(v) - 1; // bit_width uses more efficient specialized cpu instructions
}
-template
-size_t Operator>::get_index(size_t position) noexcept
+template
+size_t BitsetUtils::get_index(size_t position) noexcept
{
- return position / block_size;
+ return position / block_size;
}
-template
-size_t Operator>::get_offset(size_t position) noexcept
+template
+size_t BitsetUtils::get_offset(size_t position) noexcept
{
- return position % block_size;
+ return position % block_size;
}
/* Builder */
@@ -710,7 +855,7 @@ void Builder>::resize_to_fit(const Other& other)
{
if (m_blocks.size() < other.get_blocks().size())
{
- m_blocks.resize(other.get_blocks().size(), m_default_bit_value ? BitsetOperator::block_ones : BitsetOperator::block_zeroes);
+ m_blocks.resize(other.get_blocks().size(), m_default_bit_value ? BitsetUtils::block_ones : BitsetUtils::block_zeroes);
}
assert(m_blocks.size() > 0);
@@ -738,14 +883,14 @@ Builder>::Builder() : Builder(0)
template
Builder>::Builder(std::size_t size) :
m_default_bit_value(false),
- m_blocks((size / BitsetOperator::block_size) + 1, BitsetOperator::block_zeroes)
+ m_blocks((size / BitsetUtils::block_size) +1, BitsetUtils::block_zeroes)
{
}
template
Builder>::Builder(std::size_t size, bool default_bit_value) :
m_default_bit_value(default_bit_value),
- m_blocks((size / BitsetOperator::block_size) + 1, default_bit_value ? BitsetOperator::block_ones : BitsetOperator::block_zeroes)
+ m_blocks((size / BitsetUtils::block_size) +1, default_bit_value ? BitsetUtils::block_ones : BitsetUtils::block_zeroes)
{
}
@@ -780,7 +925,7 @@ void Builder>::shrink_to_fit()
{
int32_t last_non_default_block_index = m_blocks.size() - 1;
- const Block default_block = m_default_bit_value ? BitsetOperator::block_ones : BitsetOperator::block_zeroes;
+ const Block default_block = m_default_bit_value ? BitsetUtils::block_ones : BitsetUtils::block_zeroes;
for (; last_non_default_block_index >= 0; --last_non_default_block_index)
{
@@ -796,12 +941,12 @@ void Builder>::shrink_to_fit()
template
void Builder>::set(std::size_t position)
{
- const std::size_t index = BitsetOperator::get_index(position);
- const std::size_t offset = BitsetOperator::get_offset(position);
+ const std::size_t index = BitsetUtils::get_index(position);
+ const std::size_t offset = BitsetUtils::get_offset(position);
if (index >= m_blocks.size())
{
- m_blocks.resize(index + 1, m_default_bit_value ? BitsetOperator::block_ones : BitsetOperator::block_zeroes);
+ m_blocks.resize(index + 1, m_default_bit_value ? BitsetUtils::block_ones : BitsetUtils::block_zeroes);
}
m_blocks[index] |= (static_cast(1) << offset); // Set the bit at the offset
@@ -810,12 +955,12 @@ void Builder>::set(std::size_t position)
template
void Builder>::unset(std::size_t position)
{
- const std::size_t index = BitsetOperator::get_index(position);
- const std::size_t offset = BitsetOperator::get_offset(position);
+ const std::size_t index = BitsetUtils::get_index(position);
+ const std::size_t offset = BitsetUtils::get_offset(position);
if (index >= m_blocks.size())
{
- m_blocks.resize(index + 1, m_default_bit_value ? BitsetOperator::block_ones : BitsetOperator::block_zeroes);
+ m_blocks.resize(index + 1, m_default_bit_value ? BitsetUtils::block_ones : BitsetUtils::block_zeroes);
}
m_blocks[index] &= ~(static_cast(1) << offset); // Set the bit at the offset
@@ -826,7 +971,7 @@ void Builder>::unset_all()
{
assert(m_blocks.size() > 0);
- m_blocks[0] = (m_default_bit_value) ? BitsetOperator::block_ones : BitsetOperator::block_zeroes;
+ m_blocks[0] = (m_default_bit_value) ? BitsetUtils::block_ones : BitsetUtils::block_zeroes;
m_blocks.resize(1);
}
@@ -968,14 +1113,24 @@ requires HasBlockType && HasCompatibleTagType Builder<
return *this;
}
+template
+template
+requires HasBlockType && HasCompatibleTagType
+bool Builder>::is_superseteq(const B& other) { return BitsetUtils::is_superseteq(*this, other); }
+
+template
+template
+requires HasBlockType && HasCompatibleTagType
+bool Builder>::are_disjoint(const B& other) { return BitsetUtils::are_disjoint(*this, other); }
+
template
bool Builder>::get(std::size_t position) const
{
- const std::size_t index = BitsetOperator::get_index(position);
+ const std::size_t index = BitsetUtils::get_index(position);
if (index < m_blocks.size())
{
- const std::size_t offset = BitsetOperator::get_offset(position);
+ const std::size_t offset = BitsetUtils::get_offset(position);
return (m_blocks[index] & (static_cast(1) << offset)) != 0;
}
else
@@ -987,14 +1142,14 @@ bool Builder>::get(std::size_t position) const
template
size_t Builder>::count() const
{
- return BitsetOperator::count(*this);
+ return BitsetUtils::count(*this);
}
template
std::size_t Builder>::next_set_bit(std::size_t position) const
{
- std::size_t index = BitsetOperator::get_index(position);
- std::size_t offset = BitsetOperator::get_offset(position);
+ std::size_t index = BitsetUtils::get_index(position);
+ std::size_t offset = BitsetUtils::get_offset(position);
for (auto iter = m_blocks.begin() + index; iter < m_blocks.end(); ++iter)
{
@@ -1004,15 +1159,15 @@ std::size_t Builder>::next_set_bit(std::size_t position) cons
if (value)
{
// If there are set bits in the current value
- const auto lsb_position = BitsetOperator::get_lsb_position(value);
- return index * BitsetOperator::block_size + offset + lsb_position;
+ const auto lsb_position = BitsetUtils::get_lsb_position(value);
+ return index * BitsetUtils::block_size + offset + lsb_position;
}
// Reset offset for the next value
offset = 0;
}
- return BitsetOperator::no_position;
+ return BitsetUtils::no_position;
}
template
@@ -1059,18 +1214,28 @@ View>::View(uint8_t* buf) : m_buf(buf)
assert(m_buf);
}
+template
+template
+requires HasBlockType && HasCompatibleTagType
+bool View>::is_superseteq(const B& other) { return BitsetUtils::is_superseteq(*this, other); }
+
+template
+template
+requires HasBlockType && HasCompatibleTagType
+bool View>::are_disjoint(const B& other) { return BitsetUtils::are_disjoint(*this, other); }
+
template
bool View>::get(std::size_t position) const
{
assert(m_buf);
- return BitsetOperator::get(*this, position);
+ return BitsetUtils::get(*this, position);
}
template
size_t View>::count() const
{
assert(m_buf);
- return BitsetOperator::count(*this);
+ return BitsetUtils::count(*this);
}
template
@@ -1160,18 +1325,28 @@ ConstView>::ConstView(const BitsetView& view) : m_buf(view.bu
{
}
+template
+template
+requires HasBlockType && HasCompatibleTagType
+bool ConstView>::is_superseteq(const B& other) { return BitsetUtils::is_superseteq(*this, other); }
+
+template
+template
+requires HasBlockType