Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/byterator.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,27 @@ The last case above is particularly useful for dealing with enumerations:
enum struct E : std::uint32_t { A, B, C };
auto v3 = i.read<std::uint8_t, E>();
----

`byterator` can also advance to alignment boundaries:

[source,cpp]
----
i.advance_to_alignment<std::uint32_t>(); // advance to next uint32_t alignment
i.advance_to_alignment<std::uint32_t>(0); // the same thing - now a no-op
i.advance_to_alignment<std::uint32_t>(-1); // advance to previous uint32_t alignment

i.advance_to_alignment<std::uint32_t>(n); // advance to nth next uint32_t alignment
i.advance_to_alignment<std::uint32_t>(-n); // advance to nth previous uint32_t alignment
----

If the `byterator` is already located at the requested alignment boundary (and
the argument is zero or omitted), it doesn't change.

NOTE: `advance_to_alignment` advances to the absolute alignment, _not_ the
alignment relative to the underlying storage. For example, if the start of the
underlying storage is _not_ 4-byte aligned, calling
`advance_to_alignment<std::uint32_t>()` will result in an offset from the start
that is _not_ a multiple of 4.

TIP: `advance_to_alignment<T>(n)` is equivalent to `advance_to_alignment<T>()`
followed by `advance<T>()` `n` times.
9 changes: 9 additions & 0 deletions include/stdx/byterator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ template <typename T> class byterator {
return advance(sizeof(V));
}

template <typename V = std::uint8_t>
auto advance_to_alignment(difference_type n = 0) -> decltype(auto) {
auto p = bit_cast<std::uintptr_t>(ptr);
constexpr auto offset = sizeof(V) - 1u;
constexpr auto mask = ~offset;
ptr = bit_cast<byte_t *>((p + offset) & mask);
return advance(n * static_cast<difference_type>(sizeof(V)));
}

template <typename V = std::uint8_t, typename R = V,
std::enable_if_t<std::is_trivially_copyable_v<V>, int> = 0>
[[nodiscard]] auto read() -> R {
Expand Down
47 changes: 47 additions & 0 deletions test/byterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,50 @@ TEST_CASE("read enum (constrained size alias)", "[byterator]") {
CHECK(i.readu8<E2>() == E2::A);
CHECK((i == j));
}

TEST_CASE("advance to next alignment", "[byterator]") {
auto const a =
std::array{stdx::to_be<std::uint64_t>(0x0102'0304'0506'0708),
stdx::to_be<std::uint64_t>(0x0a0b'0c0d'0e0f'1011)};
auto base = stdx::byterator{std::begin(a)};
auto i = stdx::byterator{std::begin(a)};
i.advance_to_alignment();
CHECK(i == base);

++i;
i.advance_to_alignment();
CHECK(i == std::next(base));

i.advance_to_alignment<std::uint16_t>();
CHECK(i == std::next(base, 2));

i.advance_to_alignment<std::uint32_t>();
CHECK(i == std::next(base, 4));

i.advance_to_alignment<std::uint64_t>();
CHECK(i == std::next(base, 8));

i.advance_to_alignment<std::uint64_t>(); // already there
CHECK(i == std::next(base, 8));
}

TEST_CASE("advance to previous alignment", "[byterator]") {
auto const a =
std::array{stdx::to_be<std::uint64_t>(0x0102'0304'0506'0708),
stdx::to_be<std::uint64_t>(0x0a0b'0c0d'0e0f'1011)};
auto base = stdx::byterator{std::begin(a)};
auto i = stdx::byterator{std::begin(a)};

i.advance(8);
i.advance_to_alignment(-1);
CHECK(i == std::next(base, 7));

i.advance_to_alignment<std::uint16_t>(-1);
CHECK(i == std::next(base, 6));

i.advance_to_alignment<std::uint32_t>(-1);
CHECK(i == std::next(base, 4));

i.advance_to_alignment<std::uint64_t>(-1);
CHECK(i == base);
}