Skip to main content

sark_http/h1/codec/decode/
trailer.rs

1use core::{mem, num::NonZero};
2
3use chunk::trailer;
4use o3::{
5    buffer::{
6        resident,
7        storage::Owned,
8        write::{Sink as _, SpareExt as _, consume},
9    },
10    mem::Rejected,
11};
12
13use crate::{
14    field,
15    h1::{chunk, field::Entry},
16};
17
18/// Partial trailer syntax paired with the exact borrowed prefix it admitted.
19pub struct Pending<'input> {
20    bytes: &'input [u8],
21    consumed: NonZero<usize>,
22    parser: trailer::Trailer,
23}
24
25/// Append-only ownership of the trailer prefix and its admitted syntax state.
26pub struct State<'d> {
27    source: Owned<resident::Lease<'d>>,
28    parser: trailer::Trailer,
29}
30
31/// Newly admitted input retained with its original owner through capacity growth.
32#[must_use = "append the admitted bytes to transfer their framing proof"]
33pub struct Plan<'input, 'd, P = Phase<NonZero<usize>>> {
34    state: State<'d>,
35    bytes: &'input [u8],
36    next: P,
37}
38
39pub struct Phase<T>(Next<T>);
40
41enum Next<T> {
42    More {
43        cursor: trailer::Cursor,
44        consumed: Option<T>,
45    },
46    Complete(T),
47}
48
49pub struct More<T> {
50    cursor: trailer::Cursor,
51    consumed: Option<T>,
52}
53
54pub struct Ready<T>(T);
55
56/// The exact completed trailer owner, excluding the terminating empty line.
57pub struct Complete<'d> {
58    source: Owned<resident::Lease<'d>>,
59}
60
61#[derive(Debug)]
62pub enum StoreError {
63    Prefix(NonZero<usize>),
64    Capacity(consume::Error),
65}
66
67#[derive(Debug)]
68pub enum AppendError<E> {
69    Capacity(consume::Error),
70    Ready(E),
71}
72
73impl<E> From<consume::Error> for AppendError<E> {
74    fn from(error: consume::Error) -> Self {
75        Self::Capacity(error)
76    }
77}
78
79impl<'input> Pending<'input> {
80    pub(in crate::h1::codec::decode) fn new(partial: trailer::Partial<'input>) -> Self {
81        let (bytes, consumed, parser) = partial.into_parts();
82        Self {
83            bytes,
84            consumed,
85            parser,
86        }
87    }
88
89    pub const fn consumed(&self) -> NonZero<usize> {
90        self.consumed
91    }
92
93    pub const fn len(&self) -> usize {
94        self.bytes.len()
95    }
96
97    pub const fn is_empty(&self) -> bool {
98        self.bytes.is_empty()
99    }
100
101    pub const fn capacity(&self) -> usize {
102        self.parser.capacity()
103    }
104
105    /// Copies the exact borrowed prefix before transferring its parsing proof.
106    pub fn try_store<'d>(
107        self,
108        mut target: Owned<resident::Lease<'d>>,
109    ) -> Result<State<'d>, Rejected<(Self, Owned<resident::Lease<'d>>), StoreError>> {
110        if let Err(error) = StoreError::copy(&mut target, self.bytes) {
111            return Err(Rejected::new((self, target), error));
112        }
113        Ok(State {
114            source: target,
115            parser: self.parser,
116        })
117    }
118}
119
120impl<'d> State<'d> {
121    pub fn len(&self) -> usize {
122        self.source.len()
123    }
124
125    pub fn is_empty(&self) -> bool {
126        self.source.is_empty()
127    }
128
129    pub fn prepare<'input>(
130        self,
131        input: &'input [u8],
132    ) -> Result<Plan<'input, 'd>, Rejected<Self, chunk::Error>> {
133        let (next, bytes) = match self.parser.scan(input) {
134            Ok(next) => next,
135            Err(error) => return Err(Rejected::new(self, error)),
136        };
137        let next = match next {
138            trailer::Progress::More { next, consumed } => Next::More {
139                cursor: next,
140                consumed,
141            },
142            trailer::Progress::Complete { consumed, .. } => Next::Complete(consumed),
143        };
144        Ok(Plan {
145            state: self,
146            bytes,
147            next: Phase(next),
148        })
149    }
150}
151
152impl<'input, 'd> Plan<'input, 'd> {
153    /// Binds each consumed prefix to its caller's linear input proof once.
154    pub fn try_map<T, E>(
155        self,
156        bind: impl FnOnce(NonZero<usize>) -> Result<T, E>,
157    ) -> Result<Plan<'input, 'd, Phase<T>>, Rejected<State<'d>, E>> {
158        let next = match self.next.0 {
159            Next::More { cursor, consumed } => {
160                let consumed = match consumed.map(bind).transpose() {
161                    Ok(consumed) => consumed,
162                    Err(error) => return Err(Rejected::new(self.state, error)),
163                };
164                Next::More { cursor, consumed }
165            }
166            Next::Complete(consumed) => match bind(consumed) {
167                Ok(consumed) => Next::Complete(consumed),
168                Err(error) => return Err(Rejected::new(self.state, error)),
169            },
170        };
171        Ok(Plan {
172            state: self.state,
173            bytes: self.bytes,
174            next: Phase(next),
175        })
176    }
177}
178
179impl<'input, 'd, T> Plan<'input, 'd, Phase<T>> {
180    pub fn split(self) -> Result<Plan<'input, 'd, Ready<T>>, Plan<'input, 'd, More<T>>> {
181        match self.next.0 {
182            Next::Complete(consumed) => Ok(Plan {
183                state: self.state,
184                bytes: self.bytes,
185                next: Ready(consumed),
186            }),
187            Next::More { cursor, consumed } => Err(Plan {
188                state: self.state,
189                bytes: self.bytes,
190                next: More { cursor, consumed },
191            }),
192        }
193    }
194}
195
196impl<'d, P> Plan<'_, 'd, P> {
197    pub fn len(&self) -> usize {
198        self.state.len()
199    }
200
201    pub fn is_empty(&self) -> bool {
202        self.state.is_empty()
203    }
204
205    pub fn cancel(self) -> State<'d> {
206        self.state
207    }
208
209    /// Copies only the retained prefix; the new input's admission remains intact.
210    pub fn try_grow(
211        &mut self,
212        mut target: Owned<resident::Lease<'d>>,
213    ) -> Result<(), Rejected<Owned<resident::Lease<'d>>, StoreError>> {
214        if let Err(error) = StoreError::copy(&mut target, self.state.source.as_slice()) {
215            return Err(Rejected::new(target, error));
216        }
217        self.state.source = target;
218        Ok(())
219    }
220}
221
222impl<'d, T> Plan<'_, 'd, More<T>> {
223    pub fn try_append(&mut self) -> Result<Option<T>, consume::Error> {
224        let mut spare = self.state.source.spare();
225        spare.write_slice(self.bytes)?;
226        spare.commit();
227        self.state.parser.cursor = self.next.cursor;
228        self.bytes = &[];
229        Ok(self.next.consumed.take())
230    }
231}
232
233impl<'d, T> Plan<'_, 'd, Ready<T>> {
234    /// Borrows completed field lines before appending or releasing either source.
235    pub fn fields(&self) -> [&[u8]; 2] {
236        let prefix = self.state.source.as_slice();
237        let left = prefix.len() - 2usize.saturating_sub(self.bytes.len());
238        let right = self.bytes.len().saturating_sub(2);
239        // SAFETY: Ready is constructed only from the completed trailer phase,
240        // which admitted the final CRLF across this unchanged retained prefix
241        // and exact borrowed input. Removing those two bytes cannot exceed
242        // either source, including a CRLF split between the two owners.
243        unsafe {
244            [
245                prefix.get_unchecked(..left),
246                self.bytes.get_unchecked(..right),
247            ]
248        }
249    }
250
251    /// Publishes completed bytes only after capacity and final work succeed.
252    pub fn try_append<V, E>(
253        self,
254        ready: impl FnOnce() -> Result<V, E>,
255    ) -> Result<(Complete<'d>, T, V), Rejected<Self, AppendError<E>>> {
256        let mut this = self;
257        let mut spare = this.state.source.spare();
258        let value = match spare.try_append(this.bytes, || ready().map_err(AppendError::Ready)) {
259            Ok(value) => value,
260            Err(error) => {
261                return Err(Rejected::new(this, error));
262            }
263        };
264        spare.commit();
265        let mut source = this.state.source;
266        let len = source.len() - 2;
267        source.truncate(len);
268        Ok((Complete { source }, this.next.0, value))
269    }
270}
271
272impl<'d> Complete<'d> {
273    pub fn fields(&self) -> Fields<'_> {
274        Fields::new(self.source.as_slice())
275    }
276
277    pub fn into_owner(self) -> Owned<resident::Lease<'d>> {
278        self.source
279    }
280}
281
282impl StoreError {
283    fn copy(target: &mut Owned<resident::Lease<'_>>, bytes: &[u8]) -> Result<(), Self> {
284        if let Some(len) = NonZero::new(target.len()) {
285            return Err(Self::Prefix(len));
286        }
287        target.try_extend(bytes).map_err(Self::Capacity)
288    }
289}
290
291/// Admitted field lines, excluding the section's terminating empty line.
292/// Each retained line has a permitted name, colon, value, and CRLF.
293pub struct Fields<'a> {
294    lines: &'a [u8],
295}
296
297impl<'a> Fields<'a> {
298    pub(super) fn new(lines: &'a [u8]) -> Self {
299        Self { lines }
300    }
301
302    /// Returns the remaining field section without the terminating empty line.
303    pub const fn section(&self) -> &'a [u8] {
304        self.lines
305    }
306}
307
308impl<'a> Iterator for Fields<'a> {
309    type Item = (&'a [u8], &'a [u8]);
310
311    fn next(&mut self) -> Option<Self::Item> {
312        let (name, value, rest) = Entry::split(self.lines)?;
313        self.lines = rest;
314        Some((name, field::Error::trim_ows(value)))
315    }
316}
317
318const _: () = assert!(mem::size_of::<Fields<'_>>() == 2 * mem::size_of::<usize>());