16 #include <type_traits>
30 inline uint16_t
readU16BE(
const uint8_t *data,
size_t index) {
31 return (
static_cast<uint16_t
>(data[index]) << 8) |
32 static_cast<uint16_t
>(data[index + 1]);
42 inline uint32_t
readU32BE(
const uint8_t *data,
size_t index) {
43 return (
static_cast<uint32_t
>(data[index]) << 24) |
44 (
static_cast<uint32_t
>(data[index + 1]) << 16) |
45 (
static_cast<uint32_t
>(data[index + 2]) << 8) |
46 static_cast<uint32_t
>(data[index + 3]);
72 explicit ByteArray(
const uint8_t *data,
size_t size) : std::vector<uint8_t>(data, data + size) {}
82 ByteArray(
const std::initializer_list<uint8_t> &init_list) : std::vector<uint8_t>(init_list) {}
95 if (index + 1 >= this->size()) {
96 throw std::out_of_range(
"Index out of range for writeU16BE");
98 (*this)[index] =
static_cast<uint8_t
>((value >> 8) & 0xFF);
99 (*this)[index + 1] =
static_cast<uint8_t
>(value & 0xFF);
111 emplace_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
112 emplace_back(
static_cast<uint8_t
>(value & 0xFF));
126 if (index + 3 >= this->size()) {
127 throw std::out_of_range(
"Index out of range for writeU32BE");
129 (*this)[index] =
static_cast<uint8_t
>((value >> 24) & 0xFF);
130 (*this)[index + 1] =
static_cast<uint8_t
>((value >> 16) & 0xFF);
131 (*this)[index + 2] =
static_cast<uint8_t
>((value >> 8) & 0xFF);
132 (*this)[index + 3] =
static_cast<uint8_t
>(value & 0xFF);
144 emplace_back(
static_cast<uint8_t
>((value >> 24) & 0xFF));
145 emplace_back(
static_cast<uint8_t
>((value >> 16) & 0xFF));
146 emplace_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
147 emplace_back(
static_cast<uint8_t
>(value & 0xFF));
166 static_assert(std::is_enum_v<E>,
"writeEnum requires an enum type");
168 using UnderlyingType = std::underlying_type_t<E>;
169 auto integral_value =
static_cast<UnderlyingType
>(value);
171 if constexpr (
sizeof(UnderlyingType) == 1) {
172 emplace_back(
static_cast<uint8_t
>(integral_value));
173 }
else if constexpr (
sizeof(UnderlyingType) == 2) {
174 writeU16BE(
static_cast<uint16_t
>(integral_value));
175 }
else if constexpr (
sizeof(UnderlyingType) == 4) {
176 writeU32BE(
static_cast<uint32_t
>(integral_value));
178 static_assert(
sizeof(UnderlyingType) <= 4,
"Enum underlying type too large (max 32-bit supported)");
195 static_assert(std::is_enum_v<E>,
"writeEnumAt requires an enum type");
197 using UnderlyingType = std::underlying_type_t<E>;
198 auto integral_value =
static_cast<UnderlyingType
>(value);
200 if constexpr (
sizeof(UnderlyingType) == 1) {
201 if (index >= this->size()) {
202 throw std::out_of_range(
"Index out of range for writeEnumAt");
204 (*this)[index] =
static_cast<uint8_t
>(integral_value);
205 }
else if constexpr (
sizeof(UnderlyingType) == 2) {
206 writeU16At(index,
static_cast<uint16_t
>(integral_value));
207 }
else if constexpr (
sizeof(UnderlyingType) == 4) {
208 writeU32At(index,
static_cast<uint32_t
>(integral_value));
210 static_assert(
sizeof(UnderlyingType) <= 4,
"Enum underlying type too large (max 32-bit supported)");
225 if (index + 1 >= this->size()) {
226 throw std::out_of_range(
"Index out of range for readU16BE");
242 if (index + 3 >= this->size()) {
243 throw std::out_of_range(
"Index out of range for readU32BE");
266 static_assert(std::is_enum_v<E>,
"readEnum requires an enum type");
268 using UnderlyingType = std::underlying_type_t<E>;
270 if constexpr (
sizeof(UnderlyingType) == 1) {
271 if (index >= this->size()) {
272 throw std::out_of_range(
"Index out of range for readEnum");
274 return static_cast<E
>((*this)[index]);
275 }
else if constexpr (
sizeof(UnderlyingType) == 2) {
277 }
else if constexpr (
sizeof(UnderlyingType) == 4) {
280 static_assert(
sizeof(UnderlyingType) <= 4,
"Enum underlying type too large (max 32-bit supported)");
304 std::ios_base::fmtflags flags(os.flags());
306 for (
size_t i = 0; i < arr.size(); ++i) {
310 os << std::hex << std::uppercase << std::setw(2) << std::setfill(
'0')
311 <<
static_cast<unsigned int>(arr[i]);
uint32_t readU32BE(const uint8_t *data, size_t index)
Reads a 32-bit unsigned integer in big-endian format from a byte array.
uint16_t readU16BE(const uint8_t *data, size_t index)
Reads a 16-bit unsigned integer in big-endian format from a byte array.
std::pair< const uint8_t *, size_t > ByteArrayRef
Reference to raw array of bytes.
std::ostream & operator<<(std::ostream &os, const ByteArray &arr)
Stream operator for ByteArray.
A dynamic array of bytes with utility methods for network protocol handling.
ByteArray(const std::initializer_list< uint8_t > &init_list)
ByteArray()=default
Default constructor - creates an empty ByteArray.
E readEnum(size_t index) const
uint16_t readU16BE(size_t index) const
Reads a 16-bit unsigned integer in big-endian format from a specific index.
ByteArray(const uint8_t *data, size_t size)
Constructs a ByteArray from a raw byte array.
void writeEnumAt(size_t index, E value)
Writes an enum class value at a specific index as its underlying integral type.
void writeU16At(size_t index, uint16_t value)
Writes a 16-bit unsigned integer in big-endian format at a specific index.
void writeU32BE(uint32_t value)
Appends a 32-bit unsigned integer in big-endian format to the end.
void writeU32At(size_t index, uint32_t value)
Writes a 32-bit unsigned integer in big-endian format at a specific index.
uint32_t readU32BE(size_t index) const
Reads a 32-bit unsigned integer in big-endian format from a specific index.
void writeU16BE(uint16_t value)
Appends a 16-bit unsigned integer in big-endian format to the end.