Skip to content

Commit 048215e

Browse files
committed
✨ Add byterator::advance
Problem: - Sometimes we want to advance a `byterator`, particularly by the size of a type, and it's annoying to have to discard the result of `read` (which is marked `[[nodiscard]]`). Solution: - Implement `advance` and `advanceuXX` functions on `byterator`. Note: - It's also possible to use `std::advance`. But the `advanceuXX` functions match their `peek`, `read` and `write` counterparts.
1 parent 42b656a commit 048215e

File tree

3 files changed

+91
-4
lines changed

3 files changed

+91
-4
lines changed

docs/byterator.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,22 @@ auto value = i.readu16();
6363
----
6464
auto v8 = i.peeku8(); // read value without advancing
6565
v8 = i.readu8(); // read and advance
66+
i.advanceu8(); // advance only
6667
i.writeu8(v8); // write and advance
6768
6869
auto v16 = i.peeku16(); // read value without advancing
6970
v16 = i.readu16(); // read and advance
71+
i.advanceu16(); // advance only
7072
i.writeu16(v16); // write and advance
7173
7274
auto v32 = i.peeku32(); // read value without advancing
7375
v32 = i.readu32(); // read and advance
76+
i.advanceu32(); // advance only
7477
i.writeu32(v32); // write and advance
7578
7679
auto v64 = i.peeku64(); // read value without advancing
7780
v64 = i.readu64(); // read and advance
81+
i.advanceu64(); // advance only
7882
i.writeu64(v64); // write and advance
7983
----
8084

include/stdx/byterator.hpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <stdx/bit.hpp>
44
#include <stdx/memory.hpp>
5+
#include <stdx/type_traits.hpp>
56
#include <stdx/utility.hpp>
67

78
#include <cstddef>
@@ -27,11 +28,13 @@ constexpr auto iterator_value_type()
2728
-> decltype(*std::declval<typename std::iterator_traits<It>::pointer>());
2829

2930
template <typename It>
30-
using iterator_value_t = decltype(iterator_value_type<It>());
31+
using iterator_value_t =
32+
std::remove_reference_t<decltype(iterator_value_type<It>())>;
3133
} // namespace detail
3234

