Skip to main content

sark_core/http/response/
hot.rs

1use o3::buffer::{Borrowed, Bytes, Owned, Retained, Shared};
2
3use super::direct::INLINE_HOT_TEXT_PARTS;
4use super::{Body, DEFAULT_HEADER_CAPACITY, HeadInner};
5
6#[derive(Clone)]
7pub enum TextItem<'req> {
8    Static(&'static [u8]),
9    Shared(Shared),
10    Borrowed(Bytes<Borrowed<'req>>),
11    Retained(Bytes<Retained>),
12}
13
14impl<'req> std::fmt::Debug for TextItem<'req> {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            Self::Static(bytes) => f
18                .debug_struct("TextItem::Static")
19                .field("len", &bytes.len())
20                .finish(),
21            Self::Shared(bytes) => f
22                .debug_struct("TextItem::Shared")
23                .field("len", &bytes.len())
24                .finish(),
25            Self::Borrowed(bytes) => f
26                .debug_struct("TextItem::Borrowed")
27                .field("len", &bytes.len())
28                .finish(),
29            Self::Retained(bytes) => f
30                .debug_struct("TextItem::Retained")
31                .field("len", &bytes.len())
32                .finish(),
33        }
34    }
35}
36
37impl<'req> TextItem<'req> {
38    pub(crate) fn placeholder() -> Self {
39        Self::Static(&[])
40    }
41
42    pub(crate) fn as_bytes(&self) -> &[u8] {
43        match self {
44            Self::Static(bytes) => bytes,
45            Self::Shared(bytes) => bytes.as_ref(),
46            Self::Borrowed(bytes) => bytes.as_slice(),
47            Self::Retained(bytes) => bytes.as_slice(),
48        }
49    }
50
51    pub(crate) fn len(&self) -> usize {
52        self.as_bytes().len()
53    }
54}
55
56#[allow(clippy::large_enum_variant)]
57#[derive(Clone, Debug)]
58pub enum HotHeadInner<'req, const N: usize = DEFAULT_HEADER_CAPACITY> {
59    Wire(Shared),
60    Direct(HeadInner<'req, N>),
61}
62
63impl<'req, const N: usize> HotHeadInner<'req, N> {
64    pub(super) fn into_bytes(self) -> Shared {
65        match self {
66            Self::Wire(bytes) => bytes,
67            Self::Direct(head) => {
68                let mut out = Owned::with_capacity(head.wire_len());
69                head.write_into_owned(&mut out);
70                out.freeze()
71            }
72        }
73    }
74}
75
76pub struct TextBody<'req> {
77    items: [TextItem<'req>; INLINE_HOT_TEXT_PARTS],
78    len: u8,
79    body_len: usize,
80}
81
82impl<'req> Clone for TextBody<'req> {
83    fn clone(&self) -> Self {
84        let len = usize::from(self.len);
85        let items = std::array::from_fn(|idx| {
86            if idx < len {
87                self.items[idx].clone()
88            } else {
89                TextItem::placeholder()
90            }
91        });
92        Self {
93            items,
94            len: self.len,
95            body_len: self.body_len,
96        }
97    }
98}
99
100impl<'req> std::fmt::Debug for TextBody<'req> {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        f.debug_struct("HotText")
103            .field("len", &self.len)
104            .field("body_len", &self.body_len)
105            .finish()
106    }
107}
108
109impl<'req> Default for TextBody<'req> {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115impl<'req> TextBody<'req> {
116    pub fn new() -> Self {
117        Self {
118            items: std::array::from_fn(|_| TextItem::placeholder()),
119            len: 0,
120            body_len: 0,
121        }
122    }
123
124    pub fn from_items<const N: usize>(items: [TextItem<'req>; N]) -> Self {
125        assert!(
126            N <= INLINE_HOT_TEXT_PARTS,
127            "hot text part overflow: max {}",
128            INLINE_HOT_TEXT_PARTS
129        );
130        let mut iter = IntoIterator::into_iter(items);
131        let mut body_len = 0usize;
132        let entries = std::array::from_fn(|_| match iter.next() {
133            Some(item) => {
134                body_len += item.len();
135                item
136            }
137            None => TextItem::placeholder(),
138        });
139        Self {
140            items: entries,
141            len: N as u8,
142            body_len,
143        }
144    }
145
146    pub fn from_static(bytes: &'static [u8]) -> Self {
147        let mut body = Self::new();
148        body.push_static(bytes);
149        body
150    }
151
152    pub fn len(&self) -> usize {
153        self.body_len
154    }
155
156    pub fn is_empty(&self) -> bool {
157        self.body_len == 0
158    }
159
160    pub fn push_static(&mut self, bytes: &'static [u8]) -> &mut Self {
161        if !bytes.is_empty() {
162            self.push_item(TextItem::Static(bytes));
163        }
164        self
165    }
166
167    pub fn push_borrowed(&mut self, bytes: Bytes<Borrowed<'req>>) -> &mut Self {
168        if !bytes.is_empty() {
169            self.push_item(TextItem::Borrowed(bytes));
170        }
171        self
172    }
173
174    pub fn push_retained(&mut self, bytes: Bytes<Retained>) -> &mut Self {
175        if !bytes.is_empty() {
176            self.push_item(TextItem::Retained(bytes));
177        }
178        self
179    }
180
181    pub(crate) fn write_to(&self, out: &mut [u8]) -> usize {
182        let mut off = 0usize;
183        for item in &self.items[..usize::from(self.len)] {
184            let bytes = item.as_bytes();
185            let end = off + bytes.len();
186            out[off..end].copy_from_slice(bytes);
187            off = end;
188        }
189        off
190    }
191
192    pub fn into_bytes(self) -> Shared {
193        let mut out = Owned::with_capacity(self.body_len);
194        for item in &self.items[..usize::from(self.len)] {
195            out.extend_from_slice(item.as_bytes());
196        }
197        out.freeze()
198    }
199
200    fn push_item(&mut self, item: TextItem<'req>) {
201        assert!(
202            usize::from(self.len) < INLINE_HOT_TEXT_PARTS,
203            "hot text part overflow: max {}",
204            INLINE_HOT_TEXT_PARTS
205        );
206        self.body_len += item.len();
207        self.items[usize::from(self.len)] = item;
208        self.len += 1;
209    }
210}
211
212impl<'req, const N: usize> From<[TextItem<'req>; N]> for TextBody<'req> {
213    fn from(items: [TextItem<'req>; N]) -> Self {
214        Self::from_items(items)
215    }
216}
217
218#[allow(clippy::large_enum_variant)]
219#[derive(Clone)]
220pub enum HotBodyInner<'req> {
221    Owned(Vec<u8>),
222    Shared(Shared),
223    Borrowed(Bytes<Borrowed<'req>>),
224    Retained(Bytes<Retained>),
225    Text(TextBody<'req>),
226    StaticSlice(&'static [u8]),
227}
228
229impl<'req> HotBodyInner<'req> {
230    pub(crate) fn body_len(&self) -> usize {
231        match self {
232            Self::Owned(body) => body.len(),
233            Self::Shared(body) => body.len(),
234            Self::Borrowed(body) => body.len(),
235            Self::Retained(body) => body.len(),
236            Self::Text(body) => body.len(),
237            Self::StaticSlice(body) => body.len(),
238        }
239    }
240
241    pub(crate) fn write_to(&self, out: &mut [u8]) -> usize {
242        match self {
243            Self::Owned(body) => {
244                let s = body.as_slice();
245                out[..s.len()].copy_from_slice(s);
246                s.len()
247            }
248            Self::Shared(body) => {
249                let s = body.as_ref();
250                out[..s.len()].copy_from_slice(s);
251                s.len()
252            }
253            Self::Borrowed(body) => {
254                let s = body.as_slice();
255                out[..s.len()].copy_from_slice(s);
256                s.len()
257            }
258            Self::Retained(body) => {
259                let s = body.as_slice();
260                out[..s.len()].copy_from_slice(s);
261                s.len()
262            }
263            Self::Text(body) => body.write_to(out),
264            Self::StaticSlice(body) => {
265                out[..body.len()].copy_from_slice(body);
266                body.len()
267            }
268        }
269    }
270
271    pub(crate) fn into_shared(self) -> Shared {
272        match self {
273            Self::Owned(body) => Shared::from(body),
274            Self::Shared(body) => body,
275            Self::Borrowed(body) => Shared::copy_from_slice(body.as_slice()),
276            Self::Retained(body) => body.into_shared(),
277            Self::Text(body) => body.into_bytes(),
278            Self::StaticSlice(body) => Shared::from_static(body),
279        }
280    }
281}
282
283impl<'req> From<Body<'req>> for HotBodyInner<'req> {
284    fn from(body: Body<'req>) -> Self {
285        match body {
286            Body::Owned(body) => Self::Owned(body),
287            Body::Shared(body) => Self::Shared(body),
288            Body::Borrowed(body) => Self::Borrowed(body),
289            Body::Retained(body) => Self::Retained(body),
290            Body::StaticSlice(body) => Self::StaticSlice(body),
291        }
292    }
293}
294
295impl From<HotBodyInner<'static>> for Body<'static> {
296    fn from(body: HotBodyInner<'static>) -> Self {
297        match body {
298            HotBodyInner::Owned(body) => Self::Owned(body),
299            HotBodyInner::Shared(body) => Self::Shared(body),
300            HotBodyInner::Borrowed(body) => Self::Shared(Shared::copy_from_slice(body.as_slice())),
301            HotBodyInner::Retained(body) => Self::Retained(body),
302            HotBodyInner::Text(body) => Self::Shared(body.into_bytes()),
303            HotBodyInner::StaticSlice(body) => Self::StaticSlice(body),
304        }
305    }
306}
307
308impl<'req> std::fmt::Debug for HotBodyInner<'req> {
309    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310        match self {
311            Self::Owned(body) => f
312                .debug_struct("HotBody::Owned")
313                .field("len", &body.len())
314                .finish(),
315            Self::Shared(body) => f
316                .debug_struct("HotBody::Shared")
317                .field("len", &body.len())
318                .finish(),
319            Self::Borrowed(body) => f
320                .debug_struct("HotBody::Borrowed")
321                .field("len", &body.len())
322                .finish(),
323            Self::Retained(body) => f
324                .debug_struct("HotBody::Retained")
325                .field("len", &body.len())
326                .finish(),
327            Self::Text(body) => f
328                .debug_struct("HotBody::Text")
329                .field("len", &body.len())
330                .finish(),
331            Self::StaticSlice(body) => f
332                .debug_struct("HotBody::StaticSlice")
333                .field("len", &body.len())
334                .finish(),
335        }
336    }
337}