1mod str;
8mod table;
9
10pub(crate) use str::{Str, validated_string};
11pub(crate) use table::{Blank, Table};
12
13use crate::{DateTime, Encoding, Greeting, Protocol, TlsVersion};
14use std::{fmt, net::IpAddr};
15
16pub(crate) const NONE: u32 = u32::MAX;
17const FIRST_TEXT_ITEMS: usize = 16;
18const INLINE_IDS: usize = 4;
19const INLINE_PARAMS: usize = 2;
20const INLINE_CONTENT_TYPES: usize = 2;
21const INLINE_TEXT_ITEMS: usize = 8;
22const INLINE_ADDRESS_ITEMS: usize = 4;
23
24pub type PartId = u32;
27
28pub type MessageId = u32;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
32pub(crate) struct Span {
33 pub(crate) start: u32,
34 pub(crate) len: u32,
35}
36
37impl Span {
38 pub(crate) fn range(self) -> std::ops::Range<usize> {
39 let start = self.start as usize;
40 start..start + self.len as usize
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub(crate) struct MessageEntry {
46 pub(crate) root: PartId,
47 pub(crate) parts_end: PartId,
48 pub(crate) source: u32,
49 pub(crate) container: PartId,
50 pub(crate) lists: u32,
51 pub(crate) text_len: u32,
52 pub(crate) html_len: u32,
53 pub(crate) attachments_len: u32,
54}
55
56impl Blank for MessageEntry {
57 const BLANK: MessageEntry = MessageEntry {
58 root: NONE,
59 parts_end: NONE,
60 source: 0,
61 container: NONE,
62 lists: 0,
63 text_len: 0,
64 html_len: 0,
65 attachments_len: 0,
66 };
67}
68
69#[derive(Debug, Clone)]
70pub(crate) struct Messages {
71 root: MessageEntry,
72 has_root: bool,
73 nested: Vec<MessageEntry>,
74}
75
76impl Default for Messages {
77 fn default() -> Self {
78 Messages::EMPTY
79 }
80}
81
82impl Messages {
83 pub(crate) const EMPTY: Messages = Messages {
84 root: MessageEntry::BLANK,
85 has_root: false,
86 nested: Vec::new(),
87 };
88
89 #[inline]
90 pub(crate) fn len(&self) -> usize {
91 usize::from(self.has_root) + self.nested.len()
92 }
93
94 #[inline]
95 pub(crate) fn get(&self, index: usize) -> Option<&MessageEntry> {
96 match index.checked_sub(1) {
97 None => Some(&self.root),
98 Some(index) => self.nested.get(index),
99 }
100 }
101
102 #[inline]
103 pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut MessageEntry> {
104 match index.checked_sub(1) {
105 None => Some(&mut self.root),
106 Some(index) => self.nested.get_mut(index),
107 }
108 }
109
110 pub(crate) fn first(&self) -> Option<&MessageEntry> {
111 Some(&self.root)
112 }
113
114 pub(crate) fn push(&mut self, entry: MessageEntry, first: usize) -> MessageId {
115 if !self.has_root {
116 self.root = entry;
117 self.has_root = true;
118 return 0;
119 }
120 if self.nested.capacity() == 0 {
121 self.nested.reserve_exact(first);
122 }
123 self.nested.push(entry);
124 self.nested.len() as MessageId
125 }
126
127 pub(crate) fn pop(&mut self) -> Option<MessageEntry> {
128 self.nested.pop()
129 }
130
131 pub(crate) fn clear(&mut self) {
132 self.root = MessageEntry::BLANK;
133 self.has_root = false;
134 self.nested.clear();
135 }
136
137 #[cfg(test)]
138 pub(crate) fn iter(&self) -> impl Iterator<Item = &MessageEntry> {
139 std::iter::once(&self.root)
140 .take(usize::from(self.has_root))
141 .chain(&self.nested)
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146#[repr(u8)]
147pub(crate) enum KindTag {
148 Text,
149 Html,
150 Binary,
151 InlineBinary,
152 Multipart,
153 Message,
154}
155
156pub(crate) mod flag {
157 pub(crate) const MISSING_DELIMITER: u16 = 1;
158 pub(crate) const FALLBACK_DELIMITER: u16 = 1 << 1;
159 pub(crate) const NO_BLANK_LINE: u16 = 1 << 2;
160 pub(crate) const UNTERMINATED: u16 = 1 << 3;
161 pub(crate) const NESTING_LIMIT: u16 = 1 << 4;
162 pub(crate) const LIMIT_REACHED: u16 = 1 << 5;
163 pub(crate) const MASK: u16 = 0xff;
164}
165
166pub(crate) mod role {
167 pub(crate) const IN_TEXT_BODY: u16 = 1 << 8;
168 pub(crate) const IN_HTML_BODY: u16 = 1 << 9;
169 pub(crate) const ATTACHMENT: u16 = 1 << 10;
170 pub(crate) const INLINE: u16 = 1 << 11;
171 pub(crate) const COPIED: u16 = 1 << 12;
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175pub(crate) struct PartEntry {
176 pub(crate) offset_header: u32,
177 pub(crate) offset_body: u32,
178 pub(crate) offset_end: u32,
179 pub(crate) headers: Span,
180 pub(crate) parent: PartId,
181 pub(crate) message: MessageId,
182 pub(crate) content_type: u32,
183 pub(crate) content_disposition: u32,
184 pub(crate) children: Span,
185 pub(crate) kind: KindTag,
186 pub(crate) encoding: Encoding,
187 pub(crate) flags: u16,
188}
189
190impl PartEntry {
191 pub(crate) const EMPTY: PartEntry = PartEntry {
192 offset_header: 0,
193 offset_body: 0,
194 offset_end: 0,
195 headers: Span { start: 0, len: 0 },
196 parent: NONE,
197 message: 0,
198 content_type: NONE,
199 content_disposition: NONE,
200 children: Span { start: 0, len: 0 },
201 kind: KindTag::Text,
202 encoding: Encoding::None,
203 flags: 0,
204 };
205
206 pub(crate) fn has_role(&self, role: u16) -> bool {
207 self.flags & role != 0
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
212pub(crate) enum Value {
213 #[default]
214 Empty,
215 Text(Str),
216 TextList(Span),
217 Address(Span),
218 DateTime(DateTime),
219 ContentType(u32),
220 Received(u32),
221}
222
223pub(crate) const OTHER_NAME: u16 = u16::MAX;
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226pub(crate) struct HeaderEntry {
227 pub(crate) offset_field: u32,
228 pub(crate) offset_start: u32,
229 pub(crate) offset_end: u32,
230 pub(crate) value: Value,
231 pub(crate) name: u16,
232 pub(crate) other_name: u32,
233}
234
235pub(crate) const GROUP_BIT: u32 = 1 << 31;
236
237#[derive(Debug, Clone, Copy, PartialEq, Eq)]
238pub(crate) struct AddressEntry {
239 pub(crate) name: Str,
240 pub(crate) address: Str,
241 pub(crate) group: u32,
242}
243
244impl Blank for AddressEntry {
245 const BLANK: AddressEntry = AddressEntry {
246 name: Str::NONE,
247 address: Str::NONE,
248 group: 0,
249 };
250}
251
252impl AddressEntry {
253 pub(crate) fn is_group(&self) -> bool {
254 self.group & GROUP_BIT != 0
255 }
256
257 pub(crate) fn members(&self) -> usize {
258 (self.group & !GROUP_BIT) as usize
259 }
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263pub(crate) struct ParamEntry {
264 pub(crate) name: Str,
265 pub(crate) value: Str,
266}
267
268impl Blank for ParamEntry {
269 const BLANK: ParamEntry = ParamEntry {
270 name: Str::NONE,
271 value: Str::NONE,
272 };
273}
274
275#[derive(Debug, Clone, Copy, PartialEq, Eq)]
276pub(crate) struct ContentTypeEntry {
277 pub(crate) ctype: Str,
278 pub(crate) subtype: Str,
279 pub(crate) params: Span,
280}
281
282impl Blank for ContentTypeEntry {
283 const BLANK: ContentTypeEntry = ContentTypeEntry {
284 ctype: Str::NONE,
285 subtype: Str::NONE,
286 params: Span { start: 0, len: 0 },
287 };
288}
289
290#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
291pub(crate) enum HostEntry {
292 #[default]
293 None,
294 Name(Str),
295 Ip(IpAddr),
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq)]
299pub(crate) struct ReceivedEntry {
300 pub(crate) from: HostEntry,
301 pub(crate) from_ip: Option<IpAddr>,
302 pub(crate) from_iprev: Str,
303 pub(crate) by: HostEntry,
304 pub(crate) for_: Str,
305 pub(crate) with: Option<Protocol>,
306 pub(crate) tls_version: Option<TlsVersion>,
307 pub(crate) tls_cipher: Str,
308 pub(crate) id: Str,
309 pub(crate) ident: Str,
310 pub(crate) helo: HostEntry,
311 pub(crate) helo_cmd: Option<Greeting>,
312 pub(crate) via: Str,
313 pub(crate) date: Option<DateTime>,
314}
315
316impl ReceivedEntry {
317 pub(crate) const EMPTY: ReceivedEntry = ReceivedEntry {
318 from: HostEntry::None,
319 from_ip: None,
320 from_iprev: Str::NONE,
321 by: HostEntry::None,
322 for_: Str::NONE,
323 with: None,
324 tls_version: None,
325 tls_cipher: Str::NONE,
326 id: Str::NONE,
327 ident: Str::NONE,
328 helo: HostEntry::None,
329 helo_cmd: None,
330 via: Str::NONE,
331 date: None,
332 };
333}
334
335impl Default for ReceivedEntry {
336 fn default() -> Self {
337 ReceivedEntry::EMPTY
338 }
339}
340
341#[derive(Debug, Clone, Default)]
342pub(crate) struct Scratch {
343 pub(crate) frames: Vec<crate::parser::Frame>,
344 pub(crate) pending: Vec<PartId>,
345 pub(crate) bytes: Vec<u8>,
346 pub(crate) text: String,
347 pub(crate) continuations: Vec<(Str, u32, Str)>,
348}
349
350impl Scratch {
351 const EMPTY: Scratch = Scratch {
352 frames: Vec::new(),
353 pending: Vec::new(),
354 bytes: Vec::new(),
355 text: String::new(),
356 continuations: Vec::new(),
357 };
358
359 fn clear(&mut self) {
360 self.frames.clear();
361 self.pending.clear();
362 self.bytes.clear();
363 self.text.clear();
364 self.continuations.clear();
365 }
366}
367
368#[derive(Debug, Clone)]
369pub(crate) struct SourceBuffer {
370 pub(crate) bytes: Box<[u8]>,
371 pub(crate) container: PartId,
372}
373
374#[derive(Debug, Clone, Default)]
375pub(crate) struct MessageData {
376 pub(crate) messages: Messages,
377 pub(crate) parts: Vec<PartEntry>,
378 pub(crate) headers: Vec<HeaderEntry>,
379 pub(crate) ids: Table<PartId, INLINE_IDS>,
380 pub(crate) address_items: Table<AddressEntry, INLINE_ADDRESS_ITEMS>,
381 pub(crate) params: Table<ParamEntry, INLINE_PARAMS>,
382 pub(crate) content_types: Table<ContentTypeEntry, INLINE_CONTENT_TYPES>,
383 pub(crate) text_items: Table<Str, INLINE_TEXT_ITEMS>,
384 pub(crate) received: Vec<ReceivedEntry>,
385 pub(crate) strings: String,
386 pub(crate) sources: Vec<SourceBuffer>,
387 pub(crate) scratch: Scratch,
388}
389
390impl MessageData {
391 pub(crate) const EMPTY: MessageData = MessageData {
392 messages: Messages::EMPTY,
393 parts: Vec::new(),
394 headers: Vec::new(),
395 ids: Table::EMPTY,
396 address_items: Table::EMPTY,
397 params: Table::EMPTY,
398 content_types: Table::EMPTY,
399 text_items: Table::EMPTY,
400 received: Vec::new(),
401 strings: String::new(),
402 sources: Vec::new(),
403 scratch: Scratch::EMPTY,
404 };
405
406 pub(crate) fn empty_message() -> Self {
407 let mut messages = Messages::EMPTY;
408 messages.push(
409 MessageEntry {
410 root: 0,
411 parts_end: 1,
412 ..MessageEntry::BLANK
413 },
414 0,
415 );
416 MessageData {
417 messages,
418 parts: vec![PartEntry::EMPTY],
419 ..MessageData::EMPTY
420 }
421 }
422
423 fn capacity_bytes(&self) -> usize {
424 [
425 vec_bytes(&self.messages.nested),
426 vec_bytes(&self.parts),
427 vec_bytes(&self.headers),
428 self.ids.capacity_bytes(),
429 self.address_items.capacity_bytes(),
430 self.params.capacity_bytes(),
431 self.content_types.capacity_bytes(),
432 self.text_items.capacity_bytes(),
433 vec_bytes(&self.received),
434 self.strings.capacity(),
435 vec_bytes(&self.sources),
436 self.sources.iter().map(|source| source.bytes.len()).sum(),
437 vec_bytes(&self.scratch.frames),
438 vec_bytes(&self.scratch.pending),
439 self.scratch.bytes.capacity(),
440 self.scratch.text.capacity(),
441 vec_bytes(&self.scratch.continuations),
442 ]
443 .into_iter()
444 .sum()
445 }
446
447 pub(crate) fn push_text_item(&mut self, item: Str) -> u32 {
448 let index = self.text_items.len() as u32;
449 self.text_items.push(item, FIRST_TEXT_ITEMS);
450 index
451 }
452
453 pub(crate) fn clear(&mut self) {
454 self.messages.clear();
455 self.parts.clear();
456 self.headers.clear();
457 self.ids.clear();
458 self.address_items.clear();
459 self.params.clear();
460 self.content_types.clear();
461 self.text_items.clear();
462 self.received.clear();
463 self.strings.clear();
464 self.sources.clear();
465 self.scratch.clear();
466 }
467
468 pub(crate) fn source<'a>(&'a self, raw: &'a [u8], source: u32) -> &'a [u8] {
469 match source.checked_sub(1) {
470 None => raw,
471 Some(index) => self
472 .sources
473 .get(index as usize)
474 .map_or(&[], |source| source.bytes.as_ref()),
475 }
476 }
477
478 pub(crate) fn source_container(&self, source: u32) -> Option<PartId> {
479 self.sources
480 .get(source.checked_sub(1)? as usize)
481 .map(|source| source.container)
482 }
483
484 pub(crate) fn message_source<'a>(&'a self, raw: &'a [u8], message: MessageId) -> &'a [u8] {
485 if message == 0 {
486 return raw;
487 }
488 self.messages
489 .get(message as usize)
490 .map_or(raw, |entry| self.source(raw, entry.source))
491 }
492}
493
494#[derive(Clone, Default)]
534pub struct MessageBuffers {
535 pub(crate) data: MessageData,
536}
537
538impl fmt::Debug for MessageBuffers {
539 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
540 f.debug_struct("MessageBuffers").finish_non_exhaustive()
541 }
542}
543
544impl MessageBuffers {
545 pub fn new() -> Self {
547 MessageBuffers::default()
548 }
549
550 pub fn shrink_to(&mut self, max_bytes: usize) {
571 if self.data.capacity_bytes() > max_bytes {
572 self.data = MessageData::default();
573 }
574 }
575}
576
577fn vec_bytes<T>(items: &Vec<T>) -> usize {
578 items.capacity() * size_of::<T>()
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584 use std::mem::size_of;
585
586 #[test]
587 fn root_message_stays_inline() {
588 let entry = |root| MessageEntry {
589 root,
590 ..MessageEntry::BLANK
591 };
592 let mut messages = Messages::default();
593 assert_eq!(messages.len(), 0);
594 assert_eq!(messages.iter().count(), 0);
595 assert_eq!(messages.get(0), Some(&MessageEntry::BLANK));
596 assert_eq!(messages.push(entry(1), 8), 0);
597 assert_eq!(messages.push(entry(2), 8), 1);
598 assert_eq!(messages.push(entry(3), 8), 2);
599 assert_eq!(messages.len(), 3);
600 assert_eq!(
601 messages.iter().map(|entry| entry.root).collect::<Vec<_>>(),
602 [1, 2, 3]
603 );
604 if let Some(nested) = messages.get_mut(1) {
605 nested.root = 7;
606 }
607 assert_eq!(messages.get(1).map(|entry| entry.root), Some(7));
608 assert_eq!(messages.get(3), None);
609 assert_eq!(messages.pop().map(|entry| entry.root), Some(3));
610 assert_eq!(messages.first().map(|entry| entry.root), Some(1));
611 messages.clear();
612 assert_eq!(messages.len(), 0);
613 assert_eq!(messages.push(entry(4), 8), 0);
614 assert_eq!(messages.len(), 1);
615 }
616
617 #[test]
618 fn buffers_shrink_above_the_bound() {
619 let mut raw = b"Content-Type: multipart/mixed; boundary=b\n\n".to_vec();
620 for index in 0..500 {
621 raw.extend_from_slice(format!("--b\nX-Index: {index}\n\npart {index}\n").as_bytes());
622 }
623 raw.extend_from_slice(b"--b--\n");
624 let parser = crate::MessageParser::new();
625 let mut buffers = MessageBuffers::new();
626 assert_eq!(buffers.data.capacity_bytes(), 0);
627 let message = parser.parse_with(&raw, &mut buffers).expect("parses");
628 buffers = message.into_buffers();
629 let held = buffers.data.capacity_bytes();
630 assert!(held >= 501 * size_of::<PartEntry>());
631 buffers.shrink_to(held);
632 assert_eq!(buffers.data.capacity_bytes(), held);
633 buffers.shrink_to(held - 1);
634 assert_eq!(buffers.data.capacity_bytes(), 0);
635 }
636
637 #[test]
638 fn empty_message_has_one_root_part() {
639 let data = MessageData::empty_message();
640 assert_eq!(data.messages.len(), 1);
641 assert_eq!(data.messages.first().map(|entry| entry.root), Some(0));
642 assert_eq!(data.parts, [PartEntry::EMPTY]);
643 assert_eq!(data.source_container(0), None);
644 assert_eq!(data.source_container(1), None);
645 }
646
647 #[test]
648 fn entry_sizes() {
649 assert_eq!(size_of::<Str>(), 8);
650 assert_eq!(size_of::<Value>(), 12);
651 assert_eq!(size_of::<MessageEntry>(), 32);
652 assert_eq!(size_of::<PartEntry>(), 48);
653 assert_eq!(size_of::<HeaderEntry>(), 32);
654 assert_eq!(size_of::<AddressEntry>(), 20);
655 assert_eq!(size_of::<ParamEntry>(), 16);
656 assert_eq!(size_of::<ContentTypeEntry>(), 24);
657 assert!(size_of::<ReceivedEntry>() <= 160);
658 assert!(size_of::<crate::parser::Frame>() <= 96);
659 }
660}