form_net/multipart/
owned_head.rs1use alloc::vec::Vec;
5use core::fmt;
6use core::ops::Range;
7
8use crate::multipart::filename::{FileName, FileNameSource};
9use crate::multipart::parser::{Charset, PartHead, PartKind};
10
11#[derive(Clone, Default)]
31pub struct OwnedPartHead {
32 buf: Vec<u8>,
33 index: u32,
34 block: Range<usize>,
35 name: Range<usize>,
36 name_raw: Range<usize>,
37 kind: Option<PartKind>,
38 filename: Option<(Range<usize>, FileNameSource)>,
39 content_type: Option<Range<usize>>,
40 charset: Option<Charset>,
41}
42
43impl fmt::Debug for OwnedPartHead {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 fmt::Debug::fmt(&self.view(), f)
46 }
47}
48
49impl OwnedPartHead {
50 #[must_use]
52 pub fn new() -> OwnedPartHead {
53 OwnedPartHead::default()
54 }
55
56 #[must_use]
58 pub fn with_capacity(capacity: usize) -> OwnedPartHead {
59 let mut head = OwnedPartHead::default();
60 head.buf = Vec::with_capacity(capacity);
61 head
62 }
63
64 fn push(&mut self, bytes: &[u8]) -> Range<usize> {
65 let start = self.buf.len();
66 crate::wipe::append_wiping(&mut self.buf, bytes, bytes.len(), usize::MAX);
68 start..self.buf.len()
69 }
70
71 pub fn copy_from(&mut self, head: &PartHead<'_>) {
78 crate::wipe::wipe(&mut self.buf);
79 self.buf.clear();
80 let block = head.block();
81 let outside = |s: &[u8]| {
82 if within(block, s).is_some() {
83 0
84 } else {
85 s.len()
86 }
87 };
88 let needed = [
89 block.len(),
90 outside(head.name().as_bytes()),
91 outside(head.name_raw()),
92 head.filename().map_or(0, |f| outside(f.raw())),
93 head.content_type().map_or(0, |c| outside(c.raw())),
94 ]
95 .iter()
96 .fold(0usize, |a, n| a.saturating_add(*n));
97 if needed > self.buf.capacity() {
98 let mut old = core::mem::take(&mut self.buf);
99 crate::wipe::wipe_vec(&mut old);
100 drop(old);
101 self.buf = Vec::with_capacity(needed);
102 }
103 self.index = head.index();
104 self.block = self.push(block);
105 let base = self.block.start;
106 let mut place = |s: &[u8]| match within(block, s) {
107 Some(r) => r.start.saturating_add(base)..r.end.saturating_add(base),
108 None => {
109 let start = self.buf.len();
111 crate::wipe::append_wiping(&mut self.buf, s, s.len(), usize::MAX);
112 start..self.buf.len()
113 }
114 };
115 self.name = place(head.name().as_bytes());
116 self.name_raw = place(head.name_raw());
117 self.kind = Some(head.kind());
118 self.filename = head.filename().map(|f| (place(f.raw()), f.source()));
119 self.content_type = head.content_type().map(|c| place(c.raw()));
120 self.charset = Some(head.charset());
121 }
122
123 #[must_use]
125 pub fn view(&self) -> PartHead<'_> {
126 let get = |r: &Range<usize>| self.buf.get(r.clone()).unwrap_or(&[]);
127 PartHead::from_parts(
128 self.index,
129 get(&self.block),
130 core::str::from_utf8(get(&self.name)).unwrap_or(""),
131 get(&self.name_raw),
132 self.kind.unwrap_or(PartKind::Field),
133 self.filename
134 .as_ref()
135 .map(|(r, source)| FileName::new(get(r), *source)),
136 self.content_type.as_ref().map(get),
137 self.charset.unwrap_or(Charset::Unspecified),
138 )
139 }
140}
141
142impl Drop for OwnedPartHead {
143 fn drop(&mut self) {
144 crate::wipe::wipe(&mut self.buf);
149 }
150}
151
152fn within(whole: &[u8], part: &[u8]) -> Option<Range<usize>> {
155 let start = (part.as_ptr() as usize).checked_sub(whole.as_ptr() as usize)?;
156 let end = start.checked_add(part.len())?;
157 (end <= whole.len()).then_some(start..end)
158}