1use alloc::vec::Vec;
9use core::fmt;
10
11const WORD_BYTES: usize = 4;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum WireError {
16 PayloadTooLarge { len: usize },
17}
18
19impl fmt::Display for WireError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 WireError::PayloadTooLarge { len } => {
23 write!(f, "payload length {len} exceeds u32 framing limit")
24 }
25 }
26 }
27}
28
29fn frame_len_word(len: usize) -> Result<u32, WireError> {
30 u32::try_from(len).map_err(|_| WireError::PayloadTooLarge { len })
31}
32
33pub fn read_framed_bytes_with(mut read_word: impl FnMut() -> u32) -> Vec<u8> {
37 let len = read_word() as usize;
38 let words_needed = len.div_ceil(WORD_BYTES);
39
40 let mut bytes = Vec::with_capacity(len);
41 let mut remaining = len;
42 for _ in 0..words_needed {
43 let word_bytes = read_word().to_be_bytes();
44 let bytes_to_take = remaining.min(WORD_BYTES);
45 bytes.extend_from_slice(&word_bytes[..bytes_to_take]);
46 remaining -= bytes_to_take;
47 }
48
49 bytes
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct EndOfFrame {
57 pub shortfall: usize,
58}
59
60pub trait FramedRead {
65 fn read(&mut self, out: &mut [u8]) -> Result<(), EndOfFrame>;
68}
69
70pub struct FramedReader<F: FnMut() -> u32> {
79 read_word: F,
80 len: usize,
81 remaining: usize,
82 pulled_words: usize,
85 word: [u8; WORD_BYTES],
86 word_pos: usize,
88}
89
90impl<F: FnMut() -> u32> FramedReader<F> {
91 pub fn new(mut read_word: F) -> Self {
93 let len = read_word() as usize;
94 Self {
95 read_word,
96 len,
97 remaining: len,
98 pulled_words: 0,
99 word: [0u8; WORD_BYTES],
100 word_pos: WORD_BYTES,
102 }
103 }
104
105 pub fn payload_len(&self) -> usize {
107 self.len
108 }
109
110 pub fn remaining(&self) -> usize {
113 self.remaining
114 }
115
116 fn frame_words(&self) -> usize {
118 self.len.div_ceil(WORD_BYTES)
119 }
120
121 pub fn discard_rest_of_frame(&mut self) {
136 let frame_words = self.frame_words();
137 while self.pulled_words < frame_words {
138 (self.read_word)();
139 self.pulled_words += 1;
140 }
141 }
142}
143
144impl<F: FnMut() -> u32> FramedRead for FramedReader<F> {
145 fn read(&mut self, out: &mut [u8]) -> Result<(), EndOfFrame> {
146 if self.remaining < out.len() {
149 return Err(EndOfFrame {
150 shortfall: out.len() - self.remaining,
151 });
152 }
153 let mut written = 0;
154 while written < out.len() {
155 if self.word_pos == WORD_BYTES {
156 self.word = (self.read_word)().to_be_bytes();
157 self.word_pos = 0;
158 self.pulled_words += 1;
159 }
160 let available = (WORD_BYTES - self.word_pos).min(self.remaining);
163 let n = available.min(out.len() - written);
164 out[written..written + n].copy_from_slice(&self.word[self.word_pos..self.word_pos + n]);
165 self.word_pos += n;
166 self.remaining -= n;
167 written += n;
168 }
169 Ok(())
170 }
171}
172
173pub fn frame_words_from_bytes(bytes: &[u8]) -> Result<Vec<u32>, WireError> {
175 let len_word = frame_len_word(bytes.len())?;
176 let word_count = bytes.len().div_ceil(WORD_BYTES);
177 let mut words = Vec::with_capacity(1 + word_count);
178 words.push(len_word);
179 for chunk in bytes.chunks(WORD_BYTES) {
180 let mut padded = [0u8; WORD_BYTES];
181 padded[..chunk.len()].copy_from_slice(chunk);
182 words.push(u32::from_be_bytes(padded));
183 }
184 Ok(words)
185}
186
187#[cfg(test)]
188mod tests {
189 use super::{frame_len_word, frame_words_from_bytes, read_framed_bytes_with, WireError};
190
191 #[test]
192 fn framing_roundtrip() {
193 let bytes = b"airbender";
194 let words = frame_words_from_bytes(bytes).expect("frame words");
195 assert_eq!(words[0], bytes.len() as u32);
196 let mut cursor = 0;
197 let reconstructed = read_framed_bytes_with(|| {
198 let word = words[cursor];
199 cursor += 1;
200 word
201 });
202 assert_eq!(reconstructed, bytes);
203 }
204
205 #[test]
206 fn closure_reader_handles_partial_word() {
207 let bytes = [0x12u8, 0x34, 0x56];
208 let words = frame_words_from_bytes(&bytes).expect("frame words");
209 let mut cursor = 0;
210 let reconstructed = read_framed_bytes_with(|| {
211 let word = words[cursor];
212 cursor += 1;
213 word
214 });
215 assert_eq!(reconstructed, bytes);
216 }
217
218 #[test]
219 fn rejects_lengths_above_u32_max() {
220 let err = frame_len_word(usize::MAX).expect_err("must reject oversized length");
221 assert_eq!(err, WireError::PayloadTooLarge { len: usize::MAX });
222 }
223
224 #[test]
225 fn framed_reader_streams_same_bytes_as_buffered() {
226 use super::{FramedRead, FramedReader};
227
228 for bytes in [b"".as_slice(), b"abcd", b"abcde", b"airbender!!"] {
230 let words = frame_words_from_bytes(bytes).expect("frame words");
231 let mut cursor = 0;
232 let mut reader = FramedReader::new(|| {
233 let word = words[cursor];
234 cursor += 1;
235 word
236 });
237 assert_eq!(reader.payload_len(), bytes.len());
238
239 let mut out = alloc::vec![0u8; bytes.len()];
240 reader.read(&mut out).expect("read payload");
241 assert_eq!(out, bytes);
242 assert_eq!(reader.remaining(), 0, "payload fully consumed");
243
244 let mut extra = [0u8; 1];
246 assert!(reader.read(&mut extra).is_err());
247 }
248 }
249
250 #[test]
251 fn discard_consumes_exactly_the_rest_of_the_frame() {
252 use super::{FramedRead, FramedReader};
253
254 let first = frame_words_from_bytes(b"hello world").expect("frame 1"); let second = frame_words_from_bytes(b"next").expect("frame 2");
257 let mut words = first.clone();
258 words.extend_from_slice(&second);
259
260 let mut cursor = 0;
261 let mut reader = FramedReader::new(|| {
262 let word = words[cursor];
263 cursor += 1;
264 word
265 });
266
267 let mut out = [0u8; 2];
269 reader.read(&mut out).expect("partial read");
270 assert_eq!(&out, b"he");
271 reader.discard_rest_of_frame();
272
273 assert_eq!(cursor, first.len());
276 assert_eq!(words[cursor], b"next".len() as u32);
277 }
278}