Skip to main content

sark_http/request/body/
mod.rs

1use std::iter::FusedIterator;
2
3use o3::buffer::{
4    bytes::scope,
5    hold::ranges,
6    segments, storage,
7    write::{Sink as _, consume},
8};
9
10use crate::{
11    owner::{handoff, handoff::stage},
12    payload::{
13        owned::{self, Segments},
14        response,
15        response::Body as _,
16    },
17};
18
19mod error;
20pub mod iter;
21mod retained;
22mod view;
23
24pub use error::Error;
25#[doc(hidden)]
26pub use retained::Retained;
27pub use view::View;
28
29/// Transport storage that can expose request-body projections without changing ownership.
30///
31pub trait Source<'d> {
32    type Body<'a>: Into<Ref<'a, 'd>>
33    where
34        Self: 'a,
35        'd: 'a;
36
37    fn try_project<V, T>(
38        &mut self,
39        value: V,
40        project: impl for<'a> FnOnce(V, View<'a, 'd>) -> T,
41    ) -> Result<T, (V, Error)>;
42
43    fn body<'a>(&'a self) -> Self::Body<'a>
44    where
45        'd: 'a;
46}
47
48#[derive(Clone, Copy, Debug)]
49enum RefStorage<'req, 'd> {
50    Discarded,
51    Empty(&'req [u8]),
52    /// One nonempty slice, admitted by `Ref::from_slice`.
53    Slice(&'req [u8]),
54    Retained(Retained<'req, 'd>),
55    Ranges(&'req ranges::Set<'req, 'req, [u8]>),
56}
57
58/// Borrowed logical body preserving transport segmentation.
59#[derive(Clone, Copy, Debug)]
60pub struct Ref<'req, 'd> {
61    storage: RefStorage<'req, 'd>,
62    len: usize,
63}
64
65impl<'req, 'd> Ref<'req, 'd> {
66    /// Classifies an empty view or one nonempty borrowed segment.
67    pub const fn from_slice(bytes: &'req [u8]) -> Self {
68        let storage = if bytes.is_empty() {
69            RefStorage::Empty(bytes)
70        } else {
71            RefStorage::Slice(bytes)
72        };
73        Self {
74            storage,
75            len: bytes.len(),
76        }
77    }
78
79    #[doc(hidden)]
80    pub const fn discarded(len: usize) -> Self {
81        Self {
82            storage: RefStorage::Discarded,
83            len,
84        }
85    }
86
87    #[doc(hidden)]
88    pub const fn from_ranges(ranges: &'req ranges::Set<'req, 'req, [u8]>) -> Self {
89        Self {
90            storage: RefStorage::Ranges(ranges),
91            len: ranges.len(),
92        }
93    }
94
95    pub const fn len(&self) -> usize {
96        self.len
97    }
98
99    pub const fn is_empty(&self) -> bool {
100        self.len == 0
101    }
102
103    pub fn count(&self) -> segments::Count {
104        match self.storage {
105            RefStorage::Discarded | RefStorage::Empty(_) => segments::Count::EMPTY,
106            RefStorage::Slice(_) => segments::Count::ONE,
107            RefStorage::Retained(body) => body.count(),
108            RefStorage::Ranges(ranges) => ranges.count(),
109        }
110    }
111
112    pub fn segment(&self, index: usize) -> Option<&'req [u8]> {
113        match self.storage {
114            RefStorage::Discarded | RefStorage::Empty(_) => None,
115            RefStorage::Slice(bytes) => (index == 0).then_some(bytes),
116            RefStorage::Retained(body) => body.segment(index),
117            RefStorage::Ranges(ranges) => ranges.get(index).map(|view| view.as_slice()),
118        }
119    }
120
121    pub fn segments(self) -> iter::Slices<'req, 'd> {
122        iter::Slices::new(self)
123    }
124
125    pub fn as_slice(&self) -> Option<&'req [u8]> {
126        match self.storage {
127            RefStorage::Discarded => None,
128            RefStorage::Empty(bytes) => Some(bytes),
129            RefStorage::Slice(bytes) => Some(bytes),
130            RefStorage::Retained(body) => body.as_slice(),
131            RefStorage::Ranges(ranges) => ranges.contiguous(),
132        }
133    }
134
135    /// Copies the logical body into one exact shared owner.
136    pub fn try_copy(self) -> Result<storage::Shared, owned::Error> {
137        if let RefStorage::Discarded = self.storage {
138            return Err(owned::Error::Discarded);
139        }
140        storage::Exact::try_build(self.len(), |out| {
141            for part in self.segments() {
142                out.write_slice(part)?;
143            }
144            Ok::<(), consume::Error>(())
145        })
146        .map(storage::Exact::into_shared)
147        .map_err(owned::Error::Build)
148    }
149
150    pub(crate) const fn try_retained(self) -> Result<Retained<'req, 'd>, Self> {
151        match self.storage {
152            RefStorage::Retained(body) => Ok(body),
153            _ => Err(self),
154        }
155    }
156}
157
158impl<'src, 'd> response::Body<'d> for Ref<'src, 'd> {
159    const INLINE: bool = true;
160
161    fn len(&self) -> usize {
162        self.len
163    }
164
165    fn as_slice(&self) -> Option<&[u8]> {
166        Self::as_slice(self)
167    }
168
169    fn segments(&self) -> impl ExactSizeIterator<Item = &[u8]> + FusedIterator {
170        iter::Slices::new(*self)
171    }
172
173    fn try_resolve<'req, H: scope::Source, O: stage::Body<'req, 'd>>(
174        self,
175        slot: &handoff::Slot<'req, 'd, H, O>,
176    ) -> Result<handoff::Plan<'req, 'd, H::Selection, O>, owned::Error> {
177        let origin = slot.origin();
178        if !self.is_empty() {
179            if let RefStorage::Retained(view) = self.storage {
180                return handoff::Plan::try_retain(origin, view);
181            }
182            return handoff::Plan::try_view(origin, self);
183        }
184        Ok(handoff::Plan::new(origin.clear()))
185    }
186}
187
188impl<'d> Source<'d> for &[u8] {
189    type Body<'a>
190        = Ref<'a, 'd>
191    where
192        Self: 'a,
193        'd: 'a;
194
195    fn try_project<V, T>(
196        &mut self,
197        value: V,
198        project: impl for<'a> FnOnce(V, View<'a, 'd>) -> T,
199    ) -> Result<T, (V, Error)> {
200        Ok(project(value, View::Slice(self)))
201    }
202
203    fn body<'a>(&'a self) -> Self::Body<'a>
204    where
205        'd: 'a,
206    {
207        Ref::from_slice(self)
208    }
209}
210
211impl<'req, 'd> Source<'d> for Ref<'req, 'd> {
212    type Body<'a>
213        = Ref<'a, 'd>
214    where
215        Self: 'a,
216        'd: 'a;
217
218    fn try_project<V, T>(
219        &mut self,
220        value: V,
221        project: impl for<'a> FnOnce(V, View<'a, 'd>) -> T,
222    ) -> Result<T, (V, Error)> {
223        let view = match self.storage {
224            RefStorage::Retained(owner) => owner.contiguous(),
225            _ => self.as_slice().map(View::Slice),
226        };
227        match view {
228            Some(view) => Ok(project(value, view)),
229            None => Err((
230                value,
231                Error::Borrowed {
232                    len: self.len(),
233                    count: self.count(),
234                },
235            )),
236        }
237    }
238
239    fn body<'a>(&'a self) -> Self::Body<'a>
240    where
241        'd: 'a,
242    {
243        *self
244    }
245}
246
247const _: () = {
248    assert!(core::mem::size_of::<Ref<'static, 'static>>() == 4 * core::mem::size_of::<usize>());
249};
250
251impl<'req, 'd> From<&'req Segments<'d>> for Ref<'req, 'd> {
252    fn from(body: &'req Segments<'d>) -> Self {
253        Self {
254            storage: RefStorage::Retained(Retained::Segments(body)),
255            len: body.len(),
256        }
257    }
258}