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}