Skip to content

Commit

Permalink
ToBitSpan(vector)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sunday111 committed Nov 9, 2024
1 parent a6ad8d2 commit 878e1b0
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 17 deletions.
91 changes: 76 additions & 15 deletions include/ass/bit_span.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,49 @@
#include <bit>
#include <cassert>
#include <span>
#include <vector>

#include "macro/empty_bases.hpp"

namespace ass::bit_span_detail
{

template <typename T>
struct IsUintStdArrayT : std::false_type
{
};

template <std::unsigned_integral T, size_t N>
struct IsUintStdArrayT<std::array<T, N>> : std::true_type
{
};

template <std::unsigned_integral T, size_t N>
struct IsUintStdArrayT<const std::array<T, N>> : std::true_type
{
};

template <typename T>
concept uint_std_array = IsUintStdArrayT<T>::value;

template <typename T>
struct IsUintStdVectorT : std::false_type
{
};

template <std::unsigned_integral T, typename Allocator>
struct IsUintStdVectorT<std::vector<T, Allocator>> : std::true_type
{
};

template <std::unsigned_integral T, typename Allocator>
struct IsUintStdVectorT<const std::vector<T, Allocator>> : std::true_type
{
};

template <typename T>
concept uint_std_vector = IsUintStdVectorT<T>::value;

template <size_t value>
struct StaticSizeContainer
{
Expand Down Expand Up @@ -79,6 +116,8 @@ class ASS_EMPTY_BASES BitSpan : public bit_span_detail::SizeContainer<static_ext
public bit_span_detail::PartsCountContainer<static_extents.parts_count>
{
public:
static constexpr bool kCanModifyData = !std::is_const_v<Part>;

[[nodiscard]] static constexpr size_t BitsPerPart()
{
return sizeof(Part) * 8;
Expand All @@ -89,7 +128,7 @@ class ASS_EMPTY_BASES BitSpan : public bit_span_detail::SizeContainer<static_ext
return static_extents.size != std::dynamic_extent;
}

[[nodiscard]] static constexpr size_t GetSize() noexcept
[[nodiscard]] static consteval size_t GetSize() noexcept
requires(HasStaticSize())
{
return static_extents.size;
Expand All @@ -101,12 +140,12 @@ class ASS_EMPTY_BASES BitSpan : public bit_span_detail::SizeContainer<static_ext
return this->size_;
}

[[nodiscard]] static constexpr bool HasStaticCapacity()
[[nodiscard]] static consteval bool HasStaticCapacity()
{
return static_extents.parts_count != std::dynamic_extent;
}

[[nodiscard]] static constexpr size_t GetPartsCount() noexcept
[[nodiscard]] static consteval size_t GetPartsCount() noexcept
requires(HasStaticCapacity())
{
return static_extents.parts_count;
Expand Down Expand Up @@ -179,23 +218,23 @@ class ASS_EMPTY_BASES BitSpan : public bit_span_detail::SizeContainer<static_ext
}

constexpr void Flip() const
requires(!std::is_const_v<Part>)
requires(kCanModifyData)
{
if constexpr (static_extents.size == 0) return;
if (GetSize() == 0) return;

FlipNonEmpty();
}

[[nodiscard]] bool Get(size_t index) const
[[nodiscard]] constexpr bool Get(size_t index) const
{
assert(index < GetSize());
const PurePart mask = PurePart{1} << (index % BitsPerPart());
return parts_[index / BitsPerPart()] & mask; // NOLINT
}

void Set(size_t index, bool value) const
requires(!std::is_const_v<Part>)
constexpr void Set(size_t index, bool value) const
requires(kCanModifyData)
{
assert(index < GetSize());
const Part mask = PurePart{1} << (index % BitsPerPart());
Expand Down Expand Up @@ -332,18 +371,40 @@ template <BitSpanSize size, std::unsigned_integral Part, size_t span_extent>
return BitSpan<Part, {.size = size.size}>{parts.data(), {.parts_count = parts.size()}};
}

template <BitSpanSize size, std::unsigned_integral Part, size_t array_size>
[[nodiscard]] constexpr auto ToBitSpan(const std::array<Part, array_size>& parts)
template <BitSpanSize size, bit_span_detail::uint_std_array Array>
[[nodiscard]] constexpr auto ToBitSpan(Array& parts)
{
using Part = std::remove_reference_t<decltype(parts.front())>;
constexpr size_t parts_count = std::tuple_size_v<Array>;
static_assert(size.size <= parts_count * sizeof(Part) * 8);
return BitSpan<Part, {.parts_count = parts_count, .size = size.size}>{parts.data()};
}

template <bit_span_detail::uint_std_array Array>
[[nodiscard]] constexpr auto ToBitSpan(Array& parts, BitSpanSize size)
{
static_assert(size.size <= array_size * sizeof(Part) * 8);
return BitSpan<const Part, {.parts_count = array_size, .size = size.size}>{parts.data()};
using Part = std::remove_reference_t<decltype(parts.front())>;
constexpr size_t parts_count = std::tuple_size_v<Array>;
assert(size.size <= parts_count * sizeof(Part) * 8);
return BitSpan<Part, {.parts_count = parts_count}>{parts.data(), {.size = size.size}};
}

template <std::unsigned_integral Part, size_t array_size>
[[nodiscard]] constexpr auto ToBitSpan(const std::array<Part, array_size>& parts, BitSpanSize size)
template <BitSpanSize size = {.size = std::dynamic_extent}, bit_span_detail::uint_std_vector Vector>
[[nodiscard]] constexpr auto ToBitSpan(Vector& data)
{
assert(size.size <= array_size * sizeof(Part) * 8);
return BitSpan<const Part, {.parts_count = array_size}>{parts.data(), {.size = size.size}};
using Part = std::remove_reference_t<decltype(data.front())>;
constexpr size_t bits_per_part = sizeof(Part) * 8;

if constexpr (size.size == std::dynamic_extent)
{
return BitSpan<Part>{data.data(), {.parts_count = data.size(), .size = data.size() * bits_per_part}};
}
else
{
constexpr size_t parts_count = (size.size / bits_per_part) + ((size.size % bits_per_part) != 0);
assert(parts_count <= data.size());
return BitSpan<Part, {.parts_count = parts_count, .size = size.size}>{data.data()};
}
}

} // namespace ass
90 changes: 88 additions & 2 deletions tests/bit_span_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,28 +330,68 @@ TEST(BitSpanTest, Flip)

TEST(BitSpanTest, ToBitSpan)
{
std::array<uint8_t, 2> non_const_data{0b0011'1000, 0b1000'1110};
static constexpr std::array<uint8_t, 2> data{0b0011'1000, 0b1000'1110};
constexpr auto static_extent_span = std::span{data};
constexpr auto dynamic_extent_span = std::span<const uint8_t>{data.data(), data.size()};

// from std::array and static size
auto test_bits = [&](const auto& bit_span) -> bool
{
for (size_t i = 0; i != bit_span.GetSize(); ++i)
{
bool actual = bit_span.Get(i);
bool expected = (data[i / 8] & (1 << (i % 8))) != 0;
if (actual != expected) return false;
}

return true;
};

// from const std::array and static size
{
constexpr auto bit_span = ToBitSpan<{.size = 16}>(data);
static_assert(bit_span.HasStaticCapacity());
static_assert(bit_span.HasStaticSize());
static_assert(!bit_span.kCanModifyData);
static_assert(bit_span.GetSize() == 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
static_assert(test_bits(bit_span));
}

// from non const std::array and static size
{
const auto bit_span = ToBitSpan<{.size = 16}>(non_const_data);
static_assert(bit_span.HasStaticCapacity());
static_assert(bit_span.HasStaticSize());
static_assert(bit_span.kCanModifyData);
static_assert(bit_span.GetSize() == 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
}

// from std::array and dynamic size
// from const std::array and dynamic size
{
constexpr auto bit_span = ToBitSpan(data, {.size = 16});
static_assert(bit_span.HasStaticCapacity());
static_assert(!bit_span.HasStaticSize());
static_assert(!bit_span.kCanModifyData);
static_assert(bit_span.GetSize() == 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
static_assert(test_bits(bit_span));
}

// from non-const std::array and dynamic size
{
const auto bit_span = ToBitSpan(non_const_data, {.size = 16});
static_assert(bit_span.HasStaticCapacity());
static_assert(!bit_span.HasStaticSize());
static_assert(bit_span.kCanModifyData);
ASSERT_EQ(bit_span.GetSize(), 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
ASSERT_TRUE(test_bits(bit_span));
}

// static extent of parts span and static size value
Expand All @@ -362,6 +402,7 @@ TEST(BitSpanTest, ToBitSpan)
static_assert(bit_span.GetSize() == 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
static_assert(test_bits(bit_span));
}

// static extent of parts span and dynamic size value
Expand All @@ -372,6 +413,7 @@ TEST(BitSpanTest, ToBitSpan)
static_assert(bit_span.GetSize() == 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
static_assert(test_bits(bit_span));
}

// dynamic extent of parts span and static size value
Expand All @@ -382,6 +424,7 @@ TEST(BitSpanTest, ToBitSpan)
static_assert(bit_span.GetSize() == 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
static_assert(test_bits(bit_span));
}

// dynamic extent of parts span and dynamic size value
Expand All @@ -392,6 +435,49 @@ TEST(BitSpanTest, ToBitSpan)
static_assert(bit_span.GetSize() == 16);
static_assert(bit_span.GetPartsCount() == 2);
static_assert(bit_span.GetCapacity() == 16);
static_assert(test_bits(bit_span));
}
}

TEST(BitSpanTest, VectorToBitSpan)
{
std::vector<uint8_t> data{0b0011'1000, 0b1000'1110};
const auto& const_data = data;

auto test_bits = [&](const auto& bit_span) -> bool
{
for (size_t i = 0; i != bit_span.GetSize(); ++i)
{
bool actual = bit_span.Get(i);
bool expected = (data[i / 8] & (1 << (i % 8))) != 0;
if (actual != expected) return false;
}

return true;
};

// deduced (dynamic) size and capacity
{
[[maybe_unused]] const auto bit_span = ToBitSpan(data);
static_assert(!bit_span.HasStaticCapacity());
static_assert(!bit_span.HasStaticSize());
static_assert(bit_span.kCanModifyData);
ASSERT_EQ(bit_span.GetSize(), 16);
ASSERT_EQ(bit_span.GetPartsCount(), 2);
ASSERT_EQ(bit_span.GetCapacity(), 16);
ASSERT_TRUE(test_bits(bit_span));
}

// static size, deduced capacity
{
[[maybe_unused]] const auto bit_span = ToBitSpan<{.size = 13}>(data);
static_assert(bit_span.HasStaticCapacity());
static_assert(bit_span.HasStaticSize());
static_assert(bit_span.kCanModifyData);
static_assert(bit_span.GetSize() == 13);
ASSERT_EQ(bit_span.GetPartsCount(), 2);
ASSERT_EQ(bit_span.GetCapacity(), 16);
ASSERT_TRUE(test_bits(bit_span));
}
}
} // namespace ass

0 comments on commit 878e1b0

Please sign in to comment.