1use core::marker;
3
4use o3::buffer::{resident, segments};
5use sark_http::payload::owned;
6
7use crate::duplex::{self, admit};
8
9const INPUT_FINISH_PENDING: u8 = 1;
10const INPUT_FINISH_HELD: u8 = 1 << 1;
11const INITIAL_ACTIVATION: u8 = 1 << 2;
12
13#[derive(Clone, Copy, PartialEq, Eq)]
14#[repr(u8)]
15enum Phase {
16 Idle,
17 Turn,
18 Wait,
19 Activate,
20 FinishOutput,
21 FinishSession,
22 QueuedWait,
23 QueuedActivate,
24 QueuedFinishOutput,
25 QueuedFinishSession,
26 Finished,
27}
28
29impl Phase {
30 const fn action(self) -> Option<Staged> {
31 let (dir, resume) = match self {
32 Self::Wait => (duplex::Directive::Wait, Resume::Idle),
33 Self::Activate => (duplex::Directive::Activate, Resume::Idle),
34 Self::FinishOutput => (duplex::Directive::FinishOutput, Resume::Idle),
35 Self::FinishSession => (duplex::Directive::FinishSession, Resume::Idle),
36 Self::QueuedWait => (duplex::Directive::Wait, Resume::Turn),
37 Self::QueuedActivate => (duplex::Directive::Activate, Resume::Turn),
38 Self::QueuedFinishOutput => (duplex::Directive::FinishOutput, Resume::Turn),
39 Self::QueuedFinishSession => (duplex::Directive::FinishSession, Resume::Turn),
40 Self::Idle | Self::Turn | Self::Finished => return None,
41 };
42 Some(Staged {
43 directive: dir,
44 resume,
45 })
46 }
47
48 const fn from_action(dir: duplex::Directive, resume: Resume) -> Self {
49 match (dir, resume) {
50 (duplex::Directive::Wait, Resume::Idle) => Self::Wait,
51 (duplex::Directive::Activate, Resume::Idle) => Self::Activate,
52 (duplex::Directive::FinishOutput, Resume::Idle) => Self::FinishOutput,
53 (duplex::Directive::FinishSession, Resume::Idle) => Self::FinishSession,
54 (duplex::Directive::Wait, Resume::Turn) => Self::QueuedWait,
55 (duplex::Directive::Activate, Resume::Turn) => Self::QueuedActivate,
56 (duplex::Directive::FinishOutput, Resume::Turn) => Self::QueuedFinishOutput,
57 (duplex::Directive::FinishSession, Resume::Turn) => Self::QueuedFinishSession,
58 }
59 }
60}
61
62#[derive(Clone, Copy)]
63#[repr(u8)]
64enum Resume {
65 Idle,
66 Turn,
67}
68
69#[derive(Clone, Copy)]
70struct Staged {
71 directive: duplex::Directive,
72 resume: Resume,
73}
74
75#[derive(Clone, Copy, PartialEq, Eq)]
77#[repr(u8)]
78pub enum Output {
79 Open,
80 Finished,
81}
82
83#[derive(Clone, Copy)]
84struct Next {
85 settled: After,
86 flags: u8,
87 output: Output,
88}
89
90impl Next {
91 const fn after(&self) -> After {
92 match self.settled {
93 After::Finished => After::Finished,
94 After::Turn => After::Turn,
95 After::Idle if self.flags & INPUT_FINISH_PENDING != 0 => After::Turn,
96 After::Idle => After::Idle,
97 }
98 }
99
100 const fn reduce(mut flags: u8, mut output: Output, staged: Staged) -> Self {
101 let settled = match staged.directive {
102 duplex::Directive::Wait => match staged.resume {
103 Resume::Idle => After::Idle,
104 Resume::Turn => After::Turn,
105 },
106 duplex::Directive::Activate => After::Turn,
107 duplex::Directive::FinishOutput => {
108 output = Output::Finished;
109 if flags == INPUT_FINISH_HELD {
110 flags = 0;
111 After::Finished
112 } else {
113 match staged.resume {
114 Resume::Idle => After::Idle,
115 Resume::Turn => After::Turn,
116 }
117 }
118 }
119 duplex::Directive::FinishSession => {
120 flags = 0;
121 output = Output::Finished;
122 After::Finished
123 }
124 };
125 Self {
126 settled,
127 flags,
128 output,
129 }
130 }
131}
132
133pub struct Machine<'d, D: duplex::Session<'d>> {
135 pending: Option<segments::Nonempty<resident::Shared<'d>>>,
137 session: D,
138 phase: Phase,
139 flags: u8,
140 output: Output,
141}
142
143#[must_use = "commit the input owner or keep it at the carrier boundary"]
145pub struct Input<'a, 'd, D: duplex::Session<'d>>(&'a mut Machine<'d, D>);
146pub enum Access<T> {
148 Vacant(T),
149 Blocked,
150}
151#[derive(Clone, Copy, PartialEq, Eq)]
153#[repr(u8)]
154pub enum Progress {
155 Idle,
156 Turn,
157 Finished,
158}
159#[derive(Clone, Copy, PartialEq, Eq)]
161#[repr(u8)]
162pub enum Hold {
163 Released,
164 Held,
165}
166impl<'d, D: duplex::Session<'d>> Input<'_, 'd, D> {
167 pub fn commit(self, input: owned::Segments<'d>) {
168 if let Some(input) = input.take() {
169 self.0.pending = Some(input);
170 }
171 }
172}
173#[must_use = "commit the validated input finish"]
175pub struct Finish<'a, 'd, D: duplex::Session<'d>>(&'a mut Machine<'d, D>);
176
177impl<'d, D: duplex::Session<'d>> Finish<'_, 'd, D> {
178 pub fn commit(self) {
179 self.0.flags |= INPUT_FINISH_PENDING | INPUT_FINISH_HELD;
180 }
181}
182
183#[derive(Clone, Copy, PartialEq, Eq)]
185#[repr(u8)]
186pub enum End {
187 Open,
188 FinishOutput,
189 FinishSession,
190}
191
192#[derive(Clone, Copy, PartialEq, Eq)]
194#[repr(u8)]
195pub enum After {
196 Idle,
197 Turn,
198 Finished,
199}
200
201pub enum Transaction<R, E> {
203 Idle,
204 Committed { receipt: R, after: After },
205 Pending { receipt: R },
206 Rejected(E),
207 Violation(Violation),
208}
209
210#[derive(Clone, Copy, PartialEq, Eq)]
212pub enum Violation {
213 OutputAfterFinish,
214 PrefixCapacity { written: usize, capacity: usize },
215 PrefixPending,
216}
217
218#[derive(Clone, Copy, Debug, PartialEq, Eq)]
220pub enum FinishError {
221 Finished,
222 Closed,
223}
224
225impl<'d, D: duplex::Session<'d>> Machine<'d, D> {
226 pub fn new(inner: D) -> Self {
227 Self {
228 session: inner,
229 pending: None,
230 phase: Phase::Turn,
231 flags: INITIAL_ACTIVATION,
232 output: Output::Open,
233 }
234 }
235
236 pub(super) fn with_finish(inner: D) -> Self {
237 Self {
238 flags: INPUT_FINISH_PENDING | INPUT_FINISH_HELD | INITIAL_ACTIVATION,
239 ..Self::new(inner)
240 }
241 }
242
243 pub fn input(&mut self) -> Access<Input<'_, 'd, D>> {
245 match self.access() {
246 Access::Vacant(()) => Access::Vacant(Input(self)),
247 Access::Blocked => Access::Blocked,
248 }
249 }
250
251 pub const fn access(&self) -> Access<()> {
252 match (&self.pending, self.phase) {
253 (None, Phase::Idle | Phase::Turn) => Access::Vacant(()),
254 (None | Some(_), _) => Access::Blocked,
255 }
256 }
257
258 pub const fn progress(&self) -> Progress {
259 if self.pending.is_some() || self.flags & INPUT_FINISH_PENDING != 0 {
260 return Progress::Turn;
261 }
262 match self.phase {
263 Phase::Idle => Progress::Idle,
264 Phase::Turn
265 | Phase::Wait
266 | Phase::Activate
267 | Phase::FinishOutput
268 | Phase::FinishSession
269 | Phase::QueuedWait
270 | Phase::QueuedActivate
271 | Phase::QueuedFinishOutput
272 | Phase::QueuedFinishSession => Progress::Turn,
273 Phase::Finished => Progress::Finished,
274 }
275 }
276
277 pub const fn output(&self) -> &Output {
278 &self.output
279 }
280
281 pub const fn hold(&self) -> Hold {
282 if self.flags & INPUT_FINISH_HELD == 0 {
283 Hold::Released
284 } else {
285 Hold::Held
286 }
287 }
288
289 pub fn finish<I>(&mut self, owner: I) -> Result<(Finish<'_, 'd, D>, I), (FinishError, I)> {
292 match self.finish_error() {
293 Some(error) => Err((error, owner)),
294 None => Ok((Finish(self), owner)),
295 }
296 }
297
298 pub fn transact<R, E>(
300 &mut self,
301 output: &mut [u8],
302 scratch: &mut Vec<u8>,
303 admit: impl for<'turn> FnOnce(
304 usize,
305 admit::Emission<'turn, 'd, D>,
306 ) -> admit::Admission<'turn, 'd, D, R, E>,
307 ) -> Transaction<R, E> {
308 let (written, staged) = match self.phase.action() {
309 Some(staged) => (0, staged),
310 None => {
311 let pending = self.pending.is_some();
312 let turn = matches!(self.phase, Phase::Turn);
313 let finish = self.flags & INPUT_FINISH_PENDING != 0;
314 if !pending && !turn && !finish {
315 return Transaction::Idle;
316 }
317 let activate = turn && (!finish || self.flags & INITIAL_ACTIVATION != 0);
319 let resume = if turn && (pending || !activate) {
320 Resume::Turn
321 } else {
322 Resume::Idle
323 };
324 let (written, action) = if let Some(input) = self.pending.take() {
325 self.session
326 .receive(owned::Segments::from(input), output, scratch)
327 } else if activate {
328 self.flags &= !INITIAL_ACTIVATION;
329 self.session.activate(output, scratch)
330 } else {
331 self.flags &= !INPUT_FINISH_PENDING;
332 self.session.finish(output, scratch)
333 };
334 let (output, dir) = action.into_parts();
335 self.pending = output.and_then(owned::Segments::take);
336 self.phase = Phase::from_action(dir, resume);
337 (
338 written,
339 Staged {
340 directive: dir,
341 resume,
342 },
343 )
344 }
345 };
346 if written > output.len() {
347 self.terminate();
348 return Transaction::Violation(Violation::PrefixCapacity {
349 written,
350 capacity: output.len(),
351 });
352 }
353 if matches!(self.output, Output::Finished) && (written != 0 || self.pending.is_some()) {
354 self.terminate();
355 return Transaction::Violation(Violation::OutputAfterFinish);
356 }
357 let end = match (self.output, staged.directive) {
358 (Output::Finished, _)
359 | (Output::Open, duplex::Directive::Wait | duplex::Directive::Activate) => End::Open,
360 (Output::Open, duplex::Directive::FinishOutput) => End::FinishOutput,
361 (Output::Open, duplex::Directive::FinishSession) => End::FinishSession,
362 };
363 let next = Next::reduce(self.flags, self.output, staged);
364 let after = next.after();
365 let payload = match self.pending.take() {
366 Some(output) => admit::Payload::Present {
367 output,
368 permit: admit::Permit {
369 owner: marker::PhantomData,
370 mode: marker::PhantomData,
371 },
372 },
373 None => admit::Payload::Vacant(admit::Permit {
374 owner: marker::PhantomData,
375 mode: marker::PhantomData,
376 }),
377 };
378 let emit = admit::Emission {
379 payload,
380 end,
381 after,
382 };
383
384 let admit::Admission {
385 decision,
386 owner: marker::PhantomData,
387 } = admit(written, emit);
388 match decision {
389 admit::Decision::Pending { .. } | admit::Decision::Blocked { .. } if written != 0 => {
390 self.terminate();
391 Transaction::Violation(Violation::PrefixPending)
392 }
393 admit::Decision::Committed { receipt: proof } => {
394 self.phase = match next.settled {
395 After::Idle => Phase::Idle,
396 After::Turn => Phase::Turn,
397 After::Finished => Phase::Finished,
398 };
399 self.flags = next.flags;
400 self.output = next.output;
401 Transaction::Committed {
402 receipt: proof,
403 after,
404 }
405 }
406 admit::Decision::Pending {
407 remainder: rest,
408 receipt: proof,
409 } => {
410 self.pending = Some(rest);
411 Transaction::Pending { receipt: proof }
412 }
413 admit::Decision::Blocked { receipt: proof } => Transaction::Pending { receipt: proof },
414 admit::Decision::Rejected { error } => {
415 self.terminate();
416 Transaction::Rejected(error)
417 }
418 admit::Decision::Returned {
419 error,
420 remainder: rest,
421 } => {
422 drop(rest);
423 self.terminate();
424 Transaction::Rejected(error)
425 }
426 }
427 }
428
429 fn terminate(&mut self) {
430 self.pending = None;
431 self.phase = Phase::Finished;
432 self.flags = 0;
433 self.output = Output::Finished;
434 }
435
436 fn finish_error(&self) -> Option<FinishError> {
437 match self.progress() {
438 Progress::Finished => Some(FinishError::Closed),
439 Progress::Idle | Progress::Turn
440 if self.flags & (INPUT_FINISH_PENDING | INPUT_FINISH_HELD) != 0 =>
441 {
442 Some(FinishError::Finished)
443 }
444 Progress::Idle | Progress::Turn => None,
445 }
446 }
447
448 pub fn retire(&mut self) {
450 if matches!(self.phase, Phase::Finished) {
451 return;
452 }
453 self.terminate();
454 }
455}
456
457const _: () = {
458 assert!(core::mem::size_of::<Resume>() == core::mem::size_of::<bool>());
459 assert!(core::mem::align_of::<Resume>() == core::mem::align_of::<bool>());
460 assert!(core::mem::size_of::<Next>() == 3);
461 assert!(core::mem::align_of::<Next>() == 1);
462 assert!(core::mem::size_of::<After>() == core::mem::size_of::<u8>());
463 assert!(core::mem::align_of::<After>() == core::mem::align_of::<u8>());
464 assert!(core::mem::size_of::<Output>() == core::mem::size_of::<u8>());
465 assert!(core::mem::align_of::<Output>() == core::mem::align_of::<u8>());
466 assert!(
467 core::mem::size_of::<Machine<'static, duplex::Never>>()
468 == core::mem::size_of::<Option<segments::Nonempty<resident::Shared<'static>>>>()
469 + core::mem::size_of::<usize>()
470 );
471 assert!(
472 core::mem::size_of::<Input<'static, 'static, duplex::Never>>()
473 == core::mem::size_of::<usize>()
474 );
475 assert!(
476 core::mem::size_of::<Finish<'static, 'static, duplex::Never>>()
477 == core::mem::size_of::<usize>()
478 );
479};