ic_host_process/framing/
mod.rs1use std::{
3 fmt,
4 io::{self, BufRead, Read},
5};
6
7#[derive(Debug)]
9pub enum FrameReadError {
10 EndOfStream,
12 TooLarge(Vec<u8>),
14 Unterminated(Vec<u8>),
16 Io(io::Error),
18}
19
20impl FrameReadError {
21 #[must_use]
23 pub fn rejected_bytes(&self) -> Option<&[u8]> {
24 match self {
25 Self::TooLarge(bytes) | Self::Unterminated(bytes) => Some(bytes),
26 Self::EndOfStream | Self::Io(_) => None,
27 }
28 }
29}
30
31impl fmt::Display for FrameReadError {
32 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 Self::EndOfStream => formatter.write_str("frame stream closed"),
35 Self::TooLarge(_) => formatter.write_str("frame exceeds caller byte limit"),
36 Self::Unterminated(_) => formatter.write_str("frame is missing its newline"),
37 Self::Io(_) => formatter.write_str("frame read failed"),
38 }
39 }
40}
41
42impl std::error::Error for FrameReadError {
43 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
44 match self {
45 Self::Io(error) => Some(error),
46 _ => None,
47 }
48 }
49}
50
51pub fn read_line(reader: &mut impl BufRead, maximum: u64) -> Result<Vec<u8>, FrameReadError> {
61 let mut bytes = Vec::new();
62 reader
63 .take(maximum.saturating_add(1))
64 .read_until(b'\n', &mut bytes)
65 .map_err(FrameReadError::Io)?;
66 if bytes.is_empty() {
67 return Err(FrameReadError::EndOfStream);
68 }
69 if bytes.len() as u64 > maximum {
70 return Err(FrameReadError::TooLarge(bytes));
71 }
72 if !bytes.ends_with(b"\n") {
73 return Err(FrameReadError::Unterminated(bytes));
74 }
75 Ok(bytes)
76}
77
78pub struct LineFramer {
85 maximum: u64,
86 pending: Option<Vec<u8>>,
87}
88
89impl LineFramer {
90 #[must_use]
92 pub const fn new(maximum: u64) -> Self {
93 Self {
94 maximum,
95 pending: Some(Vec::new()),
96 }
97 }
98
99 pub fn feed(
110 &mut self,
111 chunk: &[u8],
112 mut emit: impl FnMut(&[u8]) -> io::Result<()>,
113 ) -> Result<(), FrameReadError> {
114 let mut pending = self.pending.take().ok_or_else(refused)?;
115 for fragment in chunk.split_inclusive(|byte| *byte == b'\n') {
116 let remaining = self.maximum.saturating_sub(pending.len() as u64);
117 if fragment.len() as u64 > remaining {
118 let prefix = usize::try_from(remaining)
119 .map_err(|error| FrameReadError::Io(io::Error::other(error)))?
120 + 1;
121 pending
122 .try_reserve_exact(prefix)
123 .map_err(|error| FrameReadError::Io(io::Error::other(error)))?;
124 pending.extend_from_slice(&fragment[..prefix]);
125 return Err(FrameReadError::TooLarge(pending));
126 }
127 pending
128 .try_reserve_exact(fragment.len())
129 .map_err(|error| FrameReadError::Io(io::Error::other(error)))?;
130 pending.extend_from_slice(fragment);
131 if pending.ends_with(b"\n") {
132 emit(&pending).map_err(FrameReadError::Io)?;
133 pending.clear();
134 }
135 }
136 self.pending = Some(pending);
137 Ok(())
138 }
139
140 pub fn finish(self) -> Result<(), FrameReadError> {
147 let pending = self.pending.ok_or_else(refused)?;
148 if pending.is_empty() {
149 Ok(())
150 } else {
151 Err(FrameReadError::Unterminated(pending))
152 }
153 }
154}
155
156fn refused() -> FrameReadError {
157 FrameReadError::Io(io::Error::new(
158 io::ErrorKind::InvalidInput,
159 "framer previously refused the stream",
160 ))
161}
162
163#[cfg(test)]
164mod tests;