Skip to content

Commit

Permalink
implement bit_span
Browse files Browse the repository at this point in the history
for easy iteration of the bits in some data

Change-Id: I6f1dee0448ff071e9b820aa5a020120717b61ab2
  • Loading branch information
garymm committed Jul 1, 2023
1 parent 48a1b57 commit 1adcaed
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 0 deletions.
1 change: 1 addition & 0 deletions huffman/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions huffman/huffman.hpp
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions huffman/src/bit.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#pragma once

#include <cassert>
Expand Down
88 changes: 88 additions & 0 deletions huffman/src/bit_span.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@

#include "huffman/src/bit.hpp"
#include "huffman/src/detail/iterator_interface.hpp"

#include <bitset>
#include <cassert>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits>

namespace gpu_deflate::huffman {
class bit_span
{

const std::byte* data_;
size_t bit_size_;

public:
class iterator : public detail::iterator_interface<iterator>
{
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<difference_type>::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<std::uint8_t>(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<difference_type>(offset_) + n;
assert(newOffset >= 0);
offset_ = static_cast<size_t>(newOffset);
return *this;
}

friend constexpr auto
operator-(const iterator& lhs, const iterator& rhs) -> difference_type
{
assert(lhs.parent_ == rhs.parent_);

return static_cast<difference_type>(lhs.offset_) -
static_cast<difference_type>(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
10 changes: 10 additions & 0 deletions huffman/test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
31 changes: 31 additions & 0 deletions huffman/test/bit_span_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "huffman/huffman.hpp"

#include <boost/ut.hpp>

#include <array>
#include <climits>
#include <cstddef>
#include <cstdint>

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<std::byte, 2> 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};
})));
};
}

0 comments on commit 1adcaed

Please sign in to comment.