1use std::convert::Infallible;
2
3use o3::base::cell::region;
4use o3::buffer::segments;
5
6pub mod raw;
7mod seal;
8pub(in crate::body::credit) use seal::Seal;
9
10pub trait Origin: Seal + Copy {}
12
13impl Origin for raw::Stored {}
14impl<'id> Origin for region::Identity<'id> {}
15
16#[derive(Clone, Copy, PartialEq, Eq, Debug)]
17pub struct Amount {
18 bytes: usize,
19 chunks: segments::Count,
20}
21
22impl Amount {
23 pub const fn new(bytes: usize, chunks: segments::Count) -> Self {
24 Self { bytes, chunks }
25 }
26
27 pub const fn bytes(self) -> usize {
28 self.bytes
29 }
30
31 pub const fn chunks(self) -> segments::Count {
32 self.chunks
33 }
34
35 pub const fn is_empty(self) -> bool {
36 self.bytes == 0 && self.chunks.is_empty()
37 }
38
39 fn try_add(self, added: Self, scope: Scope) -> Result<Self, Error> {
40 let bytes = self
41 .bytes
42 .checked_add(added.bytes)
43 .ok_or(Error::ByteOverflow {
44 scope,
45 current: self.bytes,
46 added: added.bytes,
47 })?;
48 let chunks = self
49 .chunks
50 .checked_add(added.chunks)
51 .ok_or(Error::ChunkOverflow {
52 scope,
53 current: self.chunks,
54 added: added.chunks,
55 })?;
56 Ok(Self { bytes, chunks })
57 }
58
59 fn try_fit(self, max: Limits, scope: Scope) -> Result<Self, Error> {
60 if self.bytes > max.bytes {
61 return Err(Error::ByteLimit {
62 scope,
63 actual: self.bytes,
64 limit: max.bytes,
65 });
66 }
67 if u128::from(self.chunks.into_raw()) > max.chunks as u128 {
68 return Err(Error::ChunkLimit {
69 scope,
70 actual: self.chunks,
71 limit: max.chunks,
72 });
73 }
74 Ok(self)
75 }
76
77 fn try_sub(self, removed: Self, scope: Scope) -> Result<Self, Error> {
78 let bytes = self
79 .bytes
80 .checked_sub(removed.bytes)
81 .ok_or(Error::ByteUnderflow {
82 scope,
83 current: self.bytes,
84 removed: removed.bytes,
85 })?;
86 let chunks = self
87 .chunks
88 .checked_sub(removed.chunks)
89 .ok_or(Error::ChunkUnderflow {
90 scope,
91 current: self.chunks,
92 removed: removed.chunks,
93 })?;
94 Ok(Self { bytes, chunks })
95 }
96}
97
98impl Default for Amount {
99 fn default() -> Self {
100 Self::new(0, segments::Count::EMPTY)
101 }
102}
103
104#[derive(Clone, Copy, PartialEq, Eq)]
106pub enum Disposition {
107 Discarded,
108 Admitted,
109}
110
111#[derive(PartialEq, Eq)]
113pub enum Mutation {
114 Head,
115 Reserved(Amount),
116 Released {
117 credit: Amount,
118 disposition: Disposition,
119 },
120 BuildReady,
121}
122
123impl Mode for Mutation {
124 fn committed(change: Mutation) -> Option<Self> {
125 Some(change)
126 }
127
128 fn credit(bytes: usize, chunks: segments::Count) -> Amount {
129 Amount::new(bytes, chunks)
130 }
131}
132
133pub trait Mode: Sized {
138 fn committed(change: Mutation) -> Option<Self>;
141
142 fn head() -> Option<Self> {
143 Self::committed(Mutation::Head)
144 }
145
146 fn reserved(credit: Amount) -> Option<Self> {
147 (!credit.is_empty())
148 .then_some(Mutation::Reserved(credit))
149 .and_then(Self::committed)
150 }
151
152 fn released(credit: Amount, cause: Disposition) -> Option<Self> {
153 (!credit.is_empty())
154 .then_some(Mutation::Released {
155 credit,
156 disposition: cause,
157 })
158 .and_then(Self::committed)
159 }
160
161 fn build_ready() -> Option<Self> {
162 Self::committed(Mutation::BuildReady)
163 }
164
165 fn credit(bytes: usize, chunks: segments::Count) -> Amount;
166}
167
168impl Mode for Infallible {
169 fn committed(_: Mutation) -> Option<Self> {
170 None
171 }
172
173 fn credit(_: usize, _: segments::Count) -> Amount {
174 Amount::default()
175 }
176}
177
178pub trait Evidence {
180 type Change: Mode;
181
182 fn record(&mut self, change: Self::Change);
183
184 fn head(&mut self) {
185 if let Some(change) = Self::Change::head() {
186 self.record(change);
187 }
188 }
189
190 fn credit(bytes: usize, chunks: segments::Count) -> Amount
191 where
192 Self: Sized,
193 {
194 Self::Change::credit(bytes, chunks)
195 }
196
197 fn reserved(&mut self, credit: Amount) {
198 if let Some(change) = Self::Change::reserved(credit) {
199 self.record(change);
200 }
201 }
202
203 fn released(&mut self, credit: Amount, kind: Disposition) {
204 if let Some(change) = Self::Change::released(credit, kind) {
205 self.record(change);
206 }
207 }
208
209 fn build_ready(&mut self) {
210 if let Some(change) = Self::Change::build_ready() {
211 self.record(change);
212 }
213 }
214}
215
216impl<E: Evidence + ?Sized> Evidence for &mut E {
217 type Change = E::Change;
218
219 fn record(&mut self, change: Self::Change) {
220 E::record(self, change);
221 }
222}
223
224#[derive(Clone, Copy, Default)]
227pub struct Disabled;
228
229impl Disabled {
230 pub const fn new() -> Self {
231 Self
232 }
233}
234
235impl Evidence for Disabled {
236 type Change = Infallible;
237
238 fn record(&mut self, change: Infallible) {
241 match change {}
242 }
243}
244
245#[must_use = "released credit must be recorded with its final disposition"]
247#[derive(Debug)]
248pub struct Release<O: Origin = raw::Stored> {
249 pub(in crate::body) amount: Amount,
250 _origin: O,
251}
252
253#[must_use = "a rejected release must be explicitly abandoned"]
255pub enum Discard<O: Origin = raw::Stored> {
256 Applied,
257 Rejected { release: Release<O>, error: Error },
258}
259
260impl<O: Origin> Discard<O> {
261 pub fn abandon<E: Evidence>(self, evidence: E) -> Result<(), Error> {
263 match self {
264 Self::Applied => Ok(()),
265 Self::Rejected { release, error } => {
266 release.discard(evidence);
267 Err(error)
268 }
269 }
270 }
271}
272
273impl<O: Origin> Release<O> {
274 pub(in crate::body) const fn new(amount: Amount, origin: O) -> Self {
275 Self {
276 amount,
277 _origin: origin,
278 }
279 }
280
281 pub fn try_discard<E: Evidence>(
282 mut self,
283 ledger: &mut raw::Ledger<'_, O>,
284 mut evidence: E,
285 ) -> Discard<O> {
286 let change = match ledger.amount().try_drain(&mut self.amount) {
287 Ok(change) => change,
288 Err(error) => {
289 return Discard::Rejected {
290 release: self,
291 error,
292 };
293 }
294 };
295 let freed = change.commit();
296 evidence.released(freed, Disposition::Discarded);
297 Discard::Applied
298 }
299
300 pub fn discard<E: Evidence>(self, mut evidence: E) {
302 evidence.released(self.amount, Disposition::Discarded);
303 }
304}
305
306#[derive(Clone, Copy, PartialEq, Eq)]
308pub struct Limits {
309 pub bytes: usize,
310 pub chunks: usize,
311}
312
313impl Limits {
314 pub const fn new(bytes: usize, chunks: usize) -> Self {
315 Self { bytes, chunks }
316 }
317}
318
319#[derive(Clone, Copy, PartialEq, Eq, Debug)]
320pub enum Scope {
321 Request,
322 Connection,
323}
324
325#[derive(Clone, Copy, PartialEq, Eq, Debug)]
326pub enum Error {
327 ByteOverflow {
328 scope: Scope,
329 current: usize,
330 added: usize,
331 },
332 ChunkOverflow {
333 scope: Scope,
334 current: segments::Count,
335 added: segments::Count,
336 },
337 ByteLimit {
338 scope: Scope,
339 actual: usize,
340 limit: usize,
341 },
342 ChunkLimit {
343 scope: Scope,
344 actual: segments::Count,
345 limit: usize,
346 },
347 ByteUnderflow {
348 scope: Scope,
349 current: usize,
350 removed: usize,
351 },
352 ChunkUnderflow {
353 scope: Scope,
354 current: segments::Count,
355 removed: segments::Count,
356 },
357}
358
359pub(in crate::body) struct Change<'a> {
361 request: &'a mut Amount,
362 connection: &'a mut Amount,
363 next_request: Amount,
364 next_connection: Amount,
365 changed: Amount,
366}
367
368impl Change<'_> {
369 pub(in crate::body) fn commit(self) -> Amount {
370 *self.request = self.next_request;
371 *self.connection = self.next_connection;
372 self.changed
373 }
374}
375
376impl Amount {
377 pub(in crate::body) fn try_commit<'a>(
378 &'a mut self,
379 req: &'a mut Amount,
380 amount: Amount,
381 rmax: Limits,
382 cmax: Limits,
383 ) -> Result<Change<'a>, Error> {
384 let next = req.try_add(amount, Scope::Request)?;
385 let total = self.try_add(amount, Scope::Connection)?;
386 let next = next.try_fit(rmax, Scope::Request)?;
387 let total = total.try_fit(cmax, Scope::Connection)?;
388
389 Ok(Change {
390 request: req,
391 connection: self,
392 next_request: next,
393 next_connection: total,
394 changed: amount,
395 })
396 }
397
398 pub(in crate::body) fn try_release<'a>(
399 &'a mut self,
400 req: &'a mut Amount,
401 amount: Amount,
402 ) -> Result<Change<'a>, Error> {
403 let next = req.try_sub(amount, Scope::Request)?;
404 let total = self.try_sub(amount, Scope::Connection)?;
405 Ok(Change {
406 request: req,
407 connection: self,
408 next_request: next,
409 next_connection: total,
410 changed: amount,
411 })
412 }
413
414 pub(in crate::body) fn try_drain<'a>(
415 &'a mut self,
416 req: &'a mut Amount,
417 ) -> Result<Change<'a>, Error> {
418 let freed = *req;
419 self.try_release(req, freed)
420 }
421}
422
423const _: () = {
424 assert!(core::mem::size_of::<Amount>() == 2 * core::mem::size_of::<usize>());
425 assert!(core::mem::size_of::<Release>() == core::mem::size_of::<Amount>());
426 assert!(
427 core::mem::size_of::<Release<region::Identity<'static>>>()
428 == core::mem::size_of::<Amount>()
429 );
430 assert!(core::mem::size_of::<Disabled>() == 0);
431};