Skip to content

Commit

Permalink
handle non-byte aligned data with bit_span
Browse files Browse the repository at this point in the history
Change-Id: Id460151a080e8a1c55e382182e34e60eed71c78c
  • Loading branch information
oliverlee committed Sep 21, 2023
1 parent d27c8d9 commit c1b8daa
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 8 deletions.
23 changes: 17 additions & 6 deletions huffman/src/bit_span.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class bit_span : public std::ranges::view_interface<bit_span>
{
const std::byte* data_;
std::size_t bit_size_;
std::uint8_t bit_offset_; // always less than CHAR_BIT

public:
/// An iterator over the bits in a bit_span.
Expand All @@ -34,7 +35,7 @@ class bit_span : public std::ranges::view_interface<bit_span>
using pointer = void;

iterator() = default;
constexpr iterator(const bit_span& parent, size_t offset)
constexpr iterator(const bit_span& parent, std::size_t offset)
: parent_(&parent), offset_(offset)
{
assert(offset_ <= std::numeric_limits<difference_type>::max());
Expand Down Expand Up @@ -74,9 +75,19 @@ class bit_span : public std::ranges::view_interface<bit_span>
///
/// @param data a pointer to the first byte of the data.
/// @param bit_size the number of bits in the data.
constexpr bit_span(const std::byte* data, std::size_t bit_size)
: data_(data), bit_size_(bit_size)
{}
/// @param bit_offset bit offset of data, allowing a non-byte aligned range
///
/// @pre offset < CHAR_BIT
///
constexpr bit_span(
const std::byte* data, std::size_t bit_size, std::uint8_t bit_offset = {})
: data_{data}, bit_size_{bit_size}, bit_offset_{bit_offset}
{
assert(
bit_offset < CHAR_BIT and
"bit offset exceeds number of bits in a "
"byte");
}

template <std::ranges::contiguous_range R>
requires std::ranges::borrowed_range<R>
Expand All @@ -90,12 +101,12 @@ class bit_span : public std::ranges::view_interface<bit_span>
[[nodiscard]]
constexpr auto begin() const -> iterator
{
return iterator{*this, 0};
return iterator{*this, bit_offset_};
};
[[nodiscard]]
constexpr auto end() const -> iterator
{
return iterator{*this, bit_size_};
return iterator{*this, bit_offset_ + bit_size_};
};
};
} // namespace starflate::huffman
36 changes: 34 additions & 2 deletions huffman/test/bit_span_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <climits>
#include <cstddef>
#include <cstdint>
#include <vector>

auto main() -> int
{
Expand All @@ -17,8 +18,7 @@ auto main() -> int
using namespace huffman::literals;

test("basic") = [] {
static constexpr std::array<std::byte, 2> data{
std::byte{0b10101010}, std::byte{0xff}};
static constexpr std::array data{std::byte{0b10101010}, std::byte{0xff}};
// leave off the last bit of the last byte
constexpr huffman::bit_span span{data.data(), (data.size() * CHAR_BIT) - 1};
constexpr std::string_view expected = "101010101111111";
Expand Down Expand Up @@ -56,4 +56,36 @@ auto main() -> int

// NOLINTEND(readability-magic-numbers)
};

test("usable with non byte-aligned data") = [] {
static constexpr auto data =
std::array{std::byte{0b10101010}, std::byte{0xff}};

static constexpr auto bit_size = 7;
static constexpr auto bit_offset = 3;
constexpr auto bs = huffman::bit_span{data.begin(), bit_size, bit_offset};

// from first byte
static_assert(bs[0] == 0_b);
static_assert(bs[1] == 1_b);
static_assert(bs[2] == 0_b);
static_assert(bs[3] == 1_b);
static_assert(bs[4] == 0_b);

// from second byte
expect(bs[5] == 1_b);
expect(bs[6] == 1_b);
};

using ::boost::ut::operator|;

test("aborts if bit offset too large") = [](auto bit_offset) {
static constexpr auto data =
std::array{std::byte{0b10101010}, std::byte{0xff}};

expect(aborts([&] {
static constexpr auto bit_size = 7;
huffman::bit_span{data.begin(), bit_size, bit_offset};
}));
} | std::vector<std::uint8_t>{8, 9, 10};
}

0 comments on commit c1b8daa

Please sign in to comment.