diff --git a/docs/byterator.adoc b/docs/byterator.adoc index f986ea1..b2c475b 100644 --- a/docs/byterator.adoc +++ b/docs/byterator.adoc @@ -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(); ---- + +`byterator` can also advance to alignment boundaries: + +[source,cpp] +---- +i.advance_to_alignment(); // advance to next uint32_t alignment +i.advance_to_alignment(0); // the same thing - now a no-op +i.advance_to_alignment(-1); // advance to previous uint32_t alignment + +i.advance_to_alignment(n); // advance to nth next uint32_t alignment +i.advance_to_alignment(-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()` will result in an offset from the start +that is _not_ a multiple of 4. + +TIP: `advance_to_alignment(n)` is equivalent to `advance_to_alignment()` +followed by `advance()` `n` times. diff --git a/include/stdx/byterator.hpp b/include/stdx/byterator.hpp index 9d46cd8..532ff03 100644 --- a/include/stdx/byterator.hpp +++ b/include/stdx/byterator.hpp @@ -150,6 +150,15 @@ template class byterator { return advance(sizeof(V)); } + template + auto advance_to_alignment(difference_type n = 0) -> decltype(auto) { + auto p = bit_cast(ptr); + constexpr auto offset = sizeof(V) - 1u; + constexpr auto mask = ~offset; + ptr = bit_cast((p + offset) & mask); + return advance(n * static_cast(sizeof(V))); + } + template , int> = 0> [[nodiscard]] auto read() -> R { diff --git a/test/byterator.cpp b/test/byterator.cpp index 23fd2b4..2fef49b 100644 --- a/test/byterator.cpp +++ b/test/byterator.cpp @@ -389,3 +389,50 @@ TEST_CASE("read enum (constrained size alias)", "[byterator]") { CHECK(i.readu8() == E2::A); CHECK((i == j)); } + +TEST_CASE("advance to next alignment", "[byterator]") { + auto const a = + std::array{stdx::to_be(0x0102'0304'0506'0708), + stdx::to_be(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(); + CHECK(i == std::next(base, 2)); + + i.advance_to_alignment(); + CHECK(i == std::next(base, 4)); + + i.advance_to_alignment(); + CHECK(i == std::next(base, 8)); + + i.advance_to_alignment(); // already there + CHECK(i == std::next(base, 8)); +} + +TEST_CASE("advance to previous alignment", "[byterator]") { + auto const a = + std::array{stdx::to_be(0x0102'0304'0506'0708), + stdx::to_be(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(-1); + CHECK(i == std::next(base, 6)); + + i.advance_to_alignment(-1); + CHECK(i == std::next(base, 4)); + + i.advance_to_alignment(-1); + CHECK(i == base); +}