1use core::{marker, num::NonZero};
2
3use o3::buffer::segments;
4
5use crate::body::{
6 self, credit, ingress,
7 input::Parts,
8 stream::{Admission, Drive, Stream},
9};
10
11enum State<B, O> {
12 Building(B),
13 Ready(O),
14}
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum Error<E> {
18 LengthMismatch,
19 Credit(credit::Error),
20 Build(E),
21 Release { build: E, error: credit::Error },
22}
23
24pub(super) enum Push<B, E> {
25 Accepted(B),
26 Failed { build: B, error: body::Error },
27 Rejected { build: B, error: E },
28}
29
30pub struct Flow<'d, B: body::Build<'d>> {
32 state: State<B, B::Output>,
33 pub(in crate::body) lifetime: marker::PhantomData<&'d ()>,
34}
35
36#[must_use = "admit the output or discard its retained owner"]
38pub struct Ready<'d, B: body::Build<'d>, O: credit::Origin = credit::raw::Stored> {
39 pub(in crate::body) state: ingress::Finished<O>,
40 pub(in crate::body) output: B::Output,
41 pub(in crate::body) lifetime: marker::PhantomData<&'d ()>,
42}
43
44impl<'d, B: body::Build<'d>, O: credit::Origin> Ready<'d, B, O> {
45 pub fn try_admit<E: credit::Evidence>(
46 mut self,
47 ledger: &mut credit::raw::Ledger<'_, O>,
48 mut event: E,
49 ) -> Admission<'d, B, E, O> {
50 match self.state.try_drain(ledger, &mut event) {
51 Ok(()) => Admission::Accepted(self.output),
52 Err(error) => Admission::Rejected {
53 ready: self,
54 evidence: event,
55 error,
56 },
57 }
58 }
59
60 pub fn discard<E: credit::Evidence>(
61 self,
62 ledger: &mut credit::raw::Ledger<'_, O>,
63 event: E,
64 ) -> credit::Discard<O> {
65 self.into_release().try_discard(ledger, event)
66 }
67
68 pub fn into_release(self) -> credit::Release<O> {
69 let Self { state, output, .. } = self;
70 drop(output);
71 state.into_state().abandon()
72 }
73}
74
75impl<'d, B: body::Build<'d>> Flow<'d, B> {
76 pub const fn new(build: B) -> Self {
77 Self {
78 state: State::Building(build),
79 lifetime: marker::PhantomData,
80 }
81 }
82
83 pub(super) fn from_done(build: B, len: usize) -> Self {
84 Self::new(build.finish_input(len))
85 }
86
87 fn from_output(output: B::Output) -> Self {
88 Self {
89 state: State::Ready(output),
90 lifetime: marker::PhantomData,
91 }
92 }
93
94 pub(super) fn try_parts<const COUNT: bool, I>(
95 mut build: B,
96 parts: I,
97 bytes: usize,
98 chunks: segments::Count,
99 ) -> Push<Self, <B as body::Push<'d>>::Error>
100 where
101 B: body::Push<'d>,
102 I: Parts<'d>,
103 {
104 if I::EXACT {
105 let size = parts.dimensions();
106 if size.bytes() != bytes || COUNT && size.chunks() != chunks {
107 return Push::Failed {
108 build: Self::new(build),
109 error: body::Error::SegmentMismatch,
110 };
111 }
112 }
113 let mut seen = 0usize;
114 let mut count = segments::Count::EMPTY;
115 for part in parts {
116 if !I::EXACT && part.is_empty() {
117 continue;
118 }
119 if !I::EXACT {
120 let Some(total) = seen.checked_add(part.len()).filter(|total| *total <= bytes)
121 else {
122 return Push::Failed {
123 build: Self::new(build),
124 error: body::Error::SegmentMismatch,
125 };
126 };
127 seen = total;
128 if COUNT {
129 let Some(tally) = count
130 .checked_add(segments::Count::ONE)
131 .filter(|tally| *tally <= chunks)
132 else {
133 return Push::Failed {
134 build: Self::new(build),
135 error: body::Error::SegmentMismatch,
136 };
137 };
138 count = tally;
139 }
140 }
141 build = match body::Push::push(build, part) {
142 body::Transfer::Accepted(build) => build,
143 body::Transfer::Rejected { build, error } => {
144 return Push::Rejected {
145 build: Self::new(build),
146 error,
147 };
148 }
149 };
150 }
151 if !I::EXACT && (seen != bytes || COUNT && count != chunks) {
152 return Push::Failed {
153 build: Self::new(build),
154 error: body::Error::SegmentMismatch,
155 };
156 }
157 Push::Accepted(Self::new(build))
158 }
159
160 pub(super) fn try_push<I, E, O: credit::Origin>(
161 self,
162 turn: &mut ingress::Turn<'_, '_, E, O>,
163 parts: I,
164 bytes: usize,
165 chunks: segments::Count,
166 rmax: credit::Limits,
167 cmax: credit::Limits,
168 ) -> Push<Self, <B as body::Push<'d>>::Error>
169 where
170 B: body::Push<'d>,
171 I: Parts<'d>,
172 E: credit::Evidence,
173 {
174 let State::Building(build) = self.state else {
175 return Push::Failed {
176 build: self,
177 error: body::Error::InputFinished,
178 };
179 };
180 let next = match turn.try_advance(bytes) {
181 Ok(next) => next,
182 Err(error) => {
183 return Push::Failed {
184 build: Self::new(build),
185 error,
186 };
187 }
188 };
189 let amount = credit::Amount::new(bytes, chunks);
190 let next = match next.try_reserve(amount, rmax, cmax) {
191 Ok(next) => next.commit(),
192 Err(error) => {
193 return Push::Failed {
194 build: Self::new(build),
195 error: body::Error::Credit(error),
196 };
197 }
198 };
199
200 let transfer = Self::try_parts::<true, _>(build, parts, bytes, chunks);
201 if matches!(transfer, Push::Accepted(_)) {
202 next.commit();
203 }
204 transfer
205 }
206
207 pub fn try_exact<E: credit::Evidence, O: credit::Origin>(
210 self,
211 turn: &mut ingress::Turn<'_, '_, E, O>,
212 len: usize,
213 ) -> body::Transfer<Self, body::Error> {
214 if let Err(error) = turn.state_mut().try_exact(len) {
215 return body::Transfer::Rejected { build: self, error };
216 }
217 self.try_finish(turn.state_mut())
218 }
219
220 pub(super) fn try_finish<O: credit::Origin>(
221 self,
222 state: &mut ingress::State<O>,
223 ) -> body::Transfer<Self, body::Error> {
224 self.try_end_with(|| state.try_end())
225 }
226
227 pub(super) fn try_complete<O: credit::Origin>(
228 self,
229 state: &mut ingress::State<O>,
230 ) -> body::Transfer<Self, body::Error> {
231 self.try_end_with(|| {
232 if state.is_finished() {
233 Ok(state.logical_len())
234 } else {
235 state.try_end()
236 }
237 })
238 }
239
240 fn try_end_with(
241 self,
242 finish: impl FnOnce() -> Result<usize, body::Error>,
243 ) -> body::Transfer<Self, body::Error> {
244 let State::Building(build) = self.state else {
245 return body::Transfer::Rejected {
246 build: self,
247 error: body::Error::InputFinished,
248 };
249 };
250 let len = match finish() {
251 Ok(len) => len,
252 Err(error) => {
253 return body::Transfer::Rejected {
254 build: Self::new(build),
255 error,
256 };
257 }
258 };
259 body::Transfer::Accepted(Self::new(build.finish_input(len)))
260 }
261
262 pub(super) fn try_drive<E: credit::Evidence, O: credit::Origin>(
263 self,
264 mut state: ingress::State<O>,
265 ledger: &mut credit::raw::Ledger<'_, O>,
266 event: &mut E,
267 budget: NonZero<usize>,
268 ) -> Drive<'d, B, O> {
269 let build = match self.state {
270 State::Building(build) => build,
271 State::Ready(output) => {
272 return match state.try_ready() {
273 Ok(state) => Drive::Ready(Ready::<B, O> {
274 state,
275 output,
276 lifetime: self.lifetime,
277 }),
278 Err(state) => Drive::Rejected {
279 stream: Stream::from_parts(state, Self::from_output(output)),
280 error: body::Error::InputIncomplete,
281 },
282 };
283 }
284 };
285 match build.step(budget) {
286 body::Step::NeedInput { build, released } => {
287 let flow = Self::new(build);
288 let result = ingress::Turn::new(&mut state, ledger, &mut *event)
289 .try_release(released, credit::Disposition::Discarded);
290 let error = match result {
291 Ok(()) if state.is_finished() => Error::LengthMismatch,
292 Ok(()) => return Drive::NeedInput(Stream::from_parts(state, flow)),
293 Err(error) => Error::Credit(error),
294 };
295 Drive::Failed {
296 stream: Stream::from_parts(state, flow),
297 error,
298 }
299 }
300 body::Step::Pending { build, released } => {
301 let flow = Self::new(build);
302 let result = ingress::Turn::new(&mut state, ledger, &mut *event)
303 .try_release(released, credit::Disposition::Discarded);
304 match result {
305 Ok(()) => Drive::Pending(Stream::from_parts(state, flow)),
306 Err(error) => Drive::Failed {
307 stream: Stream::from_parts(state, flow),
308 error: Error::Credit(error),
309 },
310 }
311 }
312 body::Step::Ready { output, released } => {
313 if let Err(error) = ingress::Turn::new(&mut state, ledger, &mut *event)
314 .try_release(released, credit::Disposition::Discarded)
315 {
316 return Drive::Failed {
317 stream: Stream::from_parts(state, Self::from_output(output)),
318 error: Error::Credit(error),
319 };
320 }
321 let state = match state.try_ready() {
322 Ok(state) => state,
323 Err(state) => {
324 return Drive::Rejected {
325 stream: Stream::from_parts(state, Self::from_output(output)),
326 error: body::Error::InputIncomplete,
327 };
328 }
329 };
330 event.build_ready();
331 Drive::Ready(Ready::<B, O> {
332 state,
333 output,
334 lifetime: self.lifetime,
335 })
336 }
337 body::Step::Failed {
338 build,
339 error,
340 released,
341 } => {
342 let flow = Self::new(build);
343 let error = match ingress::Turn::new(&mut state, ledger, &mut *event)
344 .try_release(released, credit::Disposition::Discarded)
345 {
346 Ok(()) => Error::Build(error),
347 Err(release) => Error::Release {
348 build: error,
349 error: release,
350 },
351 };
352 Drive::Failed {
353 stream: Stream::from_parts(state, flow),
354 error,
355 }
356 }
357 }
358 }
359}
360
361const _: () = {
362 assert!(core::mem::size_of::<Error<u8>>() == 4 * core::mem::size_of::<usize>());
363};