pub const LENGTH_PREFIX_BYTES: usize = 4;
pub fn delimit(frame: &[u8], out: &mut Vec<u8>) {
out.extend_from_slice(&length_prefix(frame.len()));
out.extend_from_slice(frame);
}
pub fn length_prefix(frame_len: usize) -> [u8; LENGTH_PREFIX_BYTES] {
(frame_len as u32).to_be_bytes()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Oversize {
pub frame_bytes: usize,
pub max_frame_bytes: usize,
}
impl std::fmt::Display for Oversize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "frame of {} bytes over the {}-byte limit", self.frame_bytes, self.max_frame_bytes)
}
}
impl std::error::Error for Oversize {}
#[derive(Clone, Debug)]
pub struct LengthDelimited {
buf: Vec<u8>,
max_frame_bytes: usize,
}
impl LengthDelimited {
pub fn new(max_frame_bytes: usize) -> Self {
Self {
buf: Vec::new(),
max_frame_bytes,
}
}
pub fn feed(&mut self, bytes: &[u8]) {
self.buf.extend_from_slice(bytes);
}
pub fn next_frame(&mut self) -> Result<Option<Vec<u8>>, Oversize> {
if self.buf.len() < LENGTH_PREFIX_BYTES {
return Ok(None);
}
let declared = u32::from_be_bytes(self.buf[..LENGTH_PREFIX_BYTES].try_into().expect("four bytes")) as usize;
if declared > self.max_frame_bytes {
return Err(Oversize {
frame_bytes: declared,
max_frame_bytes: self.max_frame_bytes,
});
}
if self.buf.len() < LENGTH_PREFIX_BYTES + declared {
return Ok(None);
}
let frame = self.buf[LENGTH_PREFIX_BYTES..LENGTH_PREFIX_BYTES + declared].to_vec();
self.buf.drain(..LENGTH_PREFIX_BYTES + declared);
Ok(Some(frame))
}
pub fn buffered(&self) -> usize {
self.buf.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_frame_survives_the_round_trip() {
let mut wire = Vec::new();
delimit(b"hello", &mut wire);
delimit(b"", &mut wire);
delimit(b"world", &mut wire);
let mut framing = LengthDelimited::new(64);
framing.feed(&wire);
assert_eq!(framing.next_frame(), Ok(Some(b"hello".to_vec())));
assert_eq!(framing.next_frame(), Ok(Some(Vec::new())), "an empty frame is a frame");
assert_eq!(framing.next_frame(), Ok(Some(b"world".to_vec())));
assert_eq!(framing.next_frame(), Ok(None));
assert_eq!(framing.buffered(), 0);
}
#[test]
fn boundaries_fall_wherever_the_stream_puts_them() {
let mut wire = Vec::new();
delimit(b"split across reads", &mut wire);
let mut framing = LengthDelimited::new(64);
let mut frames = Vec::new();
for byte in wire {
framing.feed(&[byte]);
while let Some(frame) = framing.next_frame().unwrap() {
frames.push(frame);
}
}
assert_eq!(frames, vec![b"split across reads".to_vec()]);
}
#[test]
fn a_declared_length_past_the_limit_is_an_error_not_an_allocation() {
let mut framing = LengthDelimited::new(16);
framing.feed(&length_prefix(1_000_000));
assert_eq!(
framing.next_frame(),
Err(Oversize {
frame_bytes: 1_000_000,
max_frame_bytes: 16
}),
"the lie is caught before any body bytes arrive"
);
}
#[test]
fn the_writer_helpers_agree_with_each_other() {
let mut buffered = Vec::new();
delimit(b"abc", &mut buffered);
let mut separate = length_prefix(3).to_vec();
separate.extend_from_slice(b"abc");
assert_eq!(buffered, separate);
}
}