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