Skip to main content

dope_core/io/transfer/
progress.rs

1use std::mem;
2
3use o3::buffer::{self, segments::scatter};
4
5use crate::io::transfer;
6
7/// Contiguous transfer input whose progress is bound to this exact slice.
8pub struct Bytes<'data> {
9    bytes: &'data [u8],
10    progress: State,
11}
12
13/// Prefix selected from one [`Bytes`] cursor and not yet committed.
14#[must_use = "the selected bytes must either be committed or left untouched"]
15pub struct Slice<'a, 'data> {
16    bytes: &'data [u8],
17    step: Step<'a>,
18}
19
20impl<'data> Bytes<'data> {
21    #[must_use]
22    pub fn new(view: transfer::View<'data>) -> Self {
23        let (bytes, len) = view.into_parts();
24        Self {
25            bytes,
26            progress: State::new(len),
27        }
28    }
29
30    #[must_use]
31    pub const fn is_empty(&self) -> bool {
32        self.progress.is_empty()
33    }
34
35    pub fn take(&mut self, limit: transfer::Len) -> Slice<'_, 'data> {
36        let start = self.bytes.len() - self.progress.left().into_raw();
37        let step = self.progress.reserve(limit);
38        let end = start + step.amount().into_raw();
39        // SAFETY: `Bytes::new` binds the state limit to this exact slice.
40        // `State::reserve` selects no more than its uncommitted remainder, so
41        // `start..end` remains inside `bytes` and cannot overflow.
42        let bytes = unsafe { self.bytes.get_unchecked(start..end) };
43        Slice { bytes, step }
44    }
45
46    #[must_use]
47    pub const fn into_len(self) -> transfer::Len {
48        self.progress.len()
49    }
50}
51
52impl Slice<'_, '_> {
53    #[must_use]
54    pub const fn as_slice(&self) -> &[u8] {
55        self.bytes
56    }
57
58    #[must_use]
59    pub const fn amount(&self) -> transfer::Len {
60        self.step.amount()
61    }
62
63    pub fn commit(self) {
64        self.step.commit();
65    }
66}
67
68const _: () =
69    assert!(mem::size_of::<Bytes<'_>>() == mem::size_of::<&[u8]>() + mem::size_of::<State>());
70
71/// Exact amount rejected by a bounded progress transition.
72#[derive(Clone, Copy, Eq, PartialEq)]
73pub struct Exceeded {
74    requested: transfer::Len,
75    available: transfer::Len,
76}
77
78impl Exceeded {
79    const fn new(req: transfer::Len, avail: transfer::Len) -> Self {
80        Self {
81            requested: req,
82            available: avail,
83        }
84    }
85
86    pub const fn requested(self) -> transfer::Len {
87        self.requested
88    }
89
90    pub const fn available(self) -> transfer::Len {
91        self.available
92    }
93}
94
95/// Monotonic committed prefix of one bounded transfer.
96pub struct State {
97    limit: transfer::Len,
98    remaining: transfer::Len,
99}
100
101impl State {
102    #[must_use]
103    pub const fn new(limit: transfer::Len) -> Self {
104        Self {
105            limit,
106            remaining: limit,
107        }
108    }
109
110    #[must_use]
111    pub const fn is_empty(&self) -> bool {
112        self.remaining.is_zero()
113    }
114
115    /// Returns the uncommitted suffix without discarding its backend bound.
116    #[must_use]
117    pub const fn left(&self) -> transfer::Len {
118        self.remaining
119    }
120
121    #[must_use]
122    pub const fn len(&self) -> transfer::Len {
123        self.limit.abs_diff(self.remaining)
124    }
125
126    pub fn reserve(&mut self, limit: transfer::Len) -> Step<'_> {
127        let amount = limit.min(self.remaining);
128        Step {
129            progress: self,
130            amount,
131        }
132    }
133
134    /// Advances an exact scatter cursor within this transfer's remaining budget.
135    pub fn scatter<'a>(
136        &mut self,
137        cursor: &mut impl buffer::raw::Scatter<'a>,
138    ) -> Option<buffer::raw::Chunk<'a, { transfer::MAX_BYTES }>> {
139        let chunk = scatter::Take::new(cursor, self.remaining.into_raw())
140            .next_chunk::<{ transfer::MAX_BYTES }>()?;
141        let count = transfer::Len::from_count(chunk.len().into_count());
142        self.remaining = self.remaining.abs_diff(count);
143        Some(chunk)
144    }
145
146    /// Prepares an exact extended state when the whole amount fits.
147    pub fn try_extend(&self, amount: transfer::Len) -> Result<Self, Exceeded> {
148        if amount.is_greater(self.remaining) {
149            return Err(Exceeded::new(amount, self.remaining));
150        }
151        Ok(Self {
152            limit: self.limit,
153            remaining: self.remaining.abs_diff(amount),
154        })
155    }
156}
157
158/// Prefix selected from one [`State`] and not yet committed.
159pub struct Step<'a> {
160    progress: &'a mut State,
161    amount: transfer::Len,
162}
163
164impl Step<'_> {
165    /// Returns the selected prefix without discarding its backend bound.
166    #[must_use]
167    pub const fn amount(&self) -> transfer::Len {
168        self.amount
169    }
170
171    pub fn commit(self) {
172        self.progress.remaining = self.progress.remaining.abs_diff(self.amount);
173    }
174}
175
176const _: () = {
177    assert!(mem::size_of::<State>() == 2 * mem::size_of::<u32>());
178    let state = mem::size_of::<&mut State>() + mem::size_of::<u32>();
179    let align = mem::align_of::<Step<'_>>();
180    assert!(mem::size_of::<Step<'_>>() == state.div_ceil(align) * align);
181};