From 5bcb131028f6812f3c1c6e56d586648af9a21f15 Mon Sep 17 00:00:00 2001 From: Gary Miguel Date: Sun, 25 Jun 2023 17:47:07 -0700 Subject: [PATCH] implement bit_span for easy iteration of the bits in some data Change-Id: I6f1dee0448ff071e9b820aa5a020120717b61ab2 --- huffman/BUILD.bazel | 1 + huffman/huffman.hpp | 1 + huffman/src/bit.hpp | 1 + huffman/src/bit_span.hpp | 88 ++++++++++++++++++++++++++++++++++ huffman/test/BUILD.bazel | 10 ++++ huffman/test/bit_span_test.cpp | 31 ++++++++++++ 6 files changed, 132 insertions(+) create mode 100644 huffman/src/bit_span.hpp create mode 100644 huffman/test/bit_span_test.cpp diff --git a/huffman/BUILD.bazel b/huffman/BUILD.bazel index f7cc9a7..a0391cb 100644 --- a/huffman/BUILD.bazel +++ b/huffman/BUILD.bazel @@ -4,6 +4,7 @@ cc_library( name = "huffman", srcs = [ "src/bit.hpp", + "src/bit_span.hpp", "src/code.hpp", "src/detail/base_view.hpp", "src/detail/iterator_interface.hpp", diff --git a/huffman/huffman.hpp b/huffman/huffman.hpp index 867d8e7..986be4b 100644 --- a/huffman/huffman.hpp +++ b/huffman/huffman.hpp @@ -1,6 +1,7 @@ #pragma once #include "huffman/src/bit.hpp" +#include "huffman/src/bit_span.hpp" #include "huffman/src/code.hpp" #include "huffman/src/encoding.hpp" #include "huffman/src/table.hpp" diff --git a/huffman/src/bit.hpp b/huffman/src/bit.hpp index 670cfe6..a0fc4f4 100644 --- a/huffman/src/bit.hpp +++ b/huffman/src/bit.hpp @@ -1,3 +1,4 @@ + #pragma once #include diff --git a/huffman/src/bit_span.hpp b/huffman/src/bit_span.hpp new file mode 100644 index 0000000..0bfc8ea --- /dev/null +++ b/huffman/src/bit_span.hpp @@ -0,0 +1,88 @@ + +#include "huffman/src/bit.hpp" +#include "huffman/src/detail/iterator_interface.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace gpu_deflate::huffman { +class bit_span +{ + + const std::byte* data_; + size_t bit_size_; + +public: + class iterator : public detail::iterator_interface + { + private: + const bit_span* parent_; + size_t offset_; + + public: + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + using value_type = bit; + using reference = bit; + using pointer = void; + + iterator() = default; + constexpr iterator(const bit_span& parent, size_t offset) + : parent_(&parent), offset_(offset) + { + assert(offset_ <= std::numeric_limits::max()); + } + + constexpr auto operator*() const -> reference + { + // TODO: switch to std::bitset once GCC's + // std::bitset<_Nb>::reference::operator bool() is constexpr + std::uint8_t byte{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + static_cast(parent_->data_[offset_ / CHAR_BIT])}; + // most significant bit first + return bit{bool(byte & 1 << (CHAR_BIT - 1 - (offset_ % CHAR_BIT)))}; + } + + constexpr auto operator+=(difference_type n) -> iterator& + { + const auto newOffset = static_cast(offset_) + n; + assert(newOffset >= 0); + offset_ = static_cast(newOffset); + return *this; + } + + friend constexpr auto + operator-(const iterator& lhs, const iterator& rhs) -> difference_type + { + assert(lhs.parent_ == rhs.parent_); + + return static_cast(lhs.offset_) - + static_cast(rhs.offset_); + } + + friend constexpr auto + operator<=>(const iterator&, const iterator&) = default; + }; + + constexpr bit_span(const std::byte* data, size_t bit_size) + : data_(data), bit_size_(bit_size) + {} + + [[nodiscard]] + constexpr auto begin() const -> iterator + { + return iterator{*this, 0}; + }; + [[nodiscard]] + constexpr auto end() const -> iterator + { + return iterator{*this, bit_size_}; + }; +}; +} // namespace gpu_deflate::huffman diff --git a/huffman/test/BUILD.bazel b/huffman/test/BUILD.bazel index 97a3092..cee9e2a 100644 --- a/huffman/test/BUILD.bazel +++ b/huffman/test/BUILD.bazel @@ -60,6 +60,16 @@ cc_test( ], ) +cc_test( + name = "bit_span_test", + timeout = "short", + srcs = ["bit_span_test.cpp"], + deps = [ + "//huffman", + "@boost_ut", + ], +) + cc_binary( name = "bench", srcs = ["bench.cpp"], diff --git a/huffman/test/bit_span_test.cpp b/huffman/test/bit_span_test.cpp new file mode 100644 index 0000000..5048ee2 --- /dev/null +++ b/huffman/test/bit_span_test.cpp @@ -0,0 +1,31 @@ +#include "huffman/huffman.hpp" + +#include + +#include +#include +#include +#include + +auto main() -> int +{ + using ::boost::ut::aborts; + using ::boost::ut::expect; + using ::boost::ut::test; + + namespace huffman = ::gpu_deflate::huffman; + using namespace huffman::literals; + + test("basic") = [] { + 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"; + expect(std::ranges::equal( + span, + expected | std::views::transform([](char c) { + return huffman::bit{c}; + }))); + }; +}