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