1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
//! # Simple SOME/IP
//!
//! [](https://github.com/luminartech/simple_someip/actions/workflows/ci.yml)
//! [](https://app.codecov.io/gh/luminartech/simple_someip)
//! [](https://crates.io/crates/simple-someip)
//!
//! A Rust implementation of the [SOME/IP](https://github.com/some-ip-com/open-someip-spec)
//! automotive communication protocol — remote procedure calls, event notifications, service
//! discovery, and wire-format serialization.
//!
//! The core protocol layer (`protocol`, `e2e`, and trait modules) is `no_std`-compatible with
//! zero heap allocation, making it suitable for embedded targets. Optional `client` and `server`
//! modules provide async tokio-based networking for `std` environments.
//!
//! ## Modules
//!
//! | Module | `no_std` | Description |
//! |--------|----------|-------------|
//! | [`protocol`] | Yes | Wire format: headers, messages, message types, return codes, and service discovery (SD) entries/options |
//! | [`e2e`] | Yes | End-to-End protection — Profile 4 (CRC-32) and Profile 5 (CRC-16) |
//! | [`WireFormat`] / [`PayloadWireFormat`] | Yes | Traits for serializing messages and defining custom payload types |
//! | `client` | No | Async client trait surface — service discovery, subscriptions, request/response (feature `client`; add `client-tokio` for `Client::new`) |
//! | `server` | No | Async server trait surface — service offering, event publishing, subscription management (feature `server`; add `server-tokio` for `Server::new`) |
//!
//! ## Feature Flags
//!
//! | Feature | Default | Description |
//! |---------|---------|-------------|
//! | `std` | yes | Enables std-dependent helpers (`RawPayload`, `VecSdHeader`) and the `Arc<Mutex<E2ERegistry>>` / `Arc<RwLock<…>>` default lock-handle impls used by the tokio backends. |
//! | `client` | no | Trait-surface client. Pure `no_std`-clean (does not pull `extern crate alloc`). Caller supplies `Spawner` / `Timer` / `ChannelFactory` / `TransportFactory` / `E2ERegistryHandle` / `InterfaceHandle` impls. |
//! | `client-tokio` | no | Adds the `Client::new` / `TokioSpawner` / `TokioTransport` convenience defaults; implies `client` + std + tokio + socket2. |
//! | `server` | no | Trait-surface server. Alloc-free since PR #124: the no-alloc path is `Server::new_with_handles` + `run_with_buffers` with static handles. The `Arc`-backed conveniences (`new_with_deps`, `run`) are gated behind the internal `_alloc` feature (pulled in by `std` / `embassy_channels`). |
//! | `server-tokio` | no | Adds the `Server::new` / `TokioTransport` / `TokioTimer` convenience defaults; implies `server` + std + tokio + socket2. |
//! | `bare_metal` | no | Activates embassy-sync, the `static_channels` module (no-alloc `ChannelFactory`), `AtomicInterfaceHandle`, `StaticE2EHandle`, and `StaticSubscriptionHandle`. All five are pure `no_std` (no allocator required). See `examples/bare_metal_client/` and `examples/bare_metal_server/` for runnable bare-metal integration examples. |
//! | `embassy_channels` | no | Heap-backed `EmbassySyncChannels` `ChannelFactory`. Implies `bare_metal` and pulls `extern crate alloc;` into the crate; **on `no_std`, downstream consumers must provide a `#[global_allocator]`**. Useful for tests / early prototypes before sizing static pools. |
//!
//! The default feature set is `["std"]`, which links `std` and enables
//! the `RawPayload` / `VecSdHeader` helpers. For a minimal build with
//! no allocator requirement — the `protocol`, trait, `transport`, and
//! `e2e` modules only — pass `--no-default-features`. The
//! trait-surface canary workspace members (`examples/bare_metal_client`,
//! `examples/bare_metal_server`) depend on the crate with
//! `default-features = false, features = ["bare_metal", "client"]` /
//! `["bare_metal", "server"]` and validate that configuration when built
//! in isolation (`cargo build -p bare_metal_client` /
//! `cargo build -p bare_metal_server`), rather than as part of a workspace-wide
//! build where features may be unified across members.
//!
//! ## Examples
//!
//! ### Encoding a SOME/IP-SD header (`no_std`)
//!
//! ```rust
//! use simple_someip::WireFormat;
//! use simple_someip::protocol::sd::{self, Entry, RebootFlag, ServiceEntry};
//!
//! // Build an SD header with a FindService entry
//! let entries = [Entry::FindService(ServiceEntry::find(0x1234))];
//! // A fresh process should set RebootFlag::RecentlyRebooted until its
//! // session counter wraps past 0xFFFF for the first time.
//! let sd_header =
//! sd::Header::new(sd::Flags::new_sd(RebootFlag::RecentlyRebooted), &entries, &[]);
//!
//! // Encode to bytes
//! let mut buf = [0u8; 64];
//! let n = sd_header.encode(&mut buf.as_mut_slice()).unwrap();
//!
//! // Decode from bytes (zero-copy view)
//! let view = sd::SdHeaderView::parse(&buf[..n]).unwrap();
//! assert_eq!(view.entry_count(), 1);
//! ```
//!
//! ### Async client (requires `feature = "client-tokio"`)
//!
//! ```rust,no_run
//! # #[cfg(feature = "client-tokio")]
//! # fn wrapper() {
//! use simple_someip::{Client, ClientUpdate, RawPayload};
//!
//! #[tokio::main]
//! async fn main() {
//! // Client::new returns a Clone-able handle, an update stream, and
//! // the run-loop future. Spawn the future on the tokio runtime;
//! // the returned future depends on `tokio::select!` / `tokio::time`
//! // / tokio sockets, so it is not executor-agnostic today.
//! let (client, mut updates, run) = Client::<RawPayload, _, _, _>::new([192, 168, 1, 100].into());
//! let _run_task = tokio::spawn(run);
//! client.bind_discovery().await.unwrap();
//!
//! while let Some(update) = updates.recv().await {
//! match update {
//! ClientUpdate::DiscoveryUpdated(msg) => { /* SD message received */ }
//! ClientUpdate::Unicast { message, e2e_status, source } => { /* unicast reply */ }
//! ClientUpdate::SenderRebooted(addr) => { /* remote reboot */ }
//! ClientUpdate::Error(err) => { /* error */ }
//! }
//! }
//! }
//! # }
//! ```
//!
//! ## References
//!
//! - [Open SOME/IP Specification](https://github.com/some-ip-com/open-someip-spec)
// embassy-executor's `nightly` feature expands `#[embassy_executor::task]`
// to a `static TaskPool` whose `type Fut = impl Future` requires this. Only
// enabled with `bare-metal-runtime` (which owns the executor + task); the
// crate otherwise builds on stable.
// `bare-metal-runtime` is a no-alloc feature (it uses the no-alloc server
// handles, e.g. `started: &'static AtomicBool`) and pulls a nightly-only
// crate feature. It is therefore mutually exclusive with the alloc features
// (`std` / `_alloc` / `embassy_channels` / the `*-tokio` features that imply
// `std`). Combining them — e.g. `--all-features` — otherwise fails deep in
// the runtime with a cryptic type error; surface the real reason here. Build
// the runtime on its own: `--no-default-features --features bare-metal-runtime`
// (plus `client` / `server`). CI runs it in a dedicated nightly lane.
compile_error!;
extern crate std;
// `alloc` is required by:
// - `embassy_channels` — `EmbassySyncChannels` heap-allocates an
// `Arc<Channel<...>>` per oneshot/bounded/unbounded.
// - the allocator-backed server conveniences (`new_with_deps` /
// `new_passive_with_deps`, `run`/`run_inner`'s owned buffers, the
// `Arc` `StartedLatch`). The core `server` engine is alloc-free
// since PR #124 (`new_with_handles` + `run_with_buffers`).
//
// The `static_channels` module (under `bare_metal` alone) does
// NOT need alloc — users wanting `client` + `bare_metal` without
// allocator get the no-alloc oneshot/mpsc primitives via the
// macro. Pure `bare_metal` without `client` / `server` /
// `embassy_channels` also stays alloc-free.
// Pulls `alloc` into scope. Gated on the internal `_alloc` feature
// (implied by `std` and `embassy_channels`). The
// `Arc<T>: SharedHandle<T>` impl in `transport.rs` shares the same
// gate so they move in lockstep.
extern crate alloc;
/// Maximum size, in bytes, of UDP payloads for `client` / `server` send
/// paths that serialize into a fixed-size buffer of this size.
///
/// Paths currently capped by this constant:
/// - `client::SocketManager::send` (unicast + SD outbound)
/// - `server::EventPublisher::publish_event`
/// - `server::EventPublisher::publish_raw_event`
///
/// When one of these paths is actually reached and serialization is
/// attempted, messages larger than this cap fail with
/// `client::Error::Capacity("udp_buffer")` or
/// `server::Error::Capacity("udp_buffer")`, depending on the path.
/// Paths that return early before
/// attempting serialization (e.g. `publish_event` when there are no
/// subscribers) are not affected. The remaining outbound SD paths
/// (`OfferService` announcements, `SubscribeAck` / `SubscribeNack`)
/// serialize into stack buffers of this same size — the phase-21
/// per-event-allocation cleanup (`7c58649`) removed the former heap
/// `Vec` buffers, so every outbound path is capped by this constant.
///
/// Note that this is an application-level UDP payload limit, not an
/// Ethernet-MTU-safe size: a 1500-byte UDP payload exceeds a 1500-byte
/// L2 MTU once IP/UDP headers are added (IPv4 leaves 1472 bytes of UDP
/// payload, IPv6 leaves 1452), so sends at this size may fragment or
/// fail depending on the network stack. Bare-metal ports targeting a
/// smaller link MTU may want to lower this by forking.
pub const UDP_BUFFER_SIZE: usize = 1500;
/// Fixed-capacity pool of `&'static mut [u8]` receive/scratch buffers.
/// Pure `no_std` (uses only `core::`). Exposed without a feature gate so
/// both the bare-metal and std/tokio paths can reach [`buffer_pool::BufferPool`]
/// and [`buffer_pool::BufferLease`].
/// SOME/IP client for discovering services and exchanging messages.
/// End-to-end (E2E) protection utilities for SOME/IP payloads.
/// no_std / no-alloc [`PayloadWireFormat`] mirroring the std-only
/// `RawPayload` with `heapless::Vec`-backed storage. Available whenever
/// the `bare_metal` feature is enabled.
/// SOME/IP protocol primitives: headers, messages, return codes, and service discovery.
/// A general-purpose, heap-allocated [`PayloadWireFormat`] implementation.
/// SOME/IP server for offering services and handling incoming requests.
///
/// The engine is generic over [`transport::TransportFactory`] +
/// [`transport::Timer`] + [`transport::E2ERegistryHandle`] +
/// [`server::SubscriptionHandle`], so the bare `server` feature exposes the
/// trait-surface server. The `server-tokio` feature additionally provides
/// the tokio convenience constructors (`server::Server::new`,
/// `server::Server::new_with_loopback`, `server::Server::new_passive`)
/// that default the type parameters to
/// `Arc<Mutex<E2ERegistry>>` / `Arc<RwLock<SubscriptionManager>>` /
/// `TokioTransport` / `TokioTimer`.
/// Tokio + `socket2` implementation of the [`transport`] traits. Provided
/// as the default `std` backend — available whenever `client-tokio` or
/// `server-tokio` is enabled.
/// Reusable bare-metal SOME/IP runtime: callback-driven transport + RX
/// mailbox + the embassy executor and single composed task, so a
/// platform integrates by supplying only its catalog + I/O callbacks.
/// Spawnable, embassy-agnostic async futures (offer announce, subscribe
/// announce, event RX+dispatch) plus a sync publish helper, so a
/// bare-metal firmware only spawns futures and provides socket I/O.
/// `embassy-sync`-backed implementation of [`transport::ChannelFactory`].
/// Available whenever the `embassy_channels` feature is enabled. Uses
/// heap allocation (`Arc<Channel<...>>`) — for no-alloc, use
/// [`static_channels`] instead.
/// Pure, no-alloc SOME/IP + SD datagram codec: transport-agnostic
/// builders/parsers used by the server receive loop, the firmware shim,
/// and the spawnable futures in [`bare_metal_tasks`].
/// Static-pool no-alloc primitives for [`transport::ChannelFactory`].
/// Backs the consumer-declared static `OneshotPool` / `MpscPool`
/// instances that the [`define_static_channels!`] macro
/// generates per-`T` `*Pooled<MyChannels>` impls against.
/// Executor-agnostic UDP transport abstraction used by the client and
/// server modules. `no_std`-compatible; a default `std + tokio` backend
/// ships in `tokio_transport` (available under the `client-tokio` /
/// `server-tokio` features) — the link is rendered as a code literal
/// because the target module is feature-gated and would break
/// default-feature rustdoc builds.
pub use ;
pub use ;
pub use ;
pub use ;
// `ClientChannelTypes`, `ControlMessage`, `SendMessage`, `ReceivedMessage`
// are intentionally NOT re-exported at crate root — they are
// implementation-detail-with-a-public-name (reachable as
// `simple_someip::client::ControlMessage` etc. for the
// `define_static_channels!` macro) rather than first-class crate-API
// types. Elevating them to crate root would lock their shape into
// the public-API contract and tempt generic users into hitting the
// `ClientChannelTypes` elaboration limit at the wrong call site.
pub use ;
pub use ;
pub use ;
pub use AtomicInterfaceHandle;
pub use ;
pub use ;
/// Parse a decimal `usize` from a compile-time optional env var string.
///
/// Used to size internal constants from `SIMPLE_SOMEIP_MAX_*` env vars
/// injected by the host build system (e.g. `CMake` via `.cargo/config.toml`).
/// Returns `default` when the variable is absent or empty.
/// Panics at compile time if the string contains a non-digit character.
///
/// Gated on `server`: every caller lives in the server module or in the
/// `bare-metal-runtime` runtime (which itself implies `server`), so a
/// `client`-only / `bare_metal`-only build would otherwise see it as dead code.
pub const