3335
template <typename T> class byterator {
34-
using byte_t = std::remove_reference_t<forward_like_t<T, std::byte>>;
36+
using byte_t =
37+
stdx::conditional_t<std::is_const_v<T>, std::byte const, std::byte>;
3538
byte_t *ptr;
3639

3740
[[nodiscard]] friend constexpr auto operator==(byterator const &x,
@@ -189,6 +192,10 @@ template <typename T> class byterator {
189192
ptr -= d;
190193
return *this;
191194
}
195+
constexpr auto advance(difference_type d) -> byterator & {
196+
ptr += d;
197+
return *this;
198+
}
192199

193200
[[nodiscard]] friend constexpr auto operator+(byterator i,
194201
difference_type d)
@@ -228,11 +235,15 @@ template <typename T> class byterator {
228235
return static_cast<R>(v);
229236
}
230237

238+
template <typename V = std::uint8_t> auto advance() -> decltype(auto) {
239+
return advance(sizeof(V));
240+
}
241+
231242
template <typename V = std::uint8_t, typename R = V,
232243
std::enable_if_t<std::is_trivially_copyable_v<V>, int> = 0>
233244
[[nodiscard]] auto read() -> R {
234245
R ret = peek<V, R>();
235-
ptr += sizeof(V);
246+
advance<V>();
236247
return ret;
237248
}
238249

@@ -242,12 +253,15 @@ template <typename T> class byterator {
242253
auto write(V &&v) -> void {
243254
using R = remove_cvref_t<V>;
244255
std::memcpy(ptr, std::addressof(v), sizeof(R));
245-
ptr += sizeof(R);
256+
advance<R>();
246257
}
247258

248259
template <typename V = std::uint8_t> [[nodiscard]] auto peeku8() {
249260
return peek<std::uint8_t, V>();
250261
}
262+
template <typename V = std::uint8_t> auto advanceu8() -> decltype(auto) {
263+
return advance<std::uint8_t>();
264+
}
251265
template <typename V = std::uint8_t> [[nodiscard]] auto readu8() {
252266
return read<std::uint8_t, V>();
253267
}
@@ -258,6 +272,9 @@ template <typename T> class byterator {
258272
template <typename V = std::uint16_t> [[nodiscard]] auto peeku16() {
259273
return peek<std::uint16_t, V>();
260274
}
275+
template <typename V = std::uint16_t> auto advanceu16() -> decltype(auto) {
276+
return advance<std::uint16_t>();
277+
}
261278
template <typename V = std::uint16_t> [[nodiscard]] auto readu16() {
262279
return read<std::uint16_t, V>();
263280
}
@@ -268,6 +285,9 @@ template <typename T> class byterator {
268285
template <typename V = std::uint32_t> [[nodiscard]] auto peeku32() {
269286
return peek<std::uint32_t, V>();
270287
}
288+
template <typename V = std::uint32_t> auto advanceu32() -> decltype(auto) {
289+
return advance<std::uint32_t>();
290+
}
271291
template <typename V = std::uint32_t> [[nodiscard]] auto readu32() {
272292
return read<std::uint32_t, V>();
273293
}
@@ -278,6 +298,9 @@ template <typename T> class byterator {
278298
template <typename V = std::uint64_t> [[nodiscard]] auto peeku64() {
279299
return peek<std::uint64_t, V>();
280300
}
301+
template <typename V = std::uint64_t> auto advanceu64() -> decltype(auto) {
302+
return advance<std::uint64_t>();
303+
}
281304
template <typename V = std::uint64_t> [[nodiscard]] auto readu64() {
282305
return read<std::uint64_t, V>();
283306
}

test/byterator.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ TEST_CASE("random access arithmetic", "[byterator]") {
7777
CHECK((i == b));
7878
}
7979

80+
TEST_CASE("advance", "[byterator]") {
81+
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
82+
stdx::to_be<std::uint16_t>(0x0304)};
83+
auto const b = stdx::byterator{std::begin(a)};
84+
auto i = b;
85+
CHECK((i + 1 != b));
86+
i.advance();
87+
CHECK((i == b + 1));
88+
CHECK(i - b == 1);
89+
CHECK((i - 1 == b));
90+
i.advance(-1);
91+
CHECK((i == b));
92+
static_assert(std::is_same_v<decltype(i.advance()),
93+
stdx::byterator<std::uint16_t const> &>);
94+
}
95+
8096
TEST_CASE("equality comparable", "[byterator]") {
8197
auto const a = std::array{1, 2, 3, 4};
8298
auto x = stdx::byterator{std::begin(a)};
@@ -142,6 +158,15 @@ TEST_CASE("peek uint8_t", "[byterator]") {
142158
CHECK((i == std::begin(a)));
143159
}
144160

161+
TEST_CASE("advance uint8_t", "[byterator]") {
162+
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
163+
stdx::to_be<std::uint16_t>(0x0304)};
164+
auto i = stdx::byterator{std::begin(a)};
165+
auto j = std::next(i);
166+
i.advanceu8();
167+
CHECK((i == j));
168+
}
169+
145170
TEST_CASE("read uint8_t", "[byterator]") {
146171
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
147172
stdx::to_be<std::uint16_t>(0x0304)};
@@ -171,6 +196,15 @@ TEST_CASE("peek uint16_t", "[byterator]") {
171196
CHECK((i == std::begin(a)));
172197
}
173198

199+
TEST_CASE("advance uint16_t", "[byterator]") {
200+
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
201+
stdx::to_be<std::uint16_t>(0x0304)};
202+
auto i = stdx::byterator{std::begin(a)};
203+
auto j = i + 2;
204+
i.advanceu16();
205+
CHECK((i == j));
206+
}
207+
174208
TEST_CASE("read uint16_t", "[byterator]") {
175209
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
176210
stdx::to_be<std::uint16_t>(0x0304)};
@@ -200,6 +234,14 @@ TEST_CASE("peek uint32_t", "[byterator]") {
200234
CHECK((i == std::begin(a)));
201235
}
202236

237+
TEST_CASE("advance uint32_t", "[byterator]") {
238+
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
239+
stdx::to_be<std::uint16_t>(0x0304)};
240+
auto i = stdx::byterator{std::begin(a)};
241+
i.advanceu32();
242+
CHECK((i == std::end(a)));
243+
}
244+
203245
TEST_CASE("read uint32_t", "[byterator]") {
204246
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
205247
stdx::to_be<std::uint16_t>(0x0304)};
@@ -229,6 +271,15 @@ TEST_CASE("peek uint64_t", "[byterator]") {
229271
CHECK((i == std::begin(a)));
230272
}
231273

274+
TEST_CASE("advance uint64_t", "[byterator]") {
275+
auto const a = std::array{
276+
stdx::to_be<std::uint16_t>(0x0102), stdx::to_be<std::uint16_t>(0x0304),
277+
stdx::to_be<std::uint16_t>(0x0506), stdx::to_be<std::uint16_t>(0x0708)};
278+
auto i = stdx::byterator{std::begin(a)};
279+
i.advanceu64();
280+
CHECK((i == std::end(a)));
281+
}
282+
232283
TEST_CASE("read uint64_t", "[byterator]") {
233284
auto const a = std::array{
234285
stdx::to_be<std::uint16_t>(0x0102), stdx::to_be<std::uint16_t>(0x0304),
@@ -264,6 +315,15 @@ TEST_CASE("peek enum", "[byterator]") {
264315
CHECK(i.peek<E>() == E::A);
265316
}
266317

318+
TEST_CASE("advance enum", "[byterator]") {
319+
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
320+
stdx::to_be<std::uint16_t>(0x0304)};
321+
auto i = stdx::byterator{std::begin(a)};
322+
auto j = std::next(i);
323+
i.advance<E>();
324+
CHECK(i == j);
325+
}
326+
267327
TEST_CASE("read enum", "[byterator]") {
268328
auto const a = std::array{stdx::to_be<std::uint16_t>(0x0102),
269329
stdx::to_be<std::uint16_t>(0x0304)};

0 commit comments

Comments
 (0)