sark_core/http/response/
hot.rs1use 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}