Skip to content

Commit dfc8cea

Browse files
authored
Merge pull request #380 from elbeno/advance-to-boundary
✨ Add `byterator::advance_to_alignment`
2 parents 08af9c2 + c553349 commit dfc8cea

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

docs/byterator.adoc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,27 @@ The last case above is particularly useful for dealing with enumerations:
102102
enum struct E : std::uint32_t { A, B, C };
103103
auto v3 = i.read<std::uint8_t, E>();
104104
----
105+
106+
`byterator` can also advance to alignment boundaries:
107+
108+
[source,cpp]
109+
----
110+
i.advance_to_alignment<std::uint32_t>(); // advance to next uint32_t alignment
111+
i.advance_to_alignment<std::uint32_t>(0); // the same thing - now a no-op
112+
i.advance_to_alignment<std::uint32_t>(-1); // advance to previous uint32_t alignment
113+
114+
i.advance_to_alignment<std::uint32_t>(n); // advance to nth next uint32_t alignment
115+
i.advance_to_alignment<std::uint32_t>(-n); // advance to nth previous uint32_t alignment
116+
----
117+
118+
If the `byterator` is already located at the requested alignment boundary (and
119+
the argument is zero or omitted), it doesn't change.
120+
121+
NOTE: `advance_to_alignment` advances to the absolute alignment, _not_ the
122+
alignment relative to the underlying storage. For example, if the start of the
123+
underlying storage is _not_ 4-byte aligned, calling
124+
`advance_to_alignment<std::uint32_t>()` will result in an offset from the start
125+
that is _not_ a multiple of 4.
126+
127+
TIP: `advance_to_alignment<T>(n)` is equivalent to `advance_to_alignment<T>()`
128+
followed by `advance<T>()` `n` times.

include/stdx/byterator.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,15 @@ template <typename T> class byterator {
150150
return advance(sizeof(V));
151151
}
152152

153+
template <typename V = std::uint8_t>
154+
auto advance_to_alignment(difference_type n = 0) -> decltype(auto) {
155+
auto p = bit_cast<std::uintptr_t>(ptr);
156+
constexpr auto offset = sizeof(V) - 1u;
157+
constexpr auto mask = ~offset;
158+
ptr = bit_cast<byte_t *>((p + offset) & mask);
159+
return advance(n * static_cast<difference_type>(sizeof(V)));
160+
}
161+
153162
template <typename V = std::uint8_t, typename R = V,
154163
std::enable_if_t<std::is_trivially_copyable_v<V>, int> = 0>
155164
[[nodiscard]] auto read() -> R {

test/byterator.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,50 @@ TEST_CASE("read enum (constrained size alias)", "[byterator]") {
389389
CHECK(i.readu8<E2>() == E2::A);
390390
CHECK((i == j));
391391
}
392+
393+
TEST_CASE("advance to next alignment", "[byterator]") {
394+
auto const a =
395+
std::array{stdx::to_be<std::uint64_t>(0x0102'0304'0506'0708),
396+
stdx::to_be<std::uint64_t>(0x0a0b'0c0d'0e0f'1011)};
397+
auto base = stdx::byterator{std::begin(a)};
398+
auto i = stdx::byterator{std::begin(a)};
399+
i.advance_to_alignment();
400+
CHECK(i == base);
401+
402+
++i;
403+
i.advance_to_alignment();
404+
CHECK(i == std::next(base));
405+
406+
i.advance_to_alignment<std::uint16_t>();
407+
CHECK(i == std::next(base, 2));
408+
409+
i.advance_to_alignment<std::uint32_t>();
410+
CHECK(i == std::next(base, 4));
411+
412+
i.advance_to_alignment<std::uint64_t>();
413+
CHECK(i == std::next(base, 8));
414+
415+
i.advance_to_alignment<std::uint64_t>(); // already there
416+
CHECK(i == std::next(base, 8));
417+
}
418+
419+
TEST_CASE("advance to previous alignment", "[byterator]") {
420+
auto const a =
421+
std::array{stdx::to_be<std::uint64_t>(0x0102'0304'0506'0708),
422+
stdx::to_be<std::uint64_t>(0x0a0b'0c0d'0e0f'1011)};
423+
auto base = stdx::byterator{std::begin(a)};
424+
auto i = stdx::byterator{std::begin(a)};
425+
426+
i.advance(8);
427+
i.advance_to_alignment(-1);
428+
CHECK(i == std::next(base, 7));
429+
430+
i.advance_to_alignment<std::uint16_t>(-1);
431+
CHECK(i == std::next(base, 6));
432+
433+
i.advance_to_alignment<std::uint32_t>(-1);
434+
CHECK(i == std::next(base, 4));
435+
436+
i.advance_to_alignment<std::uint64_t>(-1);
437+
CHECK(i == base);
438+
}

0 commit comments

Comments
 (0)