ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
dds.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file dds.h
6 * @brief The DDSI-RTPS Message framing codec (PROTOCORE_ENABLE_DDS).
7 *
8 * Governing standard: OMG "The Real-time Publish-Subscribe Protocol DDS Interoperability Wire
9 * Protocol (DDSI-RTPS) Specification" version 2.5, OMG document formal/2022-04-01. DDS and its
10 * wire protocol are OMG specifications, not IETF ones, so no RFC governs them and none is cited.
11 *
12 * DDSI-RTPS sec 8.3.3: a Message is a fixed-size leading Header followed by a variable number of
13 * Submessages, and each Submessage is a SubmessageHeader followed by SubmessageElements.
14 *
15 * Header, 20 octets (sec 9.4.4) 'R' 'T' 'P' 'S' | version 2 | vendorId 2 | guidPrefix 12
16 * SubmessageHeader, 4 (sec 9.4.5.1) submessageId 1 | flags 1 | octetsToNextHeader 2
17 *
18 * sec 9.4.5.1 maps the EndiannessFlag onto the least-significant bit of flags, E = flags & 0x01,
19 * with E=0 big-endian and E=1 little-endian, and octetsToNextHeader is a CDR ushort in that order.
20 *
21 * sec 9.4.1: a Message carries no length of its own, the transport supplies it. Over UDP that
22 * length is the UDP payload length.
23 *
24 * This module is the Message and SubmessageHeader framing. The Submessage contents (a Data
25 * Submessage's serializedPayload, a Heartbeat's SequenceNumber set, the SPDP and SEDP discovery
26 * topics) layer on top of it.
27 *
28 * The module exports one symbol, @ref Rtps. Everything in dds.c has internal linkage.
29 *
30 * @author Douglas Quigg (dstroy0)
31 * @date 2026
32 */
33
34#ifndef PROTOCORE_DDS_H
35#define PROTOCORE_DDS_H
36
37#include "protocore_config.h" // the entry point: protocore_types.h for the widths
38
39#if PROTOCORE_ENABLE_DDS
40
42
43/** @brief DDSI-RTPS sec 9.4.5.1 SubmessageKind: the submessageId octet of a SubmessageHeader. */
44#define RTPS_SM_PAD 0x01
45#define RTPS_SM_ACKNACK 0x06
46#define RTPS_SM_HEARTBEAT 0x07
47#define RTPS_SM_GAP 0x08
48#define RTPS_SM_INFO_TS 0x09
49#define RTPS_SM_INFO_SRC 0x0c
50#define RTPS_SM_INFO_REPLY_IP4 0x0d
51#define RTPS_SM_INFO_DST 0x0e
52#define RTPS_SM_INFO_REPLY 0x0f
53#define RTPS_SM_DATA 0x15
54#define RTPS_SM_DATA_FRAG 0x16
55#define RTPS_FLAG_ENDIAN 0x01 ///< EndiannessFlag 'E', flags bit 0: E=1 little-endian (sec 9.4.5.1).
56#define RTPS_HEADER_LEN 20 ///< Header: magic 4 + version 2 + vendorId 2 + guidPrefix 12 (sec 9.4.4).
57#define RTPS_GUIDPREFIX_LEN 12 ///< GuidPrefix_t is 12 octets (sec 9.3.1.1).
58
59/**
60 * @brief One Submessage a parse surfaces: its SubmessageHeader fields and its contents.
61 *
62 * @param submessage_id the SubmessageKind octet (sec 9.4.5.1)
63 * @param flags the 8 SubmessageFlags, bit 0 the EndiannessFlag
64 * @param contents the Submessage contents, NULL when there are none
65 * @param contents_len how many octets they run
66 * @param arg the caller's pointer, handed back untouched
67 */
68typedef void (*protocore_rtps_submessage_cb)(uint8_t submessage_id, uint8_t flags, const uint8_t *contents,
69 size_t contents_len, void *arg);
70
71/** @brief sec 8.3.3.1 Table 8.14: the Header fields a build stamps, less the protocol and version. */
72typedef struct
73{
74 const uint8_t *guid_prefix; ///< guidPrefix, the 12-octet default GUID prefix for the Message
75 const uint8_t *vendor_id; ///< vendorId, 2 octets; VENDORID_UNKNOWN is {0, 0} (sec 9.3.2.1)
76} RtpsHeaderArgs;
77
78/** @brief sec 8.3.3.3 Table 8.16: one Submessage, its SubmessageHeader fields and its contents. */
79typedef struct
80{
81 const uint8_t *contents; ///< the Submessage contents, NULL only when contents_len is 0
82 uint16_t contents_len; ///< their length, written as octetsToNextHeader (sec 9.4.5.1)
83 uint8_t submessage_id; ///< the SubmessageKind octet, one of RTPS_SM_*
84 uint8_t flags; ///< the 8 SubmessageFlags; OR RTPS_FLAG_ENDIAN for little-endian
85} RtpsSubmessageArgs;
86
87/** @brief Where a build lays its octets down. */
88typedef struct
89{
90 uint8_t *buf; ///< the buffer a build writes into
91 size_t cap; ///< how much room it has
92} RtpsOutArgs;
93
94/** @brief The Message a parse walks, its length supplied by the transport (sec 9.4.1). */
95typedef struct
96{
97 const uint8_t *msg; ///< the whole Message, Header first
98 size_t len; ///< its octet count, over UDP the payload length
99} RtpsMessageArgs;
100
101/** @brief Where a parse surfaces each Submessage it walks. */
102typedef struct
103{
104 protocore_rtps_submessage_cb on_submessage; ///< called once per Submessage, NULL to only validate
105 void *arg; ///< handed back to it untouched
106} RtpsSinkArgs;
107
108/**
109 * @brief The DDSI-RTPS Message framing codec.
110 *
111 * A caller sets the members a call takes, invokes it through ::Rtps, and reads the outcome off the
112 * same handle.
113 *
114 * No slot member: the codec keeps no rows, so no call names one.
115 *
116 * @var RtpsNs::hdr the Header fields a header stamps (sec 8.3.3.1)
117 * @var RtpsNs::sub the SubmessageHeader fields and contents a submessage writes (sec 8.3.3.3)
118 * @var RtpsNs::out the buffer a header or a submessage writes into
119 * @var RtpsNs::msg the Message a parse walks (sec 9.4.1)
120 * @var RtpsNs::sink where a parse surfaces each Submessage
121 * @var RtpsNs::ok a parse's verdict: the Header is valid and every Submessage fits
122 * @var RtpsNs::n the octets a header or a submessage wrote, 0 when it did not fit
123 * @var RtpsNs::header build the 20-octet Header into @c out (sec 9.4.4)
124 * @var RtpsNs::submessage build one SubmessageHeader and its contents into @c out (sec 9.4.5.1)
125 * @var RtpsNs::parse validate the Header and walk the Submessages, surfacing each to @c sink
126 */
127typedef struct
128{
129 RtpsHeaderArgs hdr; ///< what a Header stamps
130 RtpsSubmessageArgs sub; ///< what one Submessage says
131 RtpsOutArgs out; ///< where a build lands
132 RtpsMessageArgs msg; ///< what a parse walks
133 RtpsSinkArgs sink; ///< where a parse reports
134 proto_bool ok;
135 size_t n;
136} RtpsVars;
137
138/** @brief The operands and the outcome. */
139extern RtpsVars RtpsV;
140
141/** @brief The entries. */
142typedef struct
143{
144 void (*const header)(uint8_t *work);
145 void (*const submessage)(uint8_t *work);
146 void (*const parse)(uint8_t *work);
147} RtpsNs;
148
149// What the table binds, defined once in the .c and taking one parameter each: everything
150// else an entry needs is an operand in RtpsV or a region of the borrow at a fixed offset.
151void protocore_rtps_header(uint8_t *work);
152void protocore_rtps_submessage(uint8_t *work);
153void protocore_rtps_parse(uint8_t *work);
154
155// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
156// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
157// `Rtps.header(work)` resolves to a named function and becomes a DIRECT call. An extern table
158// leaves the call indirect and the symbol live at every level, -O2 -flto included.
159static const RtpsNs Rtps __attribute__((unused)) = {
160 .header = protocore_rtps_header,
161 .submessage = protocore_rtps_submessage,
162 .parse = protocore_rtps_parse,
163};
164
165/** @brief The protocol version a built Header stamps, major then minor (sec 8.3.3.1). */
166extern const uint8_t RTPS_VERSION[2];
167
169
170#endif // PROTOCORE_ENABLE_DDS
171
172#endif // PROTOCORE_DDS_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97