diff --git a/huffman/src/bit_span.hpp b/huffman/src/bit_span.hpp index b53720e..22b4383 100644 --- a/huffman/src/bit_span.hpp +++ b/huffman/src/bit_span.hpp @@ -17,10 +17,9 @@ namespace starflate::huffman { /// A non-owning span of bits. Allows for iteration over the individual bits. class bit_span : public std::ranges::view_interface { - const std::byte* data_; - std::size_t bit_size_; - std::size_t init_bit_size_; // initial value of bit_size_ - std::uint8_t bit_offset_; // always less than CHAR_BIT + const std::byte* data_{nullptr}; + 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. @@ -39,7 +38,7 @@ class bit_span : public std::ranges::view_interface iterator() = default; constexpr iterator(const bit_span& parent, std::size_t offset) - : parent_(&parent), offset_(offset) + : parent_{&parent}, offset_{offset} { assert(offset_ <= std::numeric_limits::max()); } @@ -74,6 +73,10 @@ class bit_span : public std::ranges::view_interface operator<=>(const iterator&, const iterator&) = default; }; + /// Constructs an empty bit_span + /// + bit_span() = default; + /// Constructs a bit_span from the given data. /// /// @param data a pointer to the first byte of the data. @@ -85,10 +88,7 @@ class bit_span : public std::ranges::view_interface // NOLINTBEGIN(bugprone-easily-swappable-parameters) constexpr bit_span( const std::byte* data, std::size_t bit_size, std::uint8_t bit_offset = {}) - : data_{data}, - bit_size_{bit_size}, - init_bit_size_{bit_size}, - bit_offset_{bit_offset} + : data_{data}, bit_size_{bit_size}, bit_offset_{bit_offset} // NOLINTEND(bugprone-easily-swappable-parameters) { assert( @@ -145,16 +145,23 @@ class bit_span : public std::ranges::view_interface /// Consumes the given number of bits. Advances the start of the view. /// /// @pre n <= std::ranges::size(*this) - constexpr auto consume(std::size_t n) -> void + /// + constexpr auto consume(std::size_t n) & -> bit_span& { - assert(n <= bit_size_); + assert(n <= bit_size_); // pre bit_size_ -= n; - // invariant - assert(bit_offset_ < CHAR_BIT); + const auto distance = bit_offset_ + n; std::advance(data_, distance / CHAR_BIT); - bit_offset_ = static_cast(distance % CHAR_BIT); + + assert(bit_offset_ < CHAR_BIT); // post + return *this; + } + constexpr auto consume(std::size_t n) && -> bit_span&& + { + consume(n); + return std::move(*this); } /// Consumes bits until the start is aligned to a byte boundary. diff --git a/huffman/test/bit_span_test.cpp b/huffman/test/bit_span_test.cpp index 2fed589..1ef954a 100644 --- a/huffman/test/bit_span_test.cpp +++ b/huffman/test/bit_span_test.cpp @@ -13,6 +13,7 @@ auto main() -> int { using ::boost::ut::aborts; + using ::boost::ut::eq; using ::boost::ut::expect; using ::boost::ut::test; @@ -31,6 +32,12 @@ auto main() -> int }))); }; + test("default constructible") = [] { + constexpr auto bits = huffman::bit_span{}; + + expect(eq(0, bits.size())); + }; + test("indexable") = [] { static constexpr auto data = huffman::byte_array(0b10101010, 0xff); constexpr auto bs = huffman::bit_span{data}; @@ -114,6 +121,21 @@ auto main() -> int } } | std::views::iota(0UZ, 2UZ * (CHAR_BIT + 1UZ)); + test("consume returns reference") = [] { + static constexpr auto data = std::byte{}; + + constexpr auto consumed_size = [] { + auto bits = huffman::bit_span{&data, CHAR_BIT}; + return bits.consume(1).size(); + }(); + + expect(eq(CHAR_BIT - 1, consumed_size)); + + constexpr auto consumed_bits = + huffman::bit_span{&data, CHAR_BIT}.consume(1); + expect(eq(CHAR_BIT - 1, consumed_bits.size())); + }; + test("consume_to_byte_boundary") = [] { static constexpr auto data = huffman::byte_array(0b10101010, 0b01010101); huffman::bit_span span{data};