1#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
6#![allow(unsafe_op_in_unsafe_fn, clippy::missing_safety_doc)]
7#[doc = " Status code. Zero on success.\n\n Phase 376 W3.d step B — the VALUES are upstream rmw's. `RMW_RET_OK` was\n already 0 on both sides; everything else moved from a negative code to\n upstream's positive one, so a status means the same number on both sides of\n the seam: OK 0, ERROR 1, TIMEOUT 2, UNSUPPORTED 3, BAD_ALLOC 10,\n INVALID_ARGUMENT 11, INCORRECT_RMW_IMPLEMENTATION 12,\n NODE_NAME_NON_EXISTENT 203.\n\n (Written as prose, not an indented block: bindgen copies this comment into\n `generated.rs` verbatim, and rustdoc reads an indented block there as a Rust\n DOCTEST — which then fails to compile. Caught by `cargo test`.)\n\n This is why step A had to come first. Eleven slots used to return a COUNT\n or a FLAG as a non-negative value and a status as a negative one; with\n `ERROR` at 1, a return of `1` would have meant both \"one message\" and\n \"failed\". Every one of those slots now reports through an out-parameter, so\n the sign carries nothing and the numbers are free to move.\n\n Codes upstream does not define live in the EXTENSION RANGE at 1000+, so a\n future upstream addition can never collide with one of ours. That range is\n the one place we knowingly add to upstream's namespace.\n\n Signedness is kept (`int32_t`, not an unsigned type) to match upstream's\n `rmw_ret_t` exactly. Nothing returns a negative value any more."]
10pub type rmw_ret_t = i32;
11#[doc = " Nanoseconds since a clock's epoch — upstream's `rmw_time_point_value_t`."]
12pub type rmw_time_point_value_t = i64;
13#[doc = " Visit one network flow endpoint. Return `false` to stop.\n\n Upstream fills an ALLOCATING `rmw_network_flow_endpoint_array_t` through an\n `rcutils_allocator_t *`. There is no allocator at this seam and the flow\n count is a property of the OS's routing, not of anything the caller can\n size in advance — so it streams, exactly like the graph slots."]
14pub type rmw_network_flow_endpoint_visit_fn = ::core::option::Option<
15 unsafe extern "C" fn(
16 ctx: *mut core::ffi::c_void,
17 endpoint: *const rmw_network_flow_endpoint_t,
18 ) -> bool,
19>;
20#[doc = " Visit a subscription's content filter. Return value ignored: there is\n exactly one filter per subscription, so this is a callback only to avoid\n handing back an allocated `rmw_subscription_content_filter_options_t`.\n\n `expression` and every `parameters[i]` are BORROWED for the call. A\n subscription with no filter is reported as `expression == NULL`, which is\n what upstream's empty options struct means."]
21pub type rmw_content_filter_visit_fn = ::core::option::Option<
22 unsafe extern "C" fn(
23 ctx: *mut core::ffi::c_void,
24 expression: *const core::ffi::c_char,
25 parameters: *const *const core::ffi::c_char,
26 parameter_count: usize,
27 ),
28>;
29pub type rmw_status_event_callback_t = ::core::option::Option<
30 unsafe extern "C" fn(
31 kind: rmw_event_type_t::Type,
32 payload: *const rmw_event_payload_t,
33 user_context: *mut core::ffi::c_void,
34 ),
35>;
36#[doc = " Upstream `rmw_event_callback_t` — the callback the `set_on_new_*` slots\n install. Distinct from `rmw_status_event_callback_t` above, which is the DDS\n STATUS-event callback; upstream binds this name to this shape and we now\n match it."]
37pub type rmw_event_callback_t = ::core::option::Option<
38 unsafe extern "C" fn(user_data: *const core::ffi::c_void, number_of_events: usize),
39>;
40#[doc = " Visit one node. `enclave` is NULL where the backend does not track one —\n which is what lets a single slot answer both `rmw_get_node_names` and\n `rmw_get_node_names_with_enclaves`. Return `false` to stop."]
41pub type rmw_node_visit_fn = ::core::option::Option<
42 unsafe extern "C" fn(
43 ctx: *mut core::ffi::c_void,
44 node_name: *const core::ffi::c_char,
45 node_namespace: *const core::ffi::c_char,
46 enclave: *const core::ffi::c_char,
47 ) -> bool,
48>;
49#[doc = " Visit one name and the types on it. `types_count` may legitimately be 0 on a\n partially discovered graph — reporting the name without a type beats\n dropping it. Return `false` to stop."]
50pub type rmw_names_and_types_visit_fn = ::core::option::Option<
51 unsafe extern "C" fn(
52 ctx: *mut core::ffi::c_void,
53 name: *const core::ffi::c_char,
54 types: *const *const core::ffi::c_char,
55 types_count: usize,
56 ) -> bool,
57>;
58#[doc = " Visit one discovered endpoint. Return `false` to stop."]
59pub type rmw_topic_endpoint_info_visit_fn = ::core::option::Option<
60 unsafe extern "C" fn(
61 ctx: *mut core::ffi::c_void,
62 info: *const rmw_topic_endpoint_info_t,
63 ) -> bool,
64>;
65#[doc = " Runtime-pluggable custom transport. The runtime never\n dereferences `user_data`; it's the caller's per-transport\n context, threaded back into every callback's first argument.\n\n THIS declaration is the ABI single source of truth (RFC-0054): Rust\n consumes the committed bindgen output of this header, and\n `nros_rmw::NrosTransportOps` is the hand-written Rust-side view kept in\n lockstep with it — not the other way round. The previous wording had that\n backwards (issue 0331). Layout equivalence is asserted on both sides: see\n `nros_transport_ops_t` in `nros-rmw-cffi/tests/c_stubs/abi_layout_check.c`\n and the `const _` size/align block beside\n `nros_rmw_cffi_set_custom_transport` in `nros-rmw-cffi/src/lib.rs`. Same\n layout, same threading contract, same return codes."]
66pub type nros_transport_ops_t = nros_transport_ops_s;
67#[doc = " Global identifier for a publisher — upstream `rmw_gid_t`, field for field.\n\n Phase 376 W4. Mirrors upstream exactly, including the 24-byte width and the\n `implementation_identifier`. The identifier matters MORE here than upstream:\n `nros_rmw_cffi_register_named` admits several backends in one image, so two\n gids are comparable only when it matches.\n\n Comparison is over the whole array, so a producer MUST zero-pad an\n identifier shorter than 24 bytes rather than leave the tail undefined —\n otherwise two gids naming the same entity compare unequal on stack garbage.\n\n **One type and one value.** A gid obtained from a take\n (`rmw_message_info_t::publisher_gid`) and one obtained from\n `get_gid_for_publisher` are the same width (the phase-467 RMW gap-closure\n design study's Q1(a)) and, since issue 1495, produced from ONE source on\n every backend that produces either: Cyclone's writer GUID on both sides\n (`take_with_info`); zenoh's XXH3-128 of the entity's liveliness keyexpr,\n which is `rmw_zenoh_cpp`'s own derivation and so also the gid a stock\n graph query prints for the entity. */\n/** Bytes to READ — phase-406 W2.\n\n `len` is a FACT: how many bytes exist. Nothing is written through this, and\n `const` says so — `publish` handing a backend a mutable pointer is an\n invitation.\n\n PASSED BY VALUE. Two words, and there is nothing to report back."]
68#[repr(C)]
69#[derive(Debug, Copy, Clone)]
70pub struct rmw_byte_span_t {
71 pub data: *const u8,
72 pub len: usize,
73}
74#[doc = " Room to WRITE — phase-406 W2.\n\n This is upstream's `rmw_serialized_message_t` MINUS THE ALLOCATOR. That is\n not a coincidence and it is the whole argument: upstream's is an\n `rcutils_uint8_array_t`, `{buffer, buffer_length, buffer_capacity,\n allocator}`, and this ABI declined it because of the last field. Drop that\n field and the remaining three are exactly what a caller-owned destination\n needs, so the \"carries an allocator\" objection does not transfer to this.\n\n `capacity` is a LIMIT (in) and `len` is a RESULT (out). They are separate\n fields rather than one overloaded `len` because \"capacity on the way in,\n length on the way out\" is the `snprintf` ambiguity, and it is a bug\n generator.\n\n PASSED BY POINTER, always. The callee must set `len`, and a by-value copy\n would discard it — code that compiles, runs, and yields zero-length\n messages."]
75#[repr(C)]
76#[derive(Debug, Copy, Clone)]
77pub struct rmw_mut_byte_span_t {
78 pub data: *mut u8,
79 #[doc = " In: bytes available at `data`."]
80 pub capacity: usize,
81 #[doc = " Out: bytes actually written.\n\n On `NROS_RMW_RET_BUFFER_TOO_SMALL` from any of the three TAKE slots —\n the subscription `take` (issue 1612), the service `take_request` and\n the client `take_response` (issue 1632) — it is the size the refused\n sample, request or reply NEEDED, or `NROS_RMW_TAKE_LEN_UNKNOWN` when\n the backend cannot know it. The caller pre-sets it to that value on\n all three. Undefined on every other failure."]
82 pub len: usize,
83}
84#[doc = " A message type's identity — phase-406 W1.\n\n Upstream passes `const rosidl_message_type_support_t *`: a runtime-dispatch\n handle carrying `{typesupport_identifier, data, func}`, where `func` walks a\n type description at run time. This ABI resolves types at BUILD time, so\n there is nothing for `func` to do and the handle's contents do not cross.\n\n What DOES cross is the identity, and it was crossing as two loose\n `const char *` wedged between `topic_name` and `qos` — so the argument order\n did not even line up with upstream's, and every create slot took two\n arguments where ROS 2 takes one. Grouping them costs nothing: codegen emits\n one `static const rmw_message_type_support_t` per type, exactly as\n `ROSIDL_GET_MSG_TYPE_SUPPORT(...)` already hands back a pointer to a static.\n\n NAMED `rmw_`, NOT `rosidl_`, and not `nros_`. This ABI is a standard\n interface, so a vendor prefix would say the interface is ours — but\n `rosidl_message_type_support_t` belongs to `rosidl_runtime_c`, a package we\n do not implement, and redefining it would collide with a host build that\n legitimately has it in scope. Reusing `rmw_publisher_t` is safe because we\n ARE the rmw implementation and own that name; `rmw_` is ours to spend and\n neutral to a reader.\n\n Future type-carried data (a serialize/deserialize pair, a bounded-size hint)\n lands here without changing any slot's arity again — which matters, because\n appending to a hand-mirrored FFI struct is what `check-ffi-struct-mirrors`\n exists for."]
85#[repr(C)]
86#[derive(Debug, Copy, Clone)]
87pub struct rmw_message_type_support_t {
88 #[doc = " Fully-qualified ROS type, e.g. `\"std_msgs/msg/String\"`. Borrowed;\n must outlive every entity created with it, which a codegen `static`\n satisfies by construction."]
89 pub type_name: *const core::ffi::c_char,
90 #[doc = " RIHS type hash, e.g. `\"RIHS01_...\"`, or NULL where the backend does not\n carry one. NULL is \"not supplied\", never \"empty\"."]
91 pub type_hash: *const core::ffi::c_char,
92}
93#[doc = " A service type's identity. See @ref rmw_message_type_support_t.\n\n Separate from the message form for the reason upstream separates them:\n `rosidl_service_type_support_t` and `rosidl_message_type_support_t` are\n distinct types there, and collapsing them here would let a service type be\n passed where a message type is required with no diagnostic."]
94#[repr(C)]
95#[derive(Debug, Copy, Clone)]
96pub struct rmw_service_type_support_t {
97 #[doc = " Fully-qualified ROS service type, e.g. `\"example_interfaces/srv/AddTwoInts\"`."]
98 pub type_name: *const core::ffi::c_char,
99 #[doc = " RIHS type hash, or NULL."]
100 pub type_hash: *const core::ffi::c_char,
101}
102#[repr(C)]
103#[derive(Debug, Copy, Clone)]
104pub struct rmw_gid_t {
105 #[doc = " Which backend produced this gid; gids from different backends are not\n comparable. Borrowed, static for the life of the image."]
106 pub implementation_identifier: *const core::ffi::c_char,
107 #[doc = " The identifier bytes, zero-padded to the full width."]
108 pub data: [u8; 24usize],
109}
110#[doc = " Per-sample metadata — upstream `rmw_message_info_t`, field for field.\n\n Phase 376 W4. Two channels carry this metadata to Rust callers today. A\n backend that fills `take_with_info` reports it here, in caller-owned\n storage, and the runtime dispatches that slot whenever it is non-NULL\n (issue 1495 — before that nothing called it, and a C/C++ backend such as\n Cyclone delivered every sample with no metadata at all). The Rust adapter\n instead writes `MESSAGE_INFO_TABLE`, a side table in `nros-rmw-cffi` keyed\n on the subscription's `backend_data` ADDRESS. That table is a workaround,\n not a design: it claims a pool slot per subscription and never releases\n it, so a reused handle address inherits the previous subscription's\n metadata.\n\n Passing the struct by pointer on the take call — which is what upstream does\n — removes all of that: the caller owns the storage, it lives exactly as long\n as the call, and every backend can fill it.\n\n Retiring the side table is NOT part of this change; the `take_with_info`\n slots are the mechanism that makes retiring it possible."]
111#[repr(C)]
112#[derive(Debug, Copy, Clone)]
113pub struct rmw_message_info_t {
114 #[doc = " Publisher's clock at publication, ns. 0 = no source timestamp."]
115 pub source_timestamp: rmw_time_point_value_t,
116 #[doc = " Subscriber's clock at reception, ns. 0 = receptions are not stamped."]
117 pub received_timestamp: rmw_time_point_value_t,
118 #[doc = " Publisher-side sequence, or\n `RMW_MESSAGE_INFO_SEQUENCE_NUMBER_UNSUPPORTED`. Whether this is real is\n what `feature_supported` answers."]
119 pub publication_sequence_number: u64,
120 #[doc = " Subscriber-side reception count, or the same sentinel."]
121 pub reception_sequence_number: u64,
122 #[doc = " Which publisher sent it. All-zero `data` = unknown."]
123 pub publisher_gid: rmw_gid_t,
124 #[doc = " True when the sample never left the image (Zephyr's\n `Z_FEATURE_LOCAL_SUBSCRIBER`, DDS intra-process)."]
125 pub from_intra_process: bool,
126}
127#[doc = " Identity of one service request — upstream `rmw_request_id_t`, field for\n field.\n\n `sequence_number` is the number the CLIENT assigned to the request, which\n together with `writer_guid` makes the pair unique across clients. It is not\n the backend's reply-correlation token (`take_request_with_info`'s\n `seq_out`), and the two need not agree."]
128#[repr(C)]
129#[derive(Debug, Copy, Clone)]
130pub struct rmw_request_id_t {
131 #[doc = " The requesting client's identity. All zero = not reported."]
132 pub writer_guid: [i8; 16usize],
133 #[doc = " The client-assigned sequence number of this request."]
134 pub sequence_number: i64,
135}
136#[doc = " Per-request metadata — upstream `rmw_service_info_t`, field for field.\n\n Issue 2081. Filled by `take_request_with_info` for a request and, since\n issue 2136, by `take_response_with_info` for a reply. As in\n `rmw_message_info_t`, a field the backend cannot report is ZERO: a\n timestamp of 0 means \"not stamped\", and an all-zero\n `request_id.writer_guid` means the request id is not reported. The runtime\n turns each into `None`, never into a value.\n\n The timestamps are the SENDER's and the RECEIVER's, as upstream's are: for\n a request, the client's send time and the server's arrival time; for a\n reply, the server's send time and the client's arrival time."]
137#[repr(C)]
138#[derive(Debug, Copy, Clone)]
139pub struct rmw_service_info_t {
140 #[doc = " Sender's clock when the message was sent, ns. 0 = not reported."]
141 pub source_timestamp: rmw_time_point_value_t,
142 #[doc = " Receiver's clock when the message was received, ns. 0 = not reported."]
143 pub received_timestamp: rmw_time_point_value_t,
144 #[doc = " The request: who sent it, and its client-assigned sequence number."]
145 pub request_id: rmw_request_id_t,
146}
147#[doc = " Full DDS-shaped QoS profile.\n\n Matches the field set of upstream `rmw_qos_profile_t`. Backends\n advertise per-policy support via the runtime's\n `supported_qos_policies()` query; entities created with a profile\n the active backend can't honour return\n `NROS_RMW_RET_INCOMPATIBLE_QOS` synchronously at create time\n — no silent downgrade.\n\n Zero-valued fields (\"off\") preserve the cheap default for apps\n that don't request the policy:\n - `deadline_ms = 0` → infinite deadline (no check).\n - `lifespan_ms = 0` → infinite lifespan (no expiry).\n - `liveliness_kind = NONE` → no liveliness tracking.\n - `liveliness_lease_ms = 0` → infinite lease.\n\n **Boundary semantics (phase-301, issue 0241).** Durations are u32\n MILLISECONDS; that width is part of the contract:\n - `0` = unset/no-check (matches upstream `RMW_QOS_*_DEFAULT`, the\n zero time — a \"real 0-duration\" is inexpressible upstream too).\n - `NROS_RMW_DURATION_INFINITE_MS` = explicit infinite.\n - Callers lowering finer-grained times MUST round sub-ms values UP\n to 1 ms (rounding down would silently turn a real deadline into\n \"no deadline\") and MUST reject values past the u32-ms range\n (other than the infinite sentinel) at create time\n (`NROS_RMW_RET_INVALID_ARGUMENT`) — never clamp.\n\n `depth` is `uint16_t` (max 65 535). Embedded ROS application queue\n depths are typically 1–100; the 16-bit width saves two bytes per\n entity vs the upstream 32-bit choice. A requested depth the width\n cannot represent is a create-time error, never a silent saturate\n (phase-301, issue 0241).\n\n **Pure policy mirror (phase-301, issue 0240).** Transport hints\n (`tx_express`, `rx_buffer_hint`) moved OUT of this struct into\n `rmw_publisher_options_t` / `rmw_subscription_options_t` —\n the upstream `rmw_publisher_options_t` / `rmw_subscription_options_t`\n home for exactly that class. QoS carries DDS policy only; hint growth\n no longer churns this ABI."]
148#[repr(C)]
149#[derive(Debug, Copy, Clone)]
150pub struct rmw_qos_profile_t {
151 #[doc = "< @see NROS_RMW_RELIABILITY_*"]
152 pub reliability: u8,
153 #[doc = "< @see NROS_RMW_DURABILITY_*"]
154 pub durability: u8,
155 #[doc = "< @see NROS_RMW_HISTORY_*"]
156 pub history: u8,
157 #[doc = "< @see rmw_liveliness_kind_t"]
158 pub liveliness_kind: u8,
159 pub depth: u16,
160 #[doc = "< Reserved; must be zero."]
161 pub _reserved0: u16,
162 #[doc = " Subscription: max acceptable inter-arrival time, ms. Publisher:\n max acceptable inter-publish (offered rate), ms.\n 0 = infinite (no deadline)."]
163 pub deadline_ms: u32,
164 #[doc = " Sample expiry, ms. Subscription filters samples older than\n this. 0 = infinite (no expiry)."]
165 pub lifespan_ms: u32,
166 #[doc = " Liveliness lease, ms. Publisher must assert liveliness\n within this window or be considered dead. 0 = infinite."]
167 pub liveliness_lease_ms: u32,
168 #[doc = " If non-zero, topic-name encoding skips the ROS `/rt/` prefix\n and uses raw application names. Matches upstream\n `avoid_ros_namespace_conventions`. `0` = false, non-zero =\n true. (`uint8_t` instead of `bool`; `sizeof(_Bool)` is impl-\n defined per C99 — `uint8_t` keeps the layout stable across\n toolchains.)"]
169 pub avoid_ros_namespace_conventions: u8,
170 #[doc = "< Reserved; must be zero."]
171 pub _reserved1: [u8; 3usize],
172}
173#[doc = " One discovered endpoint — upstream `rmw_topic_endpoint_info_t`.\n\n Every string is BORROWED for the duration of the visit that hands this out;\n a caller that needs one past the callback copies it. That is what lets the\n graph slots stream without an allocator."]
174#[repr(C)]
175#[derive(Debug, Copy, Clone)]
176pub struct rmw_topic_endpoint_info_t {
177 #[doc = " Node that owns the endpoint."]
178 pub node_name: *const core::ffi::c_char,
179 #[doc = " That node's namespace."]
180 pub node_namespace: *const core::ffi::c_char,
181 #[doc = " Fully-qualified type on the wire, e.g. `\"std_msgs/msg/Int32\"`."]
182 pub topic_type: *const core::ffi::c_char,
183 #[doc = " Publisher or subscription."]
184 pub endpoint_type: rmw_endpoint_type_t::Type,
185 #[doc = " The endpoint's identity; `data` all-zero when the backend has none."]
186 pub endpoint_gid: rmw_gid_t,
187 #[doc = " The GRANTED profile, not the requested one — which is the whole reason a\n consumer asks. A backend that cannot read back a remote's granted QoS\n reports what it MATCHED on, and must not substitute the local entity's\n requested profile."]
188 pub qos_profile: rmw_qos_profile_t,
189}
190#[doc = " One network flow endpoint — upstream `rmw_network_flow_endpoint_t`, field\n for field. Unlike the graph structs this one carries no pointers, so it\n costs nothing to mirror exactly and a caller may copy it wholesale."]
191#[repr(C)]
192#[derive(Debug, Copy, Clone)]
193pub struct rmw_network_flow_endpoint_t {
194 pub transport_protocol: rmw_transport_protocol_t::Type,
195 pub internet_protocol: rmw_internet_protocol_t::Type,
196 pub transport_port: u16,
197 #[doc = " Publisher-side only; 0 elsewhere."]
198 pub flow_label: u32,
199 #[doc = " Differentiated Services Code Point. Publisher-side only; 0 elsewhere."]
200 pub dscp: u8,
201 pub internet_address: [core::ffi::c_char; 48usize],
202}
203#[doc = " Publisher creation options — the home for publisher-side transport\n hints (upstream: `rmw_publisher_options_t`). Passed as a NULLable\n trailing param to `create_publisher`; NULL = all defaults.\n/\n/**\n Session creation options — the home for init-time context that\n `create_session`'s flat argument list cannot grow without another ABI break\n (issue 0808). Passed as a NULLable trailing param; NULL = all defaults.\n\n The carrier question 0808 opened had two candidates: encode this behind the\n locator string, or take one options struct. The struct wins on precedent —\n `rmw_publisher_options_t` and `rmw_subscription_options_t` already solved\n exactly this problem for entities, with the same NULLable-trailing-param\n shape — and on cost: parsing config out of a locator means every backend\n reimplements a parser, which is code size on a target plus a new class of\n silent misparse. One break, then the struct grows.\n\n Of Humble's eight `rmw_init_options_t` fields this carries the two that were\n GAPS (issue 0785). `domain_id` stays a named argument because every backend\n needs it; `security_options` remains declined on the target (a DDS-SROS2\n keystore path, and there is neither a filesystem nor a security plugin\n where this ABI runs); `allocator`, `instance_id`, `impl` and\n `implementation_identifier` are answered elsewhere or declined ABI-wide.\n/\n/**\n One backend-specific session configuration property.\n\n The counterpart of `RmwConfig::properties` on the Rust side, which every\n backend already accepts and which — until phase-206 W3 — NO non-Rust caller\n could reach: the cffi adapter built `properties: &[]` and threw the options\n pointer away, so a C or C++ entry could state no transport configuration at\n all, on any platform.\n\n Backend-specific by design. For zenoh this is zenoh-pico's run-time option\n set — `zp_config_insert(config, Z_CONFIG_<X>_KEY, value)`, which upstream\n calls \"the primary configuration method\" and which has no file format for\n the pico client — with the accepted names derived from zenoh-pico's own\n `config.h` (`zpico_config_keys.h`). A backend that does not recognise a name\n MUST fail the session rather than drop it: a silently ignored configuration\n line is indistinguishable from one that took effect.\n\n Both strings are NUL-terminated and BORROWED for the duration of the\n `create_session` call."]
204#[repr(C)]
205#[derive(Debug, Copy, Clone)]
206pub struct rmw_session_property_t {
207 #[doc = "< Property name; never NULL."]
208 pub key: *const core::ffi::c_char,
209 #[doc = "< Property value; never NULL."]
210 pub value: *const core::ffi::c_char,
211}
212#[repr(C)]
213#[derive(Debug, Copy, Clone)]
214pub struct rmw_session_options_t {
215 #[doc = " Restrict discovery to this host. Upstream `rmw_localhost_only_t`,\n narrowed to a flag: 0 = the system default, non-zero = localhost only.\n\n A backend that cannot restrict discovery must IGNORE this rather than\n fail — same contract as `mode`. Cyclone is the one that can honour it."]
216 pub localhost_only: u8,
217 #[doc = "< Reserved; must be zero."]
218 pub _reserved: [u8; 7usize],
219 #[doc = " The security enclave this session belongs to, or NULL.\n\n Borrowed for the duration of the call. Carried so\n `rmw_get_node_names_with_enclaves` stops being a HOLLOW grouping: the\n visitor's `enclave` argument was structurally always NULL because\n nothing in this ABI accepted one (issue 0785). A backend that does not\n track enclaves still reports NULL, which is now a fact about the\n backend rather than about the seam."]
220 pub enclave: *const core::ffi::c_char,
221 #[doc = " Backend-specific configuration properties, or NULL.\n\n Borrowed for the duration of the call, as is every string they point\n at. `property_count` must be 0 when this is NULL, and must not exceed\n `RMW_SESSION_MAX_PROPERTIES`; a violation is\n `RMW_RET_INVALID_ARGUMENT`, not a truncation."]
222 pub properties: *const rmw_session_property_t,
223 #[doc = "< Number of entries in `properties`."]
224 pub property_count: usize,
225}
226#[repr(C)]
227#[derive(Debug, Copy, Clone)]
228pub struct rmw_publisher_options_t {
229 #[doc = " phase-279 (#145) — express hint (`TopicInfo::tx_express` across\n the C ABI): non-zero = this publisher's samples bypass transport\n tx batching. A transport hint, not a DDS policy — no RxO\n matching."]
230 pub tx_express: u8,
231 #[doc = "< Reserved; must be zero."]
232 pub _reserved: [u8; 7usize],
233}
234#[doc = " Subscription creation options — the home for subscription-side\n transport hints (upstream: `rmw_subscription_options_t`). Passed as a\n NULLable trailing param to `create_subscription`; NULL = all defaults."]
235#[repr(C)]
236#[derive(Debug, Copy, Clone)]
237pub struct rmw_subscription_options_t {
238 #[doc = " Phase 231 (RFC-0038) — receive-buffer size hint, bytes, so a\n size-classing backend (zenoh-pico) can pick a small/large receive\n buffer. `0` = unset. A transport hint, not a DDS policy.\n\n Phase 403 W1 — the paragraph above is what this field MEANS; the rules\n below are what it OBLIGES, and they are normative. They were prose in\n RFC-0005 (\"Receive-buffer sizing: what a backend is obliged to do\") and\n nowhere else, so a third party reading only this header could not tell\n whether ignoring the hint was conformant. It is.\n\n - ADVISORY. A backend MAY ignore this field entirely. A backend with a\n single receive-buffer size is conformant, and so is one that rounds\n 68 up to 1024.\n - `0` IS \"THIS CALLER SAID NOTHING\", NOT \"THIS TYPE HAS NO BOUND\".\n Every message type carries a derived upper bound — bounded in the\n `.msg` (`string<=64`) or capped in `nros-codegen.toml`, and an\n unbounded type is a BUILD ERROR rather than something that falls back\n to a configured default — so the runtime always has a number to put\n here. `0` therefore reaches a backend only from a caller that\n supplied no options at all (`options == NULL`, or a zero-filled\n struct from a hand-rolled C caller). A backend must never read it as\n a claim that the type is unbounded, and must not treat a non-zero\n hint as a tighter bound than the type's own.\n - IT IS NOT THE TAKE BUFFER. `take`'s `buf` / `buf_len` are owned by\n the RUNTIME, and `buf_len` is authoritative on every call — including\n when it disagrees with this hint, and when it changes between calls\n for the same subscription. A backend must not cache a length from\n here and write `buf` against it.\n - MUST NOT LIE. A backend MUST NOT copy a sample that does not fit the\n caller's take buffer and return `NROS_RMW_RET_OK`. A sample larger\n than `buf_len` is reported as a failure — `*taken = false` with\n `NROS_RMW_RET_BUFFER_TOO_SMALL` — never as a truncated success. The\n runtime turns that into a diagnostic naming the buffer\n (`report_dropped_take`); a silent truncation is a corrupt message\n with no symptom at all. This is the ONE guarantee the hint carries,\n and it holds whether or not the backend honoured the hint.\n - NOT A WIRE BOUND. A remote publisher is bound by the `.msg`, never by\n our config, so this is no promise about incoming sample size. A\n backend must not size a fixed structure from it in a way that makes a\n larger sample undeliverable WITHOUT a diagnostic.\n\n There is no reverse channel: the vtable's `required_rx_bytes` query\n (a backend saying what it would rather the take buffer were) was\n deleted unread — see the note where it stood in `rmw_vtable.h`."]
239 pub rx_buffer_hint: u32,
240 #[doc = " Phase-519 W3 (RFC-0074, \"On XRCE\") — the rate this subscription\n DECLARED it can take, `arrival_curve.rate_hz`, in millihertz. `0` =\n undeclared, which is what every caller that names nothing passes, so\n the field took the old `_reserved[4]` bytes and the struct is the same\n 8 bytes it was.\n\n - ADVISORY, like `rx_buffer_hint`. A backend MAY ignore it. zenoh does:\n its enforcement point 1 is the router's downsampling rule, which a\n deployment step installs, so the device has nothing to ask for.\n - The XRCE backend asks the agent for\n `max_bytes_per_second = rate_hz x rx_buffer_hint`, and only when that\n is safe to ask for: `rate_hz >= 1` (the agent's token bucket holds\n one second of the rate, and a sample bigger than the bucket stops the\n stream for good), a non-zero `rx_buffer_hint`, and a product that\n fits the wire field's `uint16_t`. Otherwise it asks for everything,\n as before, and logs why.\n - IT PACES, IT DOES NOT FILTER. The agent takes a sample and then waits\n for tokens, so what arrives is up to one period (`1 / rate_hz`) old,\n plus more for every sample the agent-side reader's history holds.\n Measured in RFC-0074: median age 104 ms at depth 1 for a 10 Hz\n declaration, against 0.2 ms unpaced."]
241 pub arrival_rate_mhz: u32,
242}
243#[doc = " Per-process RMW session — the entity returned by `vtable->create_session`.\n\n Carries the node identity (used for diagnostics + wire-level\n topic-key derivation in some backends) plus the opaque\n backend-private state.\n\n The 8-byte `_reserved` slot is sized for a forthcoming\n `vtable: const struct nros_rmw_vtable_t *` field that Phase 104's\n multi-instance work will land here. Backends and runtime keep\n these bytes zero."]
244#[repr(C)]
245#[derive(Debug, Copy, Clone)]
246pub struct rmw_session_t {
247 #[doc = " Node name (borrowed from caller; outlives the session)."]
248 pub node_name: *const core::ffi::c_char,
249 #[doc = " Node namespace (borrowed from caller; outlives the session)."]
250 pub namespace_: *const core::ffi::c_char,
251 #[doc = " Reserved for future fields (Phase 104 vtable pointer slot);\n must be zero."]
252 pub _reserved: [u8; 8usize],
253 #[doc = " Opaque backend state. NULL for an uninitialised session."]
254 pub backend_data: *mut core::ffi::c_void,
255}
256#[doc = " A graph node — upstream `rmw_node_t`, minus what an image has no use for.\n\n Phase 376 W4. Storage is CALLER-OWNED, like every other entity here: the\n runtime hands `create_node` a zero-initialised shell and the backend writes\n its `backend_data` into it.\n\n **Why a node exists at all when an image opens ONE session.** The session\n half of that statement holds; the node half does not, and our own code says\n so. `Executor` keeps a node table, and `CffiSession::entity_view` exists\n SOLELY to fabricate a per-call session carrying the entity's owning-node\n identity — its own comment reads \"one session can host N graph nodes\". The\n zenoh backend then re-derives a node registry from that string by\n linear-scanning declared tokens. So node identity already reaches the\n backend, through a side channel, in every image.\n\n **Done, W5/B1 (2026-08-24).** `create_publisher` / `create_subscription` /\n `create_service` / `create_client` take `const rmw_node_t *` the way\n upstream does. That retired the entity_view fabrication — the shim now\n owns a node table and calls `create_node` once per distinct\n `(name, namespace)`, which is only true because `Executor::create_node`\n deduplicates (W5/B1.a).\n\n **Still owed:** zenoh's `ensure_node_liveliness` still linear-scans its own\n `per_node_liveliness` table. Retiring it needs a `create_node` method on the\n Rust `Rmw`/`Session` trait plus a trampoline in `RustBackendAdapter`, so\n that a Rust backend can be TOLD about a node the way a C one is. The slot\n and the table it needs both exist now; only that trait hop is missing. Do\n not read this paragraph as done — the first draft of this comment said the\n registry was retired, which it was not.\n\n Not carried from upstream: `implementation_identifier` and `data` (one\n image links one backend per session, so there is nothing to disambiguate).\n\n `session` IS carried, and is our `context`. Upstream's node reaches its\n context that way and every `rmw_create_*` relies on it; a node with no route\n to its session cannot be the only argument those slots get, which is what\n made this field the precondition for the whole change rather than a\n convenience. Set by the runtime BEFORE `create_node`, and stable for the\n node's life."]
257#[repr(C)]
258#[derive(Debug, Copy, Clone)]
259pub struct rmw_node_t {
260 #[doc = " Node name. Borrowed; outlives the node."]
261 pub name: *const core::ffi::c_char,
262 #[doc = " Node namespace. Borrowed; outlives the node."]
263 pub namespace_: *const core::ffi::c_char,
264 #[doc = " The session this node lives on — upstream's `context`. Set by the\n runtime before `create_node`; never NULL in a node the runtime hands to\n a slot. A backend reaches its own session state through\n `node->session->backend_data`."]
265 pub session: *mut rmw_session_t,
266 #[doc = " Reserved; must be zero."]
267 pub _reserved: [u8; 8usize],
268 #[doc = " Opaque backend state. NULL until `create_node` succeeds."]
269 pub backend_data: *mut core::ffi::c_void,
270}
271#[doc = " Publisher entity.\n\n Created by `vtable->create_publisher`; destroyed by\n `vtable->destroy_publisher`. The runtime owns the storage; the\n runtime fills `topic_name` / `type_name` / `qos` before the\n create call. The backend writes `backend_data`.\n\n `can_loan_messages` matches upstream `rmw_publisher_t`'s field of\n the same name — `true` means the backend exposes the\n `borrow_loaned_message` / `publish_loaned_message` primitive.\n\n **The runtime DERIVES it; a backend does not write it** (issue 0814).\n Its value is exactly `vtable->borrow_loaned_message != NULL`, because\n that is the same fact, and a fact with two spellings drifts — this one\n had drifted in both directions at once. Anything a backend writes here\n is overwritten.\n\n This paragraph used to read \"the runtime reads it once at create time\n and picks the publish path accordingly; no per-call probe\". That was\n never true: nothing has ever branched on the field, and the publish\n path is chosen per call from `vtable->borrow_loaned_message` itself.\n\n To refuse a loan for a PARTICULAR entity, return\n `RMW_RET_UNSUPPORTED` from `borrow_loaned_message`. The per-entity\n answer belongs on the call, which the runtime consults; not on this\n flag, which it does not."]
272#[repr(C)]
273#[derive(Debug, Copy, Clone)]
274pub struct rmw_publisher_t {
275 #[doc = " Topic name (borrowed; outlives the publisher)."]
276 pub topic_name: *const core::ffi::c_char,
277 #[doc = " ROS-2-style fully-qualified type name\n (e.g., `\"std_msgs/msg/Int32\"`). Borrowed; outlives the publisher."]
278 pub type_name: *const core::ffi::c_char,
279 #[doc = " QoS subset honoured by this publisher."]
280 pub qos: rmw_qos_profile_t,
281 #[doc = " Backend exposes `borrow_loaned_message` / `publish_loaned_message`.\n DERIVED by the runtime from `vtable->borrow_loaned_message`;\n a backend's write is overwritten (issue 0814)."]
282 pub can_loan_messages: bool,
283 #[doc = " Reserved for future fields; must be zero."]
284 pub _reserved: [u8; 7usize],
285 #[doc = " Opaque backend state. NULL if creation failed."]
286 pub backend_data: *mut core::ffi::c_void,
287}
288#[doc = " Subscription entity (phase-301: renamed from `subscriber` to the upstream `rmw_subscription_t` term). Same shape as the publisher; `can_loan_messages`\n means the backend exposes the receive-side loan primitive."]
289#[repr(C)]
290#[derive(Debug, Copy, Clone)]
291pub struct rmw_subscription_t {
292 #[doc = " Topic name (borrowed; outlives the subscription)."]
293 pub topic_name: *const core::ffi::c_char,
294 #[doc = " Fully-qualified type name. Borrowed."]
295 pub type_name: *const core::ffi::c_char,
296 #[doc = " QoS subset honoured by this subscription."]
297 pub qos: rmw_qos_profile_t,
298 #[doc = " Backend exposes loan_recv / release_recv (Phase 99).\n DERIVED by the runtime from `vtable->take_loaned_message`;\n a backend's write is overwritten (issue 0814)."]
299 pub can_loan_messages: bool,
300 #[doc = " Reserved for future fields; must be zero."]
301 pub _reserved: [u8; 7usize],
302 #[doc = " Opaque backend state. NULL if creation failed."]
303 pub backend_data: *mut core::ffi::c_void,
304}
305#[doc = " Service entity (phase-301: renamed from `service_server` to the upstream `rmw_service_t` term).\n\n Service entities have no QoS in the nros subset (the upstream\n `rmw_qos_profile_services_default` distinction does not generalise\n across non-DDS backends — see book `concepts/ros2-comparison.md`).\n\n No `can_loan_messages` field — service request/reply currently\n always goes through `take_request` / `send_response` byte-buffer\n APIs. If a future backend wants service-side lending, the\n `_reserved[8]` block accommodates the bool + 7 padding bytes\n without an ABI break."]
306#[repr(C)]
307#[derive(Debug, Copy, Clone)]
308pub struct rmw_service_t {
309 #[doc = " Service name (borrowed; outlives the server)."]
310 pub service_name: *const core::ffi::c_char,
311 #[doc = " Fully-qualified service type name (e.g.,\n `\"example_interfaces/srv/AddTwoInts\"`). Borrowed."]
312 pub type_name: *const core::ffi::c_char,
313 #[doc = " Reserved for future fields; must be zero."]
314 pub _reserved: [u8; 8usize],
315 #[doc = " Opaque backend state. NULL if creation failed."]
316 pub backend_data: *mut core::ffi::c_void,
317}
318#[doc = " Client entity (phase-301: renamed from `service_client` to the upstream `rmw_client_t` term). Same shape as the service."]
319#[repr(C)]
320#[derive(Debug, Copy, Clone)]
321pub struct rmw_client_t {
322 #[doc = " Service name (borrowed; outlives the client)."]
323 pub service_name: *const core::ffi::c_char,
324 #[doc = " Fully-qualified service type name. Borrowed."]
325 pub type_name: *const core::ffi::c_char,
326 #[doc = " Reserved for future fields; must be zero."]
327 pub _reserved: [u8; 8usize],
328 #[doc = " Opaque backend state. NULL if creation failed."]
329 pub backend_data: *mut core::ffi::c_void,
330}
331#[doc = " Liveliness payload. Mirrors the DDS\n `rmw_liveliness_changed_status_t` shape."]
332#[repr(C)]
333#[derive(Debug, Copy, Clone)]
334pub struct rmw_liveliness_changed_status_t {
335 pub alive_count: u16,
336 pub not_alive_count: u16,
337 pub alive_count_change: i16,
338 pub not_alive_count_change: i16,
339}
340#[doc = " Count payload. Used for `NROS_RMW_EVENT_MESSAGE_LOST`,\n `NROS_RMW_EVENT_REQUESTED_DEADLINE_MISSED`,\n `NROS_RMW_EVENT_LIVELINESS_LOST`,\n `NROS_RMW_EVENT_OFFERED_DEADLINE_MISSED` — all share the same shape."]
341#[repr(C)]
342#[derive(Debug, Copy, Clone)]
343pub struct rmw_count_status_t {
344 pub total_count: u32,
345 pub total_count_change: u32,
346}
347#[doc = " Matched payload — `NROS_RMW_EVENT_PUBLICATION_MATCHED` /\n `NROS_RMW_EVENT_SUBSCRIPTION_MATCHED`. Upstream `rmw_matched_status_t`\n (Iron+), same four fields in the same order.\n\n Width is the one deviation: upstream's three counts are `size_t`; ours\n are `uint32_t`, like every other count in this header, so the union keeps\n one layout on 32- and 64-bit targets. `current_count_change` is `int32_t`\n on both sides."]
348#[repr(C)]
349#[derive(Debug, Copy, Clone)]
350pub struct rmw_matched_status_t {
351 #[doc = " Cumulative matches over the entity's lifetime."]
352 pub total_count: u32,
353 #[doc = " Change in `total_count` since the last report."]
354 pub total_count_change: u32,
355 #[doc = " Endpoints matched RIGHT NOW."]
356 pub current_count: u32,
357 #[doc = " Change in `current_count` since the last report; negative when an\n endpoint left."]
358 pub current_count_change: i32,
359}
360#[doc = " Incompatible-type payload — `NROS_RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE`\n / `NROS_RMW_EVENT_SUBSCRIPTION_INCOMPATIBLE_TYPE`. Upstream\n `rmw_incompatible_type_status_t` (Iron+); upstream's fields are `int32_t`,\n ours `uint32_t` like `rmw_count_status_t`, since both only grow."]
361#[repr(C)]
362#[derive(Debug, Copy, Clone)]
363pub struct rmw_incompatible_type_status_t {
364 pub total_count: u32,
365 pub total_count_change: u32,
366}
367#[repr(C)]
368#[derive(Debug, Copy, Clone)]
369pub struct rmw_loan_token_t {
370 _unused: [u8; 0],
371}
372#[repr(C)]
373#[derive(Debug, Copy, Clone)]
374pub struct rmw_node_visitor_t {
375 pub visit: rmw_node_visit_fn,
376 pub ctx: *mut core::ffi::c_void,
377}
378#[repr(C)]
379#[derive(Debug, Copy, Clone)]
380pub struct rmw_names_and_types_visitor_t {
381 pub visit: rmw_names_and_types_visit_fn,
382 pub ctx: *mut core::ffi::c_void,
383}
384#[repr(C)]
385#[derive(Debug, Copy, Clone)]
386pub struct rmw_topic_endpoint_info_visitor_t {
387 pub visit: rmw_topic_endpoint_info_visit_fn,
388 pub ctx: *mut core::ffi::c_void,
389}
390#[repr(C)]
391#[derive(Debug, Copy, Clone)]
392pub struct nros_rmw_vtable_t {
393 #[doc = " Create a session (phase-301: renamed from `open` to the table's\n own `create_*` convention). The runtime supplies a\n zero-initialised `rmw_session_t` via @p out with\n `node_name` / `namespace_` already filled. The backend writes\n `out->backend_data`.\n\n @param mode One of `nros_rmw_session_mode_t`. Passed as `uint8_t`\n rather than the enum to keep the slot's width fixed\n across compilers. A backend with no peer/client\n distinction must IGNORE it, not reject it.\n @param options NULLable; NULL means every default. Issue 0808 — the\n home for init-time context this flat list cannot grow\n without another break. `mode` is NOT moved into it: doing\n so would be a second break for no gain, and it is already\n a named argument every backend reads. What moved in are the\n two fields issue 0785 measured as GAPS, `localhost_only`\n and `enclave`."]
394 pub create_session: ::core::option::Option<
395 unsafe extern "C" fn(
396 locator: *const core::ffi::c_char,
397 mode: u8,
398 domain_id: u32,
399 node_name: *const core::ffi::c_char,
400 options: *const rmw_session_options_t,
401 out: *mut rmw_session_t,
402 ) -> rmw_ret_t,
403 >,
404 pub destroy_session:
405 ::core::option::Option<unsafe extern "C" fn(session: *mut rmw_session_t) -> rmw_ret_t>,
406 pub drive_io: ::core::option::Option<
407 unsafe extern "C" fn(session: *mut rmw_session_t, timeout_ms: i32) -> rmw_ret_t,
408 >,
409 #[doc = " Create a publisher. The runtime fills `out->topic_name`,\n `out->type_name`, `out->qos` before this call; the backend\n writes `out->backend_data`. `out->can_loan_messages` is DERIVED\n by the runtime, not written here (issue 0814).\n `options` carries transport hints (phase-301: moved out of the\n QoS struct); NULL = all defaults."]
410 pub create_publisher: ::core::option::Option<
411 unsafe extern "C" fn(
412 node: *const rmw_node_t,
413 type_support: *const rmw_message_type_support_t,
414 topic_name: *const core::ffi::c_char,
415 domain_id: u32,
416 qos: *const rmw_qos_profile_t,
417 options: *const rmw_publisher_options_t,
418 out: *mut rmw_publisher_t,
419 ) -> rmw_ret_t,
420 >,
421 pub destroy_publisher:
422 ::core::option::Option<unsafe extern "C" fn(publisher: *mut rmw_publisher_t) -> rmw_ret_t>,
423 pub publish: ::core::option::Option<
424 unsafe extern "C" fn(
425 publisher: *const rmw_publisher_t,
426 payload: rmw_byte_span_t,
427 ) -> rmw_ret_t,
428 >,
429 #[doc = " `options` carries transport hints (phase-301: moved out of the\n QoS struct); NULL = all defaults."]
430 pub create_subscription: ::core::option::Option<
431 unsafe extern "C" fn(
432 node: *const rmw_node_t,
433 type_support: *const rmw_message_type_support_t,
434 topic_name: *const core::ffi::c_char,
435 domain_id: u32,
436 qos: *const rmw_qos_profile_t,
437 options: *const rmw_subscription_options_t,
438 out: *mut rmw_subscription_t,
439 ) -> rmw_ret_t,
440 >,
441 pub destroy_subscription: ::core::option::Option<
442 unsafe extern "C" fn(subscription: *mut rmw_subscription_t) -> rmw_ret_t,
443 >,
444 #[doc = " Upstream `rmw_take`. Phase 376 W3.b/W3.d step A.\n\n `*taken` says whether a message was copied; `*out_len` is\n how many bytes, meaningful only when taken. Both are\n written only on `NROS_RMW_RET_OK`.\n\n Second slot to retire `NROS_RMW_RET_NO_DATA`: an empty\n subscription is `taken = false` with OK, which is what\n upstream's `taken` out-parameter means.\n\n Deviations from upstream, declared:\n - `buf` / `buf_len` / `*out_len` replace upstream's typed\n `void *ros_message`. There is no typesupport indirection\n on target — the payload is bytes and the caller owns the\n buffer, so it needs the length back.\n - no `rmw_subscription_allocation_t *`: it is an OPAQUE\n per-implementation handle (`{const char *implementation_identifier;\n void *data;}` in Humble's `rmw/types.h` — no allocator in\n it), and the only thing that produces one is\n `rmw_init_subscription_allocation`, whose other parameters\n are a typesupport pointer and a sequence bound, both\n declined ABI-wide. Nothing can make one, so the argument has\n nothing to point at. Two earlier reasons here were wrong:\n \"pools are baked\" (issue 0777 — cyclonedds calls\n `ddsrt_calloc` on this very path) and then \"upstream\n pre-sizes an `rcutils_allocator_t`\" (there is none).\n\n Phase 403 W1 — `buf_len` is AUTHORITATIVE on every call, and a\n sample that does not fit it is a FAILURE (`*taken = false` with\n `NROS_RMW_RET_BUFFER_TOO_SMALL`), never a truncated success.\n That holds however the backend treated\n `rmw_subscription_options_t.rx_buffer_hint`, whose doc writes the\n rule out in full.\n\n Issue 1612 — on that failure `out->len` is the size the sample\n NEEDED, so the caller can say how big the dropped sample was rather\n than only that it did not fit. A backend that cannot know leaves it at\n `NROS_RMW_TAKE_LEN_UNKNOWN`, which is what the caller pre-sets."]
445 pub take: ::core::option::Option<
446 unsafe extern "C" fn(
447 subscription: *const rmw_subscription_t,
448 out: *mut rmw_mut_byte_span_t,
449 taken: *mut bool,
450 ) -> rmw_ret_t,
451 >,
452 #[doc = " Phase 376 W3.d step A — status in the return, answer in the\n out-parameter, so no slot multiplexes a flag with a status.\n `*out_has_data` is written only on `NROS_RMW_RET_OK`.\n\n RTOS addition: upstream has no equivalent, because a hosted\n caller reaches for a wait-set. This is the poll a loop with\n no wait-set needs, and it allocates nothing.\n\n Logically read-only, and that is weaker than it sounds: zenoh's\n implementation fires deadline and liveliness callbacks from inside this\n probe and writes their cells, and cyclonedds' peeks its reader (which\n marks samples READ). The rule a backend must actually keep is that a\n probe may not CONSUME a message — the sample a `has_data` reports must\n still be there for the `take` that follows. The stronger \"must not\n mutate subscription state\" was recorded here and true of nobody\n (issue 0780)."]
453 pub has_data: ::core::option::Option<
454 unsafe extern "C" fn(
455 subscription: *mut rmw_subscription_t,
456 out_has_data: *mut bool,
457 ) -> rmw_ret_t,
458 >,
459 pub create_service: ::core::option::Option<
460 unsafe extern "C" fn(
461 node: *const rmw_node_t,
462 type_support: *const rmw_service_type_support_t,
463 service_name: *const core::ffi::c_char,
464 domain_id: u32,
465 qos: *const rmw_qos_profile_t,
466 out: *mut rmw_service_t,
467 ) -> rmw_ret_t,
468 >,
469 pub destroy_service:
470 ::core::option::Option<unsafe extern "C" fn(server: *mut rmw_service_t) -> rmw_ret_t>,
471 #[doc = " Upstream `rmw_take_request`. Phase 376 W3.b/W3.d step A.\n\n `*taken` says whether a request was copied, `*out_len` how\n many bytes, `*seq_out` the sequence number to reply against.\n All three are written only on `NROS_RMW_RET_OK`.\n\n Deviations from upstream, declared: the payload is bytes\n (`buf` / `buf_len` / `*out_len`) rather than a typed\n `void *ros_request`, and `*seq_out` stands in for\n `rmw_service_info_t *` — an RTOS reply needs the sequence\n and nothing else in that struct. A backend that has the rest\n (who sent the request, when) reports it through\n `take_request_with_info` (issue 2081).\n\n `taken = false` with `NROS_RMW_RET_OK` means NOTHING WAS\n PENDING, and only that (upstream `rmw.h`). A backend that\n holds a request it cannot accept right now — every\n reply-correlation slot held by a request not yet answered\n with `send_response` — returns `NROS_RMW_RET_WOULD_BLOCK`,\n consumes nothing, and leaves the request for a later take.\n Folding that into `taken = false` makes a saturated server\n indistinguishable from an idle one (issue 1088).\n\n Issue 1632 — a request bigger than `request->capacity` is refused with\n `NROS_RMW_RET_BUFFER_TOO_SMALL`, and `request->len` then carries the\n size it NEEDED (`NROS_RMW_TAKE_LEN_UNKNOWN`, which the caller pre-sets,\n when the backend cannot know) — the `take` slot's rule, widened."]
472 pub take_request: ::core::option::Option<
473 unsafe extern "C" fn(
474 server: *const rmw_service_t,
475 request: *mut rmw_mut_byte_span_t,
476 seq_out: *mut i64,
477 taken: *mut bool,
478 ) -> rmw_ret_t,
479 >,
480 #[doc = " Phase 376 W3.d step A — the service-side sibling of\n `has_data`; same contract, same reason."]
481 pub has_request: ::core::option::Option<
482 unsafe extern "C" fn(server: *mut rmw_service_t, out_has_request: *mut bool) -> rmw_ret_t,
483 >,
484 pub send_response: ::core::option::Option<
485 unsafe extern "C" fn(
486 server: *const rmw_service_t,
487 seq: i64,
488 response: rmw_byte_span_t,
489 ) -> rmw_ret_t,
490 >,
491 pub create_client: ::core::option::Option<
492 unsafe extern "C" fn(
493 node: *const rmw_node_t,
494 type_support: *const rmw_service_type_support_t,
495 service_name: *const core::ffi::c_char,
496 domain_id: u32,
497 qos: *const rmw_qos_profile_t,
498 out: *mut rmw_client_t,
499 ) -> rmw_ret_t,
500 >,
501 pub destroy_client:
502 ::core::option::Option<unsafe extern "C" fn(client: *mut rmw_client_t) -> rmw_ret_t>,
503 #[doc = " Phase 130.4 — non-blocking send_request_raw. Phase-301: the\n deprecated blocking `call_raw` slot is DELETED (rmw has no\n blocking call); this + `take_response` is the ONE\n request/reply path and both slots are now REQUIRED for a backend\n that supports services.\n\n Sends the request to the backend without blocking for a\n reply. Returns immediately.\n\n `*sequence_id` is the id the backend assigned, written only on\n `NROS_RMW_RET_OK`. Upstream returns it for one reason and it is the\n same reason here: a client with two calls outstanding has nothing else\n to match a reply against.\n\n Issue 0778 — this out-parameter was ABSENT until 2026-08-25, and every\n backend computed the id and threw it away (cyclonedds a\n `RequestId{guid, seq}`, zenoh a `fetch_add` into the rmw attachment,\n xrce `uxr_buffer_request`'s id). With nothing to correlate BY, each\n invented a policy: cyclonedds ABANDONED the first request when a second\n was sent, zenoh took FIRST REPLY WINS on the grounds that \"a queryable\n is idempotent at the application layer\" — which this ABI cannot\n enforce and which is false for `send_goal` and `SetParameters`, both of\n which travel this path. Same application code, different behaviour per\n transport. The id is what deletes both policies.\n\n Issue 2009 — the caller pre-sets `*sequence_id` to\n `NROS_RMW_SEQUENCE_ID_UNKNOWN`; a backend that cannot assign ids\n leaves it there, and the runtime then keeps one call in flight on that\n client instead of matching replies it cannot tell apart."]
504 pub send_request: ::core::option::Option<
505 unsafe extern "C" fn(
506 client: *const rmw_client_t,
507 request: rmw_byte_span_t,
508 sequence_id: *mut i64,
509 ) -> rmw_ret_t,
510 >,
511 #[doc = " Upstream `rmw_take_response`. Same shape and the same\n declared deviations as `take_request`.\n\n `*seq_out` is the `sequence_id` of the request this reply answers,\n written only when `*taken` is true. It is the other half of issue\n 0778: handing the id out at send time is useless if it does not come\n back. Mirrors `take_request`'s `seq_out`, which the SERVER side has\n always had — the asymmetry was the tell.\n\n Issue 2009 — the runtime delivers a reply ONLY to the request whose id\n it carries, and drops a reply whose request was cancelled or is not\n pending. `*seq_out` is pre-set to `NROS_RMW_SEQUENCE_ID_UNKNOWN`; a\n reply that comes back with it is delivered only when one request is\n pending on the client.\n\n (The paragraph that used to sit here described `>= 0` = bytes and\n \"other negative = backend error\", the pre-W3.d shape, three phases\n after step A moved the count to an out-parameter and step B made the\n errors positive.)\n\n Issue 1632 — on `NROS_RMW_RET_BUFFER_TOO_SMALL`, `reply->len` is the\n size the reply NEEDED, as for `take_request`.\n\n A backend that has the rest of upstream's `rmw_service_info_t` (the\n reply's timestamps, the request id it answers) reports it through\n `take_response_with_info` (issue 2136)."]
512 pub take_response: ::core::option::Option<
513 unsafe extern "C" fn(
514 client: *const rmw_client_t,
515 reply: *mut rmw_mut_byte_span_t,
516 seq_out: *mut i64,
517 taken: *mut bool,
518 ) -> rmw_ret_t,
519 >,
520 #[doc = " Register a callback for a subscription-side event. NULL function\n pointer = backend doesn't generate any subscription events.\n Specific kind unsupported on a backend that supports some\n events = `NROS_RMW_RET_UNSUPPORTED` return.\n `deadline_ms` is consulted for `REQUESTED_DEADLINE_MISSED`\n only; ignored otherwise."]
521 pub subscription_event_init: ::core::option::Option<
522 unsafe extern "C" fn(
523 subscription: *const rmw_subscription_t,
524 kind: rmw_event_type_t::Type,
525 deadline_ms: u32,
526 cb: rmw_status_event_callback_t,
527 user_context: *mut core::ffi::c_void,
528 ) -> rmw_ret_t,
529 >,
530 #[doc = " Upstream `rmw_take_event`, subscription side.\n\n `*taken` says whether an event was copied into `*out`; both are written\n only on `NROS_RMW_RET_OK`. `kind` selects which event to drain, and\n which member of the payload union is valid.\n\n Deviations from upstream, declared: no `rmw_event_t *` — that handle is\n declined, so the entity plus the kind identifies the event — and the\n payload is our `rmw_event_payload_t` union rather than a `void *` the\n caller must know the shape of.\n\n NULL is the normal answer for a backend that delivers status events\n through the `*_event_init` callback and has a safe context to do it\n from. It is NOT the right answer for a backend whose notifications\n arrive on a thread of its own."]
531 pub subscription_take_event: ::core::option::Option<
532 unsafe extern "C" fn(
533 subscription: *const rmw_subscription_t,
534 kind: rmw_event_type_t::Type,
535 out: *mut rmw_event_payload_t,
536 taken: *mut bool,
537 ) -> rmw_ret_t,
538 >,
539 #[doc = " Upstream `rmw_take_event`, publisher side. Same contract as\n `subscription_take_event`; `rmw_take_event` is recorded as GROUPED onto\n that one, since upstream has a single name for both."]
540 pub publisher_take_event: ::core::option::Option<
541 unsafe extern "C" fn(
542 publisher: *const rmw_publisher_t,
543 kind: rmw_event_type_t::Type,
544 out: *mut rmw_event_payload_t,
545 taken: *mut bool,
546 ) -> rmw_ret_t,
547 >,
548 pub publisher_event_init: ::core::option::Option<
549 unsafe extern "C" fn(
550 publisher: *const rmw_publisher_t,
551 kind: rmw_event_type_t::Type,
552 deadline_ms: u32,
553 cb: rmw_status_event_callback_t,
554 user_context: *mut core::ffi::c_void,
555 ) -> rmw_ret_t,
556 >,
557 #[doc = " Phase 108.B — manually assert this publisher's liveliness.\n Required for `MANUAL_BY_TOPIC` / `MANUAL_BY_NODE` liveliness\n kinds; no-op (return `NROS_RMW_RET_OK`) for other kinds.\n NULL function pointer = backend doesn't support manual\n liveliness; runtime returns `NROS_RMW_RET_OK` for AUTOMATIC /\n NONE callers and `NROS_RMW_RET_UNSUPPORTED` for MANUAL_*."]
558 pub publisher_assert_liveliness: ::core::option::Option<
559 unsafe extern "C" fn(publisher: *const rmw_publisher_t) -> rmw_ret_t,
560 >,
561 #[doc = " Phase 110.0 — backend's next internal-event deadline in\n milliseconds from now (lease keepalive, heartbeat, reader\n ACK-NACK timeout, etc.). The runtime caps its `drive_io`\n timeout against `min(user_timeout, timer_deadline, this)` so\n quiet links don't wake early, see no user-visible work, and\n round-trip back into `drive_io`.\n\n Phase 376 W3.d step A — `*out_ms` carries the value and\n `*has_deadline` whether there is one; both are written only\n on `NROS_RMW_RET_OK`.\n\n This slot was the ONE member of the eleven that step B's\n renumbering did not force: its old negative return was a\n \"no deadline\" SENTINEL, not an error code, so nothing would\n have collided. It is converted anyway because it had the\n shape every other conversion found a silent failure in — a\n backend that FAILED to compute its deadline returned `-1`\n and was read as \"quiet link\", which is exactly the reading\n that makes the executor sleep longer. It now has an error\n channel it never had.\n\n NULL function pointer is permitted — the runtime treats it\n the same as `*has_deadline = false`."]
562 pub next_deadline_ms: ::core::option::Option<
563 unsafe extern "C" fn(
564 session: *const rmw_session_t,
565 out_ms: *mut u32,
566 has_deadline: *mut bool,
567 ) -> rmw_ret_t,
568 >,
569 #[doc = " Phase 124.B.1 — executor wake callback.\n\n The runtime calls this once per session right after `open`\n with `cb` pointing at a runtime-supplied function and `ctx`\n pointing at the executor's wake state. The backend stores\n both in its per-session state and calls `cb(ctx)` whenever\n its transport-notification path fires — datagram arrival,\n condvar wake-up, select-fd ready, etc. The runtime cb does\n flag-write + condvar-signal atomically so a `spin_once`\n blocked on the wake condvar resumes immediately.\n\n `cb == NULL` clears any previously installed callback; the\n backend must drop the stored (cb, ctx) and never invoke\n again after this returns.\n\n NULL slot = backend has no asynchronous wake path (purely\n poll-driven: bare-metal). The runtime still drains the\n session on its deadline-bound cv-wait boundary.\n\n phase-521 W4 — a backend that also fills `readiness_source`\n is believed on THAT: the runtime treats the session as woken\n by this callback only when it reports\n `NROS_RMW_READINESS_NOTIFY`. So a backend may fill this slot\n for every session and install the callback only where its\n transport can call it — XRCE hands it to the UDP stack's\n receive notification on lwIP and NetX, and installs nothing\n over a descriptor or a board's custom transport."]
570 pub set_wake_callback: ::core::option::Option<
571 unsafe extern "C" fn(
572 session: *mut rmw_session_t,
573 cb: ::core::option::Option<unsafe extern "C" fn(ctx: *mut core::ffi::c_void)>,
574 ctx: *mut core::ffi::c_void,
575 ) -> rmw_ret_t,
576 >,
577 #[doc = " Phase 124.A — zero-copy publisher loan.\n\n Reserve a writable slot of at least `requested_len` bytes inside\n the backend's outbound buffer. Returns:\n * `NROS_RMW_RET_OK` + writes `*out_buf` / `*out_cap` / `*out_token`.\n * `NROS_RMW_RET_WOULD_BLOCK` if the backend has no slot\n available (caller may retry or fall back to a copy path).\n This line said `TRY_AGAIN` until issue 1126 — a spelling\n `rmw_ret.h` has never defined, in the header that\n IS the ABI contract a third-party backend writes against.\n The runtime reads `WOULD_BLOCK` (and `NO_DATA`) as \"no slot,\n not an error\"; every other code propagates as a failure.\n * `NROS_RMW_RET_INVALID_ARGUMENT` on bad pointers / size.\n\n `*out_cap` may exceed `requested_len`. The slot's bytes are\n valid until the matching `publish_loaned_message` or\n `return_loaned_message_from_publisher` runs.\n `*out_token` is an opaque per-loan handle the backend uses to\n match commit / discard back to the right slot.\n\n NULL function pointer = backend doesn't natively lend. The\n fallback then depends on whether the image has an allocator:\n with `alloc`, `try_lend_slot` allocates a staging `Vec` PER\n LOAN, hands its pointer back as the slot, and commit memcpys\n it into `publish`; without `alloc` there is no fallback at\n all and the loan is refused PERMANENTLY, not \"try again\"\n (issue 0814 — both facts are about the image and the vtable,\n neither can change while the publisher lives).\n\n This said \"a per-publisher staging arena\" until 2026-09-11.\n There is no per-publisher buffer anywhere in this tree — the\n same fiction issue 0782 corrected 200 lines below, on\n `publish_streamed`, and left standing here."]
578 pub borrow_loaned_message: ::core::option::Option<
579 unsafe extern "C" fn(
580 publisher: *const rmw_publisher_t,
581 requested_len: usize,
582 out_slot: *mut rmw_mut_byte_span_t,
583 out_token: *mut *mut rmw_loan_token_t,
584 ) -> rmw_ret_t,
585 >,
586 #[doc = " Phase 124.A — commit a previously loaned slot.\n\n `token` MUST be a value returned from a prior `pub_loan` on the\n same publisher. `actual_len` is the byte count actually\n written into the slot (≤ the loan's `out_cap`). Triggers the\n wire send.\n\n NULL = paired NULL with `pub_loan`."]
587 pub publish_loaned_message: ::core::option::Option<
588 unsafe extern "C" fn(
589 publisher: *const rmw_publisher_t,
590 token: *mut rmw_loan_token_t,
591 actual_len: usize,
592 ) -> rmw_ret_t,
593 >,
594 #[doc = " Phase 124.A — abandon a previously loaned slot.\n\n Releases the slot without sending. `token` MUST be a value\n returned from a prior `pub_loan` on the same publisher.\n\n NULL = paired NULL with `pub_loan`."]
595 pub return_loaned_message_from_publisher: ::core::option::Option<
596 unsafe extern "C" fn(
597 publisher: *const rmw_publisher_t,
598 token: *mut rmw_loan_token_t,
599 ) -> rmw_ret_t,
600 >,
601 #[doc = " Upstream `rmw_take_loaned_message`. Phase 376 W3.b/W3.d step A.\n\n `*taken` says whether a view was handed out; `*out_buf`,\n `*out_len` and `*out_token` describe it and are meaningful\n only when taken. All are written only on\n `NROS_RMW_RET_OK`.\n\n Before this, the length was returned AND written to\n `*out_len`, and the runtime used the return — so a backend\n that disagreed with itself had one of its two answers\n silently ignored. There is now one length.\n\n Deviation from upstream, declared: upstream loans a typed\n `void **loaned_message`; ours is a byte view plus an opaque\n token to release, because there is no typesupport on target\n and the backend owns the buffer until `sub_release`.\n\n **No backend fills this slot today** (Cyclone NULL, XRCE NULL, and the\n Rust adapter leaves it at `EMPTY_VTABLE`), so every `try_borrow` through\n the C ABI takes the copy fallback. The slot is carried, not deleted,\n because it is the only shape that can hand a view to a caller which\n outlives the call — `nros-c` / `nros-cpp` `try_borrow` — where\n `process_raw_in_place`'s scoped callback cannot reach. Zenoh's native\n zero-copy receive is live but arrives through Rust\n `SlotBorrowing for ZenohSubscriber`, not through here. Recorded so\n \"the slot exists\" is not read as \"the capability works\": issue 0781."]
602 pub take_loaned_message: ::core::option::Option<
603 unsafe extern "C" fn(
604 subscription: *const rmw_subscription_t,
605 out_view: *mut rmw_byte_span_t,
606 out_token: *mut *mut rmw_loan_token_t,
607 taken: *mut bool,
608 ) -> rmw_ret_t,
609 >,
610 #[doc = " Phase 124.A — release a previously borrowed view.\n\n `token` MUST be a value returned from a prior `sub_borrow`\n on the same subscription. Lets the next message advance into\n the buffer.\n\n NULL = paired NULL with `sub_borrow`."]
611 pub return_loaned_message_from_subscription: ::core::option::Option<
612 unsafe extern "C" fn(
613 subscription: *const rmw_subscription_t,
614 token: *mut rmw_loan_token_t,
615 ) -> rmw_ret_t,
616 >,
617 #[doc = " Phase 124.C.1 — service-server availability probe.\n\n `*out_available` says whether ≥ 1 matching server has been\n discovered on the RMW graph; the return is a plain status.\n (This opened by describing the answer as the RETURN — `1`,\n `0`, or a negative code — the pre-W3.d shape that the rest of\n this same block goes on to explain was retired. Corrected\n 2026-09-11, phase-428 W12.) The runtime exposes this to user\n code as `rclc_client_server_available()` /\n `Client<S>::server_available()` — clients use it to gate the\n first request so a startup-ordering race doesn't surface as\n a request-side timeout.\n\n Implementation notes per backend:\n - **Zenoh**: `z_session` tracks matched queryables via\n interest declarations.\n - **Cyclone DDS / dust-DDS**: built-in topic readers expose\n matched-pub counts.\n - **XRCE**: agent has no participant enumeration; return\n `NROS_RMW_RET_UNSUPPORTED`.\n\n NULL function pointer = backend cannot answer; the runtime\n surfaces `NROS_RMW_RET_UNSUPPORTED` to the caller.\n\n Phase 376 W3.d step A — upstream's shape: the STATUS is the\n return value and the answer is an out-parameter. Previously\n this slot multiplexed both through one `int32_t` (1 = yes,\n 0 = no, negative = error), which is what makes upstream's\n positive `RMW_RET_ERROR = 1` unadoptable — `1` would mean\n both \"available\" and \"failed\". Splitting them is what lets\n step B renumber at all.\n\n A backend writes `*out_available` only on\n `NROS_RMW_RET_OK`; on any error the caller's value is\n untouched. The old contract's tolerance for \"any positive\n value other than 1 means available\" is gone with the int:\n a `bool` has no non-spec value to be lenient about.\n\n Deviation from upstream, declared: no `node` parameter.\n `rmw_service_server_is_available` takes both a node and a\n client; an image has no node object to pass — the client\n reaches its session directly."]
618 pub service_server_is_available: ::core::option::Option<
619 unsafe extern "C" fn(client: *const rmw_client_t, out_available: *mut bool) -> rmw_ret_t,
620 >,
621 #[doc = " Upstream `rmw_take_sequence`. Phase 376 W3.b/W3.d step A —\n the COUNT moves to `*taken`, matching upstream's\n `size_t *taken`, and the return carries only a status.\n `*taken` is written only on `NROS_RMW_RET_OK`; a partial\n drain reports what it got rather than erroring.\n\n Drains up to `max_msgs` queued messages into a contiguous\n caller buffer in a single backend call, avoiding N × vtable\n dispatch when a burst-sensor subscription catches up on a\n backlog (e.g. a 100 Hz IMU feed polled at 10 Hz).\n\n Storage contract:\n * `buf` is a contiguous `max_msgs * per_msg_cap` block.\n * The i-th delivered message lives at `buf + i * per_msg_cap`\n and has byte length `out_lens[i]`.\n * `out_lens` is at least `max_msgs` entries long.\n\n NULL slot = the backend does not natively batch; the runtime\n loops the single `take` instead, which gives identical\n observable behaviour (each message still costs one vtable hop)\n while letting user code commit to the batched API.\n\n Issue 0971 — which leaves a question the count alone cannot\n answer: WHY the drain stopped. A batch that ends because a\n message did not fit `per_msg_cap` returns the same shape as\n one that drained the reader, and the message that stopped it\n is consumed — deliberately, for the reason the single take\n consumes it too: a sample left behind that no caller can\n ever take is a stuck subscription\n (`nros-verification`'s `take_post_fix` /\n `no_silent_truncation`).\n\n So a backend that stops a drain for a reason the caller must\n hear PARKS that status on the subscription and returns it\n from the NEXT `take` or `take_sequence`, which takes nothing\n else that call. That rule is what makes the fallback note\n above true rather than aspirational: without it the runtime's\n `take` loop and a native batch answer the same\n condition differently — the loop erroring out and discarding\n the count it had already earned, the native path reporting a\n count and no reason."]
622 pub take_sequence: ::core::option::Option<
623 unsafe extern "C" fn(
624 subscription: *const rmw_subscription_t,
625 buf: *mut u8,
626 per_msg_cap: usize,
627 max_msgs: usize,
628 out_lens: *mut usize,
629 taken: *mut usize,
630 ) -> rmw_ret_t,
631 >,
632 #[doc = " Phase 124.E.1 — streamed publish.\n\n Caller hands the backend two callbacks. The backend invokes\n `size_cb` once to learn the total payload length, allocates\n a single slot of that size in its outbound buffer, then\n invokes `chunk_cb` repeatedly to fill the slot in chunks\n until the buffer is full. What it saves is the CALLER-side\n buffer holding a whole serialised message.\n\n That justification used to read \"saves the per-publisher\n staging buffer … where the staging buffer dominates `.bss`\",\n which described a design this tree does not have (issue 0782).\n There is no per-publisher buffer and none of it is in `.bss`:\n `EmbeddedPublisher::publish` serialises into a per-CALL STACK\n array of `DEFAULT_TX_BUF` (= `NROS_SUBSCRIPTION_BUFFER_SIZE`,\n 1024 by default), and the runtime's own NULL-slot fallback\n stages into a 4 KiB stack array and refuses anything larger.\n The saving is real and it is STACK — which on an MCU with\n small per-task stacks is the tighter budget of the two — but a\n reason has to name the thing it is about.\n\n Callback contract:\n * `size_cb(*out_total_len, user_ctx)` — write the exact\n total payload length, in bytes, to `*out_total_len`.\n Called exactly once per `publish_streamed` invocation.\n * `chunk_cb(out_buf, cap, *out_written, user_ctx)` —\n write up to `cap` bytes starting at `out_buf`, then\n report the count written via `*out_written`. The backend\n may call `chunk_cb` repeatedly until the total promised\n by `size_cb` has been delivered. `*out_written == 0`\n means EOF; the backend tears down the slot.\n\n Lesson from micro-ROS's\n `rmw_uros_set_continous_serialization_callbacks`: pass the\n callbacks per-call rather than binding them to publisher\n state, so different messages on the same publisher can use\n different serialisation strategies.\n\n NULL function pointer = backend doesn't stream; the runtime\n stages into a 4 KiB buffer on the CALLER's stack and then\n `publish`es it, refusing a total larger than that with\n `NROS_RMW_RET_BUFFER_TOO_SMALL`. The cap is a constant in\n `CffiPublisher::publish_streamed`, not a knob: this said\n \"the configured NROS_MAX_STREAM_CHUNK\" until 2026-09-11 and\n no such setting has ever existed, so a reader looking for\n the knob to raise found nothing and had no way to know the\n limit was 4096 (phase-428 W12)."]
633 pub publish_streamed: ::core::option::Option<
634 unsafe extern "C" fn(
635 publisher: *mut rmw_publisher_t,
636 size_cb: ::core::option::Option<
637 unsafe extern "C" fn(out_total_len: *mut usize, user_ctx: *mut core::ffi::c_void),
638 >,
639 chunk_cb: ::core::option::Option<
640 unsafe extern "C" fn(
641 out_buf: *mut u8,
642 cap: usize,
643 out_written: *mut usize,
644 user_ctx: *mut core::ffi::c_void,
645 ),
646 >,
647 user_ctx: *mut core::ffi::c_void,
648 ) -> rmw_ret_t,
649 >,
650 #[doc = " Phase 124.F.1 — session-level connectivity probe.\n\n Sends a wire-level round-trip probe (\"is the peer / agent /\n router still reachable?\") and waits up to `timeout_ms` for\n a reply. No discovery state required — cheaper than the\n service-availability probe (which needs matched-publication\n bookkeeping). Lesson from micro-ROS's\n `rmw_uros_ping_agent`.\n\n Returns:\n * `NROS_RMW_RET_OK` — peer responded within budget.\n * `NROS_RMW_RET_TIMEOUT` — no reply before `timeout_ms`.\n * `NROS_RMW_RET_UNSUPPORTED` — backend can't probe (DDS\n with no participant introspection).\n * any other named constant — backend error. (This read\n \"other negative\" until 2026-09-11; W3.d step B made every\n error positive.)\n\n Implementation notes per backend:\n - **Zenoh**: one keep-alive frame (`zpico_send_keep_alive`);\n zenoh-pico has no round-trip ping, so OK means only that the\n local link accepted the frame.\n - **XRCE**: `uxr_ping_agent_session`.\n - **DDS**: built-in participant ping if available, else\n `NROS_RMW_RET_UNSUPPORTED`.\n\n NULL function pointer = runtime surfaces\n `NROS_RMW_RET_UNSUPPORTED` to the caller."]
651 pub ping_session: ::core::option::Option<
652 unsafe extern "C" fn(session: *mut rmw_session_t, timeout_ms: i32) -> rmw_ret_t,
653 >,
654 #[doc = " Capability query: does this subscription support process_raw_in_place()?\n The runtime consults it once at subscription registration to choose\n in-place dispatch over the buffered (copying) path.\n\n `*out_supports` is written only on `NROS_RMW_RET_OK` (Phase 376 W3.d\n step A — capability out, status returned).\n\n **The capability is the CONJUNCTION** of this probe answering true and\n `process_raw_in_place` being non-NULL. Either alone is unsupported.\n\n Why the probe is not redundant with that nullity (issue 0781 proposed\n deleting it, and this is the counterexample that stopped it):\n `RustBackendAdapter::<R>::VTABLE` is a `const`, so it installs\n `process_raw_in_place` for EVERY `R: RustBackend` — the slot is non-NULL\n whatever the backend can do. The Rust-side answer is a runtime `&self`\n method (`Subscription::supports_process_in_place`) because\n `CffiSubscription` multiplexes over whichever backend registered, so it\n cannot become an associated const the vtable initializer could branch\n on. Today `nros-rmw-zenoh` says true and `nros-rmw-metadata` takes the\n `false` default behind an identically-shaped vtable: two backends, same\n nullity, different capability. Deriving from nullity would route\n metadata's subscriptions into in-place dispatch and every take would\n return `MessageTooLarge` from the trait default.\n\n A C backend that knows its answer at compile time (XRCE says true,\n Cyclone leaves both NULL) may express it with nullity alone; the\n conjunction makes that spelling correct without making it the only one.\n\n NULL function pointer = treated as unsupported (buffered path)."]
655 pub subscription_supports_in_place: ::core::option::Option<
656 unsafe extern "C" fn(
657 subscription: *mut rmw_subscription_t,
658 out_supports: *mut bool,
659 ) -> rmw_ret_t,
660 >,
661 #[doc = " Borrow one ready message in place: hand its raw CDR bytes to `cb` (with\n the opaque `ctx`) for the duration of the call, then release the slot.\n `cb` MUST NOT re-enter this subscription's receive.\n\n Deviation from upstream, declared. Avoiding the copy is NOT the reason —\n upstream already has a name for that, `rmw_take_loaned_message`, and we\n carry it (`take_loaned_message`). What this shape buys is that the\n borrow is SCOPED: it ends when `cb` returns, so there is no release\n token to hold and nothing a caller can forget. Upstream's loan is\n unscoped — a caller who misses\n `rmw_return_loaned_message_from_subscription` retires one entry of a\n fixed-depth receive ring for good, and a target with no reclaim and no\n swap does not get that entry back. That is the RTOS constraint; \"no\n copy\" is a property both shapes share.\n\n Both are carried because they answer different callers: this one serves\n dispatch from inside the executor, where the callback frame is the\n natural scope, and the loan pair serves `nros-c` / `nros-cpp`, whose\n `try_borrow` hands a view back to a caller that outlives the call.\n\n NULL function pointer = unsupported (the runtime uses the buffered\n path); see `subscription_supports_in_place` for the conjunction. */\n/** Phase 376 W3.d step A — \"did it process one\" moves to an\n out-parameter and the return is a plain status.\n\n This retires `NROS_RMW_RET_NO_DATA` from this slot: an empty\n subscription is `*out_processed = false` with\n `NROS_RMW_RET_OK`, which is upstream's `taken = false`\n semantics. A sentinel that means \"fine, but nothing\" is\n exactly the shape that makes a status enum ambiguous.\n\n `*out_processed` is written only on OK."]
662 pub process_raw_in_place: ::core::option::Option<
663 unsafe extern "C" fn(
664 subscription: *mut rmw_subscription_t,
665 ctx: *mut core::ffi::c_void,
666 cb: ::core::option::Option<
667 unsafe extern "C" fn(ctx: *mut core::ffi::c_void, message: rmw_byte_span_t),
668 >,
669 out_processed: *mut bool,
670 ) -> rmw_ret_t,
671 >,
672 #[doc = " Upstream `rmw_get_implementation_identifier`.\n\n The backend's name, static for the life of the image. A gid is only\n comparable with another carrying the same identifier, which matters here\n because `nros_rmw_cffi_register_named` admits several backends at once.\n\n RETURN THE STRING THE BACKEND STAMPS INTO `rmw_gid_t`. That is the\n identity that is load-bearing: `rmw_compare_gids_equal` compares it\n before the bytes, so a backend has ONE spelling of its name and this\n slot hands it out. Cyclone's `kImplementationIdentifier` is that shape.\n\n THE NANO-ROS NAME, NOT A STOCK `rmw_*_cpp` NAME. In-tree answers are the\n registry names: `\"zenoh\"`, `\"cyclonedds\"`, `\"xrce\"`, `\"uorb\"`. Upstream's\n identifier is a process-local type tag — each rmw checks a handle's\n `implementation_identifier` against its own, and the string never\n crosses the wire — so answering `\"rmw_zenoh_cpp\"` would buy no interop\n (wire compatibility is the serialization format and the gid derivation,\n which are answered elsewhere) and would tell a tool or ported code that\n branches on the name that it runs on an implementation it does not.\n\n READ by `CffiSession::implementation_identifier_cstr`, which the\n per-node accessors in every language reach\n (`Node::rmw_implementation_identifier`,\n `rclc_node_get_rmw_implementation_identifier`,\n `rclcpp::Node::rmw_implementation_identifier`) — per SESSION, because\n an image linking two backends has two answers.\n\n NULL slot: FALLBACK class — the runtime answers with the name the vtable\n was registered under (`nros_rmw_cffi_register_named`; the first such\n name if one vtable is registered twice). That name is a real fact about\n the backend, not a guess; it is what a foreign vtable that predates the\n slot is known by. (Until 2026-10-27 this slot had no reader and this doc\n had once promised that fallback with no code behind it — corrected\n phase-393 W2, implemented with the reader.)"]
673 pub get_implementation_identifier:
674 ::core::option::Option<unsafe extern "C" fn() -> *const core::ffi::c_char>,
675 #[doc = " Upstream `rmw_get_serialization_format`.\n\n The backend's wire encoding, as its cross-image identity STRING\n (RFC-0088 D2: the `u8` discriminant is assigned per image and means\n nothing outside it; the name is what two images can agree on). Static\n for the life of the image, like the identifier above.\n\n PRODUCED by every backend since phase-421 W2. It was reserved until\n then for a stated reason — \"every backend here speaks CDR, nothing\n asks, so a body would be parity shape with no reader\" — and both halves\n of that reason have since stopped being true. uORB's wire is the PX4\n struct verbatim (RFC-0011), so it answers `\"uorb\"` where every other\n backend answers `\"cdr\"`; and `nros_rmw_cffi_register_named` admits\n several backends in one image, so a bridge has two sessions whose\n formats differ and one compile-time constant cannot describe both.\n `CffiSession::serialization_format` is the reader.\n\n This is the ONLY per-session answer. `nros_node::IMAGE_SERIALIZATION_\n FORMAT` and the generated `NROS_SERIALIZATION_FORMAT` macro are\n compile-time constants and are meaningful only in a single-backend\n image; ask the slot whenever the image links more than one.\n\n NULL slot: the runtime answers NULL — it does NOT guess `\"cdr\"`. A\n backend that does not declare its format has not told anyone what it\n speaks, and inventing an answer is how the identifier slot's doc came\n to promise a fallback nobody had written (corrected phase-393 W2)."]
676 pub get_serialization_format:
677 ::core::option::Option<unsafe extern "C" fn() -> *const core::ffi::c_char>,
678 #[doc = " Upstream `rmw_feature_supported`.\n\n Whether the backend populates an optional piece of CONTENT — upstream's\n two values both concern message-info sequence numbers. Deliberately not\n expressed as slot nullity: a NULL pointer says the backend cannot\n perform an OPERATION, which is a different question from whether the\n data an implemented operation returns is populated.\n\n NULL slot: there is NOTHING TO ANSWER — no code in this tree\n reads this slot, so a NULL one is not \"answered elsewhere as\n false\", it is unreachable. This doc promised the `false`\n fallback until 2026-09-11 and no such code was ever written:\n the same fiction corrected 60 lines above on\n `get_implementation_identifier`, and left standing here\n (phase-428 W12). It earns a dispatch site when a caller\n exists."]
679 pub feature_supported:
680 ::core::option::Option<unsafe extern "C" fn(feature: rmw_feature_t::Type) -> bool>,
681 #[doc = " Upstream `rmw_get_gid_for_publisher`. Exact parity.\n\n The backend zero-pads to the full width; see `rmw_gid_t`."]
682 pub get_gid_for_publisher: ::core::option::Option<
683 unsafe extern "C" fn(publisher: *const rmw_publisher_t, gid: *mut rmw_gid_t) -> rmw_ret_t,
684 >,
685 #[doc = " Upstream `rmw_publisher_count_matched_subscriptions`. Exact parity.\n\n Every backend already tracks this to implement liveliness events — see\n `service_server_is_available`, which is the same question one entity\n over. NULL where a backend has no discovery at all (XRCE)."]
686 pub publisher_count_matched_subscriptions: ::core::option::Option<
687 unsafe extern "C" fn(
688 publisher: *const rmw_publisher_t,
689 subscription_count: *mut usize,
690 ) -> rmw_ret_t,
691 >,
692 #[doc = " Upstream `rmw_subscription_count_matched_publishers`. Exact parity."]
693 pub subscription_count_matched_publishers: ::core::option::Option<
694 unsafe extern "C" fn(
695 subscription: *const rmw_subscription_t,
696 publisher_count: *mut usize,
697 ) -> rmw_ret_t,
698 >,
699 #[doc = " Upstream `rmw_publisher_get_actual_qos`. Exact parity.\n\n We bake the REQUESTED profile and, until now, never read back the\n GRANTED one. On DDS the two differ whenever a writer and reader\n negotiate, and the difference is exactly what answers \"why is nothing\n arriving\" — so a consumer that cannot ask has to guess.\n\n PARTIAL ANSWERS ARE ALLOWED (W5/B2). A backend that can determine four\n policies and not the fifth writes the four it knows and\n `*_UNKNOWN` for the fifth, then returns `NROS_RMW_RET_OK`. Until the\n policy values took upstream's numbering there was no `UNKNOWN` to write,\n so the contract had to be all-or-nothing — a partial answer would have\n been indistinguishable from a confident one, and W4 chose\n `NROS_RMW_RET_UNSUPPORTED` over lying.\n\n `NROS_RMW_RET_UNSUPPORTED` now means what it says: this backend has no\n read-back at all. It is NOT the answer for \"I know some of it\".\n\n A caller that treats `UNKNOWN` as a value rather than as an absence gets\n a wrong comparison, so `rmw_qos_profile_check_compatible` reports it as\n a WARNING rather than an incompatibility — upstream's\n `RMW_QOS_COMPATIBILITY_WARNING`, which was unreachable here until there\n was a sentinel to trigger it.\n\n Six upstream entry points, six slots, deliberately: the name rule is\n mechanical so that no alias table has to be authored and kept true.\n Backends share ONE helper and write six one-line thunks — sharing an\n implementation is free, sharing an ABI slot is not."]
700 pub publisher_get_actual_qos: ::core::option::Option<
701 unsafe extern "C" fn(
702 publisher: *const rmw_publisher_t,
703 qos: *mut rmw_qos_profile_t,
704 ) -> rmw_ret_t,
705 >,
706 #[doc = " Upstream `rmw_subscription_get_actual_qos`. Exact parity."]
707 pub subscription_get_actual_qos: ::core::option::Option<
708 unsafe extern "C" fn(
709 subscription: *const rmw_subscription_t,
710 qos: *mut rmw_qos_profile_t,
711 ) -> rmw_ret_t,
712 >,
713 #[doc = " Upstream `rmw_client_request_publisher_get_actual_qos`. Exact parity.\n\n The four service/client read-backs carry information available NOWHERE\n else: `rmw_client_t` and `rmw_service_t` have no `qos` field, and\n `create_client` / `create_service` take ONE profile for both\n directions, so the granted per-direction profile is otherwise\n unobservable."]
714 pub client_request_publisher_get_actual_qos: ::core::option::Option<
715 unsafe extern "C" fn(client: *const rmw_client_t, qos: *mut rmw_qos_profile_t) -> rmw_ret_t,
716 >,
717 #[doc = " Upstream `rmw_client_response_subscription_get_actual_qos`."]
718 pub client_response_subscription_get_actual_qos: ::core::option::Option<
719 unsafe extern "C" fn(client: *const rmw_client_t, qos: *mut rmw_qos_profile_t) -> rmw_ret_t,
720 >,
721 #[doc = " Upstream `rmw_service_request_subscription_get_actual_qos`."]
722 pub service_request_subscription_get_actual_qos: ::core::option::Option<
723 unsafe extern "C" fn(
724 service: *const rmw_service_t,
725 qos: *mut rmw_qos_profile_t,
726 ) -> rmw_ret_t,
727 >,
728 #[doc = " Upstream `rmw_service_response_publisher_get_actual_qos`."]
729 pub service_response_publisher_get_actual_qos: ::core::option::Option<
730 unsafe extern "C" fn(
731 service: *const rmw_service_t,
732 qos: *mut rmw_qos_profile_t,
733 ) -> rmw_ret_t,
734 >,
735 #[doc = " Upstream `rmw_publisher_wait_for_all_acked`.\n\n Blocks until every sample this publisher sent has been acknowledged, or\n the timeout elapses. Without it an image that publishes and then halts\n cannot know whether anything left the box.\n\n Deviation from upstream, declared: `uint32_t timeout_ms` for upstream's\n by-value `rmw_time_t`. Every duration in this ABI is u32 milliseconds\n (issue 0241) — one width, one unit, no per-call struct.\n\n Best-effort backends (zenoh best-effort, XRCE) leave this NULL."]
736 pub publisher_wait_for_all_acked: ::core::option::Option<
737 unsafe extern "C" fn(publisher: *const rmw_publisher_t, timeout_ms: u32) -> rmw_ret_t,
738 >,
739 #[doc = " Upstream `rmw_take_with_info`.\n\n `take` plus the sample's metadata, written to caller-owned storage. See\n `rmw_message_info_t` for why this is a pointer parameter rather than the\n side table the runtime uses today.\n\n Deviations from upstream, declared: the same two `take` declares —\n bytes (`buf`/`buf_len`/`*out_len`) instead of a typed `void *`, because\n there is no typesupport on target; and no allocation argument, because\n nothing in this ABI can produce upstream's opaque\n `rmw_subscription_allocation_t` — see `take`, which carries the full\n reason and the two wrong ones that preceded it.\n\n DISPATCHED since issue 1495: a with-info take on the runtime\n calls this slot when it is non-NULL and converts the struct to\n `MessageInfo`. A NULL slot falls back to `take` plus the Rust\n adapter's side table, which is metadata for a Rust backend and\n none for a C/C++ one. (Before 1495 nothing called this slot at\n all, so filling it changed no behaviour — phase-428 W12 found\n the fallback prose describing a mechanism that did not exist.)"]
740 pub take_with_info: ::core::option::Option<
741 unsafe extern "C" fn(
742 subscription: *const rmw_subscription_t,
743 message: *mut rmw_mut_byte_span_t,
744 taken: *mut bool,
745 message_info: *mut rmw_message_info_t,
746 ) -> rmw_ret_t,
747 >,
748 #[doc = " Upstream `rmw_take_loaned_message_with_info`.\n\n `take_loaned_message` plus metadata; same deviations as that slot (a\n byte view and an opaque release token rather than a typed loan)."]
749 pub take_loaned_message_with_info: ::core::option::Option<
750 unsafe extern "C" fn(
751 subscription: *const rmw_subscription_t,
752 out_view: *mut rmw_byte_span_t,
753 out_token: *mut *mut rmw_loan_token_t,
754 taken: *mut bool,
755 message_info: *mut rmw_message_info_t,
756 ) -> rmw_ret_t,
757 >,
758 #[doc = " Upstream `rmw_get_node_names` AND `rmw_get_node_names_with_enclaves`.\n\n One slot, two upstream names: upstream split them only because appending\n to a fixed out-parameter list would have broken its ABI. A visitor has\n no such list, so the enclave is simply a fourth argument, NULL where\n untracked. Recorded in the checker's grouping table."]
759 pub get_node_names: ::core::option::Option<
760 unsafe extern "C" fn(
761 session: *const rmw_session_t,
762 visitor: rmw_node_visitor_t,
763 ) -> rmw_ret_t,
764 >,
765 #[doc = " Upstream `rmw_get_topic_names_and_types`."]
766 pub get_topic_names_and_types: ::core::option::Option<
767 unsafe extern "C" fn(
768 session: *const rmw_session_t,
769 no_demangle: bool,
770 visitor: rmw_names_and_types_visitor_t,
771 ) -> rmw_ret_t,
772 >,
773 #[doc = " Upstream `rmw_get_service_names_and_types`."]
774 pub get_service_names_and_types: ::core::option::Option<
775 unsafe extern "C" fn(
776 session: *const rmw_session_t,
777 visitor: rmw_names_and_types_visitor_t,
778 ) -> rmw_ret_t,
779 >,
780 #[doc = " Upstream `rmw_get_publisher_names_and_types_by_node`."]
781 pub get_publisher_names_and_types_by_node: ::core::option::Option<
782 unsafe extern "C" fn(
783 session: *const rmw_session_t,
784 node_name: *const core::ffi::c_char,
785 node_namespace: *const core::ffi::c_char,
786 no_demangle: bool,
787 visitor: rmw_names_and_types_visitor_t,
788 ) -> rmw_ret_t,
789 >,
790 #[doc = " Upstream `rmw_get_subscriber_names_and_types_by_node`."]
791 pub get_subscriber_names_and_types_by_node: ::core::option::Option<
792 unsafe extern "C" fn(
793 session: *const rmw_session_t,
794 node_name: *const core::ffi::c_char,
795 node_namespace: *const core::ffi::c_char,
796 no_demangle: bool,
797 visitor: rmw_names_and_types_visitor_t,
798 ) -> rmw_ret_t,
799 >,
800 #[doc = " Upstream `rmw_get_service_names_and_types_by_node`."]
801 pub get_service_names_and_types_by_node: ::core::option::Option<
802 unsafe extern "C" fn(
803 session: *const rmw_session_t,
804 node_name: *const core::ffi::c_char,
805 node_namespace: *const core::ffi::c_char,
806 visitor: rmw_names_and_types_visitor_t,
807 ) -> rmw_ret_t,
808 >,
809 #[doc = " Upstream `rmw_get_client_names_and_types_by_node`."]
810 pub get_client_names_and_types_by_node: ::core::option::Option<
811 unsafe extern "C" fn(
812 session: *const rmw_session_t,
813 node_name: *const core::ffi::c_char,
814 node_namespace: *const core::ffi::c_char,
815 visitor: rmw_names_and_types_visitor_t,
816 ) -> rmw_ret_t,
817 >,
818 #[doc = " Upstream `rmw_get_publishers_info_by_topic`."]
819 pub get_publishers_info_by_topic: ::core::option::Option<
820 unsafe extern "C" fn(
821 session: *const rmw_session_t,
822 topic_name: *const core::ffi::c_char,
823 no_mangle: bool,
824 visitor: rmw_topic_endpoint_info_visitor_t,
825 ) -> rmw_ret_t,
826 >,
827 #[doc = " Upstream `rmw_get_subscriptions_info_by_topic`."]
828 pub get_subscriptions_info_by_topic: ::core::option::Option<
829 unsafe extern "C" fn(
830 session: *const rmw_session_t,
831 topic_name: *const core::ffi::c_char,
832 no_mangle: bool,
833 visitor: rmw_topic_endpoint_info_visitor_t,
834 ) -> rmw_ret_t,
835 >,
836 #[doc = " Upstream `rmw_count_publishers`."]
837 pub count_publishers: ::core::option::Option<
838 unsafe extern "C" fn(
839 session: *const rmw_session_t,
840 topic_name: *const core::ffi::c_char,
841 count: *mut usize,
842 ) -> rmw_ret_t,
843 >,
844 #[doc = " Upstream `rmw_count_subscribers`."]
845 pub count_subscribers: ::core::option::Option<
846 unsafe extern "C" fn(
847 session: *const rmw_session_t,
848 topic_name: *const core::ffi::c_char,
849 count: *mut usize,
850 ) -> rmw_ret_t,
851 >,
852 #[doc = " Upstream `rmw_node_get_graph_guard_condition`.\n\n Registers a callback fired when the graph CHANGES. Upstream returns a\n guard condition the caller adds to a wait set; we have no wait set to\n add it to, and guard conditions are an executor concept here, so this is\n the `set_wake_callback` shape instead — the one guard condition whose\n trigger is genuinely backend knowledge.\n\n The callback is an EDGE, carrying no payload: delivering WHAT changed\n would mean buffering it, which is the graph cache a small target cannot\n afford.\n\n Named after upstream mechanically, per the campaign's rule, but the\n honest name for this shape would be set-on-graph-change-callback —\n flagged for W5 rather than decided quietly here. (Unticked: no such\n slot exists, and a backtick in this header means it does.)\n\n `callback == NULL` clears any previously installed edge; the backend\n must drop the stored (callback, user_data) and never invoke it again\n after this returns. A clear is also the runtime's CAPABILITY PROBE, so\n it must be cheap and must answer the way a real install would:\n `NROS_RMW_RET_OK` from a backend that has a graph,\n `NROS_RMW_RET_UNSUPPORTED` from one that does not. Nullity of this\n slot cannot serve as that probe on the Rust road, where the adapter\n fills every slot for every backend.\n\n NULL slot = backend has no graph-change edge (uORB, and any\n backend that discovers nothing). The runtime surfaces that as\n UNSUPPORTED and declines to create the guard condition: handing back\n one nothing will ever trigger is the silent difference RFC-0089 Part I\n refuses. phase-467 Row 8 wrote this paragraph, because the slot had\n carried no NULL sentence at all — unlike `set_wake_callback` two\n hundred lines above — and `check-rmw-slot-producers.py` reads a\n REACHABLE slot with neither a producer nor a documented NULL as\n `unimplemented`."]
853 pub node_get_graph_guard_condition: ::core::option::Option<
854 unsafe extern "C" fn(
855 session: *mut rmw_session_t,
856 callback: rmw_event_callback_t,
857 user_data: *const core::ffi::c_void,
858 ) -> rmw_ret_t,
859 >,
860 #[doc = " Upstream `rmw_create_node`.\n\n Declares a node on the graph. NULL slot is the expected implementation\n in a static image: the runtime still tracks the node, the backend simply\n has nothing to declare.\n\n Deviations from upstream, declared: no `rmw_context_t *` (an image has\n one session and reaches it directly), and the node is an OUT parameter\n rather than a returned pointer — no runtime allocation, the caller owns\n the storage, exactly as `create_publisher` does.\n\n The runtime calls this once per distinct `(name, namespace_)`."]
861 pub create_node: ::core::option::Option<
862 unsafe extern "C" fn(
863 session: *mut rmw_session_t,
864 name: *const core::ffi::c_char,
865 namespace_: *const core::ffi::c_char,
866 out: *mut rmw_node_t,
867 ) -> rmw_ret_t,
868 >,
869 #[doc = " Upstream `rmw_destroy_node`.\n\n Releases the backend's `backend_data`; the shell stays valid until its\n owner drops it. Called for every node the session created, from\n `close()`, BEFORE `destroy_session` — a backend's node state hangs off\n its session state, so the order is not free.\n\n NULL slot: nothing is called and the runtime simply forgets the node.\n Correct only for a backend that allocates nothing in `create_node`;\n a backend that fills `backend_data` there must fill this too, or it\n leaks one node's state per session close (issue 0800 — this slot had a\n producer in no backend and a consumer nowhere, so the leak was\n indistinguishable from an optional slot nobody needed)."]
870 pub destroy_node:
871 ::core::option::Option<unsafe extern "C" fn(node: *mut rmw_node_t) -> rmw_ret_t>,
872 #[doc = " Upstream `rmw_set_log_severity`. Exact parity.\n\n Sets the verbosity of the BACKEND's own logging — Cyclone's `dds_log`,\n zenoh-pico's log, the XRCE client's. Not `nros_log`: that is the\n runtime's logger, already runtime-settable through\n `nros_log::Logger::set_level`, and it needs no ABI to reach.\n\n This IS a slot rather than a plain ABI function, and the distinction is\n the one the pure functions turn on: \"what does this middleware print\"\n genuinely varies by middleware, so a per-backend answer is correct here\n rather than a defect. All three reference implementations\n (`librmw_{cyclonedds,fastrtps,zenoh}_cpp.so`) implement it with real\n bodies, which is the opposite of what they do for\n `rmw_get_serialized_message_size`.\n\n Phase 376 W5 — this was DECLINED, on the reasoning that \"log level is a\n build-time constant (nros_log); a runtime setter implies a mutable\n global\". Both clauses were false: `Logger::level` is an `AtomicU8` with a\n public `set_level` already used by tests, and the compile-time part is a\n CEILING that defaults open. The decline described a design we do not\n have.\n\n NULL slot: the backend has no adjustable logging, and the runtime\n surfaces `UNSUPPORTED`."]
873 pub set_log_severity: ::core::option::Option<
874 unsafe extern "C" fn(severity: rmw_log_severity_t::Type) -> rmw_ret_t,
875 >,
876 #[doc = " The `NROS_RMW_QOS_POLICY_*` bits this backend honours, for the session\n behind @p session. RTOS addition; upstream has no counterpart, because\n upstream links ONE rmw per process and its equivalent knowledge is\n compiled into `rmw_qos_profile_check_compatible`.\n\n The runtime validates a requested profile against this mask at entity\n create and answers `NROS_RMW_RET_INCOMPATIBLE_QOS` for a policy the\n backend cannot enforce — the no-silent-downgrade contract, decided\n where it can still be reported.\n\n WHY IT IS A SLOT AND NOT A CONSTANT. The cffi route multiplexes: which\n backend it is talking to is decided at run time by\n `nros_rmw_cffi_register_named`, and until this slot existed the route\n had no way to ask. It therefore answered the UNION of what any\n nano-ros-supported RMW honours — issue 1329, measured — so an\n application asking cyclonedds for `avoid_ros_namespace_conventions`, or\n XRCE for a deadline, was admitted and then ignored downstream. A union\n is an over-claim for every member of it.\n\n A BIT IS EARNED, NOT DECLARED. Setting one asserts that the backend\n READS the mapped profile field and either applies the request or\n refuses a value it cannot serve. Forwarding the field to somebody else\n — a discovery keyexpr, an Agent that may ignore it — is not honouring\n it. `check-qos-mask-derivation` holds each backend's mask equal to the\n set of `nros-qos-honours:` claims sited in its own sources, in both\n directions, so this slot cannot drift away from the code under it.\n\n NULL SLOT: the backend has NOT SAID, and the runtime assumes\n `NROS_RMW_QOS_POLICY_NONE`. The same is true of a non-OK return; @p\n out_mask is then not read.\n\n That choice is the one thing here worth arguing, so: the two\n alternatives are both worse. \"NULL means the union\" reinstates issue\n 1329 with the ABI as its author — a route that cannot ask answering as\n though every backend honoured everything. \"NULL means do not validate\"\n is worse still: it admits even the bits no backend implements, and it\n switches off the no-silent-downgrade contract at the one seam that\n enforces it, invisibly. NONE is the same answer\n `nros_rmw::Session::supported_qos_policies` gives an implementation\n that has written no code (phase-428 W9 moved that default from `CORE`\n to nothing for exactly this reason), so the ABI and the trait give one\n answer to one question.\n\n NONE is loud rather than silent: the affected creates fail with\n `INCOMPATIBLE_QOS` at once, naming a policy, and the route logs the\n missing slot once per session. An application cannot mistake it for\n working, which is what the union allowed for as long as it stood.\n\n So a backend that honours ANY policy must fill this slot — not as an\n ABI requirement (`first_missing_vtable_slot` does not demand it, and\n `check-rmw-required-slots` holds that set equal to what the runtime\n `.expect()`s), but because leaving it NULL is a declaration that it\n honours nothing. `check-qos-mask-derivation` refuses a backend that\n carries claims and no slot.\n\n @p session is BORROWED for the call. A backend whose answer does not\n depend on the session ignores it; one that configures policies per\n session (a connection-parameter-driven Agent, say) reads its own state\n off `session->backend_data`."]
877 pub supported_qos_policies: ::core::option::Option<
878 unsafe extern "C" fn(session: *const rmw_session_t, out_mask: *mut u32) -> rmw_ret_t,
879 >,
880 #[doc = " Upstream `rmw_subscription_set_on_new_message_callback`. Exact parity\n in name and signature.\n\n `set_wake_callback` above wakes the session's executor on ANY arrival;\n this names the entity. The backend stores `(callback, user_data)`\n against @p subscription and calls `callback(user_data, n)` from its\n arrival path when samples become takeable on it, with `n >= 1`.\n\n `n` is a LOWER BOUND, not upstream's exact count: a backend that\n cannot count calls once per arrival with 1, and none is required to\n track per-entity pending depth for it (issue 0960's RAM argument, kept).\n Nor is a backend required to replay samples that arrived BEFORE the\n registration, as upstream's backends do; a caller registers first and\n then checks `has_data`, which closes the gap.\n\n A second call REPLACES the stored registration; `callback == NULL`\n clears it. After either returns, the backend never calls the previous\n pair again. `destroy_subscription` clears it as well.\n\n `callback` may run on a backend thread, concurrently with any other\n slot, so it must be short and non-blocking (set a flag, signal a\n condition variable).\n\n @p subscription is BORROWED for the call; the backend keys the\n registration on its own `backend_data`, not on the struct's address.\n\n NULL SLOT, or `NROS_RMW_RET_UNSUPPORTED`: the backend cannot name the\n entity whose data arrived (uORB, a poll-only bare-metal link).\n The runtime then reports the registration as unsupported rather than\n OK, and an executor falls back to the session wake."]
881 pub subscription_set_on_new_message_callback: ::core::option::Option<
882 unsafe extern "C" fn(
883 subscription: *mut rmw_subscription_t,
884 callback: rmw_event_callback_t,
885 user_data: *const core::ffi::c_void,
886 ) -> rmw_ret_t,
887 >,
888 #[doc = " Upstream `rmw_service_set_on_new_request_callback`: `callback` fires\n when a request becomes takeable. Same contract as the subscription\n slot above."]
889 pub service_set_on_new_request_callback: ::core::option::Option<
890 unsafe extern "C" fn(
891 service: *mut rmw_service_t,
892 callback: rmw_event_callback_t,
893 user_data: *const core::ffi::c_void,
894 ) -> rmw_ret_t,
895 >,
896 #[doc = " Upstream `rmw_client_set_on_new_response_callback`: `callback` fires\n when a response becomes takeable. Same contract as the subscription\n slot above."]
897 pub client_set_on_new_response_callback: ::core::option::Option<
898 unsafe extern "C" fn(
899 client: *mut rmw_client_t,
900 callback: rmw_event_callback_t,
901 user_data: *const core::ffi::c_void,
902 ) -> rmw_ret_t,
903 >,
904 #[doc = " Upstream `rmw_count_clients` (Jazzy). The service twin of\n `count_publishers`: how many service CLIENTS this session can see on\n @p service_name, a ROS name (`/add_two_ints`) the backend mangles as it\n does for its own clients. Same warm-up caveat as every graph slot: a\n count reflects what has already been discovered.\n\n NULL slot, or `NROS_RMW_RET_UNSUPPORTED`: the backend has no graph\n (XRCE, uORB) or it is not running — never a fabricated 0, which would\n claim there are none."]
905 pub count_clients: ::core::option::Option<
906 unsafe extern "C" fn(
907 session: *const rmw_session_t,
908 service_name: *const core::ffi::c_char,
909 count: *mut usize,
910 ) -> rmw_ret_t,
911 >,
912 #[doc = " Upstream `rmw_count_services` (Jazzy). As `count_clients`, counting\n service SERVERS."]
913 pub count_services: ::core::option::Option<
914 unsafe extern "C" fn(
915 session: *const rmw_session_t,
916 service_name: *const core::ffi::c_char,
917 count: *mut usize,
918 ) -> rmw_ret_t,
919 >,
920 #[doc = " Upstream `rmw_get_gid_for_client` (Jazzy). Exact parity. The client\n twin of `get_gid_for_publisher`: this client's own identity, the value\n a server keys the request id on (zenoh: the gid stamped into the request\n attachment; Cyclone: the request writer's GUID). The backend zero-pads to\n the full width; see `rmw_gid_t`."]
921 pub get_gid_for_client: ::core::option::Option<
922 unsafe extern "C" fn(client: *const rmw_client_t, gid: *mut rmw_gid_t) -> rmw_ret_t,
923 >,
924 #[doc = " Upstream `rmw_event_type_is_supported` (Jazzy). Exact parity.\n\n A STATIC per-backend answer: whether ANY entity of this backend can\n carry status event @p event_type, through either event shape\n (`*_event_init` callback or `*_take_event` poll). It is the question a\n port asks at configuration so it fails there rather than at entity\n creation; registration stays the per-entity authority (a kind the\n backend supports can still be refused for one entity).\n\n A `true` is only meaningful where the backend also fills an event path\n for that kind's side; the runtime requires both.\n\n NULL slot: the backend states nothing per kind, and the runtime falls\n back to slot granularity — a kind is supported iff its side has an\n event path. A backend with neither (uORB) therefore answers\n `false` for every kind. The runtime reads this slot through ONE helper\n (cffi's `backend_event_type_is_supported`), never a per-layer guess."]
925 pub event_type_is_supported:
926 ::core::option::Option<unsafe extern "C" fn(event_type: rmw_event_type_t::Type) -> bool>,
927 #[doc = " Upstream `rmw_take_request`, with its `rmw_service_info_t`.\n\n Issue 2081 — optional, appended. `take_request` plus the request's metadata — who sent it\n (`request_id`: the client's writer GUID and its sequence number) and the\n timestamps the backend has — written to caller-owned storage. The\n service twin of `take_with_info`.\n\n Deviations from upstream, declared: the payload is bytes, as for\n `take_request`; and `*seq_out`, the backend's reply-correlation token\n that `send_response` takes back, is reported BESIDE the struct rather\n than as its `request_id.sequence_number`, because a backend's token\n (a reply-slot index on Cyclone, XRCE and zenoh) is not the client's\n sequence number, and both are needed.\n\n Every other rule is `take_request`'s: `*taken`, `request->len`,\n `*seq_out` and `*service_info` are written only on `NROS_RMW_RET_OK`\n with `*taken = true`; a field the backend cannot report is zero (see\n `rmw_service_info_t`).\n\n DISPATCHED when non-NULL: the runtime takes every request through this\n slot and converts the struct to its `ServiceInfo`. A NULL slot falls\n back to `take_request` plus the Rust adapter's side table, which is\n metadata for a Rust backend and none for a C/C++ one."]
928 pub take_request_with_info: ::core::option::Option<
929 unsafe extern "C" fn(
930 server: *const rmw_service_t,
931 request: *mut rmw_mut_byte_span_t,
932 seq_out: *mut i64,
933 taken: *mut bool,
934 service_info: *mut rmw_service_info_t,
935 ) -> rmw_ret_t,
936 >,
937 #[doc = " Upstream `rmw_take_response`, with its `rmw_service_info_t`.\n\n Issue 2136 — optional, appended. The client twin of\n `take_request_with_info`: `take_response` plus the reply's metadata,\n written to caller-owned storage —\n\n - `request_id`: the request this reply answers, as upstream's client\n sees it — the CLIENT's own request-writer GUID and the sequence\n number of its request;\n - `source_timestamp`: the SERVER's clock when it sent the reply;\n - `received_timestamp`: this client's clock when the reply arrived.\n\n Deviations from upstream, declared: the payload is bytes, as for\n `take_response`; and `*seq_out` stays beside the struct, because it is\n what the runtime matches a reply against (issue 2009) whether or not\n the backend can report the rest.\n\n Every other rule is `take_response`'s: `*taken`, `reply->len`,\n `*seq_out` and `*service_info` are written only on `NROS_RMW_RET_OK`\n with `*taken = true`; a field the backend cannot report is zero (see\n `rmw_service_info_t`), and a request id whose writer GUID is all zero\n is \"not reported\" however its sequence number reads.\n\n DISPATCHED when non-NULL: the runtime takes every reply through this\n slot and keeps the struct as the client's response info. A NULL slot\n falls back to `take_response`, and the reply reports nothing."]
938 pub take_response_with_info: ::core::option::Option<
939 unsafe extern "C" fn(
940 client: *const rmw_client_t,
941 reply: *mut rmw_mut_byte_span_t,
942 seq_out: *mut i64,
943 taken: *mut bool,
944 service_info: *mut rmw_service_info_t,
945 ) -> rmw_ret_t,
946 >,
947 #[doc = " phase-521 W3 (RFC-0113 D4) — how this session reports an arrival.\n\n Optional, appended. The executor's park ends on exactly two things:\n the tier's deadline, or readiness. This slot says which form of\n readiness the session has, so the park can be ended by data rather\n than the executor blocking inside the transport's own receive:\n\n - `NROS_RMW_READINESS_NOTIFY`: the backend calls the callback\n `set_wake_callback` installed when data arrives. `*out_handle` is\n not written.\n - `NROS_RMW_READINESS_WAITABLE`: the backend has an OS handle that\n becomes readable when data arrives — a file descriptor on POSIX and\n NuttX — written to `*out_handle`. The executor multiplexes it with\n its own wake object (`nros_platform_wake_park_handles_us`) and\n drives the session non-blockingly after the park. The handle stays\n valid until the session closes.\n - `NROS_RMW_READINESS_NONE`: neither. RFC-0113 D5: the runtime\n refuses such a session at open (phase-521 W6); the executor never\n blocks inside `drive_io`.\n\n `*out_kind` and `*out_handle` are written only on `NROS_RMW_RET_OK`.\n\n NULL slot: the session is `NROS_RMW_READINESS_NOTIFY` when\n `set_wake_callback` is non-NULL and `NROS_RMW_READINESS_NONE`\n otherwise — what the runtime inferred before this slot existed, so\n a backend that delivers a wake callback needs no change."]
948 pub readiness_source: ::core::option::Option<
949 unsafe extern "C" fn(
950 session: *const rmw_session_t,
951 out_kind: *mut u8,
952 out_handle: *mut i32,
953 ) -> rmw_ret_t,
954 >,
955}
956#[doc = " Runtime-pluggable custom transport. The runtime never\n dereferences `user_data`; it's the caller's per-transport\n context, threaded back into every callback's first argument.\n\n THIS declaration is the ABI single source of truth (RFC-0054): Rust\n consumes the committed bindgen output of this header, and\n `nros_rmw::NrosTransportOps` is the hand-written Rust-side view kept in\n lockstep with it — not the other way round. The previous wording had that\n backwards (issue 0331). Layout equivalence is asserted on both sides: see\n `nros_transport_ops_t` in `nros-rmw-cffi/tests/c_stubs/abi_layout_check.c`\n and the `const _` size/align block beside\n `nros_rmw_cffi_set_custom_transport` in `nros-rmw-cffi/src/lib.rs`. Same\n layout, same threading contract, same return codes."]
957#[repr(C)]
958#[derive(Debug, Copy, Clone)]
959pub struct nros_transport_ops_s {
960 #[doc = " ABI version. MUST equal `NROS_TRANSPORT_OPS_ABI_VERSION_V1`.\n Any other value is rejected at registration time with\n `NROS_RMW_RET_INCOMPATIBLE_ABI`."]
961 pub abi_version: u32,
962 #[doc = " Reserved padding for alignment stability across appends.\n Set to zero."]
963 pub _reserved: u32,
964 #[doc = " Opaque caller context, threaded back into every callback.\n Lifetime: must outlive the transport's active period\n (i.e. until `close` returns)."]
965 pub user_data: *mut core::ffi::c_void,
966 #[doc = " Open the underlying medium.\n\n @param user_data Caller-supplied context.\n @param params Opaque per-transport metadata (e.g. UART baud\n rate, USB-CDC endpoint id). May be NULL.\n @retval NROS_RMW_RET_OK on success.\n @retval <0 on failure (any `rmw_ret_t` error code)."]
967 pub open: ::core::option::Option<
968 unsafe extern "C" fn(
969 user_data: *mut core::ffi::c_void,
970 params: *const core::ffi::c_void,
971 ) -> i32,
972 >,
973 #[doc = " Tear the transport down. After `close` returns, the runtime\n will not invoke `read` or `write` on this transport unless\n `rclc_set_custom_transport` is called again."]
974 pub close: ::core::option::Option<unsafe extern "C" fn(user_data: *mut core::ffi::c_void)>,
975 #[doc = " Send `len` bytes from `buf`. Must NOT block beyond a brief\n hardware retry; long blocking should surface as\n `NROS_RMW_RET_TIMEOUT`.\n\n @retval NROS_RMW_RET_OK on success.\n @retval <0 on failure (any `rmw_ret_t` error code)."]
976 pub write: ::core::option::Option<
977 unsafe extern "C" fn(user_data: *mut core::ffi::c_void, buf: *const u8, len: usize) -> i32,
978 >,
979 #[doc = " Receive up to `len` bytes into `buf` within `timeout_ms`.\n\n @retval >=0 number of bytes read (may be less than `len`).\n @retval <0 on error / timeout (any `rmw_ret_t` error\n code)."]
980 pub read: ::core::option::Option<
981 unsafe extern "C" fn(
982 user_data: *mut core::ffi::c_void,
983 buf: *mut u8,
984 len: usize,
985 timeout_ms: u32,
986 ) -> i32,
987 >,
988}
989pub const NROS_RMW_RET_EXTENSION_BASE: i32 = 1000;
990pub const NROS_RMW_RET_OK: i32 = 0;
991pub const NROS_RMW_RET_ERROR: i32 = 1;
992pub const NROS_RMW_RET_TIMEOUT: i32 = 2;
993pub const NROS_RMW_RET_BAD_ALLOC: i32 = 10;
994pub const NROS_RMW_RET_INVALID_ARGUMENT: i32 = 11;
995pub const NROS_RMW_RET_UNSUPPORTED: i32 = 3;
996pub const NROS_RMW_RET_INCOMPATIBLE_QOS: i32 = 1000;
997pub const NROS_RMW_RET_TOPIC_NAME_INVALID: i32 = 1001;
998pub const NROS_RMW_RET_NODE_NAME_NON_EXISTENT: i32 = 203;
999pub const NROS_RMW_RET_LOAN_NOT_SUPPORTED: i32 = 1002;
1000pub const NROS_RMW_RET_NO_DATA: i32 = 1003;
1001pub const NROS_RMW_RET_WOULD_BLOCK: i32 = 1004;
1002pub const NROS_RMW_RET_BUFFER_TOO_SMALL: i32 = 1005;
1003pub const NROS_RMW_RET_MESSAGE_TOO_LARGE: i32 = 1006;
1004pub const NROS_RMW_RET_INCOMPATIBLE_ABI: i32 = 1007;
1005pub const NROS_RMW_RET_NO_BACKEND: i32 = 1008;
1006pub const NROS_RMW_RET_AMBIGUOUS_BACKEND: i32 = 1009;
1007pub const NROS_RMW_RET_UNKNOWN_BACKEND: i32 = 1010;
1008pub const NROS_RMW_RET_CONNECTION_FAILED: i32 = 1011;
1009pub const NROS_RMW_RET_INVALID_CONFIG: i32 = 1012;
1010pub const NROS_RMW_RET_INCORRECT_RMW_IMPLEMENTATION: i32 = 12;
1011pub const NROS_RMW_RELIABILITY_SYSTEM_DEFAULT: i32 = 0;
1012pub const NROS_RMW_RELIABILITY_RELIABLE: i32 = 1;
1013pub const NROS_RMW_RELIABILITY_BEST_EFFORT: i32 = 2;
1014pub const NROS_RMW_RELIABILITY_UNKNOWN: i32 = 3;
1015pub const NROS_RMW_RELIABILITY_BEST_AVAILABLE: i32 = 4;
1016pub const NROS_RMW_DURABILITY_SYSTEM_DEFAULT: i32 = 0;
1017pub const NROS_RMW_DURABILITY_TRANSIENT_LOCAL: i32 = 1;
1018pub const NROS_RMW_DURABILITY_VOLATILE: i32 = 2;
1019pub const NROS_RMW_DURABILITY_UNKNOWN: i32 = 3;
1020pub const NROS_RMW_DURABILITY_BEST_AVAILABLE: i32 = 4;
1021pub const NROS_RMW_HISTORY_SYSTEM_DEFAULT: i32 = 0;
1022pub const NROS_RMW_HISTORY_KEEP_LAST: i32 = 1;
1023pub const NROS_RMW_HISTORY_KEEP_ALL: i32 = 2;
1024pub const NROS_RMW_HISTORY_UNKNOWN: i32 = 3;
1025pub const RMW_GID_STORAGE_SIZE: i32 = 24;
1026pub const NROS_RMW_TAKE_LEN_UNKNOWN: i32 = 0;
1027pub const NROS_RMW_SEQUENCE_ID_UNKNOWN: i64 = -9223372036854775808;
1028pub const RMW_REQUEST_ID_WRITER_GUID_SIZE: i32 = 16;
1029pub const RMW_INET_ADDRSTRLEN: i32 = 48;
1030pub const NROS_RMW_DURATION_INFINITE_MS: i64 = 4294967295;
1031pub const NROS_RMW_DURATION_BEST_AVAILABLE_MS: i64 = 4294967294;
1032pub const RMW_SESSION_MAX_PROPERTIES: i32 = 16;
1033pub const NROS_RMW_QOS_POLICY_RELIABILITY: i32 = 1;
1034pub const NROS_RMW_QOS_POLICY_DURABILITY_VOLATILE: i32 = 2;
1035pub const NROS_RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL: i32 = 4;
1036pub const NROS_RMW_QOS_POLICY_HISTORY: i32 = 8;
1037pub const NROS_RMW_QOS_POLICY_DEPTH: i32 = 16;
1038pub const NROS_RMW_QOS_POLICY_DEADLINE: i32 = 32;
1039pub const NROS_RMW_QOS_POLICY_LIFESPAN: i32 = 64;
1040pub const NROS_RMW_QOS_POLICY_LIVELINESS_AUTOMATIC: i32 = 128;
1041pub const NROS_RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC: i32 = 256;
1042pub const NROS_RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE: i32 = 512;
1043pub const NROS_RMW_QOS_POLICY_LIVELINESS_LEASE: i32 = 1024;
1044pub const NROS_RMW_QOS_POLICY_AVOID_ROS_NAMESPACE_CONVENTIONS: i32 = 2048;
1045pub const NROS_RMW_QOS_POLICY_NONE: i32 = 0;
1046pub const NROS_RMW_QOS_POLICY_CORE: i32 = 27;
1047pub const NROS_RMW_VISITOR_DEFINED: i32 = 1;
1048pub const NROS_RMW_READINESS_NONE: i32 = 0;
1049pub const NROS_RMW_READINESS_NOTIFY: i32 = 1;
1050pub const NROS_RMW_READINESS_WAITABLE: i32 = 2;
1051#[doc = " Borrow-shaped union the backend supplies to the registered\n callback. The `kind` argument selects which member is valid.\n\n Members are APPENDED (issue 1972 grew it from 8 to 16 bytes). A caller\n owns the union it hands `*_take_event`, so a backend built against the\n older, smaller header writes within the larger one."]
1052#[repr(C)]
1053#[derive(Copy, Clone)]
1054pub union rmw_event_payload_t {
1055 pub liveliness_changed: rmw_liveliness_changed_status_t,
1056 pub count: rmw_count_status_t,
1057 pub matched: rmw_matched_status_t,
1058 pub incompatible_type: rmw_incompatible_type_status_t,
1059}
1060pub mod rmw_feature_t {
1061 #[doc = " Upstream `rmw_feature_t` — an optional piece of CONTENT a backend may or may\n not populate. Upstream defines exactly these two, both about whether\n message-info sequence numbers are real. Values mirror upstream's."]
1062 pub type Type = core::ffi::c_uint;
1063 pub const RMW_FEATURE_MESSAGE_INFO_PUBLICATION_SEQUENCE_NUMBER: Type = 0;
1064 pub const RMW_FEATURE_MESSAGE_INFO_RECEPTION_SEQUENCE_NUMBER: Type = 1;
1065}
1066pub mod rmw_liveliness_kind_t {
1067 #[doc = " Liveliness kind values for `rmw_qos_profile_t::liveliness_kind`.\n\n Upstream's numbering (W5/B2). `MANUAL_BY_NODE` and `MANUAL_BY_TOPIC` were\n SWAPPED here until 2026-08-24 — 2 meant BY_TOPIC to us and BY_NODE to\n upstream — which the cyclonedds backend then translated into a real DDS\n liveliness kind a ROS peer matches on."]
1068 pub type Type = core::ffi::c_uint;
1069 #[doc = " Let the middleware choose. Spelled `NONE` before W5/B2 and used the same\n way: nothing is asserted and nothing is tracked. Upstream has no\n separate `NONE`, so the two collapse onto value 0."]
1070 pub const NROS_RMW_LIVELINESS_SYSTEM_DEFAULT: Type = 0;
1071 #[doc = " Backend's keepalive task asserts liveliness automatically."]
1072 pub const NROS_RMW_LIVELINESS_AUTOMATIC: Type = 1;
1073 #[doc = " Application calls `assert_liveliness()` at the node level."]
1074 pub const NROS_RMW_LIVELINESS_MANUAL_BY_NODE: Type = 2;
1075 #[doc = " Application calls `assert_liveliness()` per topic explicitly."]
1076 pub const NROS_RMW_LIVELINESS_MANUAL_BY_TOPIC: Type = 3;
1077 #[doc = " The backend could not determine this policy."]
1078 pub const NROS_RMW_LIVELINESS_UNKNOWN: Type = 4;
1079 #[doc = " Upstream's `RMW_QOS_POLICY_LIVELINESS_BEST_AVAILABLE` (Iron+), issue\n 1973. A request value only — see `NROS_RMW_RELIABILITY_BEST_AVAILABLE`."]
1080 pub const NROS_RMW_LIVELINESS_BEST_AVAILABLE: Type = 5;
1081}
1082pub mod rmw_qos_compatibility_type_t {
1083 #[doc = " Verdict of a QoS compatibility check. Upstream `rmw_qos_compatibility_type_t`,\n values included.\n\n `WARNING` means \"compatible as far as could be checked, but at least one\n policy on one side is `*_UNKNOWN`\" — the backend could not read it back.\n Reachable since W5/B2 gave the policies an UNKNOWN encoding; it was defined\n and unreachable before that, so the value could not be reused for anything\n else in the meantime.\n\n A definite clash OUTRANKS an unknown: if the policies that COULD be compared\n are already incompatible the verdict is `ERROR`, because softening it to a\n warning would hide something the caller can act on."]
1084 pub type Type = core::ffi::c_uint;
1085 pub const RMW_QOS_COMPATIBILITY_OK: Type = 0;
1086 pub const RMW_QOS_COMPATIBILITY_WARNING: Type = 1;
1087 pub const RMW_QOS_COMPATIBILITY_ERROR: Type = 2;
1088}
1089pub mod nros_rmw_qos_clash_t {
1090 #[doc = " Which policies clashed, as a bitmask. A nano-ros extension: upstream reports\n the reason only as prose, which a target cannot act on."]
1091 pub type Type = core::ffi::c_uint;
1092 pub const NROS_RMW_QOS_CLASH_NONE: Type = 0;
1093 pub const NROS_RMW_QOS_CLASH_RELIABILITY: Type = 1;
1094 pub const NROS_RMW_QOS_CLASH_DURABILITY: Type = 2;
1095 pub const NROS_RMW_QOS_CLASH_DEADLINE: Type = 4;
1096 pub const NROS_RMW_QOS_CLASH_LIVELINESS_KIND: Type = 8;
1097 pub const NROS_RMW_QOS_CLASH_LIVELINESS_LEASE: Type = 16;
1098}
1099pub mod rmw_log_severity_t {
1100 #[doc = " Log severity — upstream `rmw_log_severity_t`, values included.\n\n The values are `rcutils`' ladder (`DEBUG 10`, `INFO 20`, …), not a dense\n 0..N, so they are written out rather than renumbered: a caller that has an\n `rcutils` severity in hand can pass it straight through.\n\n There is no `TRACE`. `nros_log::Severity` has one, and it maps to `DEBUG`\n crossing this seam — losing a distinction upstream never had is better than\n inventing a value a ROS-side caller cannot produce."]
1101 pub type Type = core::ffi::c_uint;
1102 pub const RMW_LOG_SEVERITY_UNSET: Type = 0;
1103 pub const RMW_LOG_SEVERITY_DEBUG: Type = 10;
1104 pub const RMW_LOG_SEVERITY_INFO: Type = 20;
1105 pub const RMW_LOG_SEVERITY_WARN: Type = 30;
1106 pub const RMW_LOG_SEVERITY_ERROR: Type = 40;
1107 pub const RMW_LOG_SEVERITY_FATAL: Type = 50;
1108}
1109pub mod rmw_endpoint_type_t {
1110 #[doc = " Which end of a topic an endpoint is — upstream `rmw_endpoint_type_t`."]
1111 pub type Type = core::ffi::c_uint;
1112 pub const RMW_ENDPOINT_INVALID: Type = 0;
1113 pub const RMW_ENDPOINT_PUBLISHER: Type = 1;
1114 pub const RMW_ENDPOINT_SUBSCRIPTION: Type = 2;
1115}
1116pub mod rmw_transport_protocol_t {
1117 #[doc = " Transport protocol of a network flow — upstream `rmw_transport_protocol_t`,\n values included."]
1118 pub type Type = core::ffi::c_uint;
1119 pub const RMW_TRANSPORT_PROTOCOL_UNKNOWN: Type = 0;
1120 pub const RMW_TRANSPORT_PROTOCOL_UDP: Type = 1;
1121 pub const RMW_TRANSPORT_PROTOCOL_TCP: Type = 2;
1122 pub const RMW_TRANSPORT_PROTOCOL_COUNT: Type = 3;
1123}
1124pub mod rmw_internet_protocol_t {
1125 #[doc = " Internet protocol of a network flow — upstream `rmw_internet_protocol_t`."]
1126 pub type Type = core::ffi::c_uint;
1127 pub const RMW_INTERNET_PROTOCOL_UNKNOWN: Type = 0;
1128 pub const RMW_INTERNET_PROTOCOL_IPV4: Type = 1;
1129 pub const RMW_INTERNET_PROTOCOL_IPV6: Type = 2;
1130 pub const RMW_INTERNET_PROTOCOL_COUNT: Type = 3;
1131}
1132pub mod rmw_event_type_t {
1133 #[doc = " Status event kinds. Stable integer values; new kinds extend the enum at\n the END, never in the middle."]
1134 pub type Type = core::ffi::c_uint;
1135 #[doc = " Subscriber: a tracked publisher's liveliness state changed."]
1136 pub const NROS_RMW_EVENT_LIVELINESS_CHANGED: Type = 0;
1137 #[doc = " Subscriber: an expected sample didn't arrive within the\n configured deadline."]
1138 pub const NROS_RMW_EVENT_REQUESTED_DEADLINE_MISSED: Type = 1;
1139 #[doc = " Subscriber: backend dropped a sample (overflow / etc.)."]
1140 pub const NROS_RMW_EVENT_MESSAGE_LOST: Type = 2;
1141 #[doc = " Publisher: this publisher missed its own liveliness assertion."]
1142 pub const NROS_RMW_EVENT_LIVELINESS_LOST: Type = 3;
1143 #[doc = " Publisher: this publisher promised X Hz, fell behind."]
1144 pub const NROS_RMW_EVENT_OFFERED_DEADLINE_MISSED: Type = 4;
1145 #[doc = " Publisher: the set of subscriptions matched to it changed (one\n appeared or left). Payload: `matched`. Upstream\n RMW_EVENT_PUBLICATION_MATCHED (Iron+), issue 1972."]
1146 pub const NROS_RMW_EVENT_PUBLICATION_MATCHED: Type = 5;
1147 #[doc = " Subscription: the set of publishers matched to it changed. Payload:\n `matched`. Upstream RMW_EVENT_SUBSCRIPTION_MATCHED."]
1148 pub const NROS_RMW_EVENT_SUBSCRIPTION_MATCHED: Type = 6;
1149 #[doc = " Publisher: an endpoint on the same topic was seen with a DIFFERENT\n type. Payload: `incompatible_type`. Upstream\n RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE."]
1150 pub const NROS_RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE: Type = 7;
1151 #[doc = " Subscription: an endpoint on the same topic was seen with a\n DIFFERENT type. Payload: `incompatible_type`. Upstream\n RMW_EVENT_SUBSCRIPTION_INCOMPATIBLE_TYPE."]
1152 pub const NROS_RMW_EVENT_SUBSCRIPTION_INCOMPATIBLE_TYPE: Type = 8;
1153}
1154pub mod nros_rmw_session_mode_t {
1155 #[doc = " Session mode for `create_session`'s @p mode parameter (issue 0331).\n\n These values were previously an undocumented bare `uint8_t` with no legal-\n value list — the only slot in the vtable without one — encoded inline as\n `0u8` / `1u8` at the Rust boundary.\n\n Divergence from `rmw.h`, recorded deliberately: Humble's\n `rmw_init_options_t` carries `instance_id`, `implementation_identifier`,\n `domain_id`, `security_options`, `localhost_only`, `enclave`, `allocator`\n and `impl` — and has NO session-mode concept. (This list said \"domain_id,\n enclave, security_options and discovery_options\" until 2026-08-24, which\n omitted five of the eight and named one, `discovery_options`, that is an\n IRON field and does not exist in the distro our recorded contract is taken\n from. What we do and do not carry from those eight is issue 0785.) This parameter is closest to zenoh's `whatami`, and a\n backend that has no such notion (cyclonedds, XRCE) is expected to IGNORE it\n rather than fail. Folding it into backend-private config behind the locator\n — so the agnostic vtable stops carrying a backend-shaped field — is the\n structural fix, and is not done here; see issue 0808. (This said \"see issue\n 0331\" until 2026-08-26. That issue documented this enum and stated in its own\n resolution that the structural half was NOT done, deferring it to issue 0330\n part 3 — which resolved a different concern entirely. Both are closed, so the\n fold was tracked by nothing; 0808 is its home.)"]
1156 pub type Type = core::ffi::c_uint;
1157 #[doc = " Connect to a router/agent as a client. The default."]
1158 pub const NROS_RMW_SESSION_MODE_CLIENT: Type = 0;
1159 #[doc = " Peer-to-peer, no router. Backends without a peer mode ignore this."]
1160 pub const NROS_RMW_SESSION_MODE_PEER: Type = 1;
1161}
1162unsafe extern "C" {
1163 #[doc = " Which policies of `offered` (a publisher's) and `requested` (a\n subscription's) are incompatible, as a bitmask — no strings, so an image\n that only needs the verdict never links the reason table.\n\n Argument order is upstream's: publisher profile first.\n\n Writes `*compatibility` and `*clash_mask` on `NROS_RMW_RET_OK`;\n `NROS_RMW_RET_INVALID_ARGUMENT` if either out-parameter is NULL."]
1164 pub fn nros_rmw_qos_incompatibility_mask(
1165 offered: rmw_qos_profile_t,
1166 requested: rmw_qos_profile_t,
1167 compatibility: *mut rmw_qos_compatibility_type_t::Type,
1168 clash_mask: *mut u32,
1169 ) -> rmw_ret_t;
1170}
1171unsafe extern "C" {
1172 #[doc = " Upstream `rmw_qos_profile_check_compatible`. Exact parity.\n\n `reason` may be NULL with `reason_size` 0 — the create-time path, which\n wants the verdict and nothing else.\n\n The reason is SELECTED, never FORMATTED: each clash bit maps to one\n `static const char[]` and they are appended by a bounded copy. Upstream's\n implementations use `snprintf`, which would drag the printf engine into\n images that deliberately excluded it.\n\n Truncation is NOT failure: the buffer is always NUL-terminated and the\n verdict is still written. Returning `BUFFER_TOO_SMALL` would make a\n small-buffer caller lose the load-bearing half of the answer."]
1173 pub fn rmw_qos_profile_check_compatible(
1174 publisher_profile: rmw_qos_profile_t,
1175 subscription_profile: rmw_qos_profile_t,
1176 compatibility: *mut rmw_qos_compatibility_type_t::Type,
1177 reason: *mut core::ffi::c_char,
1178 reason_size: usize,
1179 ) -> rmw_ret_t;
1180}
1181unsafe extern "C" {
1182 #[doc = " Upstream `rmw_compare_gids_equal`. Exact parity.\n\n Equal means the same `implementation_identifier` AND the same 24 bytes. Gids\n from different backends are never equal — which matters more here than\n upstream, because `nros_rmw_cffi_register_named` admits several backends in\n one image.\n\n Comparison is over the WHOLE array, so a producer must zero-pad; see\n `rmw_gid_t`."]
1183 pub fn rmw_compare_gids_equal(
1184 gid1: *const rmw_gid_t,
1185 gid2: *const rmw_gid_t,
1186 result: *mut bool,
1187 ) -> rmw_ret_t;
1188}
1189unsafe extern "C" {
1190 #[doc = " Phase 104.B.2 — register a backend under a stable name. Multiple\n backends can coexist (bridge nodes); consumers select via\n `nros_rmw_cffi_lookup` or the higher-level\n `Executor::node_builder(...).rmw(...)` path.\n\n Names: UTF-8, NUL-terminated, ≤ 31 bytes (excluding NUL).\n Reserved: \"zenoh\", \"dds\", \"xrce\", \"cyclonedds\", future \"uorb\".\n (\"default\" was the implicit name of the unnamed single-argument form,\n deleted by phase-482 W6; name the protocol instead.)\n\n Duplicate registration of the same name overwrites the previous\n vtable (idempotent for ctor-fires-twice).\n\n Returns:\n * NROS_RMW_RET_OK on success.\n * NROS_RMW_RET_INVALID_ARGUMENT if name or vtable is NULL,\n the name is empty, or exceeds 31 bytes.\n * NROS_RMW_RET_ERROR if the registry is full\n (NROS_RMW_MAX_BACKENDS reached)."]
1191 pub fn nros_rmw_cffi_register_named(
1192 name: *const core::ffi::c_char,
1193 vtable: *const nros_rmw_vtable_t,
1194 ) -> rmw_ret_t;
1195}
1196unsafe extern "C" {
1197 #[doc = " Look up a backend's vtable by name. Returns NULL if no backend is\n registered under `name`. The returned pointer is valid for the\n program's lifetime."]
1198 pub fn nros_rmw_cffi_lookup(name: *const core::ffi::c_char) -> *const nros_rmw_vtable_t;
1199}
1200unsafe extern "C" {
1201 #[doc = " Diagnostic helper — fills `buf` with pointers to up to `cap`\n registered backend names. Returns the total number of registered\n backends (may exceed `cap`; caller can re-query with a larger\n buffer). Pointer-valid for the program's lifetime. Pass\n `buf=NULL, cap=0` to query the count only."]
1202 pub fn nros_rmw_cffi_registered_names(buf: *mut *const core::ffi::c_char, cap: usize) -> usize;
1203}
1204unsafe extern "C" {
1205 #[doc = " Install a custom transport for subsequent session opens.\n\n The struct's contents are copied internally; the caller may\n stack-allocate. To clear the slot, pass NULL.\n\n The fn pointer is exported from the nano-ros C staticlib\n (`packages/api/nros-c/`), where it forwards to the\n `nros-rmw-cffi` registry. C++ consumers should include\n `<rclcpp/transport.hpp>` (from `nros-cpp`) which calls this\n function under the hood.\n\n @retval NROS_RMW_RET_OK on success (transport installed or\n cleared).\n @retval NROS_RMW_RET_INCOMPATIBLE_ABI when `ops` is non-NULL but\n `ops->abi_version` does not match\n `NROS_TRANSPORT_OPS_ABI_VERSION_V1`. The previously\n installed transport (if any) is left untouched."]
1206 pub fn nros_rmw_cffi_set_custom_transport(ops: *const nros_transport_ops_t) -> rmw_ret_t;
1207}