VLink  2.0.0
A high-performance communication middleware
types.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 types.h
26  * @brief Core enumerations and small value types shared by the entire VLink implementation layer.
27  *
28  * @details
29  * This is an internal implementation header consumed by every node template
30  * (@c Publisher, @c Subscriber, @c Client, @c Server, @c Setter, @c Getter),
31  * every @c Conf subclass and every @c NodeImpl backend. It is also re-exported
32  * to applications via @c vlink.h so that user code can refer to enums such as
33  * @c SecurityType when instantiating the public node templates.
34  *
35  * @par ImplType
36  * Role bitmask consumed by @c VLINK_ALLOW_IMPL_TYPE to restrict which node
37  * categories a given @c Conf can produce.
38  *
39  * | Value | Hex | Meaning |
40  * | ------------------- | ---- | ---------------------------------------- |
41  * | @c kUnknownImplType | 0x00 | Type not yet determined. |
42  * | @c kPublisher | 0x01 | Event publisher node. |
43  * | @c kSubscriber | 0x02 | Event subscriber node. |
44  * | @c kSetter | 0x04 | Field setter node. |
45  * | @c kGetter | 0x08 | Field getter node. |
46  * | @c kServer | 0x10 | Method server node. |
47  * | @c kClient | 0x20 | Method client node. |
48  *
49  * @par TransportType
50  * Resolved at URL construction time from the URI scheme.
51  *
52  * | Value | URL prefix | Backend |
53  * | ------------ | ------------- | ------------------------------------ |
54  * | @c kUnknown | (none) | Unknown or unsupported. |
55  * | @c kIntra | @c intra:// | In-process queue (no serialisation). |
56  * | @c kShm | @c shm:// | Iceoryx shared memory. |
57  * | @c kShm2 | @c shm2:// | Iceoryx2 shared memory. |
58  * | @c kZenoh | @c zenoh:// | Zenoh publish / subscribe. |
59  * | @c kDds | @c dds:// | Fast-DDS RTPS. |
60  * | @c kDdsc | @c ddsc:// | CycloneDDS. |
61  * | @c kDdsr | @c ddsr:// | RTI DDS. |
62  * | @c kDdst | @c ddst:// | TravoDDS. |
63  * | @c kSomeip | @c someip:// | SOME/IP through vsomeip. |
64  * | @c kMqtt | @c mqtt:// | MQTT publish / subscribe. |
65  * | @c kFdbus | @c fdbus:// | FDBus IPC. |
66  * | @c kQnx | @c qnx:// | QNX IPC (QNX only). |
67  *
68  * @par InitType
69  * Controls whether the public Node<> template runs @c init() immediately or
70  * defers it so the user can adjust properties beforehand.
71  *
72  * | Value | Meaning |
73  * | ----------------- | ---------------------------------------------- |
74  * | @c kWithoutInit | Defer initialisation; call @c init() manually. |
75  * | @c kWithInit | Initialise immediately in the constructor. |
76  *
77  * @par Cross-references
78  * - @c ImplType -- chosen at the @c NodeImpl base level; surfaced via
79  * @c NodeImpl::impl_type.
80  * - @c SecurityType -- second template parameter on every public node type
81  * (@c Publisher<T, SecurityType>, @c Subscriber<T, SecurityType>, ...).
82  * - @c InitType -- argument to the public node constructors that toggles
83  * immediate versus deferred initialisation.
84  */
85 
86 #pragma once
87 
88 #include <chrono>
89 #include <cstdint>
90 #include <iostream>
91 #include <string>
92 #include <string_view>
93 
94 #include "../base/bytes.h"
95 #include "../base/functional.h"
96 #include "../base/macros.h"
97 
98 namespace vlink {
99 
100 /**
101  * @enum ImplType
102  * @brief Bitmask identifying the role of a VLink node implementation.
103  *
104  * @details
105  * Values may be combined with bitwise OR to express compound capabilities,
106  * for example @c (kPublisher | kSubscriber) for a backend that handles both
107  * roles on the same topic. @c VLINK_ALLOW_IMPL_TYPE uses these combined flags
108  * to gate the @c Conf factory at compile time.
109  */
110 enum ImplType : uint8_t {
111  kUnknownImplType = 0, ///< Type not yet determined.
112  kServer = 16, ///< Method server (RPC responder).
113  kClient = 32, ///< Method client (RPC caller).
114  kPublisher = 1, ///< Event publisher (one-to-many broadcast).
115  kSubscriber = 2, ///< Event subscriber (receives broadcasts).
116  kSetter = 4, ///< Field setter (writes latest value).
117  kGetter = 8, ///< Field getter (reads latest value).
118 };
119 
120 /**
121  * @enum TransportType
122  * @brief Enumeration of every transport backend recognised by VLink.
123  *
124  * @details
125  * Resolved by @c Url when the URL string is parsed. Concrete @c Conf classes
126  * report their own backend through @c get_transport_type().
127  */
128 enum class TransportType : uint8_t {
129  kUnknown = 0, ///< Unknown or unsupported transport.
130  kIntra = 1, ///< In-process queue (@c intra://).
131  kShm = 2, ///< Iceoryx shared memory (@c shm://).
132  kShm2 = 3, ///< Iceoryx2 shared memory (@c shm2://).
133  kZenoh = 4, ///< Zenoh publish / subscribe (@c zenoh://).
134  kDds = 5, ///< Fast-DDS RTPS (@c dds://).
135  kDdsc = 6, ///< CycloneDDS (@c ddsc://).
136  kDdsr = 7, ///< RTI DDS (@c ddsr://).
137  kDdst = 8, ///< TravoDDS (@c ddst://).
138  kSomeip = 9, ///< SOME/IP through vsomeip (@c someip://).
139  kMqtt = 10, ///< MQTT (@c mqtt://).
140  kFdbus = 11, ///< FDBus IPC (@c fdbus://).
141  kQnx = 12, ///< QNX IPC (@c qnx://; QNX only).
142 };
143 
144 /**
145  * @enum InitType
146  * @brief Selects between immediate and deferred node initialisation.
147  *
148  * @details
149  * Pass @c kWithoutInit when the application needs to call
150  * @c Publisher::set_property() (or similar) before the underlying transport
151  * starts; otherwise the default @c kWithInit performs the full init inside
152  * the constructor.
153  */
154 enum class InitType : uint8_t {
155  kWithoutInit = 0, ///< Defer initialisation; call init() manually.
156  kWithInit = 1, ///< Initialise immediately in the constructor.
157 };
158 
159 /**
160  * @enum SecurityType
161  * @brief Compile-time selector for the per-node message security variant.
162  *
163  * @details
164  * Used as the second template argument of @c Publisher<T, SecurityType>,
165  * @c Subscriber<T, SecurityType> and the rest of the public node templates.
166  * @c kWithSecurity enables authenticated AES-128-GCM encryption (optionally
167  * wrapped with RSA-OAEP and signed with RSA-PSS) over the serialised payload.
168  * Both the @c intra:// transport and DDS variants using native CDR rejection
169  * the configuration; on those transports the security-prefixed node simply
170  * has no usable security object once @c init() runs.
171  */
172 enum class SecurityType : uint8_t {
173  kWithoutSecurity = 0, ///< Plain, unauthenticated transport.
174  kWithSecurity = 1, ///< Encrypted and authenticated transport.
175 };
176 
177 /**
178  * @enum ActionType
179  * @brief Labels for messages captured by the recording infrastructure.
180  *
181  * @details
182  * Used by @c NodeImpl::try_record() so the bag writer can reconstruct message
183  * flow across nodes during playback.
184  */
185 enum class ActionType : uint8_t {
186  kUnknownAction = 0, ///< Action category is not classified.
187  kClientRequest = 1, ///< RPC request emitted by a Client node.
188  kClientResponse = 2, ///< RPC response observed by a Client node.
189  kServerRequest = 3, ///< RPC request observed by a Server node.
190  kServerResponse = 4, ///< RPC response emitted by a Server node.
191  kPublish = 5, ///< Message emitted by a Publisher node.
192  kSubscribe = 6, ///< Message observed by a Subscriber node.
193  kSet = 7, ///< Value written by a Setter node.
194  kGet = 8 ///< Value observed by a Getter node.
195 };
196 
197 /**
198  * @enum SchemaType
199  * @brief Coarse runtime schema family used by discovery, bag metadata and proxy routing.
200  *
201  * @details
202  * The wire / type identifier proper lives in @c ser_type (for example a
203  * protobuf fully qualified name or a FlatBuffers table name); @c SchemaType
204  * captures only the high-level decoding family so tools can pick the matching
205  * decoder without having to mirror the @c ser_type enum.
206  */
207 enum class SchemaType : uint8_t {
208  kUnknown = 0, ///< Decoding family unknown.
209  kRaw = 1, ///< Treat the payload as opaque bytes.
210  kZeroCopy = 2, ///< Decode through the VLink zero-copy structs.
211  kProtobuf = 3, ///< Decode through the Protocol Buffers stack.
212  kFlatbuffers = 4, ///< Decode through the FlatBuffers stack.
213 };
214 
215 /**
216  * @struct Frame
217  * @brief One recorded or replayed message as it flows through the bag pipeline.
218  *
219  * @details
220  * The single unit passed through every bag push / callback / record hook (@c BagReader,
221  * @c BagWriter, @c BagProcessor, @c BagPluginInterface). Passed @b by @b const @b reference through
222  * each hop so the payload is never deep-copied on the synchronous path; a stage that must take
223  * ownership (an async reorder cache) or rewrite a field (playback URL remap) copies explicitly, and
224  * the payload @c Bytes itself is normally a shallow view at the source. @c ser_type / @c schema_type
225  * are supplied by the caller on the write side; on the read side they arrive empty at a plugin's
226  * @c on_read(), and @c BagReader fills them from the bag's URL metadata before invoking the user's
227  * output callback (so a fully-populated frame reaches consumers without a separate @c get_ser_type()
228  * lookup).
229  */
230 struct Frame final {
231  int64_t timestamp{-1}; ///< Canonical time in microseconds (record / playback).
232  std::string url; ///< Topic URL.
233  std::string ser_type; ///< Serialisation type; reader fills it before user output.
234  SchemaType schema_type{SchemaType::kUnknown}; ///< Coarse schema family; reader fills it before user output.
235  ActionType action_type{ActionType::kUnknownAction}; ///< Recorded action kind.
236  Bytes data; ///< Serialised payload bytes.
237 };
238 
239 /**
240  * @brief Frame sink reused by every bag frame interface.
241  *
242  * @details
243  * Takes the frame by @c const reference so no copy occurs on the synchronous path; a stage that must
244  * take ownership (an async reorder cache) or rewrite a field (playback URL remap) copies explicitly,
245  * and the payload @c Bytes itself is normally a shallow view at the source.
246  */
247 using FrameCallback = MoveFunction<void(const Frame&)>;
248 
249 /**
250  * @struct Timeout
251  * @brief Compile-time timeout constants used by the public blocking wait helpers.
252  *
253  * @details
254  * Provides canonical values for the @c wait_for_* family on @c Publisher,
255  * @c Subscriber, @c Client and friends.
256  */
257 struct Timeout final {
258  [[maybe_unused]] static constexpr std::chrono::milliseconds kDefaultInterval{
259  5'000}; ///< Default wait timeout: 5 seconds.
260  [[maybe_unused]] static constexpr std::chrono::milliseconds kInfinite{-1}; ///< Wait indefinitely (negative timeout).
261 };
262 
263 /**
264  * @struct SampleLostInfo
265  * @brief Aggregate of cumulative delivered / lost sample counts.
266  *
267  * @details
268  * Returned by @c SubscriberImpl::get_lost() and @c GetterImpl::get_lost().
269  * @c total counts every message that was expected (delivered or lost);
270  * @c lost counts the subset that did not arrive. Stream-friendly through
271  * @c operator<<.
272  */
273 struct SampleLostInfo final {
274  uint64_t total{0}; ///< Total number of samples expected (delivered + lost).
275  uint64_t lost{0}; ///< Number of samples that were dropped or missed.
276 
277  /**
278  * @brief Streams a human-readable summary to @p ostream.
279  *
280  * @details
281  * Output format: @c "SampleLostInfo:[total]N[lost]M".
282  *
283  * @param ostream Destination stream.
284  * @param info Instance to print.
285  * @return Reference to @p ostream.
286  */
287  VLINK_EXPORT friend std::ostream& operator<<(std::ostream& ostream, const SampleLostInfo& info) noexcept;
288 };
289 
290 /**
291  * @struct SchemaData
292  * @brief Wire-format-neutral wrapper around one serialised schema blob.
293  *
294  * @details
295  * Consumed by schema-aware tooling such as bag readers, MCAP writers and
296  * schema plugins. @c encoding stores the original schema payload encoding
297  * (for example @c "protobuf", @c "flatbuffers" or @c "vlink_msg"); the
298  * @c schema_type field exposes the coarse runtime family used by discovery,
299  * bag routing and proxy consumers.
300  */
301 struct VLINK_EXPORT SchemaData final {
302  std::string name; ///< Schema subject (usually a fully qualified message or table name).
303  std::string encoding; ///< Schema encoding identifier, e.g. @c "protobuf" or @c "flatbuffers".
304  SchemaType schema_type{SchemaType::kUnknown}; ///< Coarse runtime family derived from @c encoding.
305  Bytes data; ///< Raw serialised schema bytes (FileDescriptorSet, BFBS, ...).
306 
307  /**
308  * @brief Returns whether @p schema_type is within the supported enum range.
309  *
310  * @param schema_type Value to validate.
311  * @return @c true when @p schema_type names a defined enum member.
312  */
313  [[nodiscard]] static bool is_valid_type(SchemaType schema_type) noexcept;
314 
315  /**
316  * @brief Returns whether @p schema_type carries concrete schema metadata.
317  *
318  * @details
319  * Unlike @c is_valid_type(), excludes @c kUnknown and @c kRaw. Used by
320  * schema caching / bag embedding code to decide whether the schema can be
321  * indexed or persisted as a real schema entry.
322  *
323  * @param schema_type Value to classify.
324  * @return @c true for protobuf, flatbuffers and zero-copy families.
325  */
326  [[nodiscard]] static bool is_real_type(SchemaType schema_type) noexcept;
327 
328  /**
329  * @brief Converts a schema type to its canonical persisted encoding label.
330  *
331  * @param schema_type Value to convert.
332  * @return Canonical encoding string, or an empty view for unknown values.
333  */
334  [[nodiscard]] static std::string_view convert_type(SchemaType schema_type) noexcept;
335 
336  /**
337  * @brief Parses an encoding string back into a @c SchemaType value.
338  *
339  * @param encoding Encoding label such as @c "protobuf", @c "fbs", @c "blob" or @c "zerocopy".
340  * @return Matching @c SchemaType, or @c SchemaType::kUnknown.
341  */
342  [[nodiscard]] static SchemaType convert_encoding(std::string_view encoding) noexcept;
343 
344  /**
345  * @brief Infers a coarse schema family from a concrete @c ser_type string.
346  *
347  * @details
348  * Intentionally conservative:
349  * - Zero-copy types are recognised by the @c "vlink::zerocopy::" prefix.
350  * - Textual / raw payload types map to @c SchemaType::kRaw.
351  * - Protobuf and FlatBuffers are not guessed from name alone.
352  *
353  * @param ser_type Concrete serialisation type string.
354  * @return Inferred schema family, or @c SchemaType::kUnknown.
355  */
356  [[nodiscard]] static constexpr SchemaType infer_ser_type(std::string_view ser_type) noexcept;
357 
358  /**
359  * @brief Resolves the best available schema family from explicit, encoding and ser hints.
360  *
361  * @details
362  * Resolution order:
363  * -# Use @p schema_type when it is already known.
364  * -# Otherwise infer from @p encoding.
365  * -# Otherwise infer from @p ser_type.
366  *
367  * @param schema_type Explicit schema family hint.
368  * @param ser_type Concrete serialisation type string.
369  * @param encoding Persisted schema encoding label.
370  * @return Best-effort schema family, or @c SchemaType::kUnknown.
371  */
372  [[nodiscard]] static SchemaType resolve_type(SchemaType schema_type, std::string_view ser_type = {},
373  std::string_view encoding = {}) noexcept;
374 };
375 
376 /**
377  * @struct Version
378  * @brief Semantic version number with comparison and string-conversion helpers.
379  *
380  * @details
381  * Used by @c NodeImpl::check_version() to compare a build-time application
382  * version against the live VLink library version. All components default to
383  * @c -1, which marks the value as not yet set.
384  */
385 struct VLINK_EXPORT Version final {
386  int major{-1}; ///< Major version number; @c -1 when unset.
387  int minor{-1}; ///< Minor version number; @c -1 when unset.
388  int patch{-1}; ///< Patch version number; @c -1 when unset.
389 
390  /**
391  * @brief Tests equality with @p target.
392  *
393  * @param target Version to compare against.
394  * @return @c true when major, minor and patch all match.
395  */
396  [[nodiscard]] bool operator==(const Version& target) const noexcept;
397 
398  /**
399  * @brief Tests inequality with @p target.
400  *
401  * @param target Version to compare against.
402  * @return Logical negation of @c operator==.
403  */
404  [[nodiscard]] bool operator!=(const Version& target) const noexcept;
405 
406  /**
407  * @brief Reports whether this version is strictly older than @p target.
408  *
409  * @details
410  * Numeric ordering: compares major first, then minor, then patch.
411  *
412  * @param target Version to compare against.
413  * @return @c true when this version is less than @p target.
414  */
415  [[nodiscard]] bool operator<(const Version& target) const noexcept;
416 
417  /**
418  * @brief Reports whether this version is strictly newer than @p target.
419  *
420  * @param target Version to compare against.
421  * @return @c true when this version is greater than @p target.
422  */
423  [[nodiscard]] bool operator>(const Version& target) const noexcept;
424 
425  /**
426  * @brief Parses a version string in @c "major.minor.patch" form.
427  *
428  * @details
429  * Each component is decoded with @c std::from_chars; components missing
430  * from @p version_str retain the @c -1 sentinel.
431  *
432  * @param version_str Source string such as @c "2.1.0".
433  * @return Parsed @c Version.
434  */
435  [[nodiscard]] static Version from_string(const std::string& version_str) noexcept;
436 
437  /**
438  * @brief Serialises this version back to a @c "major.minor.patch" string.
439  *
440  * @return Formatted version string, e.g. @c "2.1.0".
441  */
442  [[nodiscard]] std::string to_string() const noexcept;
443 
444  /**
445  * @brief Returns whether every component has been set to a non-negative value.
446  *
447  * @return @c true when the version has been parsed or assigned explicitly.
448  */
449  [[nodiscard]] bool is_valid() const noexcept;
450 };
451 
452 ////////////////////////////////////////////////////////////////
453 /// Details
454 ////////////////////////////////////////////////////////////////
455 
456 constexpr SchemaType SchemaData::infer_ser_type(std::string_view ser_type) noexcept {
457  constexpr auto kHasPrefixFunction = [](std::string_view value, std::string_view prefix) noexcept {
458  if VUNLIKELY (prefix.size() > value.size()) {
459  return false;
460  }
461 
462  for (size_t i = 0; i < prefix.size(); ++i) {
463  if (value[i] != prefix[i]) {
464  return false;
465  }
466  }
467 
468  return true;
469  };
470 
471  if (kHasPrefixFunction(ser_type, "vlink::zerocopy::")) {
472  return SchemaType::kZeroCopy;
473  }
474 
475  if (ser_type == "raw" || ser_type == "string" || ser_type == "std::string" || ser_type == "text" ||
476  ser_type == "json" || ser_type == "application/json" || ser_type == "text/json") {
477  return SchemaType::kRaw;
478  }
479 
480  return SchemaType::kUnknown;
481 }
482 
483 } // namespace vlink
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLINK_EXPORT
Definition: macros.h:81