Skip to main content

nros_rmw_cffi/
generated.rs

1//! AUTO-GENERATED by scripts/gen-abi-bindings.sh (bindgen 0.72.1) — DO NOT EDIT.
2//!
3//! Source of truth: `packages/core/nros-rmw-abi/include/nros/*.h` (RFC-0054).
4//! Edit the headers, rerun the script, commit both.
5#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
6#![allow(unsafe_op_in_unsafe_fn, clippy::missing_safety_doc)]
7/* automatically generated by rust-bindgen 0.72.1 */
8
9#[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  **24, not 16, and that is a discrepancy worth knowing about.** Our own\n  `MessageInfo::publisher_gid` (`nros-core`, `PUBLISHER_GID_SIZE`) is 16 bytes,\n  while the Cyclone backend already computes 24-byte gids for the DDS graph\n  (`entity_gid_24` in `graph.cpp`). Under upstream semantics those are the SAME\n  identifier, so a gid obtained from a take cannot today be compared with one\n  from `get_gid_for_publisher` without a documented mapping — and the narrower\n  one truncates. The ABI takes upstream's width; reconciling `MessageInfo` is\n  its own change and is NOT done here. */\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. Undefined on 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. Today this metadata reaches Rust callers through\n  `MESSAGE_INFO_TABLE`, a side table in `nros-rmw-cffi` keyed on the\n  subscription's `backend_data` ADDRESS. That table is a workaround, not a\n  design, and it never crosses the seam it exists for: only the Rust\n  trampoline writes it, so a C or C++ backend has no symbol to call and\n  message info is permanently absent for them. It also claims a pool slot per\n  subscription and never releases it, so a reused handle address inherits the\n  previous subscription's 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 = " 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."]
128#[repr(C)]
129#[derive(Debug, Copy, Clone)]
130pub struct rmw_qos_profile_t {
131    #[doc = "< @see NROS_RMW_RELIABILITY_*"]
132    pub reliability: u8,
133    #[doc = "< @see NROS_RMW_DURABILITY_*"]
134    pub durability: u8,
135    #[doc = "< @see NROS_RMW_HISTORY_*"]
136    pub history: u8,
137    #[doc = "< @see rmw_liveliness_kind_t"]
138    pub liveliness_kind: u8,
139    pub depth: u16,
140    #[doc = "< Reserved; must be zero."]
141    pub _reserved0: u16,
142    #[doc = " Subscription: max acceptable inter-arrival time, ms. Publisher:\n  max acceptable inter-publish (offered rate), ms.\n  0 = infinite (no deadline)."]
143    pub deadline_ms: u32,
144    #[doc = " Sample expiry, ms. Subscription filters samples older than\n  this. 0 = infinite (no expiry)."]
145    pub lifespan_ms: u32,
146    #[doc = " Liveliness lease, ms. Publisher must assert liveliness\n  within this window or be considered dead. 0 = infinite."]
147    pub liveliness_lease_ms: u32,
148    #[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.)"]
149    pub avoid_ros_namespace_conventions: u8,
150    #[doc = "< Reserved; must be zero."]
151    pub _reserved1: [u8; 3usize],
152}
153#[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."]
154#[repr(C)]
155#[derive(Debug, Copy, Clone)]
156pub struct rmw_topic_endpoint_info_t {
157    #[doc = " Node that owns the endpoint."]
158    pub node_name: *const core::ffi::c_char,
159    #[doc = " That node's namespace."]
160    pub node_namespace: *const core::ffi::c_char,
161    #[doc = " Fully-qualified type on the wire, e.g. `\"std_msgs/msg/Int32\"`."]
162    pub topic_type: *const core::ffi::c_char,
163    #[doc = " Publisher or subscription."]
164    pub endpoint_type: rmw_endpoint_type_t::Type,
165    #[doc = " The endpoint's identity; `data` all-zero when the backend has none."]
166    pub endpoint_gid: rmw_gid_t,
167    #[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."]
168    pub qos_profile: rmw_qos_profile_t,
169}
170#[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."]
171#[repr(C)]
172#[derive(Debug, Copy, Clone)]
173pub struct rmw_network_flow_endpoint_t {
174    pub transport_protocol: rmw_transport_protocol_t::Type,
175    pub internet_protocol: rmw_internet_protocol_t::Type,
176    pub transport_port: u16,
177    #[doc = " Publisher-side only; 0 elsewhere."]
178    pub flow_label: u32,
179    #[doc = " Differentiated Services Code Point. Publisher-side only; 0 elsewhere."]
180    pub dscp: u8,
181    pub internet_address: [core::ffi::c_char; 48usize],
182}
183#[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."]
184#[repr(C)]
185#[derive(Debug, Copy, Clone)]
186pub struct rmw_session_property_t {
187    #[doc = "< Property name; never NULL."]
188    pub key: *const core::ffi::c_char,
189    #[doc = "< Property value; never NULL."]
190    pub value: *const core::ffi::c_char,
191}
192#[repr(C)]
193#[derive(Debug, Copy, Clone)]
194pub struct rmw_session_options_t {
195    #[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."]
196    pub localhost_only: u8,
197    #[doc = "< Reserved; must be zero."]
198    pub _reserved: [u8; 7usize],
199    #[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."]
200    pub enclave: *const core::ffi::c_char,
201    #[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."]
202    pub properties: *const rmw_session_property_t,
203    #[doc = "< Number of entries in `properties`."]
204    pub property_count: usize,
205}
206#[repr(C)]
207#[derive(Debug, Copy, Clone)]
208pub struct rmw_publisher_options_t {
209    #[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."]
210    pub tx_express: u8,
211    #[doc = "< Reserved; must be zero."]
212    pub _reserved: [u8; 7usize],
213}
214#[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."]
215#[repr(C)]
216#[derive(Debug, Copy, Clone)]
217pub struct rmw_subscription_options_t {
218    #[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  The vtable's OPTIONAL `required_rx_bytes` is the other direction: how a\n  backend says what it would rather the take buffer were."]
219    pub rx_buffer_hint: u32,
220    #[doc = "< Reserved; must be zero."]
221    pub _reserved: [u8; 4usize],
222}
223#[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."]
224#[repr(C)]
225#[derive(Debug, Copy, Clone)]
226pub struct rmw_session_t {
227    #[doc = " Node name (borrowed from caller; outlives the session)."]
228    pub node_name: *const core::ffi::c_char,
229    #[doc = " Node namespace (borrowed from caller; outlives the session)."]
230    pub namespace_: *const core::ffi::c_char,
231    #[doc = " Reserved for future fields (Phase 104 vtable pointer slot);\n  must be zero."]
232    pub _reserved: [u8; 8usize],
233    #[doc = " Opaque backend state. NULL for an uninitialised session."]
234    pub backend_data: *mut core::ffi::c_void,
235}
236#[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."]
237#[repr(C)]
238#[derive(Debug, Copy, Clone)]
239pub struct rmw_node_t {
240    #[doc = " Node name. Borrowed; outlives the node."]
241    pub name: *const core::ffi::c_char,
242    #[doc = " Node namespace. Borrowed; outlives the node."]
243    pub namespace_: *const core::ffi::c_char,
244    #[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`."]
245    pub session: *mut rmw_session_t,
246    #[doc = " Reserved; must be zero."]
247    pub _reserved: [u8; 8usize],
248    #[doc = " Opaque backend state. NULL until `create_node` succeeds."]
249    pub backend_data: *mut core::ffi::c_void,
250}
251#[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 `loan_publish` / `commit_publish` primitive (Phase 99).\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."]
252#[repr(C)]
253#[derive(Debug, Copy, Clone)]
254pub struct rmw_publisher_t {
255    #[doc = " Topic name (borrowed; outlives the publisher)."]
256    pub topic_name: *const core::ffi::c_char,
257    #[doc = " ROS-2-style fully-qualified type name\n  (e.g., `\"std_msgs/msg/Int32\"`). Borrowed; outlives the publisher."]
258    pub type_name: *const core::ffi::c_char,
259    #[doc = " QoS subset honoured by this publisher."]
260    pub qos: rmw_qos_profile_t,
261    #[doc = " Backend exposes loan_publish / commit_publish (Phase 99).\n  DERIVED by the runtime from `vtable->borrow_loaned_message`;\n  a backend's write is overwritten (issue 0814)."]
262    pub can_loan_messages: bool,
263    #[doc = " Reserved for future fields; must be zero."]
264    pub _reserved: [u8; 7usize],
265    #[doc = " Opaque backend state. NULL if creation failed."]
266    pub backend_data: *mut core::ffi::c_void,
267}
268#[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."]
269#[repr(C)]
270#[derive(Debug, Copy, Clone)]
271pub struct rmw_subscription_t {
272    #[doc = " Topic name (borrowed; outlives the subscription)."]
273    pub topic_name: *const core::ffi::c_char,
274    #[doc = " Fully-qualified type name. Borrowed."]
275    pub type_name: *const core::ffi::c_char,
276    #[doc = " QoS subset honoured by this subscription."]
277    pub qos: rmw_qos_profile_t,
278    #[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)."]
279    pub can_loan_messages: bool,
280    #[doc = " Reserved for future fields; must be zero."]
281    pub _reserved: [u8; 7usize],
282    #[doc = " Opaque backend state. NULL if creation failed."]
283    pub backend_data: *mut core::ffi::c_void,
284}
285#[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."]
286#[repr(C)]
287#[derive(Debug, Copy, Clone)]
288pub struct rmw_service_t {
289    #[doc = " Service name (borrowed; outlives the server)."]
290    pub service_name: *const core::ffi::c_char,
291    #[doc = " Fully-qualified service type name (e.g.,\n  `\"example_interfaces/srv/AddTwoInts\"`). Borrowed."]
292    pub type_name: *const core::ffi::c_char,
293    #[doc = " Reserved for future fields; must be zero."]
294    pub _reserved: [u8; 8usize],
295    #[doc = " Opaque backend state. NULL if creation failed."]
296    pub backend_data: *mut core::ffi::c_void,
297}
298#[doc = " Client entity (phase-301: renamed from `service_client` to the upstream `rmw_client_t` term). Same shape as the service."]
299#[repr(C)]
300#[derive(Debug, Copy, Clone)]
301pub struct rmw_client_t {
302    #[doc = " Service name (borrowed; outlives the client)."]
303    pub service_name: *const core::ffi::c_char,
304    #[doc = " Fully-qualified service type name. Borrowed."]
305    pub type_name: *const core::ffi::c_char,
306    #[doc = " Reserved for future fields; must be zero."]
307    pub _reserved: [u8; 8usize],
308    #[doc = " Opaque backend state. NULL if creation failed."]
309    pub backend_data: *mut core::ffi::c_void,
310}
311#[doc = " Liveliness payload. Mirrors the DDS\n  `rmw_liveliness_changed_status_t` shape."]
312#[repr(C)]
313#[derive(Debug, Copy, Clone)]
314pub struct rmw_liveliness_changed_status_t {
315    pub alive_count: u16,
316    pub not_alive_count: u16,
317    pub alive_count_change: i16,
318    pub not_alive_count_change: i16,
319}
320#[doc = " Count payload. Used for `MESSAGE_LOST`,\n  `REQUESTED_DEADLINE_MISSED`, `LIVELINESS_LOST`,\n  `OFFERED_DEADLINE_MISSED` — all share the same shape."]
321#[repr(C)]
322#[derive(Debug, Copy, Clone)]
323pub struct rmw_count_status_t {
324    pub total_count: u32,
325    pub total_count_change: u32,
326}
327#[repr(C)]
328#[derive(Debug, Copy, Clone)]
329pub struct rmw_loan_token_t {
330    _unused: [u8; 0],
331}
332#[repr(C)]
333#[derive(Debug, Copy, Clone)]
334pub struct rmw_node_visitor_t {
335    pub visit: rmw_node_visit_fn,
336    pub ctx: *mut core::ffi::c_void,
337}
338#[repr(C)]
339#[derive(Debug, Copy, Clone)]
340pub struct rmw_names_and_types_visitor_t {
341    pub visit: rmw_names_and_types_visit_fn,
342    pub ctx: *mut core::ffi::c_void,
343}
344#[repr(C)]
345#[derive(Debug, Copy, Clone)]
346pub struct rmw_topic_endpoint_info_visitor_t {
347    pub visit: rmw_topic_endpoint_info_visit_fn,
348    pub ctx: *mut core::ffi::c_void,
349}
350#[repr(C)]
351#[derive(Debug, Copy, Clone)]
352pub struct nros_rmw_vtable_t {
353    #[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`."]
354    pub create_session: ::core::option::Option<
355        unsafe extern "C" fn(
356            locator: *const core::ffi::c_char,
357            mode: u8,
358            domain_id: u32,
359            node_name: *const core::ffi::c_char,
360            options: *const rmw_session_options_t,
361            out: *mut rmw_session_t,
362        ) -> rmw_ret_t,
363    >,
364    pub destroy_session:
365        ::core::option::Option<unsafe extern "C" fn(session: *mut rmw_session_t) -> rmw_ret_t>,
366    pub drive_io: ::core::option::Option<
367        unsafe extern "C" fn(session: *mut rmw_session_t, timeout_ms: i32) -> rmw_ret_t,
368    >,
369    #[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."]
370    pub create_publisher: ::core::option::Option<
371        unsafe extern "C" fn(
372            node: *const rmw_node_t,
373            type_support: *const rmw_message_type_support_t,
374            topic_name: *const core::ffi::c_char,
375            domain_id: u32,
376            qos: *const rmw_qos_profile_t,
377            options: *const rmw_publisher_options_t,
378            out: *mut rmw_publisher_t,
379        ) -> rmw_ret_t,
380    >,
381    pub destroy_publisher:
382        ::core::option::Option<unsafe extern "C" fn(publisher: *mut rmw_publisher_t) -> rmw_ret_t>,
383    pub publish: ::core::option::Option<
384        unsafe extern "C" fn(
385            publisher: *const rmw_publisher_t,
386            payload: rmw_byte_span_t,
387        ) -> rmw_ret_t,
388    >,
389    #[doc = " `options` carries transport hints (phase-301: moved out of the\n  QoS struct); NULL = all defaults."]
390    pub create_subscription: ::core::option::Option<
391        unsafe extern "C" fn(
392            node: *const rmw_node_t,
393            type_support: *const rmw_message_type_support_t,
394            topic_name: *const core::ffi::c_char,
395            domain_id: u32,
396            qos: *const rmw_qos_profile_t,
397            options: *const rmw_subscription_options_t,
398            out: *mut rmw_subscription_t,
399        ) -> rmw_ret_t,
400    >,
401    pub destroy_subscription: ::core::option::Option<
402        unsafe extern "C" fn(subscription: *mut rmw_subscription_t) -> rmw_ret_t,
403    >,
404    #[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."]
405    pub take: ::core::option::Option<
406        unsafe extern "C" fn(
407            subscription: *const rmw_subscription_t,
408            out: *mut rmw_mut_byte_span_t,
409            taken: *mut bool,
410        ) -> rmw_ret_t,
411    >,
412    #[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)."]
413    pub has_data: ::core::option::Option<
414        unsafe extern "C" fn(
415            subscription: *mut rmw_subscription_t,
416            out_has_data: *mut bool,
417        ) -> rmw_ret_t,
418    >,
419    pub create_service: ::core::option::Option<
420        unsafe extern "C" fn(
421            node: *const rmw_node_t,
422            type_support: *const rmw_service_type_support_t,
423            service_name: *const core::ffi::c_char,
424            domain_id: u32,
425            qos: *const rmw_qos_profile_t,
426            out: *mut rmw_service_t,
427        ) -> rmw_ret_t,
428    >,
429    pub destroy_service:
430        ::core::option::Option<unsafe extern "C" fn(server: *mut rmw_service_t) -> rmw_ret_t>,
431    #[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."]
432    pub take_request: ::core::option::Option<
433        unsafe extern "C" fn(
434            server: *const rmw_service_t,
435            request: *mut rmw_mut_byte_span_t,
436            seq_out: *mut i64,
437            taken: *mut bool,
438        ) -> rmw_ret_t,
439    >,
440    #[doc = " Phase 376 W3.d step A — the service-side sibling of\n  `has_data`; same contract, same reason."]
441    pub has_request: ::core::option::Option<
442        unsafe extern "C" fn(server: *mut rmw_service_t, out_has_request: *mut bool) -> rmw_ret_t,
443    >,
444    pub send_response: ::core::option::Option<
445        unsafe extern "C" fn(
446            server: *const rmw_service_t,
447            seq: i64,
448            response: rmw_byte_span_t,
449        ) -> rmw_ret_t,
450    >,
451    pub create_client: ::core::option::Option<
452        unsafe extern "C" fn(
453            node: *const rmw_node_t,
454            type_support: *const rmw_service_type_support_t,
455            service_name: *const core::ffi::c_char,
456            domain_id: u32,
457            qos: *const rmw_qos_profile_t,
458            out: *mut rmw_client_t,
459        ) -> rmw_ret_t,
460    >,
461    pub destroy_client:
462        ::core::option::Option<unsafe extern "C" fn(client: *mut rmw_client_t) -> rmw_ret_t>,
463    #[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 + `try_recv_reply_raw` 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."]
464    pub send_request: ::core::option::Option<
465        unsafe extern "C" fn(
466            client: *const rmw_client_t,
467            request: rmw_byte_span_t,
468            sequence_id: *mut i64,
469        ) -> rmw_ret_t,
470    >,
471    #[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  (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.)"]
472    pub take_response: ::core::option::Option<
473        unsafe extern "C" fn(
474            client: *const rmw_client_t,
475            reply: *mut rmw_mut_byte_span_t,
476            seq_out: *mut i64,
477            taken: *mut bool,
478        ) -> rmw_ret_t,
479    >,
480    #[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."]
481    pub subscription_event_init: ::core::option::Option<
482        unsafe extern "C" fn(
483            subscription: *const rmw_subscription_t,
484            kind: rmw_event_type_t::Type,
485            deadline_ms: u32,
486            cb: rmw_status_event_callback_t,
487            user_context: *mut core::ffi::c_void,
488        ) -> rmw_ret_t,
489    >,
490    #[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."]
491    pub subscription_take_event: ::core::option::Option<
492        unsafe extern "C" fn(
493            subscription: *const rmw_subscription_t,
494            kind: rmw_event_type_t::Type,
495            out: *mut rmw_event_payload_t,
496            taken: *mut bool,
497        ) -> rmw_ret_t,
498    >,
499    #[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."]
500    pub publisher_take_event: ::core::option::Option<
501        unsafe extern "C" fn(
502            publisher: *const rmw_publisher_t,
503            kind: rmw_event_type_t::Type,
504            out: *mut rmw_event_payload_t,
505            taken: *mut bool,
506        ) -> rmw_ret_t,
507    >,
508    pub publisher_event_init: ::core::option::Option<
509        unsafe extern "C" fn(
510            publisher: *const rmw_publisher_t,
511            kind: rmw_event_type_t::Type,
512            deadline_ms: u32,
513            cb: rmw_status_event_callback_t,
514            user_context: *mut core::ffi::c_void,
515        ) -> rmw_ret_t,
516    >,
517    #[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_*."]
518    pub publisher_assert_liveliness: ::core::option::Option<
519        unsafe extern "C" fn(publisher: *const rmw_publisher_t) -> rmw_ret_t,
520    >,
521    #[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`."]
522    pub next_deadline_ms: ::core::option::Option<
523        unsafe extern "C" fn(
524            session: *const rmw_session_t,
525            out_ms: *mut u32,
526            has_deadline: *mut bool,
527        ) -> rmw_ret_t,
528    >,
529    #[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: XRCE, bare-metal). The runtime still drains the\n  session on its deadline-bound cv-wait boundary."]
530    pub set_wake_callback: ::core::option::Option<
531        unsafe extern "C" fn(
532            session: *mut rmw_session_t,
533            cb: ::core::option::Option<unsafe extern "C" fn(ctx: *mut core::ffi::c_void)>,
534            ctx: *mut core::ffi::c_void,
535        ) -> rmw_ret_t,
536    >,
537    #[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_TRY_AGAIN` if the backend has no slot\n      available (caller may retry or fall back to a copy path).\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 `pub_commit` or `pub_discard` 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  runtime falls back to a per-publisher staging arena and emits\n  a single memcpy on commit."]
538    pub borrow_loaned_message: ::core::option::Option<
539        unsafe extern "C" fn(
540            publisher: *const rmw_publisher_t,
541            requested_len: usize,
542            out_slot: *mut rmw_mut_byte_span_t,
543            out_token: *mut *mut rmw_loan_token_t,
544        ) -> rmw_ret_t,
545    >,
546    #[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`."]
547    pub publish_loaned_message: ::core::option::Option<
548        unsafe extern "C" fn(
549            publisher: *const rmw_publisher_t,
550            token: *mut rmw_loan_token_t,
551            actual_len: usize,
552        ) -> rmw_ret_t,
553    >,
554    #[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`."]
555    pub return_loaned_message_from_publisher: ::core::option::Option<
556        unsafe extern "C" fn(
557            publisher: *const rmw_publisher_t,
558            token: *mut rmw_loan_token_t,
559        ) -> rmw_ret_t,
560    >,
561    #[doc = " Phase 124.A — zero-copy subscription borrow.\n\n  Borrow a read-only view of the next available message in\n  place, without copying into a caller buffer. Returns:\n    * `>= 0` — message length; writes `*out_buf` / `*out_token`.\n    * `0` — no message ready (subscription empty).\n    * `< 0` — error (see `rmw_ret_t` codes negated).\n\n  The view is valid until the matching `sub_release` runs.\n  Only one borrow may be outstanding per subscription at a time —\n  callers MUST release before requesting another borrow.\n\n  NULL function pointer = backend doesn't natively borrow; the\n  runtime falls back to `try_recv_raw` into a staging buffer. */\n/** 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."]
562    pub take_loaned_message: ::core::option::Option<
563        unsafe extern "C" fn(
564            subscription: *const rmw_subscription_t,
565            out_view: *mut rmw_byte_span_t,
566            out_token: *mut *mut rmw_loan_token_t,
567            taken: *mut bool,
568        ) -> rmw_ret_t,
569    >,
570    #[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`."]
571    pub return_loaned_message_from_subscription: ::core::option::Option<
572        unsafe extern "C" fn(
573            subscription: *const rmw_subscription_t,
574            token: *mut rmw_loan_token_t,
575        ) -> rmw_ret_t,
576    >,
577    #[doc = " Phase 124.C.1 — service-server availability probe.\n\n  Returns `1` if ≥ 1 matching server has been discovered on the\n  RMW graph, `0` if none yet, or a negative `rmw_ret_t`\n  constant on backend error. The runtime exposes this to user\n  code as `nros_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."]
578    pub service_server_is_available: ::core::option::Option<
579        unsafe extern "C" fn(client: *const rmw_client_t, out_available: *mut bool) -> rmw_ret_t,
580    >,
581    #[doc = " Phase 124.D.1 — burst-take.\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  Returns:\n    * `>= 0` — count of messages taken (0..=max_msgs).\n    * `< 0` — `rmw_ret_t` error code; partial drains MUST\n      use the count form, not error-out.\n\n  NULL function pointer = backend doesn't natively batch; the\n  runtime emits a `try_recv_raw` loop fallback in\n  `CffiSubscriber::try_recv_sequence`. The fallback gives\n  identical observable behaviour (each call still costs N\n  vtable hops) but lets user code commit to the batched API. */\n/** 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  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 `try_recv_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  `try_recv_raw` 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."]
582    pub take_sequence: ::core::option::Option<
583        unsafe extern "C" fn(
584            subscription: *const rmw_subscription_t,
585            buf: *mut u8,
586            per_msg_cap: usize,
587            max_msgs: usize,
588            out_lens: *mut usize,
589            taken: *mut usize,
590        ) -> rmw_ret_t,
591    >,
592    #[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  falls back to a one-shot staging buffer (capped at the\n  configured `NROS_MAX_STREAM_CHUNK`) + `publish_raw`."]
593    pub publish_streamed: ::core::option::Option<
594        unsafe extern "C" fn(
595            publisher: *mut rmw_publisher_t,
596            size_cb: ::core::option::Option<
597                unsafe extern "C" fn(out_total_len: *mut usize, user_ctx: *mut core::ffi::c_void),
598            >,
599            chunk_cb: ::core::option::Option<
600                unsafe extern "C" fn(
601                    out_buf: *mut u8,
602                    cap: usize,
603                    out_written: *mut usize,
604                    user_ctx: *mut core::ffi::c_void,
605                ),
606            >,
607            user_ctx: *mut core::ffi::c_void,
608        ) -> rmw_ret_t,
609    >,
610    #[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    * other negative — backend error.\n\n  Implementation notes per backend:\n  - **Zenoh**: `z_send_ping` (or session keep-alive piggyback).\n  - **XRCE**: `uxr_ping_agent_session_until_timeout`.\n  - **DDS**: built-in participant ping if available, else\n    `RET_UNSUPPORTED`.\n\n  NULL function pointer = runtime surfaces\n  `NROS_RMW_RET_UNSUPPORTED` to the caller."]
611    pub ping_session: ::core::option::Option<
612        unsafe extern "C" fn(session: *mut rmw_session_t, timeout_ms: i32) -> rmw_ret_t,
613    >,
614    #[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)."]
615    pub subscription_supports_in_place: ::core::option::Option<
616        unsafe extern "C" fn(
617            subscription: *mut rmw_subscription_t,
618            out_supports: *mut bool,
619        ) -> rmw_ret_t,
620    >,
621    #[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."]
622    pub process_raw_in_place: ::core::option::Option<
623        unsafe extern "C" fn(
624            subscription: *mut rmw_subscription_t,
625            ctx: *mut core::ffi::c_void,
626            cb: ::core::option::Option<
627                unsafe extern "C" fn(ctx: *mut core::ffi::c_void, message: rmw_byte_span_t),
628            >,
629            out_processed: *mut bool,
630        ) -> rmw_ret_t,
631    >,
632    #[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  RESERVED, and NULL in every backend. **There is no runtime fallback**:\n  this doc said the runtime answered with the registry name, and no such\n  code was ever written — nothing in the tree calls this slot, so a NULL\n  one is not \"answered elsewhere\", it is unanswerable. Corrected 2026-08-29\n  (phase-393 W2) after grepping for the promised fallback and finding only\n  this sentence.\n\n  Filling it is NOT what makes the identity correct. The identity that is\n  load-bearing today is the one a backend stamps into `rmw_gid_t`, because\n  `rmw_compare_gids_equal` compares that string before the bytes — so what\n  matters is that a backend has ONE spelling of its name, not that it can\n  be asked for it. Cyclone's `kImplementationIdentifier` is that shape.\n\n  Do not fill this slot to move a counter: `check-rmw-slot-producers`\n  classifies any slot with a producer as `produced` whether or not\n  anything reads it, which is exactly the overstatement issue 0800 exists\n  to catch. It earns a body when a CALLER exists — a bridge image asking\n  which of two linked backends it is on."]
633    pub get_implementation_identifier:
634        ::core::option::Option<unsafe extern "C" fn() -> *const core::ffi::c_char>,
635    #[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)."]
636    pub get_serialization_format:
637        ::core::option::Option<unsafe extern "C" fn() -> *const core::ffi::c_char>,
638    #[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: the runtime answers `false` for every feature."]
639    pub feature_supported:
640        ::core::option::Option<unsafe extern "C" fn(feature: rmw_feature_t::Type) -> bool>,
641    #[doc = " Upstream `rmw_get_gid_for_publisher`. Exact parity.\n\n  The backend zero-pads to the full width; see `rmw_gid_t`."]
642    pub get_gid_for_publisher: ::core::option::Option<
643        unsafe extern "C" fn(publisher: *const rmw_publisher_t, gid: *mut rmw_gid_t) -> rmw_ret_t,
644    >,
645    #[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)."]
646    pub publisher_count_matched_subscriptions: ::core::option::Option<
647        unsafe extern "C" fn(
648            publisher: *const rmw_publisher_t,
649            subscription_count: *mut usize,
650        ) -> rmw_ret_t,
651    >,
652    #[doc = " Upstream `rmw_subscription_count_matched_publishers`. Exact parity."]
653    pub subscription_count_matched_publishers: ::core::option::Option<
654        unsafe extern "C" fn(
655            subscription: *const rmw_subscription_t,
656            publisher_count: *mut usize,
657        ) -> rmw_ret_t,
658    >,
659    #[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."]
660    pub publisher_get_actual_qos: ::core::option::Option<
661        unsafe extern "C" fn(
662            publisher: *const rmw_publisher_t,
663            qos: *mut rmw_qos_profile_t,
664        ) -> rmw_ret_t,
665    >,
666    #[doc = " Upstream `rmw_subscription_get_actual_qos`. Exact parity."]
667    pub subscription_get_actual_qos: ::core::option::Option<
668        unsafe extern "C" fn(
669            subscription: *const rmw_subscription_t,
670            qos: *mut rmw_qos_profile_t,
671        ) -> rmw_ret_t,
672    >,
673    #[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."]
674    pub client_request_publisher_get_actual_qos: ::core::option::Option<
675        unsafe extern "C" fn(client: *const rmw_client_t, qos: *mut rmw_qos_profile_t) -> rmw_ret_t,
676    >,
677    #[doc = " Upstream `rmw_client_response_subscription_get_actual_qos`."]
678    pub client_response_subscription_get_actual_qos: ::core::option::Option<
679        unsafe extern "C" fn(client: *const rmw_client_t, qos: *mut rmw_qos_profile_t) -> rmw_ret_t,
680    >,
681    #[doc = " Upstream `rmw_service_request_subscription_get_actual_qos`."]
682    pub service_request_subscription_get_actual_qos: ::core::option::Option<
683        unsafe extern "C" fn(
684            service: *const rmw_service_t,
685            qos: *mut rmw_qos_profile_t,
686        ) -> rmw_ret_t,
687    >,
688    #[doc = " Upstream `rmw_service_response_publisher_get_actual_qos`."]
689    pub service_response_publisher_get_actual_qos: ::core::option::Option<
690        unsafe extern "C" fn(
691            service: *const rmw_service_t,
692            qos: *mut rmw_qos_profile_t,
693        ) -> rmw_ret_t,
694    >,
695    #[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."]
696    pub publisher_wait_for_all_acked: ::core::option::Option<
697        unsafe extern "C" fn(publisher: *const rmw_publisher_t, timeout_ms: u32) -> rmw_ret_t,
698    >,
699    #[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  NULL slot: the runtime falls back to `take`, and the caller gets no\n  metadata — which is exactly today's behaviour for every C backend."]
700    pub take_with_info: ::core::option::Option<
701        unsafe extern "C" fn(
702            subscription: *const rmw_subscription_t,
703            message: *mut rmw_mut_byte_span_t,
704            taken: *mut bool,
705            message_info: *mut rmw_message_info_t,
706        ) -> rmw_ret_t,
707    >,
708    #[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)."]
709    pub take_loaned_message_with_info: ::core::option::Option<
710        unsafe extern "C" fn(
711            subscription: *const rmw_subscription_t,
712            out_view: *mut rmw_byte_span_t,
713            out_token: *mut *mut rmw_loan_token_t,
714            taken: *mut bool,
715            message_info: *mut rmw_message_info_t,
716        ) -> rmw_ret_t,
717    >,
718    #[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."]
719    pub get_node_names: ::core::option::Option<
720        unsafe extern "C" fn(
721            session: *const rmw_session_t,
722            visitor: rmw_node_visitor_t,
723        ) -> rmw_ret_t,
724    >,
725    #[doc = " Upstream `rmw_get_topic_names_and_types`."]
726    pub get_topic_names_and_types: ::core::option::Option<
727        unsafe extern "C" fn(
728            session: *const rmw_session_t,
729            no_demangle: bool,
730            visitor: rmw_names_and_types_visitor_t,
731        ) -> rmw_ret_t,
732    >,
733    #[doc = " Upstream `rmw_get_service_names_and_types`."]
734    pub get_service_names_and_types: ::core::option::Option<
735        unsafe extern "C" fn(
736            session: *const rmw_session_t,
737            visitor: rmw_names_and_types_visitor_t,
738        ) -> rmw_ret_t,
739    >,
740    #[doc = " Upstream `rmw_get_publisher_names_and_types_by_node`."]
741    pub get_publisher_names_and_types_by_node: ::core::option::Option<
742        unsafe extern "C" fn(
743            session: *const rmw_session_t,
744            node_name: *const core::ffi::c_char,
745            node_namespace: *const core::ffi::c_char,
746            no_demangle: bool,
747            visitor: rmw_names_and_types_visitor_t,
748        ) -> rmw_ret_t,
749    >,
750    #[doc = " Upstream `rmw_get_subscriber_names_and_types_by_node`."]
751    pub get_subscriber_names_and_types_by_node: ::core::option::Option<
752        unsafe extern "C" fn(
753            session: *const rmw_session_t,
754            node_name: *const core::ffi::c_char,
755            node_namespace: *const core::ffi::c_char,
756            no_demangle: bool,
757            visitor: rmw_names_and_types_visitor_t,
758        ) -> rmw_ret_t,
759    >,
760    #[doc = " Upstream `rmw_get_service_names_and_types_by_node`."]
761    pub get_service_names_and_types_by_node: ::core::option::Option<
762        unsafe extern "C" fn(
763            session: *const rmw_session_t,
764            node_name: *const core::ffi::c_char,
765            node_namespace: *const core::ffi::c_char,
766            visitor: rmw_names_and_types_visitor_t,
767        ) -> rmw_ret_t,
768    >,
769    #[doc = " Upstream `rmw_get_client_names_and_types_by_node`."]
770    pub get_client_names_and_types_by_node: ::core::option::Option<
771        unsafe extern "C" fn(
772            session: *const rmw_session_t,
773            node_name: *const core::ffi::c_char,
774            node_namespace: *const core::ffi::c_char,
775            visitor: rmw_names_and_types_visitor_t,
776        ) -> rmw_ret_t,
777    >,
778    #[doc = " Upstream `rmw_get_publishers_info_by_topic`."]
779    pub get_publishers_info_by_topic: ::core::option::Option<
780        unsafe extern "C" fn(
781            session: *const rmw_session_t,
782            topic_name: *const core::ffi::c_char,
783            no_mangle: bool,
784            visitor: rmw_topic_endpoint_info_visitor_t,
785        ) -> rmw_ret_t,
786    >,
787    #[doc = " Upstream `rmw_get_subscriptions_info_by_topic`."]
788    pub get_subscriptions_info_by_topic: ::core::option::Option<
789        unsafe extern "C" fn(
790            session: *const rmw_session_t,
791            topic_name: *const core::ffi::c_char,
792            no_mangle: bool,
793            visitor: rmw_topic_endpoint_info_visitor_t,
794        ) -> rmw_ret_t,
795    >,
796    #[doc = " Upstream `rmw_count_publishers`."]
797    pub count_publishers: ::core::option::Option<
798        unsafe extern "C" fn(
799            session: *const rmw_session_t,
800            topic_name: *const core::ffi::c_char,
801            count: *mut usize,
802        ) -> rmw_ret_t,
803    >,
804    #[doc = " Upstream `rmw_count_subscribers`."]
805    pub count_subscribers: ::core::option::Option<
806        unsafe extern "C" fn(
807            session: *const rmw_session_t,
808            topic_name: *const core::ffi::c_char,
809            count: *mut usize,
810        ) -> rmw_ret_t,
811    >,
812    #[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 is `set_on_graph_change_callback` — flagged\n  for W5 rather than decided quietly here."]
813    pub node_get_graph_guard_condition: ::core::option::Option<
814        unsafe extern "C" fn(
815            session: *mut rmw_session_t,
816            callback: rmw_event_callback_t,
817            user_data: *const core::ffi::c_void,
818        ) -> rmw_ret_t,
819    >,
820    #[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_)`."]
821    pub create_node: ::core::option::Option<
822        unsafe extern "C" fn(
823            session: *mut rmw_session_t,
824            name: *const core::ffi::c_char,
825            namespace_: *const core::ffi::c_char,
826            out: *mut rmw_node_t,
827        ) -> rmw_ret_t,
828    >,
829    #[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)."]
830    pub destroy_node:
831        ::core::option::Option<unsafe extern "C" fn(node: *mut rmw_node_t) -> rmw_ret_t>,
832    #[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`."]
833    pub set_log_severity: ::core::option::Option<
834        unsafe extern "C" fn(severity: rmw_log_severity_t::Type) -> rmw_ret_t,
835    >,
836    #[doc = " How many bytes of TAKE buffer this type actually needs, so the runtime\n  can stop sizing `take`'s `buf` from one global constant.\n\n  RTOS addition; upstream has no counterpart. Upstream's nearest thing is\n  `rmw_get_serialized_message_size`, which is about a MESSAGE and which\n  all three reference implementations leave unimplemented; this is about\n  the buffer the CALLER must present, and the answer is the backend's\n  because only the backend knows what its framing, size classes and\n  attachment handling add on top of the payload.\n\n  `type_name` and `type_hash` name the type (both BORROWED for the call;\n  `type_hash` may be NULL exactly as in `create_subscription`). `hint` is\n  the same number the runtime would put in\n  `rmw_subscription_options_t.rx_buffer_hint`, and carries the same\n  meaning, including that `0` says the CALLER stated nothing rather than\n  that the type is unbounded — every message type has a derived bound or\n  the build fails. On `NROS_RMW_RET_OK`, `*out_bytes` is the MINIMUM\n  take-buffer length that is sufficient for this type at this hint: a\n  `take` given that many bytes must not fail for want of room, and no\n  smaller number has that property. It may exceed `hint` — a backend that\n  frames or pads says so here rather than discovering it at `take` time —\n  and it may be smaller.\n\n  MINIMUM, tightened in phase-403 W4, and the word carries the whole\n  value of the slot. A backend that keeps size CLASSES may not answer with\n  the class it would round this type up to. Doing so is arithmetically\n  safe and useless: a 68-byte type and a 1000-byte type sharing a class\n  come back with one number, which is the global-constant answer the\n  runtime already had without asking, and the runtime would spend the\n  difference on every subscription in the image. Report what the type\n  needs; the rounding is the backend's own business and stays there.\n  If a backend genuinely cannot separate the two — its framing really\n  does make the class size the floor — then the class size IS the\n  minimum and answering it is correct; what is forbidden is reporting a\n  rounding as though it were a requirement.\n\n  This is a QUERY about a type, not about an entity: it is answerable\n  before any subscription exists, which is the point — the runtime has to\n  size the buffer in order to create one.\n\n  NULL slot: FALLBACK class (RFC-0035's NULL-slot contract) — THE HINT IS\n  THE ANSWER, never `UNSUPPORTED` to the caller. The runtime uses `hint`,\n  and since a type without a derived bound is a build error, that is a\n  real number rather than a fallback to a configured default. A backend\n  that cannot answer for a PARTICULAR type returns\n  `NROS_RMW_RET_UNSUPPORTED` and leaves `*out_bytes` untouched; the\n  runtime falls back to the hint exactly as for a NULL slot.\n\n  OPTIONAL on the merits, not for compatibility. nano-ros is unreleased\n  and this ABI may be broken, so \"a mandatory slot breaks every\n  out-of-tree backend\" is not the reason and must not be cited as one.\n  Three reasons that survive without it:\n\n  - A SLOT CANNOT BE REQUIRED BEFORE SOMETHING DISPATCHES IT. Required\n    here means `first_missing_vtable_slot` REFUSES to register a backend\n    that leaves it NULL, and `check-rmw-required-slots.sh` holds that set\n    equal to the set the runtime `.expect()`s. Nothing calls this yet —\n    phase-403 W3/W5 own the dispatch site — so requiring it now would\n    refuse working backends over a function no caller reaches. That is\n    issue 0349 exactly, and it cost three backends their registration\n    once. (W4 filled the slot for zenoh-pico, which makes the slot\n    PRODUCED; it is the CONSUMER that decides whether required is\n    reachable, and there is still none.)\n  - \"NO OPINION\" IS A REAL ANSWER, and mandatory does not delete it, only\n    relocates it: five in-tree backends would each carry the same\n    `*out_bytes = hint; return OK;` body, and the Rust ones would get it\n    from a defaulted `RustBackend` trait method — the same special case,\n    one layer up and less visible.\n  - IT IS SLOT 75, AND TWO BACKENDS INITIALISE POSITIONALLY. uORB's\n    C++14 initialiser stops at slot 17 and positional initialisation\n    cannot skip, so reaching a mandatory slot 75 means writing 58\n    meaningless entries to get there.\n\n  Promotion stays cheap and stays open: making this required later is a\n  change to the registration check, not to the struct.\n\n  DECIDED 2026-08-31: this slot stays OPTIONAL permanently. Phase-403 W4\n  recommended it after filling the slot for zenoh-pico, and that is now a\n  ruling rather than a recommendation, so promotion is off the table\n  unless the reason below stops being true.\n  The first argument above dissolves once a dispatch site exists, but the\n  other two do not, and they are the load-bearing ones: cyclonedds and\n  XRCE keep ONE receive buffer, so \"no opinion\" is their true answer and\n  requiring the slot only relocates it into identical bodies; and slot 75\n  is out of reach of uORB's positional C++14 initialiser whatever the\n  registration check says. A slot that most backends must fill with a\n  restatement of the default is worse than a NULL whose meaning the\n  header pins down, which this one does.\n\n  It does not weaken `take`'s obligation. The runtime may pass a `buf_len`\n  smaller than what this returned — it has its own memory to answer to —\n  and a sample that does not fit is still a reported failure rather than\n  a truncated success. Answering here is how a backend avoids that\n  outcome, not how it licenses one."]
837    pub required_rx_bytes: ::core::option::Option<
838        unsafe extern "C" fn(
839            type_name: *const core::ffi::c_char,
840            type_hash: *const core::ffi::c_char,
841            hint: usize,
842            out_bytes: *mut usize,
843        ) -> rmw_ret_t,
844    >,
845}
846#[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."]
847#[repr(C)]
848#[derive(Debug, Copy, Clone)]
849pub struct nros_transport_ops_s {
850    #[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`."]
851    pub abi_version: u32,
852    #[doc = " Reserved padding for alignment stability across appends.\n Set to zero."]
853    pub _reserved: u32,
854    #[doc = " Opaque caller context, threaded back into every callback.\n Lifetime: must outlive the transport's active period\n (i.e. until `close` returns)."]
855    pub user_data: *mut core::ffi::c_void,
856    #[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)."]
857    pub open: ::core::option::Option<
858        unsafe extern "C" fn(
859            user_data: *mut core::ffi::c_void,
860            params: *const core::ffi::c_void,
861        ) -> i32,
862    >,
863    #[doc = " Tear the transport down. After `close` returns, the runtime\n will not invoke `read` or `write` on this transport unless\n `nros_set_custom_transport` is called again."]
864    pub close: ::core::option::Option<unsafe extern "C" fn(user_data: *mut core::ffi::c_void)>,
865    #[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)."]
866    pub write: ::core::option::Option<
867        unsafe extern "C" fn(user_data: *mut core::ffi::c_void, buf: *const u8, len: usize) -> i32,
868    >,
869    #[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)."]
870    pub read: ::core::option::Option<
871        unsafe extern "C" fn(
872            user_data: *mut core::ffi::c_void,
873            buf: *mut u8,
874            len: usize,
875            timeout_ms: u32,
876        ) -> i32,
877    >,
878}
879pub const NROS_RMW_RET_EXTENSION_BASE: i32 = 1000;
880pub const NROS_RMW_RET_OK: i32 = 0;
881pub const NROS_RMW_RET_ERROR: i32 = 1;
882pub const NROS_RMW_RET_TIMEOUT: i32 = 2;
883pub const NROS_RMW_RET_BAD_ALLOC: i32 = 10;
884pub const NROS_RMW_RET_INVALID_ARGUMENT: i32 = 11;
885pub const NROS_RMW_RET_UNSUPPORTED: i32 = 3;
886pub const NROS_RMW_RET_INCOMPATIBLE_QOS: i32 = 1000;
887pub const NROS_RMW_RET_TOPIC_NAME_INVALID: i32 = 1001;
888pub const NROS_RMW_RET_NODE_NAME_NON_EXISTENT: i32 = 203;
889pub const NROS_RMW_RET_LOAN_NOT_SUPPORTED: i32 = 1002;
890pub const NROS_RMW_RET_NO_DATA: i32 = 1003;
891pub const NROS_RMW_RET_WOULD_BLOCK: i32 = 1004;
892pub const NROS_RMW_RET_BUFFER_TOO_SMALL: i32 = 1005;
893pub const NROS_RMW_RET_MESSAGE_TOO_LARGE: i32 = 1006;
894pub const NROS_RMW_RET_INCOMPATIBLE_ABI: i32 = 1007;
895pub const NROS_RMW_RET_NO_BACKEND: i32 = 1008;
896pub const NROS_RMW_RET_AMBIGUOUS_BACKEND: i32 = 1009;
897pub const NROS_RMW_RET_UNKNOWN_BACKEND: i32 = 1010;
898pub const NROS_RMW_RET_CONNECTION_FAILED: i32 = 1011;
899pub const NROS_RMW_RET_INVALID_CONFIG: i32 = 1012;
900pub const NROS_RMW_RET_INCORRECT_RMW_IMPLEMENTATION: i32 = 12;
901pub const NROS_RMW_RELIABILITY_SYSTEM_DEFAULT: i32 = 0;
902pub const NROS_RMW_RELIABILITY_RELIABLE: i32 = 1;
903pub const NROS_RMW_RELIABILITY_BEST_EFFORT: i32 = 2;
904pub const NROS_RMW_RELIABILITY_UNKNOWN: i32 = 3;
905pub const NROS_RMW_DURABILITY_SYSTEM_DEFAULT: i32 = 0;
906pub const NROS_RMW_DURABILITY_TRANSIENT_LOCAL: i32 = 1;
907pub const NROS_RMW_DURABILITY_VOLATILE: i32 = 2;
908pub const NROS_RMW_DURABILITY_UNKNOWN: i32 = 3;
909pub const NROS_RMW_HISTORY_SYSTEM_DEFAULT: i32 = 0;
910pub const NROS_RMW_HISTORY_KEEP_LAST: i32 = 1;
911pub const NROS_RMW_HISTORY_KEEP_ALL: i32 = 2;
912pub const NROS_RMW_HISTORY_UNKNOWN: i32 = 3;
913pub const RMW_GID_STORAGE_SIZE: i32 = 24;
914pub const RMW_INET_ADDRSTRLEN: i32 = 48;
915pub const NROS_RMW_DURATION_INFINITE_MS: i64 = 4294967295;
916pub const RMW_SESSION_MAX_PROPERTIES: i32 = 16;
917pub const NROS_RMW_VISITOR_DEFINED: i32 = 1;
918#[doc = " Borrow-shaped union the backend supplies to the registered\n  callback. The `kind` argument selects which member is valid."]
919#[repr(C)]
920#[derive(Copy, Clone)]
921pub union rmw_event_payload_t {
922    pub liveliness_changed: rmw_liveliness_changed_status_t,
923    pub count: rmw_count_status_t,
924}
925pub mod rmw_feature_t {
926    #[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."]
927    pub type Type = core::ffi::c_uint;
928    pub const RMW_FEATURE_MESSAGE_INFO_PUBLICATION_SEQUENCE_NUMBER: Type = 0;
929    pub const RMW_FEATURE_MESSAGE_INFO_RECEPTION_SEQUENCE_NUMBER: Type = 1;
930}
931pub mod rmw_liveliness_kind_t {
932    #[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."]
933    pub type Type = core::ffi::c_uint;
934    #[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."]
935    pub const NROS_RMW_LIVELINESS_SYSTEM_DEFAULT: Type = 0;
936    #[doc = " Backend's keepalive task asserts liveliness automatically."]
937    pub const NROS_RMW_LIVELINESS_AUTOMATIC: Type = 1;
938    #[doc = " Application calls `assert_liveliness()` at the node level."]
939    pub const NROS_RMW_LIVELINESS_MANUAL_BY_NODE: Type = 2;
940    #[doc = " Application calls `assert_liveliness()` per topic explicitly."]
941    pub const NROS_RMW_LIVELINESS_MANUAL_BY_TOPIC: Type = 3;
942    #[doc = " The backend could not determine this policy."]
943    pub const NROS_RMW_LIVELINESS_UNKNOWN: Type = 4;
944}
945pub mod rmw_qos_compatibility_type_t {
946    #[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."]
947    pub type Type = core::ffi::c_uint;
948    pub const RMW_QOS_COMPATIBILITY_OK: Type = 0;
949    pub const RMW_QOS_COMPATIBILITY_WARNING: Type = 1;
950    pub const RMW_QOS_COMPATIBILITY_ERROR: Type = 2;
951}
952pub mod nros_rmw_qos_clash_t {
953    #[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."]
954    pub type Type = core::ffi::c_uint;
955    pub const NROS_RMW_QOS_CLASH_NONE: Type = 0;
956    pub const NROS_RMW_QOS_CLASH_RELIABILITY: Type = 1;
957    pub const NROS_RMW_QOS_CLASH_DURABILITY: Type = 2;
958    pub const NROS_RMW_QOS_CLASH_DEADLINE: Type = 4;
959    pub const NROS_RMW_QOS_CLASH_LIVELINESS_KIND: Type = 8;
960    pub const NROS_RMW_QOS_CLASH_LIVELINESS_LEASE: Type = 16;
961}
962pub mod rmw_log_severity_t {
963    #[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."]
964    pub type Type = core::ffi::c_uint;
965    pub const RMW_LOG_SEVERITY_UNSET: Type = 0;
966    pub const RMW_LOG_SEVERITY_DEBUG: Type = 10;
967    pub const RMW_LOG_SEVERITY_INFO: Type = 20;
968    pub const RMW_LOG_SEVERITY_WARN: Type = 30;
969    pub const RMW_LOG_SEVERITY_ERROR: Type = 40;
970    pub const RMW_LOG_SEVERITY_FATAL: Type = 50;
971}
972pub mod rmw_endpoint_type_t {
973    #[doc = " Which end of a topic an endpoint is — upstream `rmw_endpoint_type_t`."]
974    pub type Type = core::ffi::c_uint;
975    pub const RMW_ENDPOINT_INVALID: Type = 0;
976    pub const RMW_ENDPOINT_PUBLISHER: Type = 1;
977    pub const RMW_ENDPOINT_SUBSCRIPTION: Type = 2;
978}
979pub mod rmw_transport_protocol_t {
980    #[doc = " Transport protocol of a network flow — upstream `rmw_transport_protocol_t`,\n  values included."]
981    pub type Type = core::ffi::c_uint;
982    pub const RMW_TRANSPORT_PROTOCOL_UNKNOWN: Type = 0;
983    pub const RMW_TRANSPORT_PROTOCOL_UDP: Type = 1;
984    pub const RMW_TRANSPORT_PROTOCOL_TCP: Type = 2;
985    pub const RMW_TRANSPORT_PROTOCOL_COUNT: Type = 3;
986}
987pub mod rmw_internet_protocol_t {
988    #[doc = " Internet protocol of a network flow — upstream `rmw_internet_protocol_t`."]
989    pub type Type = core::ffi::c_uint;
990    pub const RMW_INTERNET_PROTOCOL_UNKNOWN: Type = 0;
991    pub const RMW_INTERNET_PROTOCOL_IPV4: Type = 1;
992    pub const RMW_INTERNET_PROTOCOL_IPV6: Type = 2;
993    pub const RMW_INTERNET_PROTOCOL_COUNT: Type = 3;
994}
995pub mod rmw_event_type_t {
996    #[doc = " Tier-1 event kinds. Stable integer values; future kinds (Tier-2)\n  extend the enum at end."]
997    pub type Type = core::ffi::c_uint;
998    #[doc = " Subscriber: a tracked publisher's liveliness state changed."]
999    pub const NROS_RMW_EVENT_LIVELINESS_CHANGED: Type = 0;
1000    #[doc = " Subscriber: an expected sample didn't arrive within the\n  configured deadline."]
1001    pub const NROS_RMW_EVENT_REQUESTED_DEADLINE_MISSED: Type = 1;
1002    #[doc = " Subscriber: backend dropped a sample (overflow / etc.)."]
1003    pub const NROS_RMW_EVENT_MESSAGE_LOST: Type = 2;
1004    #[doc = " Publisher: this publisher missed its own liveliness assertion."]
1005    pub const NROS_RMW_EVENT_LIVELINESS_LOST: Type = 3;
1006    #[doc = " Publisher: this publisher promised X Hz, fell behind."]
1007    pub const NROS_RMW_EVENT_OFFERED_DEADLINE_MISSED: Type = 4;
1008}
1009pub mod nros_rmw_session_mode_t {
1010    #[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.)"]
1011    pub type Type = core::ffi::c_uint;
1012    #[doc = " Connect to a router/agent as a client. The default."]
1013    pub const NROS_RMW_SESSION_MODE_CLIENT: Type = 0;
1014    #[doc = " Peer-to-peer, no router. Backends without a peer mode ignore this."]
1015    pub const NROS_RMW_SESSION_MODE_PEER: Type = 1;
1016}
1017unsafe extern "C" {
1018    #[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."]
1019    pub fn nros_rmw_qos_incompatibility_mask(
1020        offered: rmw_qos_profile_t,
1021        requested: rmw_qos_profile_t,
1022        compatibility: *mut rmw_qos_compatibility_type_t::Type,
1023        clash_mask: *mut u32,
1024    ) -> rmw_ret_t;
1025}
1026unsafe extern "C" {
1027    #[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."]
1028    pub fn rmw_qos_profile_check_compatible(
1029        publisher_profile: rmw_qos_profile_t,
1030        subscription_profile: rmw_qos_profile_t,
1031        compatibility: *mut rmw_qos_compatibility_type_t::Type,
1032        reason: *mut core::ffi::c_char,
1033        reason_size: usize,
1034    ) -> rmw_ret_t;
1035}
1036unsafe extern "C" {
1037    #[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`."]
1038    pub fn rmw_compare_gids_equal(
1039        gid1: *const rmw_gid_t,
1040        gid2: *const rmw_gid_t,
1041        result: *mut bool,
1042    ) -> rmw_ret_t;
1043}
1044unsafe extern "C" {
1045    #[doc = " Register a custom RMW backend under the implicit name \"default\".\n  Legacy single-arg form retained for source compatibility with\n  backend ctors authored before the named registry (Phase 104.B.2).\n\n  Deprecated (Phase 128.B.5): every in-tree backend now calls\n  `nros_rmw_cffi_register_named` with its canonical name. The\n  unnamed shim will be removed in a follow-up phase.\n  Returns NROS_RMW_RET_OK."]
1046    pub fn nros_rmw_cffi_register(vtable: *const nros_rmw_vtable_t) -> rmw_ret_t;
1047}
1048unsafe extern "C" {
1049    #[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\" is the implicit name used by `nros_rmw_cffi_register`.\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)."]
1050    pub fn nros_rmw_cffi_register_named(
1051        name: *const core::ffi::c_char,
1052        vtable: *const nros_rmw_vtable_t,
1053    ) -> rmw_ret_t;
1054}
1055unsafe extern "C" {
1056    #[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."]
1057    pub fn nros_rmw_cffi_lookup(name: *const core::ffi::c_char) -> *const nros_rmw_vtable_t;
1058}
1059unsafe extern "C" {
1060    #[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."]
1061    pub fn nros_rmw_cffi_registered_names(buf: *mut *const core::ffi::c_char, cap: usize) -> usize;
1062}
1063unsafe extern "C" {
1064    #[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 `<nros/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."]
1065    pub fn nros_rmw_cffi_set_custom_transport(ops: *const nros_transport_ops_t) -> rmw_ret_t;
1066}