1use std::fmt;
4use std::io::{self, Read, Write};
5use std::num::NonZeroU32;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum Fault {
14 TooLarge { length: u32, limit: u32 },
17 Empty,
19 Cut,
21}
22
23impl Fault {
24 #[must_use]
27 pub fn of(error: &io::Error) -> Option<Self> {
28 error
29 .get_ref()
30 .and_then(|inner| inner.downcast_ref::<Self>())
31 .copied()
32 }
33}
34
35impl fmt::Display for Fault {
36 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 Self::TooLarge { length, limit } => {
39 write!(
40 formatter,
41 "frame of {length} bytes exceeds its {limit}-byte limit"
42 )
43 }
44 Self::Empty => formatter.write_str("frame length is zero"),
45 Self::Cut => formatter.write_str("input ended inside a frame"),
46 }
47 }
48}
49
50impl std::error::Error for Fault {}
51
52fn exact(input: &mut impl Read, bytes: &mut [u8]) -> io::Result<()> {
55 input.read_exact(bytes).map_err(|error| {
56 if error.kind() == io::ErrorKind::UnexpectedEof {
57 io::Error::new(io::ErrorKind::UnexpectedEof, Fault::Cut)
58 } else {
59 error
60 }
61 })
62}
63
64pub fn read(input: &mut impl Read, limit: NonZeroU32) -> io::Result<Option<Vec<u8>>> {
70 let mut header = [0_u8; 4];
71 loop {
72 match input.read(&mut header[..1]) {
73 Ok(0) => return Ok(None),
74 Ok(_) => break,
75 Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
76 Err(error) => return Err(error),
77 }
78 }
79 exact(input, &mut header[1..])?;
80 let length = u32::from_be_bytes(header);
81 if length == 0 {
82 return Err(io::Error::new(io::ErrorKind::InvalidData, Fault::Empty));
83 }
84 if length > limit.get() {
85 let limit = limit.get();
86 return Err(io::Error::new(
87 io::ErrorKind::InvalidData,
88 Fault::TooLarge { length, limit },
89 ));
90 }
91 let size = usize::try_from(length).map_err(io::Error::other)?;
92 let mut body = Vec::new();
93 body.try_reserve_exact(size).map_err(io::Error::other)?;
94 body.resize(size, 0);
95 exact(input, &mut body)?;
96 Ok(Some(body))
97}
98
99pub fn write(
105 output: &mut impl Write,
106 tag: u8,
107 payload: &[u8],
108 limit: NonZeroU32,
109) -> io::Result<()> {
110 let size = payload
111 .len()
112 .checked_add(1)
113 .ok_or_else(|| io::Error::other("frame length overflow"))?;
114 let length = u32::try_from(size).map_err(io::Error::other)?;
115 if length > limit.get() {
116 return Err(io::Error::new(
117 io::ErrorKind::InvalidData,
118 "frame exceeds its limit",
119 ));
120 }
121 output.write_all(&length.to_be_bytes())?;
122 output.write_all(&[tag])?;
123 output.write_all(payload)?;
124 output.flush()
125}