MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BinaryBuffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <bit>
4
6
7//=============================================================================
8// Padding
9//=============================================================================
10
11/**
12 * @brief Round len up to the next 4-byte boundary.
13 *
14 * Used by OSC and any other protocol requiring 4-byte field alignment.
15 * Example: padded_size(5) == 8, padded_size(4) == 4.
16 */
17[[nodiscard]] constexpr size_t padded_size(size_t len) noexcept
18{
19 return (len + 3) & ~size_t(3);
20}
21
22//=============================================================================
23// Read primitives
24//=============================================================================
25
26/**
27 * @brief Read a null-terminated string from a byte buffer.
28 *
29 * Advances offset by padded_size(len + 1). Safe: will not read past max_len.
30 *
31 * @param data Buffer start.
32 * @param max_len Total buffer size in bytes.
33 * @param offset Current read position, advanced on return.
34 * @return Parsed string, empty if offset is already at or past max_len.
35 */
36[[nodiscard]] inline std::string read_string(
37 const uint8_t* data, size_t max_len, size_t& offset)
38{
39 if (offset >= max_len) {
40 return {};
41 }
42 const auto* start = reinterpret_cast<const char*>(data + offset);
43 size_t len = strnlen(start, max_len - offset);
44 std::string result(start, len);
45 offset += padded_size(len + 1);
46 return result;
47}
48
49/**
50 * @brief Read a big-endian int32 from a byte buffer.
51 *
52 * Advances offset by 4. Caller must ensure offset + 4 <= buffer size.
53 *
54 * @param data Buffer start.
55 * @param offset Current read position, advanced by 4 on return.
56 */
57[[nodiscard]] inline int32_t read_int32(const uint8_t* data, size_t& offset)
58{
59 int32_t val {};
60 std::memcpy(&val, data + offset, 4);
61 offset += 4;
62 if constexpr (std::endian::native == std::endian::little) {
63 val = static_cast<int32_t>(std::byteswap(static_cast<uint32_t>(val)));
64 }
65 return val;
66}
67
68/**
69 * @brief Read a big-endian IEEE 754 float from a byte buffer.
70 *
71 * Advances offset by 4. Caller must ensure offset + 4 <= buffer size.
72 *
73 * @param data Buffer start.
74 * @param offset Current read position, advanced by 4 on return.
75 */
76[[nodiscard]] inline float read_float(const uint8_t* data, size_t& offset)
77{
78 uint32_t raw {};
79 std::memcpy(&raw, data + offset, 4);
80 offset += 4;
81 if constexpr (std::endian::native == std::endian::little) {
82 raw = std::byteswap(raw);
83 }
84 float val {};
85 std::memcpy(&val, &raw, 4);
86 return val;
87}
88
89/**
90 * @brief Read a length-prefixed blob from a byte buffer.
91 *
92 * Format: [int32 size (big-endian)][data, padded to 4-byte boundary].
93 * Advances offset past the blob. Returns empty vector on invalid size.
94 *
95 * @param data Buffer start.
96 * @param max_len Total buffer size in bytes.
97 * @param offset Current read position, advanced on return.
98 */
99[[nodiscard]] inline std::vector<uint8_t> read_blob(
100 const uint8_t* data, size_t max_len, size_t& offset)
101{
102 int32_t blob_size = read_int32(data, offset);
103 if (blob_size < 0
104 || offset + static_cast<size_t>(blob_size) > max_len) {
105 return {};
106 }
107 std::vector<uint8_t> result(
108 data + offset, data + offset + blob_size);
109 offset += padded_size(static_cast<size_t>(blob_size));
110 return result;
111}
112
113//=============================================================================
114// Write primitives
115//=============================================================================
116
117/**
118 * @brief Append a null-terminated string padded to 4-byte boundary.
119 *
120 * Writes the string bytes, a null terminator, then zero-padding.
121 *
122 * @param out Destination buffer.
123 * @param str String to write.
124 */
125inline void write_string(std::vector<uint8_t>& out, const std::string& str)
126{
127 out.insert(out.end(), str.begin(), str.end());
128 size_t padded = padded_size(str.size() + 1);
129 out.resize(out.size() + (padded - str.size()), 0);
130}
131
132/**
133 * @brief Append a big-endian int32.
134 *
135 * @param out Destination buffer.
136 * @param val Value to write.
137 */
138inline void write_int32(std::vector<uint8_t>& out, int32_t val)
139{
140 auto raw = static_cast<uint32_t>(val);
141 if constexpr (std::endian::native == std::endian::little) {
142 raw = std::byteswap(raw);
143 }
144 const auto* bytes = reinterpret_cast<const uint8_t*>(&raw);
145 out.insert(out.end(), bytes, bytes + 4);
146}
147
148/**
149 * @brief Append a big-endian IEEE 754 float.
150 *
151 * @param out Destination buffer.
152 * @param val Value to write.
153 */
154inline void write_float(std::vector<uint8_t>& out, float val)
155{
156 uint32_t raw {};
157 std::memcpy(&raw, &val, 4);
158 if constexpr (std::endian::native == std::endian::little) {
159 raw = std::byteswap(raw);
160 }
161 const auto* bytes = reinterpret_cast<const uint8_t*>(&raw);
162 out.insert(out.end(), bytes, bytes + 4);
163}
164
165/**
166 * @brief Append a length-prefixed blob padded to 4-byte boundary.
167 *
168 * Format: [int32 size (big-endian)][data, zero-padded to 4-byte boundary].
169 *
170 * @param out Destination buffer.
171 * @param blob Bytes to write.
172 */
173inline void write_blob(
174 std::vector<uint8_t>& out, const std::vector<uint8_t>& blob)
175{
176 write_int32(out, static_cast<int32_t>(blob.size()));
177 out.insert(out.end(), blob.begin(), blob.end());
178 size_t padded = padded_size(blob.size());
179 out.resize(out.size() + (padded - blob.size()), 0);
180}
181
182} // namespace MayaFlux::Transitive::Protocol
void write_int32(std::vector< uint8_t > &out, int32_t val)
Append a big-endian int32.
void write_blob(std::vector< uint8_t > &out, const std::vector< uint8_t > &blob)
Append a length-prefixed blob padded to 4-byte boundary.
std::vector< uint8_t > read_blob(const uint8_t *data, size_t max_len, size_t &offset)
Read a length-prefixed blob from a byte buffer.
int32_t read_int32(const uint8_t *data, size_t &offset)
Read a big-endian int32 from a byte buffer.
void write_string(std::vector< uint8_t > &out, const std::string &str)
Append a null-terminated string padded to 4-byte boundary.
std::string read_string(const uint8_t *data, size_t max_len, size_t &offset)
Read a null-terminated string from a byte buffer.
constexpr size_t padded_size(size_t len) noexcept
Round len up to the next 4-byte boundary.
float read_float(const uint8_t *data, size_t &offset)
Read a big-endian IEEE 754 float from a byte buffer.
void write_float(std::vector< uint8_t > &out, float val)
Append a big-endian IEEE 754 float.