PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
wire.h
Go to the documentation of this file.
1
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9#include <cstddef>
10#include <cstdint>
11#include <ranges>
12#include <span>
13#include <string>
14#include <type_traits>
15#include <vector>
17{
18
19// // NOLINTBEGIN(readability-identifier-naming)
20// // @brief The number of bits in a single octet (byte), used as the base for network byte order shifts.
21// static constexpr int BitsPerOctet = 8;
22
23// /// @brief The bit-shift distance required to move data across a single octet.
24// static constexpr int OneOctetShift = BitsPerOctet; // Automatically 8
25
26// /// @brief The bit-shift distance required to move data across two octets.
27// static constexpr int TwoOctetsShift = BitsPerOctet * 2; // Automatically 16
28
29// /// @brief The bit-shift distance required to move data across three octets.
30// static constexpr int ThreeOctetsShift = BitsPerOctet * 3; // Automatically 24
31
32// /// @brief Bitmask used to isolate the lowest significant byte (8 bits) of a larger integer.
33// static constexpr std::uint8_t LowByteMask = 0xFF;
34// // NOLINTEND(readability-identifier-naming)
35
36inline constexpr unsigned kBitsPerByte = 8U;
37
43template <typename T>
44 requires std::is_integral_v<T> && std::is_unsigned_v<T>
45inline void PutBigEndian(std::vector<std::uint8_t>& out, T value)
46{
47 for (std::size_t i = sizeof(T); i > 0U; --i)
48 {
49 out.push_back(static_cast<std::uint8_t>(value >> ((i - 1U) * kBitsPerByte)));
50 }
51}
52
62template <typename T>
63 requires std::is_integral_v<T> && std::is_unsigned_v<T>
64inline void PutLittleEndian(std::vector<std::uint8_t>& out, T value)
65{
66 for (std::size_t i = 0U; i < sizeof(T); ++i)
67 {
68 out.push_back(static_cast<std::uint8_t>(value >> (i * kBitsPerByte)));
69 }
70}
76template <typename T>
77 requires std::is_integral_v<T> && std::is_unsigned_v<T>
78inline void PutLittleEndian(std::uint8_t* out, T value)
79{
80 for (std::size_t i = 0U; i < sizeof(T); ++i)
81 {
82 out[i] = static_cast<std::uint8_t>(value >> (i * kBitsPerByte));
83 }
84}
85
89inline void PutU8(std::vector<std::uint8_t>& out, std::uint8_t value)
90{
91 out.push_back(value);
92}
93
97inline void PutU16(std::vector<std::uint8_t>& out, std::uint16_t value)
98{
99 PutBigEndian(out, value);
100}
101
105inline void PutU32(std::vector<std::uint8_t>& out, std::uint32_t value)
106{
107 PutBigEndian(out, value);
108}
109
114inline void PutBytes(std::vector<std::uint8_t>& out, const std::uint8_t* data, std::size_t len)
115{
116 out.insert(out.end(), data, data + len);
117}
118
123template <std::size_t N>
124inline void PutFixed(std::vector<std::uint8_t>& out, const std::array<std::uint8_t, N>& data)
125{
126 out.insert(out.end(), data.begin(), data.end());
127}
131inline void PutFixed(std::vector<std::uint8_t>& out, const std::vector<std::uint8_t>& data)
132{
133 out.insert(out.end(), data.begin(), data.end());
134}
135
139inline void PutFixed(std::vector<std::uint8_t>& out, const std::string& data)
140{
141 out.insert(out.end(), data.begin(), data.end());
142}
143
153template <typename T>
154 requires std::is_integral_v<T> && std::is_unsigned_v<T>
155[[nodiscard]] constexpr T ReadBigEndian(const std::uint8_t* data)
156{
157 T value = 0;
158
159 for (std::size_t i = 0U; i < sizeof(T); ++i)
160 {
161 value = static_cast<T>((value << kBitsPerByte) | static_cast<T>(data[i]));
162 }
163
164 return value;
165}
166
172template <typename T>
173 requires std::is_integral_v<T> && std::is_unsigned_v<T>
174[[nodiscard]] constexpr T ReadLittleEndian(const std::uint8_t* data)
175{
176 T value = 0;
177
178 for (std::size_t i = 0U; i < sizeof(T); ++i)
179 {
180 value = static_cast<T>(
181 value |
182 (static_cast<T>(data[i]) << (i * kBitsPerByte)));
183 }
184
185 return value;
186}
187
192{
193 public:
197 Reader(const std::uint8_t* data, std::size_t len)
198 : data(data), len(len)
199 {
200 }
201
204 explicit Reader(std::span<const std::uint8_t> bytes)
205 : data(bytes.data()), len(bytes.size())
206 {
207 }
208
211 explicit Reader(const std::vector<std::uint8_t>& v)
212 : data(v.data()), len(v.size())
213 {
214 }
215
218 [[nodiscard]] std::size_t Remaining() const
219 {
220 return len - pos;
221 }
222
225 [[nodiscard]] std::size_t Position() const
226 {
227 return pos;
228 }
229
232 [[nodiscard]] const std::uint8_t* Cursor() const
233 {
234 return data + pos;
235 }
236
240 void Need(std::size_t n) const
241 {
242 if (Remaining() < n)
243 {
244 throw std::out_of_range("wire::Reader: not enough data");
245 }
246 }
247
250 std::uint8_t U8()
251 {
252 Need(1);
253 return data[pos++];
254 }
255
258 std::uint16_t U16()
259 {
260 Need(sizeof(std::uint16_t));
261 const auto value = ReadBigEndian<std::uint16_t>(&data[pos]);
262 pos += sizeof(std::uint16_t);
263 return value;
264 }
265
268 std::uint32_t U32()
269 {
270 Need(sizeof(std::uint32_t));
271
272 const auto value = ReadBigEndian<std::uint32_t>(&data[pos]);
273 pos += sizeof(std::uint32_t);
274 return value;
275 }
276
279 std::uint16_t U16LittleEndian()
280 {
281 Need(sizeof(std::uint16_t));
282
283 const auto value = ReadLittleEndian<std::uint16_t>(&data[pos]);
284
285 pos += sizeof(std::uint16_t);
286 return value;
287 }
288
291 std::uint32_t U32LittleEndian()
292 {
293 Need(sizeof(std::uint32_t));
294
295 const auto value = ReadLittleEndian<std::uint32_t>(&data[pos]);
296
297 pos += sizeof(std::uint32_t);
298 return value;
299 }
300
304 std::vector<std::uint8_t> ReadBytes(std::size_t n)
305 {
306 Need(n);
307 std::vector<std::uint8_t> v(data + pos, data + pos + n);
308 pos += n;
309 return v;
310 }
311
315 template <std::size_t N>
316 std::array<std::uint8_t, N> Fixed()
317 {
318 Need(N);
319 std::array<std::uint8_t, N> value{};
320
321 std::span source{data + pos, N};
322 std::ranges::copy(source, value.begin());
323
324 pos += N;
325 return value;
326 }
327
330 void Skip(std::size_t n)
331 {
332 Need(n);
333 pos += n;
334 }
343 void SkipTo(std::size_t offset)
344 {
345 if (offset > len)
346 {
347 throw std::out_of_range("wire::Reader: offset is beyond the end of the buffer");
348 }
349
350 pos = offset;
351 }
352
353 private:
355 const std::uint8_t* data;
356
358 std::size_t len;
359
361 std::size_t pos = 0;
362};
363
364} // namespace profinet::wire
Lightweight cursor for parsing a big-endian byte buffer without copies.
Definition wire.h:192
std::uint8_t U8()
Read and consume one byte.
Definition wire.h:250
const std::uint8_t * Cursor() const
Pointer to the next unread byte.
Definition wire.h:232
std::size_t Position() const
Current read offset from the start of the buffer.
Definition wire.h:225
std::uint32_t U32()
Read and consume a big-endian 32-bit value.
Definition wire.h:268
const std::uint8_t * data
Pointer to the start of the underlying buffer (not owned).
Definition wire.h:355
Reader(const std::uint8_t *data, std::size_t len)
Construct a reader over a raw pointer/length pair.
Definition wire.h:197
std::size_t Remaining() const
Number of bytes not yet consumed.
Definition wire.h:218
std::array< std::uint8_t, N > Fixed()
Read and consume a fixed-size byte array.
Definition wire.h:316
Reader(std::span< const std::uint8_t > bytes)
Construct a reader over a byte span.
Definition wire.h:204
void SkipTo(std::size_t offset)
Move the read position to an absolute byte offset.
Definition wire.h:343
std::uint32_t U32LittleEndian()
Read and consume a little-endian 32-bit value.
Definition wire.h:291
std::size_t pos
Current read offset from the start of the buffer.
Definition wire.h:361
void Need(std::size_t n) const
Assert that at least n bytes remain.
Definition wire.h:240
void Skip(std::size_t n)
Advance the read position without returning the skipped bytes.
Definition wire.h:330
Reader(const std::vector< std::uint8_t > &v)
Construct a reader over an existing byte vector.
Definition wire.h:211
std::vector< std::uint8_t > ReadBytes(std::size_t n)
Read and consume a variable-length byte range.
Definition wire.h:304
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
Definition wire.h:258
std::uint16_t U16LittleEndian()
Read and consume a little-endian 16-bit value.
Definition wire.h:279
std::size_t len
Total length of the underlying buffer in bytes.
Definition wire.h:358
void PutBytes(std::vector< std::uint8_t > &out, const std::uint8_t *data, std::size_t len)
Append a raw byte range to a buffer.
Definition wire.h:114
void PutFixed(std::vector< std::uint8_t > &out, const std::array< std::uint8_t, N > &data)
Append a fixed-size byte array to a buffer.
Definition wire.h:124
constexpr T ReadLittleEndian(const std::uint8_t *data)
Decode an unsigned integral value from little-endian bytes.
Definition wire.h:174
constexpr T ReadBigEndian(const std::uint8_t *data)
Decode an unsigned integral value from big-endian bytes.
Definition wire.h:155
void PutU8(std::vector< std::uint8_t > &out, std::uint8_t value)
Append a single byte to a buffer.
Definition wire.h:89
constexpr unsigned kBitsPerByte
Definition wire.h:36
void PutU16(std::vector< std::uint8_t > &out, std::uint16_t value)
Append a 16-bit value to a buffer in big-endian order.
Definition wire.h:97
void PutLittleEndian(std::vector< std::uint8_t > &out, T value)
Append an unsigned integral value to a buffer in little-endian order.
Definition wire.h:64
void PutU32(std::vector< std::uint8_t > &out, std::uint32_t value)
Append a 32-bit value to a buffer in big-endian order.
Definition wire.h:105
void PutBigEndian(std::vector< std::uint8_t > &out, T value)
Append an unsigned integral value to a buffer in big-endian order.
Definition wire.h:45