pub const DELIMITER: u8 = 0;
pub fn encode_frame(json: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(json.len() + 1);
out.extend_from_slice(json);
out.push(DELIMITER);
out
}
#[derive(Debug, Default)]
pub struct FrameDecoder {
buf: Vec<u8>,
}
impl FrameDecoder {
pub fn new() -> Self {
Self { buf: Vec::new() }
}
pub fn push(&mut self, data: &[u8]) {
self.buf.extend_from_slice(data);
}
pub fn next_frame(&mut self) -> Option<Vec<u8>> {
loop {
let pos = self.buf.iter().position(|&b| b == DELIMITER)?;
let frame: Vec<u8> = self.buf.drain(..=pos).take(pos).collect();
if frame.is_empty() {
continue;
}
return Some(frame);
}
}
pub fn pending_len(&self) -> usize {
self.buf.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_appends_nul() {
assert_eq!(encode_frame(b"{}"), vec![b'{', b'}', 0]);
}
#[test]
fn decode_single_frame() {
let mut d = FrameDecoder::new();
d.push(b"{\"id\":1}\0");
assert_eq!(d.next_frame().unwrap(), b"{\"id\":1}");
assert!(d.next_frame().is_none());
}
#[test]
fn decode_multiple_frames_in_one_push() {
let mut d = FrameDecoder::new();
d.push(b"abc\0def\0");
assert_eq!(d.next_frame().unwrap(), b"abc");
assert_eq!(d.next_frame().unwrap(), b"def");
assert!(d.next_frame().is_none());
}
#[test]
fn decode_frame_spanning_multiple_pushes() {
let mut d = FrameDecoder::new();
d.push(b"{\"par");
assert!(d.next_frame().is_none());
d.push(b"t\":true}\0rest");
assert_eq!(d.next_frame().unwrap(), b"{\"part\":true}");
assert!(d.next_frame().is_none());
assert_eq!(d.pending_len(), 4); }
#[test]
fn empty_frames_are_skipped() {
let mut d = FrameDecoder::new();
d.push(b"\0\0x\0\0");
assert_eq!(d.next_frame().unwrap(), b"x");
assert!(d.next_frame().is_none());
}
#[test]
fn roundtrip() {
let mut d = FrameDecoder::new();
let f1 = encode_frame(b"hello");
let f2 = encode_frame(b"world");
d.push(&f1);
d.push(&f2);
assert_eq!(d.next_frame().unwrap(), b"hello");
assert_eq!(d.next_frame().unwrap(), b"world");
}
}