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}