VLink  2.0.0
A high-performance communication middleware
header.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 header.h
26  * @brief Fixed-size timestamp and sequencing prefix embedded by every VLink zero-copy container.
27  *
28  * @details
29  * @c Header is the foundation of the @c vlink::zerocopy family. Every other
30  * zero-copy container (@c RawData, @c CameraFrame, @c AudioFrame, @c PointCloud,
31  * @c Tensor, @c OccupancyGrid, @c ObjectArray) embeds a @c Header as its first
32  * public member so that consumers always know where to read the per-frame
33  * @c seq, @c frame_id, and dual timestamps regardless of the payload type.
34  *
35  * | Concern | Field | Purpose |
36  * | --------------- | -------------------- | ------------------------------------------------ |
37  * | Identity | @c frame_id | Source sensor / coordinate-frame label (16 char) |
38  * | Sequencing | @c seq | Monotonically increasing per-publisher counter |
39  * | Acquisition | @c time_meas | Time the producer captured the data (ns) |
40  * | Publication | @c time_pub | Time the producer dispatched the message (ns) |
41  *
42  * @par Wire format
43  * The struct is POD; @c memcpy() over its 40 bytes is the canonical serialiser.
44  * The @c sizeof() value is locked by @c static_assert and forms a permanent
45  * contract: the @c vlink::zerocopy::* containers have no forward and no backward
46  * binary compatibility. Reserved bytes count as part of the contract too.
47  * @code
48  * static_assert(sizeof(Header) == 40, "Sizeof must be 40 bytes.");
49  * @endcode
50  *
51  * @par Memory layout
52  * @code
53  * Offset Size Field
54  * ------ ---- ------------------
55  * 0 16 frame_id[16]
56  * 16 4 seq
57  * 20 4 reserved
58  * 24 8 time_meas
59  * 32 8 time_pub
60  * ------ ---- ------------------
61  * Total 40 bytes (alignas 8)
62  * @endcode
63  *
64  * @par Reserved bytes
65  * The 4-byte @c reserved slot at offset 20 is part of the wire contract. It
66  * MUST NOT be repurposed by application code: reading it back after future
67  * library upgrades would silently corrupt data. Treat the byte as opaque.
68  *
69  * @par Example
70  * @code
71  * vlink::zerocopy::RawData raw;
72  * std::strncpy(raw.header.frame_id, "lidar_top", sizeof(raw.header.frame_id) - 1);
73  * raw.header.seq = next_seq++;
74  * raw.header.time_meas = sensor_ns;
75  * raw.header.time_pub = vlink::time_ns();
76  *
77  * auto label = raw.header.frame_id_view();
78  * @endcode
79  */
80 
81 #pragma once
82 
83 #include <cstdint>
84 #include <cstring>
85 #include <string_view>
86 
87 #include "../base/macros.h"
88 #include "../version.h"
89 
90 namespace vlink {
91 
92 namespace zerocopy {
93 
94 /**
95  * @brief Encoded VLink library version stamped into every zero-copy wire envelope.
96  *
97  * @details
98  * The library version triple is packed as decimal @c major*1000000 + minor*1000 + patch
99  * (each component in @c 0..100), e.g. version @c 2.10.8 encodes as the integer @c 2010008.
100  * Producers write this @c uint32_t immediately after the begin-magic sentinel; consumers
101  * reject any payload whose @b major component differs from the local @c kWireVersion (see
102  * @c version_major()). Minor and patch components are carried for diagnostics but are not
103  * gated.
104  */
105 inline constexpr uint32_t kWireVersion = (static_cast<uint32_t>(VLINK_VERSION_MAJOR) * 1000000U) +
106  (static_cast<uint32_t>(VLINK_VERSION_MINOR) * 1000U) +
107  static_cast<uint32_t>(VLINK_VERSION_PATCH);
108 
109 /**
110  * @brief Extracts the major component from an encoded zero-copy wire version.
111  *
112  * @param version Encoded version as produced by @c kWireVersion.
113  * @return Major component, i.e. @c version / 1000000.
114  */
115 inline constexpr uint32_t version_major(uint32_t version) noexcept { return version / 1000000U; }
116 
117 /**
118  * @struct Header
119  * @brief 40-byte timestamp / sequencing metadata prefix shared by all zero-copy containers.
120  *
121  * @details
122  * The struct is intentionally pure POD with 8-byte alignment so that it can be
123  * @c memcpy'd as part of a larger container's wire snapshot. The @c sizeof
124  * value is locked at 40 bytes via @c static_assert; producers and consumers
125  * on incompatible toolchains must keep this contract intact.
126  */
127 struct VLINK_EXPORT_AND_ALIGNED(8) Header final {
128  char frame_id[16]{"unknown"}; ///< Coordinate-frame / sensor identifier; not necessarily NUL-terminated.
129  uint32_t seq{0}; ///< Monotonically increasing per-publisher sequence; wraps at UINT32_MAX.
130  uint32_t reserved{0}; ///< Reserved bytes; part of the wire contract, must not be repurposed.
131  uint64_t time_meas{0}; ///< Capture / acquisition timestamp in nanoseconds since the UNIX epoch.
132  uint64_t time_pub{0}; ///< Dispatch / publish timestamp in nanoseconds since the UNIX epoch.
133 
134  /**
135  * @brief Default constructor that asserts the 40-byte sizeof contract.
136  *
137  * @details
138  * The @c static_assert inside the constructor body guarantees that any
139  * accidental field reordering or padding change breaks the build instead of
140  * silently corrupting the wire format.
141  */
142  Header() noexcept;
143 
144  /**
145  * @brief Returns a bounded view over @c frame_id so consumers cannot read past the buffer.
146  *
147  * @details
148  * @c frame_id may be filled to its full 16-byte capacity without a terminator,
149  * so this helper uses @c strnlen to compute the view length safely.
150  *
151  * @return Non-owning view over the populated portion of @c frame_id.
152  */
153  [[nodiscard]] std::string_view frame_id_view() const noexcept {
154  return {frame_id, ::strnlen(frame_id, sizeof(frame_id))};
155  }
156 };
157 
158 } // namespace zerocopy
159 
160 } // namespace vlink
40-byte timestamp / sequencing metadata prefix shared by all zero-copy containers.
#define VLINK_VERSION_PATCH
Definition: version.h:94
#define VLINK_VERSION_MAJOR
Definition: version.h:92
#define VLINK_VERSION_MINOR
Definition: version.h:93