CARLA
Loading...
Searching...
No Matches
CycloneDDSSertype.h
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB).
2// This work is licensed under the terms of the MIT license.
3// For a copy, see <https://opensource.org/licenses/MIT>.
4
5#pragma once
6
7// Pure fragment-gather logic with no CycloneDDS dependency; also compiled
8// under CARLA_ROS2_MIDDLEWARE_TESTING so unit tests cover it.
9#if defined(CARLA_ROS2_MIDDLEWARE_CYCLONEDDS) || defined(CARLA_ROS2_MIDDLEWARE_TESTING)
10
11#include <cstddef>
12#include <cstdint>
13#include <cstring>
14
15namespace carla {
16namespace ros2 {
17
18/// Gather a fragmented receive chain into a contiguous CDR buffer.
19///
20/// Each chain node covers the byte range [min, maxp1) of the serialized
21/// sample (including the 4-byte encapsulation header). The defragmenter
22/// guarantees the chain covers [0, size) in order, but consecutive fragments
23/// may overlap; bytes already gathered are skipped and ranges are clamped to
24/// @p size. Mirrors the fragchain walk of ddsi_serdata_default.
25///
26/// @param dest Destination buffer of at least @p size bytes
27/// @param size Total serialized sample size
28/// @param fragchain Head of the fragment chain (nextfrag-linked)
29/// @param payload_of Callable mapping a chain node to its payload bytes
30/// (the bytes for [min, maxp1))
31/// @return bytes written — equal to @p size for a complete chain
32template <typename Frag, typename PayloadOf>
33inline size_t carla_cdr_gather_fragments(
34 uint8_t* dest,
35 size_t size,
36 const Frag* fragchain,
37 PayloadOf payload_of)
38{
39 size_t off = 0u;
40 for (const Frag* f = fragchain; f != nullptr; f = f->nextfrag) {
41 const size_t min = static_cast<size_t>(f->min);
42 const size_t maxp1 = (static_cast<size_t>(f->maxp1) > size)
43 ? size
44 : static_cast<size_t>(f->maxp1);
45 if (maxp1 <= off || min > off) { continue; }
46 const uint8_t* payload = payload_of(f);
47 memcpy(dest + off, payload + (off - min), maxp1 - off);
48 off = maxp1;
49 }
50 return off;
51}
52
53} // namespace ros2
54} // namespace carla
55
56#endif // CARLA_ROS2_MIDDLEWARE_CYCLONEDDS || CARLA_ROS2_MIDDLEWARE_TESTING
57
58// Everything below is only meaningful when building the CycloneDDS
59// middleware target. CARLA_ROS2_MIDDLEWARE_TESTING suppresses DDS includes
60// for unit tests.
61#ifndef CARLA_ROS2_MIDDLEWARE_TESTING
62#ifdef CARLA_ROS2_MIDDLEWARE_CYCLONEDDS
63
64#include <cstdint>
65#include <cstdlib>
66#include <cstring>
67
68#include <dds/dds.h>
69#include <dds/ddsi/ddsi_serdata.h>
70#include <dds/ddsi/ddsi_sertype.h>
71
72namespace carla {
73namespace ros2 {
74
75// ============================================================
76// Data structures
77// ============================================================
78
79/// Custom serdata for raw CDR passthrough.
80///
81/// CDR bytes (including the 4-byte DDS encapsulation header) are stored
82/// inline immediately after this struct in the same allocation. Access them
83/// via carla_cdr_data() / carla_cdr_size().
84struct carla_cdr_serdata {
85 struct ddsi_serdata sd; ///< Base — must be first member (C struct embedding)
86 uint32_t cdr_size;
87 // CDR bytes follow: reinterpret_cast<uint8_t*>(this + 1)
88};
89
90/// Custom sertype for raw CDR passthrough.
91///
92/// Stateless — all per-topic identity comes from the type_name stored in
93/// the base ddsi_sertype.
94struct carla_cdr_sertype {
95 struct ddsi_sertype c; ///< Base — must be first member (C struct embedding)
96};
97
98// CDR encoding requires 4-byte alignment for multi-byte primitives.
99// The CDR bytes follow immediately after carla_cdr_serdata in the allocation,
100// so the struct size must be a multiple of 4 to preserve alignment.
101static_assert(sizeof(struct carla_cdr_serdata) % 4 == 0,
102 "carla_cdr_serdata size must be 4-byte aligned for CDR encoding");
103
104// ============================================================
105// Public accessor helpers (inline — trivial, used by pub/sub)
106// ============================================================
107
108/// Return a pointer to the inline CDR bytes (including the 4-byte header).
109inline const uint8_t* carla_cdr_data(const struct ddsi_serdata* sd) {
110 const struct carla_cdr_serdata* csd =
111 reinterpret_cast<const struct carla_cdr_serdata*>(sd);
112 return reinterpret_cast<const uint8_t*>(csd + 1);
113}
114
115/// Return the total CDR byte count (including the 4-byte encapsulation header).
116inline uint32_t carla_cdr_size(const struct ddsi_serdata* sd) {
117 return reinterpret_cast<const struct carla_cdr_serdata*>(sd)->cdr_size;
118}
119
120// ============================================================
121// Declarations — defined once in CycloneDDSSertype.cpp
122// ============================================================
123
124/// Ops tables — defined in CycloneDDSSertype.cpp so that every translation
125/// unit sees the same object address. CycloneDDS compares ops table pointers
126/// (not just the equal() callback) when checking sertype compatibility.
127extern const struct ddsi_serdata_ops carla_cdr_serdata_ops;
128extern const struct ddsi_sertype_ops carla_cdr_sertype_ops;
129
130/// Allocate a carla_cdr_serdata with cdr_size bytes of inline storage.
131/// Defined in CycloneDDSSertype.cpp.
132extern struct ddsi_serdata* carla_cdr_alloc_serdata(
133 const struct ddsi_sertype* type,
134 enum ddsi_serdata_kind kind,
135 uint32_t cdr_size);
136
137/// Return the single shared CycloneDDS participant for this process.
138///
139/// DDS best practice is one participant per application. All CARLA publishers
140/// and subscribers share this participant so that:
141/// - Sertype interning across topics with the same type name is correct.
142/// - No cascading dds_delete() from individual pub/sub destructors can
143/// invalidate sertypes that other endpoints are still using.
144///
145/// The participant is created lazily on the first call (C++11 function-local
146/// static, thread-safe) and lives until process exit.
147extern dds_entity_t carla_cdr_get_participant();
148
149// ============================================================
150// Public API (inline — reference extern declarations above)
151// ============================================================
152
153/// Create a CycloneDDS topic that transports raw CDR bytes.
154///
155/// Allocates and registers a carla_cdr_sertype for the given type_name, then
156/// creates the topic. On return, @p registered_sertype holds the actual
157/// registered sertype pointer (which may differ from the newly allocated one
158/// if CycloneDDS interned an identical pre-existing sertype). The caller must
159/// NOT free this pointer — its lifetime is managed by the DDS participant.
160///
161/// @param participant CycloneDDS participant entity
162/// @param topic_name DDS topic name
163/// @param type_name DDS type name (used for endpoint matching)
164/// @param registered_sertype Out-parameter: receives the live sertype pointer
165/// @return topic entity on success, or a negative dds_return_t on failure
166inline dds_entity_t carla_cdr_create_topic(
167 dds_entity_t participant,
168 const char* topic_name,
169 const char* type_name,
170 struct ddsi_sertype** registered_sertype)
171{
172 struct carla_cdr_sertype* st =
173 reinterpret_cast<struct carla_cdr_sertype*>(
174 malloc(sizeof(struct carla_cdr_sertype)));
175 if (!st) { return static_cast<dds_entity_t>(DDS_RETCODE_OUT_OF_RESOURCES); }
176 ddsi_sertype_init_flags(
177 &st->c,
178 type_name,
179 &carla_cdr_sertype_ops,
180 &carla_cdr_serdata_ops,
181 DDSI_SERTYPE_FLAG_TOPICKIND_NO_KEY);
182 *registered_sertype = &st->c;
183 dds_entity_t topic = dds_create_topic_sertype(
184 participant, topic_name, registered_sertype,
185 nullptr, nullptr, nullptr);
186 if (topic < 0) {
187 // dds_create_topic_sertype takes ownership only on success.
188 // On failure, release the sertype we allocated to avoid a leak.
189 ddsi_sertype_fini(&st->c);
190 free(st);
191 *registered_sertype = nullptr;
192 }
193 return topic;
194}
195
196/// Wrap pre-serialized CDR bytes in a ddsi_serdata for use with dds_writecdr().
197///
198/// The returned serdata has refcount 1. Pass it to dds_writecdr(); CycloneDDS
199/// will call ddsi_serdata_unref() after the write completes.
200///
201/// @param type The registered sertype from carla_cdr_create_topic()
202/// @param cdr_data CDR bytes including the 4-byte DDS encapsulation header
203/// @param cdr_size Total byte count
204/// @return new ddsi_serdata*, or nullptr on allocation failure
205inline struct ddsi_serdata* carla_cdr_wrap(
206 const struct ddsi_sertype* type,
207 const uint8_t* cdr_data,
208 uint32_t cdr_size)
209{
210 struct ddsi_serdata* sd = carla_cdr_alloc_serdata(type, SDK_DATA, cdr_size);
211 if (!sd) { return nullptr; }
212 uint8_t* dest = reinterpret_cast<uint8_t*>(
213 reinterpret_cast<struct carla_cdr_serdata*>(sd) + 1);
214 memcpy(dest, cdr_data, static_cast<size_t>(cdr_size));
215 return sd;
216}
217
218} // namespace ros2
219} // namespace carla
220
221#endif // CARLA_ROS2_MIDDLEWARE_CYCLONEDDS
222#endif // !CARLA_ROS2_MIDDLEWARE_TESTING
double min(double v1, double v2)
Definition Simplify.h:285
This file contains definitions of common data structures used in traffic manager.
static double sd[6]
Definition odrSpiral.cpp:52