byteor_pipeline_kernel/lane.rs
1//! Lane traits.
2
3/// TX error that keeps "full" distinct from other failures.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[non_exhaustive]
6pub enum LaneTxError {
7 /// The lane is full.
8 Full,
9 /// The payload length exceeds the maximum slot size.
10 TooLarge {
11 /// Maximum supported payload size in bytes.
12 max: usize,
13 /// Actual payload size in bytes.
14 len: usize,
15 },
16 /// Any other TX failure.
17 Failed,
18}
19
20/// RX error for borrowed receive APIs.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[non_exhaustive]
23pub enum LaneRxError {
24 /// Any RX failure (layout/incompatibility/etc).
25 Failed,
26}
27
28/// Slot handle returned by [`LaneRxSlot`].
29///
30/// Slot passing requires that a slot can be inspected without copying bytes.
31pub trait LaneSlot {
32 /// Borrow the payload bytes.
33 fn as_bytes(&self) -> &[u8];
34}
35
36impl LaneSlot for indexbus_core::EventsSlot {
37 #[inline]
38 fn as_bytes(&self) -> &[u8] {
39 indexbus_core::EventsSlot::as_bytes(self)
40 }
41}
42
43/// RX side of a lane.
44pub trait LaneRx {
45 /// Receive one message into `buf`, returning the number of bytes written.
46 fn recv(&mut self, buf: &mut [u8]) -> Option<usize>;
47}
48
49/// RX side of a lane, borrowed receive.
50///
51/// This enables adapters to parse/decode without copying bytes into an intermediate buffer.
52///
53/// The borrowed `&[u8]` passed to the callback is only valid for the duration of the callback.
54pub trait LaneRxBorrow {
55 /// Receive one message and pass it to `f` by borrowed slice.
56 fn recv_with<F, R>(&mut self, f: F) -> core::result::Result<Option<R>, LaneRxError>
57 where
58 F: FnOnce(&[u8]) -> R;
59}
60
61/// RX side of a lane, owned slot receive.
62///
63/// This enables *slot passing*: receiving an owned slot handle (slot index) that can be forwarded
64/// to a compatible TX without copying payload bytes.
65pub trait LaneRxSlot {
66 /// Slot handle type.
67 type Slot: LaneSlot;
68
69 /// Receive one message as an owned slot.
70 fn try_recv_slot(&mut self) -> core::result::Result<Option<Self::Slot>, LaneRxError>;
71}
72
73/// RX side of a lane, batch slot receive.
74///
75/// This is a convenience API for draining up to `max` slots with a single call, allowing a lane
76/// backing to provide a more efficient implementation than looping `try_recv_slot` in the caller.
77///
78/// The default implementation repeatedly calls [`LaneRxSlot::try_recv_slot`].
79pub trait LaneRxSlotBatch: LaneRxSlot {
80 /// Receive and process up to `max` slots.
81 ///
82 /// Calls `f` once per slot. If `f` returns `false`, the drain stops early.
83 ///
84 /// Returns the number of slots received (and passed to `f`).
85 fn try_recv_slots<F>(
86 &mut self,
87 max: usize,
88 mut f: F,
89 ) -> core::result::Result<usize, LaneRxError>
90 where
91 F: FnMut(Self::Slot) -> bool,
92 {
93 let max = max.max(1);
94 let mut n = 0;
95
96 for _ in 0..max {
97 let Some(slot) = self.try_recv_slot()? else {
98 break;
99 };
100 n += 1;
101 if !f(slot) {
102 break;
103 }
104 }
105
106 Ok(n)
107 }
108}
109
110impl<T> LaneRxSlotBatch for T where T: LaneRxSlot {}
111
112/// TX side of a lane.
113pub trait LaneTx {
114 /// Publish the message.
115 fn publish(&mut self, msg: &[u8]) -> core::result::Result<(), LaneTxError>;
116}
117
118/// TX side of a lane, slot-write publish.
119///
120/// This enables adapters to write directly into the backing slot buffer, avoiding an
121/// intermediate copy.
122pub trait LaneTxWith {
123 /// Publish a message by writing it into a provided slot buffer.
124 ///
125 /// The closure returns the number of bytes written.
126 fn publish_with<F>(&mut self, f: F) -> core::result::Result<(), LaneTxError>
127 where
128 F: FnOnce(&mut [u8]) -> usize;
129}
130
131/// Error returned by fallible slot-write publish APIs.
132///
133/// This distinguishes:
134/// - backpressure (`Full`)
135/// - backing failures (`Failed`)
136/// - user-provided encoder failures (`Encode(E)`), which publish nothing.
137#[derive(Debug, PartialEq, Eq)]
138pub enum LaneTxWithError<E> {
139 /// The lane is full.
140 Full,
141 /// Any other TX failure.
142 Failed,
143 /// The user-provided encoder returned an error.
144 ///
145 /// No message is published when this occurs.
146 Encode(E),
147}
148
149impl<E> From<LaneTxError> for LaneTxWithError<E> {
150 #[inline]
151 fn from(value: LaneTxError) -> Self {
152 match value {
153 LaneTxError::Full => Self::Full,
154 LaneTxError::TooLarge { .. } | LaneTxError::Failed => Self::Failed,
155 }
156 }
157}
158
159/// TX side of a lane, slot-write publish with a fallible encoder.
160///
161/// This enables adapters/stages to write directly into the backing slot buffer and return an
162/// error without publishing.
163pub trait LaneTxWithResult {
164 /// Publish a message by writing it into a provided slot buffer.
165 ///
166 /// The closure returns the number of bytes written, or an error.
167 fn publish_with_result<F, E>(&mut self, f: F) -> core::result::Result<(), LaneTxWithError<E>>
168 where
169 F: FnOnce(&mut [u8]) -> core::result::Result<usize, E>;
170}
171
172/// Error returned when publishing an owned slot.
173///
174/// The slot is returned to the caller so it can be retried (`Full`) or dropped.
175#[derive(Debug, PartialEq, Eq)]
176pub enum LaneTxSlotError<S> {
177 /// The lane is full; the caller can retry.
178 Full(S),
179 /// Slot is incompatible with the lane backing; caller may fall back to copying.
180 Incompatible(S),
181 /// Any other failure.
182 Failed(S),
183}
184
185/// TX side of a lane, slot-forward publish.
186///
187/// Unlike [`LaneTx::publish`], this publishes by transferring ownership of a previously received
188/// slot (slot index) into a compatible queue.
189pub trait LaneTxSlot<S> {
190 /// Publish an owned slot.
191 fn publish_slot(&mut self, slot: S) -> core::result::Result<(), LaneTxSlotError<S>>;
192}