VLink  2.0.0
A high-performance communication middleware
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 byte-buffer container with a @c Header prefix.
27  *
28  * @details
29  * @c RawData is the most lightweight container in the @c vlink::zerocopy
30  * family. It pairs an opaque payload (any application-defined byte blob:
31  * Protobuf wire bytes, FlatBuffer tables, compressed frames, encrypted CAN
32  * snapshots, ...) with a 40-byte @c Header so that consumers can sequence and
33  * time-stamp the payload without parsing it. Use @c RawData whenever the
34  * payload schema is opaque to VLink, or when a higher-level layer wraps its
35  * own framing on top.
36  *
37  * | Ownership mode | How to create | Frees on destruction |
38  * | -------------- | ----------------------------------- | --------------------- |
39  * | Owned | @c create(size) | Yes |
40  * | Borrowed | @c shallow_copy(ptr, size) | No |
41  * | Deserialised | @c operator<<(bytes) | No (borrows @c bytes) |
42  *
43  * @par Wire format
44  * @c RawData is POD; the canonical serialiser is @c memcpy(). The @c sizeof
45  * value is locked by @c static_assert and forms a permanent binary contract:
46  * VLink zero-copy containers have NO forward and NO backward compatibility
47  * across versions, and every field including the @c reserved slot is part of
48  * that contract.
49  * @code
50  * static_assert(sizeof(RawData) == 64, "Sizeof must be 64 bytes.");
51  * @endcode
52  *
53  * @par Memory layout
54  * @code
55  * Offset Size Field
56  * ------ ----- --------------------------------
57  * 0 40 Header header
58  * 40 8 uint8_t* data_
59  * 48 8 size_t size_
60  * 56 1 bool is_owner_
61  * 57 1 (padding)
62  * 58 2 uint16_t reserved_buf_
63  * 60 4 (tail padding to align(8))
64  * ------ ----- --------------------------------
65  * Total 64 bytes (alignas 8)
66  *
67  * Wire envelope:
68  * [ magic_begin (4) | version (4) | RawData struct (64) | payload (size_) | magic_end (4) ]
69  * @endcode
70  *
71  * @par Reserved bytes
72  * @c reserved_buf_ travels through the wire format unchanged. It is exposed
73  * via @c get_reserved() so applications can stash a flag or minor sub-type id
74  * without inflating the struct, but the slot must not be redefined later --
75  * doing so would silently break any peer that still expects the old meaning.
76  *
77  * @par Example
78  * @code
79  * vlink::zerocopy::RawData rd;
80  * rd.header.seq = ++seq;
81  * rd.header.time_pub = vlink::time_ns();
82  * rd.create(payload.size());
83  * std::memcpy(const_cast<uint8_t*>(rd.data()), payload.data(), payload.size());
84  *
85  * vlink::Bytes wire;
86  * rd >> wire;
87  *
88  * vlink::zerocopy::RawData rx;
89  * rx << wire;
90  * @endcode
91  */
92 
93 #pragma once
94 
95 #include <cstdint>
96 
97 #include "../base/bytes.h"
98 #include "./header.h"
99 
100 namespace vlink {
101 
102 namespace zerocopy {
103 
104 /**
105  * @struct RawData
106  * @brief 64-byte POD container that wraps an opaque byte payload with a @c Header prefix.
107  *
108  * @details
109  * The struct is locked at 64 bytes on 64-bit targets via @c static_assert;
110  * 32-bit toolchains emit a build-time warning because the embedded pointer
111  * and size widths shift the layout. The wire envelope uses magic-number
112  * sentinels to detect truncation and corruption before @c memcpy of the
113  * struct header.
114  */
115 struct VLINK_EXPORT_AND_ALIGNED(8) RawData final {
116  /**
117  * @brief Default-constructs an empty container and verifies the sizeof contract.
118  */
119  RawData() noexcept;
120 
121  /**
122  * @brief Releases the owned buffer when @c is_owner() is @c true.
123  */
124  ~RawData() noexcept;
125 
126  /**
127  * @brief Deep-copies the payload of @p target into a freshly allocated owned buffer.
128  *
129  * @param target Source container to clone.
130  */
131  RawData(const RawData& target) noexcept;
132 
133  /**
134  * @brief Steals @p target's ownership and metadata; @p target is left empty.
135  *
136  * @param target Source container moved from.
137  */
138  RawData(RawData&& target) noexcept;
139 
140  /**
141  * @brief Deep-copy-assigns from @p target; self-assignment is a no-op.
142  *
143  * @param target Source container to clone.
144  * @return Reference to @c *this.
145  */
146  RawData& operator=(const RawData& target) noexcept;
147 
148  /**
149  * @brief Move-assigns @p target's resources into @c *this; self-assignment is a no-op.
150  *
151  * @param target Source container moved from.
152  * @return Reference to @c *this.
153  */
154  RawData& operator=(RawData&& target) noexcept;
155 
156  /**
157  * @brief Deserialises a @c RawData from @p bytes using zero-copy borrowing semantics.
158  *
159  * @details
160  * Validates the magic-number envelope and total length, then borrows the
161  * payload pointer from @p bytes. The caller must keep @p bytes alive for as
162  * long as this @c RawData is used.
163  *
164  * @param bytes Wire buffer previously produced by @c operator>>.
165  * @return @c true on success; @c false on magic mismatch or size mismatch.
166  */
167  bool operator<<(const Bytes& bytes) noexcept;
168 
169  /**
170  * @brief Serialises the struct snapshot plus payload into @p bytes.
171  *
172  * @param bytes Output buffer; resized automatically when its size differs from the serialized size.
173  * @return Always @c true.
174  */
175  bool operator>>(Bytes& bytes) const noexcept;
176 
177  /**
178  * @brief Validates that @p bytes carries a well-formed @c RawData envelope.
179  *
180  * @param bytes Buffer to inspect.
181  * @return @c true when the magic sentinels match and the minimum length holds.
182  */
183  [[nodiscard]] static bool check_valid(const Bytes& bytes) noexcept;
184 
185  /**
186  * @brief Total bytes that @c operator>> would write for this container.
187  *
188  * @return @c sizeof(magic_begin) + @c sizeof(version) + @c sizeof(RawData) + @c size() + @c sizeof(magic_end).
189  */
190  [[nodiscard]] size_t get_serialized_size() const noexcept;
191 
192  /**
193  * @brief Whether the payload pointer is non-null and the byte size is positive.
194  *
195  * @return @c true when the container holds usable data.
196  */
197  [[nodiscard]] bool is_valid() const noexcept;
198 
199  /**
200  * @brief Borrows @p target's payload pointer without copying.
201  *
202  * @param target Source container whose buffer must outlive @c *this.
203  * @return @c false on self-borrow, otherwise @c true.
204  */
205  bool shallow_copy(const RawData& target) noexcept;
206 
207  /**
208  * @brief Allocates (or reuses) an owned buffer and copies @p target's payload.
209  *
210  * @param target Source container to clone.
211  * @return @c false on self-copy, otherwise @c true.
212  */
213  bool deep_copy(const RawData& target) noexcept;
214 
215  /**
216  * @brief Transfers ownership from @p target; @p target becomes empty.
217  *
218  * @param target Source container moved from.
219  * @return @c false on self-move, otherwise @c true.
220  */
221  bool move_copy(RawData& target) noexcept;
222 
223  /**
224  * @brief Allocates an uninitialised owned buffer of @p size bytes.
225  *
226  * @param size Byte count; must be non-zero.
227  * @return @c false when @p size is zero, otherwise @c true.
228  */
229  bool create(size_t size) noexcept;
230 
231  /**
232  * @brief Releases the owned buffer (if any) and zeroes the @c Header.
233  */
234  void clear() noexcept;
235 
236  /**
237  * @brief Borrows an externally owned raw byte buffer without copying.
238  *
239  * @param data Non-null source pointer that must outlive @c *this.
240  * @param size Buffer length in bytes; must be non-zero.
241  * @return @c false on invalid arguments or unchanged pointer, otherwise @c true.
242  */
243  bool shallow_copy(uint8_t* data, size_t size) noexcept;
244 
245  /**
246  * @brief Copies @p size bytes from @p data into an owned buffer (allocating as needed).
247  *
248  * @param data Non-null source pointer.
249  * @param size Number of bytes to copy; must be non-zero.
250  * @return @c false on invalid arguments or aliasing, otherwise @c true.
251  */
252  bool deep_copy(uint8_t* data, size_t size) noexcept;
253 
254  /**
255  * @brief Compatibility alias for @c deep_copy(uint8_t*, size_t).
256  *
257  * @param data Source pointer.
258  * @param size Number of bytes.
259  * @return Result of the delegated @c deep_copy call.
260  */
261  bool fill_data(uint8_t* data, size_t size) noexcept;
262 
263  /**
264  * @brief Mutable accessor for the 16-bit user-reserved field carried in the wire format.
265  *
266  * @return Reference to @c reserved_buf_.
267  */
268  [[nodiscard]] uint16_t& get_reserved() noexcept;
269 
270  /**
271  * @brief Read-only pointer to the payload bytes.
272  *
273  * @return Pointer to payload start; may be non-null with @c size() == 0 for empty deserialised frames.
274  */
275  [[nodiscard]] const uint8_t* data() const noexcept;
276 
277  /**
278  * @brief Payload size in bytes (0 when empty).
279  *
280  * @return Byte count.
281  */
282  [[nodiscard]] size_t size() const noexcept;
283 
284  /**
285  * @brief Whether this container currently owns its buffer.
286  *
287  * @return @c true when the destructor would free the buffer.
288  */
289  [[nodiscard]] bool is_owner() const noexcept;
290 
291  Header header; ///< Sequencing and timestamp metadata prefix.
292 
293  static constexpr bool kZerocopyTypes{true}; ///< Marker probed by the VLink type-trait machinery.
294 
295  private:
296  uint8_t* data_{nullptr};
297  size_t size_{0};
298  bool is_owner_{false};
299  uint16_t reserved_buf_{0};
300 
301  static constexpr uint32_t kMagicNumberBegin{0x98B7F11A};
302  static constexpr uint32_t kMagicNumberEnd{0x98B7F11F};
303 };
304 
305 } // namespace zerocopy
306 
307 } // namespace vlink
Fixed-size timestamp and sequencing prefix embedded by every VLink zero-copy container.
40-byte timestamp / sequencing metadata prefix shared by all zero-copy containers.
64-byte POD container that wraps an opaque byte payload with a Header prefix.