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