Skip to main content

sark_core/http/response/
hot.rs

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