use crate::frame::{Frame, MAGIC_V1, MAGIC_V2, MAX_FRAME};
enum Decision {
NeedMore,
Resync,
Frame(usize),
}
#[derive(Clone)]
pub struct Parser {
buf: [u8; MAX_FRAME],
len: usize,
}
impl Default for Parser {
fn default() -> Self {
Self::new()
}
}
impl Parser {
pub fn new() -> Self {
Parser {
buf: [0u8; MAX_FRAME],
len: 0,
}
}
pub fn push_byte<F>(&mut self, byte: u8, crc_extra_for: &F) -> Option<Frame>
where
F: Fn(u32) -> Option<u8>,
{
if self.len >= MAX_FRAME {
self.len = 0;
}
self.buf[self.len] = byte;
self.len += 1;
loop {
match self.decide(crc_extra_for) {
Decision::NeedMore => return None,
Decision::Resync => {
self.drop_front(1);
if self.len == 0 {
return None;
}
}
Decision::Frame(total) => {
let frame = Frame::parse_with(&self.buf[..total], crc_extra_for)
.expect("decide only reports a frame the checksum accepted");
self.drop_front(total);
return Some(frame);
}
}
}
}
fn decide<F>(&self, crc_extra_for: &F) -> Decision
where
F: Fn(u32) -> Option<u8>,
{
if self.len == 0 {
return Decision::NeedMore;
}
let header_len = match self.buf[0] {
MAGIC_V1 => 6,
MAGIC_V2 => 10,
_ => return Decision::Resync,
};
let need_for_size = if self.buf[0] == MAGIC_V2 { 3 } else { 2 };
if self.len < need_for_size {
return Decision::NeedMore;
}
let plen = self.buf[1] as usize;
let signed = self.buf[0] == MAGIC_V2 && self.buf[2] & 0x01 != 0;
let total = header_len + plen + 2 + if signed { 13 } else { 0 };
if self.len < total {
return Decision::NeedMore;
}
match Frame::parse_with(&self.buf[..total], crc_extra_for) {
Ok(_) => Decision::Frame(total),
Err(_) => Decision::Resync,
}
}
fn drop_front(&mut self, n: usize) {
let n = n.min(self.len);
self.buf.copy_within(n..self.len, 0);
self.len -= n;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frame::{Frame, Header};
fn heartbeat_crc(id: u32) -> Option<u8> {
(id == 0).then_some(50)
}
fn heartbeat_frame(seq: u8) -> Frame {
Frame::encode_v2(Header::new(1, 1, seq), 0, &[0, 0, 0, 0, 6, 8, 0, 3, 3], 50).unwrap()
}
fn feed(parser: &mut Parser, bytes: &[u8]) -> Vec<Frame> {
let mut out = Vec::new();
for &byte in bytes {
if let Some(frame) = parser.push_byte(byte, &heartbeat_crc) {
out.push(frame);
}
}
out
}
#[test]
fn a_whole_frame_is_parsed() {
let mut parser = Parser::new();
let frame = heartbeat_frame(1);
let parsed = feed(&mut parser, frame.as_bytes());
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].sequence(), 1);
}
#[test]
fn a_frame_split_across_feeds_is_parsed() {
let mut parser = Parser::new();
let frame = heartbeat_frame(2);
let bytes = frame.as_bytes();
let (head, tail) = bytes.split_at(4);
assert!(feed(&mut parser, head).is_empty());
let parsed = feed(&mut parser, tail);
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].sequence(), 2);
}
#[test]
fn garbage_before_a_frame_is_skipped() {
let mut parser = Parser::new();
let frame = heartbeat_frame(3);
let mut stream = vec![0x00, 0xFF, 0x12, 0x34]; stream.extend_from_slice(frame.as_bytes());
let parsed = feed(&mut parser, &stream);
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].sequence(), 3);
}
#[test]
fn two_back_to_back_frames_both_emit() {
let mut parser = Parser::new();
let mut stream = Vec::new();
stream.extend_from_slice(heartbeat_frame(10).as_bytes());
stream.extend_from_slice(heartbeat_frame(11).as_bytes());
let parsed = feed(&mut parser, &stream);
assert_eq!(parsed.len(), 2);
assert_eq!(parsed[0].sequence(), 10);
assert_eq!(parsed[1].sequence(), 11);
}
#[test]
fn a_corrupt_frame_is_dropped_and_the_next_recovers() {
let mut parser = Parser::new();
let mut corrupt = heartbeat_frame(20).as_bytes().to_vec();
let last = corrupt.len() - 1;
corrupt[last] ^= 0xFF; let mut stream = corrupt;
stream.extend_from_slice(heartbeat_frame(21).as_bytes());
let parsed = feed(&mut parser, &stream);
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].sequence(), 21);
}
}