Skip to main content

sark_core/duplex/
input.rs

1//! Carrier-generic duplex input ownership and terminal ordering.
2use sark_http::payload::owned;
3
4use crate::{
5    duplex::{self, admit, machine},
6    manifold::stream::Boundary,
7};
8
9/// A carrier buffer whose physical owners can enter the canonical duplex ABI.
10/// Implementations are statically dispatched; no erased storage or callback is
11/// present in the hot path.
12pub trait Buf<'d> {
13    type Input;
14    type Error;
15    type Receipt;
16
17    fn state(&self) -> Buffer;
18
19    fn push(&mut self, input: Self::Input) -> Result<(), Rejected<Self::Input, Self::Error>>;
20
21    fn take(&mut self) -> Take<'d, Self::Receipt>;
22}
23
24/// Exact occupancy of a carrier-owned input buffer.
25#[derive(Clone, Copy, PartialEq, Eq)]
26#[repr(u8)]
27pub enum Buffer {
28    Empty,
29    Queued,
30}
31
32/// Exact commit result of removing one carrier input quantum.
33pub enum Take<'d, R> {
34    Empty,
35    Item {
36        input: owned::Segments<'d>,
37        receipt: R,
38    },
39}
40
41use o3::mem::Rejected;
42
43/// Failure to admit one carrier input into an established duplex direction.
44#[must_use = "a rejected duplex input remains an affine owner"]
45#[derive(Debug)]
46pub enum PushError<I, E> {
47    Finished(I),
48    Buffer(Rejected<I, E>),
49    Finish {
50        input: I,
51        error: machine::FinishError,
52    },
53}
54
55#[derive(Clone, Copy, PartialEq, Eq)]
56#[repr(u8)]
57enum Phase {
58    Open,
59    Finished,
60    Closed,
61}
62
63/// Exact external state of the carrier input direction.
64#[derive(Clone, Copy, PartialEq, Eq)]
65#[repr(u8)]
66pub enum Status {
67    Open,
68    Closed,
69}
70
71/// Exact scheduling demand of the carrier input direction.
72#[derive(Clone, Copy, PartialEq, Eq)]
73#[repr(u8)]
74pub enum Work {
75    Idle,
76    Ready,
77}
78
79/// One protocol-neutral session paired with its carrier-owned input buffer.
80pub struct State<'d, D, B>
81where
82    D: duplex::Session<'d>,
83{
84    raw: machine::Machine<'d, D>,
85    buffer: B,
86    phase: Phase,
87}
88
89/// One machine transaction joined to its affine carrier input disposition.
90#[must_use = "consume the duplex transaction and its affine carrier receipt"]
91pub struct Turn<T, F> {
92    step: T,
93    feed: Feed<F>,
94}
95
96impl<T, F> Turn<T, F> {
97    pub fn into_parts(self) -> (T, Feed<F>) {
98        (self.step, self.feed)
99    }
100}
101
102/// Exact removal disposition for one carrier input quantum.
103#[derive(PartialEq, Eq)]
104#[must_use = "consume the affine receipt for the input callback that ran"]
105pub enum Feed<R> {
106    Idle,
107    Consumed(R),
108}
109
110impl<'d, D, B> State<'d, D, B>
111where
112    D: duplex::Session<'d>,
113    B: Buf<'d>,
114{
115    pub fn open(session: D, buffer: B) -> Self {
116        Self {
117            raw: machine::Machine::new(session),
118            buffer,
119            phase: Phase::Open,
120        }
121    }
122
123    pub fn finished(session: D, buffer: B) -> Self {
124        Self {
125            raw: machine::Machine::with_finish(session),
126            buffer,
127            phase: Phase::Finished,
128        }
129    }
130
131    /// Cancels the session and returns its queued carrier input.
132    pub fn into_buffer(self) -> B {
133        self.buffer
134    }
135
136    pub const fn status(&self) -> Status {
137        match (self.phase, self.raw.progress()) {
138            (Phase::Open, machine::Progress::Idle | machine::Progress::Turn) => Status::Open,
139            (Phase::Finished | Phase::Closed, _) | (Phase::Open, machine::Progress::Finished) => {
140                Status::Closed
141            }
142        }
143    }
144
145    pub fn work(&self) -> Work {
146        match (self.raw.progress(), self.phase, self.buffer.state()) {
147            (machine::Progress::Finished, _, _)
148            | (machine::Progress::Idle, _, Buffer::Empty)
149            | (machine::Progress::Idle, Phase::Closed, Buffer::Queued) => Work::Idle,
150            (machine::Progress::Idle | machine::Progress::Turn, _, _) => Work::Ready,
151        }
152    }
153
154    /// Appends one exact DATA owner to the carrier buffer without exposing the
155    /// session machine between admission and callback consumption.
156    pub fn push(
157        &mut self,
158        input: B::Input,
159        boundary: Boundary,
160    ) -> Result<(), PushError<B::Input, B::Error>> {
161        match self.status() {
162            Status::Open => {}
163            Status::Closed => return Err(PushError::Finished(input)),
164        }
165
166        let (finish, input) = match boundary {
167            Boundary::End => match self.raw.finish(input) {
168                Ok((finish, input)) => (Some(finish), input),
169                Err((error, input)) => return Err(PushError::Finish { input, error }),
170            },
171            Boundary::More => (None, input),
172        };
173
174        self.buffer.push(input).map_err(PushError::Buffer)?;
175        if let Some(finish) = finish {
176            finish.commit();
177            self.phase = match self.buffer.state() {
178                Buffer::Empty => Phase::Closed,
179                Buffer::Queued => Phase::Finished,
180            };
181        }
182        Ok(())
183    }
184
185    /// Records one peer FIN. Empty input can enter the machine immediately;
186    /// queued bytes retain priority over the terminal callback.
187    pub fn finish(&mut self) -> Result<(), machine::FinishError> {
188        match self.phase {
189            Phase::Open => {
190                let finish = match self.raw.finish(()) {
191                    Ok((finish, ())) => finish,
192                    Err((error, ())) => return Err(error),
193                };
194                finish.commit();
195                self.phase = match self.buffer.state() {
196                    Buffer::Empty => Phase::Closed,
197                    Buffer::Queued => Phase::Finished,
198                };
199                Ok(())
200            }
201            Phase::Finished => Err(machine::FinishError::Finished),
202            Phase::Closed => Err(machine::FinishError::Closed),
203        }
204    }
205
206    /// Removes at most one carrier quantum and executes exactly one canonical
207    /// session/admission transaction. The returned receipt proves which
208    /// carrier quantum was removed during this transaction.
209    pub fn transact<R, E>(
210        &mut self,
211        admit: impl for<'turn> FnOnce(
212            admit::Emission<'turn, 'd, D>,
213        ) -> admit::Admission<'turn, 'd, D, R, E>,
214    ) -> Turn<machine::Transaction<R, E>, B::Receipt> {
215        let feed = self.feed();
216        let step = self
217            .raw
218            .transact(&mut [], &mut Vec::new(), |_, emission| admit(emission));
219        Turn { step, feed }
220    }
221
222    fn feed(&mut self) -> Feed<B::Receipt> {
223        match (self.phase, self.raw.progress()) {
224            (Phase::Closed, _) | (Phase::Open | Phase::Finished, machine::Progress::Finished) => {
225                return Feed::Idle;
226            }
227            (Phase::Open | Phase::Finished, machine::Progress::Idle | machine::Progress::Turn) => {}
228        }
229        let raw = &mut self.raw;
230        let buffer = &mut self.buffer;
231        let slot = match raw.input() {
232            machine::Access::Vacant(slot) => slot,
233            machine::Access::Blocked => return Feed::Idle,
234        };
235        match buffer.take() {
236            Take::Empty => Feed::Idle,
237            Take::Item { input, receipt } => {
238                slot.commit(input);
239                if self.phase == Phase::Finished {
240                    match buffer.state() {
241                        Buffer::Empty => self.phase = Phase::Closed,
242                        Buffer::Queued => {}
243                    }
244                };
245                Feed::Consumed(receipt)
246            }
247        }
248    }
249}