VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
raw_data.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 * Author: Thun Lu <thun.lu@zohomail.cn>
4 * Repo: https://github.com/thun-res/vlink
5 * _ __ __ _ __
6 * | | / / / / (_) ____ / /__
7 * | | / / / / / / / __ \ / //_/
8 * | |/ / / /___ / / / / / / / ,<
9 * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24/**
25 * @file raw_data.h
26 * @brief Generic zero-copy raw-byte data container for VLink transport.
27 *
28 * @details
29 * @c RawData wraps an untyped byte buffer together with a @c Header for
30 * sequencing and timestamping. It is the simplest zero-copy container in
31 * VLink and serves as a building block when the payload format is opaque or
32 * application-defined.
33 *
34 * The struct is exactly 64 bytes on 64-bit platforms (verified via
35 * @c static_assert). Three ownership modes allow callers to manage memory
36 * without extra copies wherever possible:
37 *
38 * | Mode | Created by | Owns memory |
39 * | ------------------ | ------------------------------ | ----------- |
40 * | Owned | @c create(size) | Yes |
41 * | Shallow (borrow) | @c shallow_copy(ptr, size) | No |
42 * | Deserialised | @c operator<<(bytes) | No |
43 *
44 * @par Binary wire format
45 * @code
46 * [ magic_begin (4) | RawData struct (64) | payload bytes (N) | magic_end (4) ]
47 * @endcode
48 * Magic numbers allow the receiver to detect data corruption before
49 * @c memcpy-ing the struct header.
50 *
51 * @par Usage
52 * @code
53 * vlink::zerocopy::RawData rd;
54 * rd.header.seq = 1;
55 * rd.header.time_pub = get_time_ns();
56 * rd.create(1024);
57 * std::memcpy(const_cast<uint8_t*>(rd.data()), src_buf, 1024);
58 *
59 * vlink::Bytes wire;
60 * rd >> wire; // serialise to Bytes
61 *
62 * vlink::zerocopy::RawData rd2;
63 * rd2 << wire; // deserialise from Bytes (zero-copy, borrows wire)
64 * @endcode
65 *
66 * @note
67 * - 32-bit architectures emit a compile-time warning and are not supported.
68 * - After @c operator<<, the internal data pointer references memory inside
69 * the source @c Bytes object. The @c Bytes must outlive the @c RawData.
70 * - @c fill_data is an alias for @c deep_copy(uint8_t*, size_t).
71 */
72
73#pragma once
74
75#include <cstdint>
76
77#include "../base/bytes.h"
78#include "./header.h"
79
80namespace vlink {
81
82namespace zerocopy {
83
84/**
85 * @struct RawData
86 * @brief Generic zero-copy raw-byte data container with Header metadata.
87 *
88 * @details
89 * Manages an untyped byte payload together with a @c Header for sequencing
90 * and timestamping. The struct size is fixed at 64 bytes on 64-bit targets.
91 * Copies of the struct are either shallow (borrow the data pointer) or deep
92 * (allocate and copy). The move constructor and move-assignment transfer
93 * ownership from the source without allocation.
94 */
95struct VLINK_EXPORT_AND_ALIGNED(8) RawData final {
96 /**
97 * @brief Default constructor.
98 *
99 * @details
100 * Verifies via @c static_assert that the struct is exactly 64 bytes on
101 * 64-bit platforms. 32-bit architectures emit a compile-time warning.
102 */
103 RawData() noexcept;
104
105 /**
106 * @brief Destructor.
107 *
108 * @details
109 * Frees the owned data buffer if @c is_owner() is @c true.
110 */
111 ~RawData() noexcept;
112
113 /**
114 * @brief Copy constructor.
115 *
116 * @details
117 * Performs a deep copy of @p target, allocating a new buffer and copying
118 * the payload.
119 *
120 * @param target Source to copy from.
121 */
122 RawData(const RawData& target) noexcept;
123
124 /**
125 * @brief Move constructor.
126 *
127 * @details
128 * Transfers ownership from @p target. After the call @p target is empty
129 * and no longer owns any buffer.
130 *
131 * @param target Source to move from.
132 */
133 RawData(RawData&& target) noexcept;
134
135 /**
136 * @brief Copy-assignment operator.
137 *
138 * @details
139 * Deep-copies @p target into @c *this. Self-assignment is a no-op.
140 *
141 * @param target Source to copy from.
142 * @return Reference to @c *this.
143 */
144 RawData& operator=(const RawData& target) noexcept;
145
146 /**
147 * @brief Move-assignment operator.
148 *
149 * @details
150 * Transfers ownership from @p target into @c *this. Self-assignment is a
151 * no-op. After the call @p target is empty.
152 *
153 * @param target Source to move from.
154 * @return Reference to @c *this.
155 */
156 RawData& operator=(RawData&& target) noexcept;
157
158 /**
159 * @brief Deserialises a @c RawData from a @c Bytes wire buffer.
160 *
161 * @details
162 * Validates the magic-number envelope, then @c memcpy's the struct fields
163 * from the buffer. The internal data pointer is set to point directly into
164 * @p bytes (zero-copy, @c is_owner() == false). The caller must ensure
165 * @p bytes outlives this @c RawData.
166 *
167 * @param bytes Serialised buffer produced by @c operator>>.
168 * @return @c true on success, @c false if the buffer is invalid or
169 * the total size does not match @c get_serialized_size().
170 */
171 bool operator<<(const Bytes& bytes) noexcept;
172
173 /**
174 * @brief Serialises this @c RawData into a @c Bytes wire buffer.
175 *
176 * @details
177 * Writes the magic-number envelope, the struct fields, and the payload into
178 * @p bytes. If @p bytes is the wrong size it is reallocated automatically.
179 *
180 * @param bytes Output buffer (resized if necessary).
181 * @return Always @c true.
182 */
183 bool operator>>(Bytes& bytes) const noexcept;
184
185 /**
186 * @brief Checks whether @p bytes contains a valid @c RawData wire format.
187 *
188 * @details
189 * Verifies that the buffer is large enough and that both the begin and end
190 * magic numbers match the expected constants.
191 *
192 * @param bytes Buffer to check.
193 * @return @c true if the magic numbers are present and the minimum
194 * size constraint is satisfied.
195 */
196 [[nodiscard]] static bool check_valid(const Bytes& bytes) noexcept;
197
198 /**
199 * @brief Returns the total serialised byte count for this @c RawData.
200 *
201 * @details
202 * Computed as: @code sizeof(magic_begin) + sizeof(RawData) + size() + sizeof(magic_end) @endcode
203 *
204 * @return Total number of bytes that @c operator>> will write.
205 */
206 [[nodiscard]] size_t get_serialized_size() const noexcept;
207
208 /**
209 * @brief Returns @c true if the data pointer is non-null and the size is non-zero.
210 *
211 * @return @c true when there is a valid payload available.
212 */
213 [[nodiscard]] bool is_valid() const noexcept;
214
215 /**
216 * @brief Borrows (shallow-copies) the payload from another @c RawData.
217 *
218 * @details
219 * Sets the internal pointer to the same buffer as @p target without
220 * copying data. @c is_owner() becomes @c false. The source must outlive
221 * this object. Any previously owned buffer is freed first.
222 *
223 * @param target Source to borrow from.
224 * @return @c false if @p target == @c *this, otherwise @c true.
225 */
226 bool shallow_copy(const RawData& target) noexcept;
227
228 /**
229 * @brief Deep-copies the payload from another @c RawData.
230 *
231 * @details
232 * Allocates a new buffer and copies the payload from @p target. If @c *this
233 * already owns a buffer of the same size, the data is copied in-place without
234 * reallocation.
235 *
236 * @param target Source to copy from.
237 * @return @c false if @p target == @c *this, otherwise @c true.
238 */
239 bool deep_copy(const RawData& target) noexcept;
240
241 /**
242 * @brief Transfers ownership from another @c RawData.
243 *
244 * @details
245 * Equivalent to a move operation implemented as a member function. After
246 * the call @p target is empty and @c is_owner() on @p target is @c false.
247 *
248 * @param target Source to move from.
249 * @return @c false if @p target == @c *this, otherwise @c true.
250 */
251 bool move_copy(RawData& target) noexcept;
252
253 /**
254 * @brief Allocates an owned buffer of @p size bytes.
255 *
256 * @details
257 * Any previously owned buffer is freed before the new allocation. The
258 * new buffer content is uninitialised.
259 *
260 * @param size Number of bytes to allocate. Must be non-zero.
261 * @return @c false if @p size is zero, otherwise @c true.
262 */
263 bool create(size_t size) noexcept;
264
265 /**
266 * @brief Releases all resources and resets all fields to zero.
267 *
268 * @details
269 * Frees the owned buffer if @c is_owner() is @c true. The @c Header is
270 * also zeroed.
271 */
272 void clear() noexcept;
273
274 /**
275 * @brief Borrows a raw pointer without copying.
276 *
277 * @details
278 * Sets the internal pointer to @p data without allocating. Any previously
279 * owned buffer is freed. The caller is responsible for the lifetime of
280 * @p data.
281 *
282 * @param data Pointer to the data to borrow. Must be non-null.
283 * @param size Size of the data in bytes. Must be non-zero.
284 * @return @c false if @p data is null, @p size is zero, or @p data
285 * already equals the current internal pointer.
286 */
287 bool shallow_copy(uint8_t* data, size_t size) noexcept;
288
289 /**
290 * @brief Copies data from a raw pointer into an owned buffer.
291 *
292 * @details
293 * If @c *this already owns a buffer of the same @p size the data is copied
294 * in-place; otherwise a new buffer is allocated first via @c create().
295 *
296 * @param data Source data pointer. Must be non-null.
297 * @param size Number of bytes to copy. Must be non-zero.
298 * @return @c false on invalid arguments, otherwise @c true.
299 */
300 bool deep_copy(uint8_t* data, size_t size) noexcept;
301
302 /**
303 * @brief Alias for @c deep_copy(uint8_t*, size_t).
304 *
305 * @param data Source data pointer.
306 * @param size Number of bytes.
307 * @return Result of the underlying @c deep_copy call.
308 */
309 bool fill_data(uint8_t* data, size_t size) noexcept;
310
311 /**
312 * @brief Returns a mutable reference to the 16-bit user-reserved field.
313 *
314 * @details
315 * This field travels through serialisation and can be used by the application
316 * for flags or minor sub-type identification without extending the struct.
317 *
318 * @return Mutable reference to @c reserved_buf_.
319 */
320 [[nodiscard]] uint16_t& reserved_buf() noexcept;
321
322 /**
323 * @brief Returns a read-only pointer to the payload bytes.
324 *
325 * @return Pointer to the payload, or @c nullptr if the object is empty.
326 */
327 [[nodiscard]] const uint8_t* data() const noexcept;
328
329 /**
330 * @brief Returns the payload size in bytes.
331 *
332 * @return Number of payload bytes, or 0 if the object is empty.
333 */
334 [[nodiscard]] size_t size() const noexcept;
335
336 /**
337 * @brief Returns @c true if this object owns its data buffer.
338 *
339 * @details
340 * An owned buffer is freed in the destructor. Non-owned buffers (created by
341 * @c shallow_copy or @c operator<<) are never freed.
342 *
343 * @return @c true when memory ownership is held.
344 */
345 [[nodiscard]] bool is_owner() const noexcept;
346
347 Header header; ///< Sequencing and timestamp metadata for this data packet.
348
349 static constexpr bool kZerocopyTypes{true}; /// Internal
350
351 private:
352 uint8_t* data_{nullptr};
353 size_t size_{0};
354 bool is_owner_{false};
355 uint16_t reserved_buf_{0};
356
357 static constexpr uint32_t kMagicNumberBegin{0x98B7F11A};
358 static constexpr uint32_t kMagicNumberEnd{0x98B7F11F};
359};
360
361} // namespace zerocopy
362
363} // namespace vlink
Versatile byte buffer with small-buffer optimisation, ownership semantics and compression.
Common timestamp and sequencing metadata header for VLink zero-copy data types.
#define VLINK_EXPORT_AND_ALIGNED(align_num)
Marks a class or variable as both exported and aligned to the given byte boundary.
Definition macros.h:103
Fixed-size (40-byte) metadata header embedded in all VLink zero-copy data types.
Generic zero-copy raw-byte data container with Header metadata.