use std::collections::VecDeque;
use std::io::Read;
use crate::blocks::SbfBlock;
use crate::error::{SbfError, SbfResult};
use crate::header::{SbfHeader, MIN_BLOCK_LENGTH, SBF_SYNC};
const DEFAULT_BUFFER_CAPACITY: usize = 65536;
const MAX_BUFFER_SIZE: usize = 131072;
enum Fill {
Filled,
Eof,
WouldBlock,
}
pub struct SbfReader<R: Read> {
inner: R,
buffer: VecDeque<u8>,
validate_crc: bool,
stats: ReaderStats,
}
#[derive(Debug, Clone, Default)]
pub struct ReaderStats {
pub bytes_read: u64,
pub blocks_parsed: u64,
pub crc_errors: u64,
pub parse_errors: u64,
pub bytes_skipped: u64,
}
impl<R: Read> SbfReader<R> {
pub fn new(reader: R) -> Self {
Self {
inner: reader,
buffer: VecDeque::with_capacity(DEFAULT_BUFFER_CAPACITY),
validate_crc: true,
stats: ReaderStats::default(),
}
}
pub fn with_capacity(reader: R, capacity: usize) -> Self {
Self {
inner: reader,
buffer: VecDeque::with_capacity(capacity),
validate_crc: true,
stats: ReaderStats::default(),
}
}
pub fn validate_crc(mut self, validate: bool) -> Self {
self.validate_crc = validate;
self
}
pub fn stats(&self) -> &ReaderStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = ReaderStats::default();
}
pub fn read_block(&mut self) -> SbfResult<Option<SbfBlock>> {
loop {
if let Some(sync_pos) = self.find_sync() {
if sync_pos > 0 {
self.stats.bytes_skipped += sync_pos as u64;
self.buffer.drain(0..sync_pos);
}
match self.try_parse_block() {
Ok(Some((block, consumed))) => {
self.buffer.drain(0..consumed);
self.stats.blocks_parsed += 1;
return Ok(Some(block));
}
Ok(None) => {
match self.fill_buffer()? {
Fill::Filled => {}
Fill::WouldBlock => return Err(SbfError::WouldBlock),
Fill::Eof => {
if !self.buffer.is_empty() {
return Err(SbfError::IncompleteBlock {
needed: 8,
have: self.buffer.len(),
});
}
return Ok(None);
}
}
}
Err(SbfError::InvalidSync) => {
self.buffer.remove(0);
self.stats.bytes_skipped += 1;
}
Err(SbfError::CrcMismatch { .. }) => {
self.buffer.remove(0);
self.stats.crc_errors += 1;
self.stats.bytes_skipped += 1;
}
Err(_) => {
self.buffer.remove(0);
self.stats.parse_errors += 1;
self.stats.bytes_skipped += 1;
}
}
} else {
let len = self.buffer.len();
if len > 1 {
self.stats.bytes_skipped += (len - 1) as u64;
self.buffer.drain(0..len - 1);
}
match self.fill_buffer()? {
Fill::Filled => {}
Fill::WouldBlock => return Err(SbfError::WouldBlock),
Fill::Eof => return Ok(None),
}
}
self.trim_buffer();
}
}
fn find_sync(&self) -> Option<usize> {
if self.buffer.len() < 2 {
return None;
}
(0..(self.buffer.len() - 1))
.find(|&i| self.buffer[i] == SBF_SYNC[0] && self.buffer[i + 1] == SBF_SYNC[1])
}
fn try_parse_block(&mut self) -> SbfResult<Option<(SbfBlock, usize)>> {
if self.buffer.len() < 8 {
return Ok(None);
}
let peek_len = u16::from_le_bytes([self.buffer[6], self.buffer[7]]) as usize;
if peek_len >= MIN_BLOCK_LENGTH as usize && self.buffer.len() < peek_len {
return Ok(None); }
let front_len = self.buffer.as_slices().0.len();
let contiguous_front = peek_len >= MIN_BLOCK_LENGTH as usize && front_len >= peek_len;
if !contiguous_front {
self.buffer.make_contiguous();
}
let buffer = self.buffer.as_slices().0;
let header = SbfHeader::parse(&buffer[2..])?;
let total_len = header.length as usize;
if buffer.len() < total_len {
return Ok(None);
}
if self.validate_crc {
header.validate_crc(&buffer[..total_len])?;
}
let (block, consumed) = SbfBlock::parse(&buffer[..total_len])?;
Ok(Some((block, consumed)))
}
fn fill_buffer(&mut self) -> SbfResult<Fill> {
let mut temp = [0u8; 4096];
match self.inner.read(&mut temp) {
Ok(0) => Ok(Fill::Eof),
Ok(n) => {
self.buffer.extend(&temp[..n]);
self.stats.bytes_read += n as u64;
Ok(Fill::Filled)
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(Fill::WouldBlock),
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => self.fill_buffer(),
Err(e) => Err(SbfError::Io(e)),
}
}
fn trim_buffer(&mut self) {
if self.buffer.capacity() > MAX_BUFFER_SIZE && self.buffer.len() < MAX_BUFFER_SIZE / 2 {
self.buffer.shrink_to_fit();
}
}
}
impl<R: Read> Iterator for SbfReader<R> {
type Item = SbfResult<SbfBlock>;
fn next(&mut self) -> Option<Self::Item> {
match self.read_block() {
Ok(Some(block)) => Some(Ok(block)),
Ok(None) => None,
Err(e) => Some(Err(e)),
}
}
}
pub trait SbfReadExt: Read + Sized {
fn sbf_blocks(self) -> SbfReader<Self> {
SbfReader::new(self)
}
}
impl<R: Read> SbfReadExt for R {}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_reader_empty() {
let data: &[u8] = &[];
let mut reader = SbfReader::new(Cursor::new(data));
assert!(reader.read_block().unwrap().is_none());
}
#[test]
fn test_reader_no_sync() {
let data = [0x00, 0x00, 0x00, 0x00];
let mut reader = SbfReader::new(Cursor::new(&data[..]));
assert!(reader.read_block().unwrap().is_none());
}
#[test]
fn test_reader_stats() {
let data: &[u8] = &[0x00, 0x00];
let mut reader = SbfReader::new(Cursor::new(data));
let _ = reader.read_block();
assert_eq!(reader.stats().bytes_read, 2);
}
#[test]
fn test_sbf_read_ext() {
let data: &[u8] = &[];
let reader = Cursor::new(data).sbf_blocks();
assert!(reader.validate_crc);
}
#[test]
fn crc_mismatch_reports_computed_actual() {
let mut block = vec![0u8; 16];
block[0] = SBF_SYNC[0];
block[1] = SBF_SYNC[1];
block[4..6].copy_from_slice(&5922u16.to_le_bytes()); block[6..8].copy_from_slice(&16u16.to_le_bytes()); let stored = crate::crc::crc16_ccitt(&block[4..16]);
block[2..4].copy_from_slice(&stored.to_le_bytes());
block[12] ^= 0xFF; let computed = crate::crc::crc16_ccitt(&block[4..16]);
assert_ne!(stored, computed);
let mut reader = SbfReader::new(Cursor::new(block.clone()));
while reader.buffer.len() < block.len() {
match reader.fill_buffer().unwrap() {
Fill::Filled => {}
_ => break,
}
}
match reader.try_parse_block() {
Err(SbfError::CrcMismatch { expected, actual }) => {
assert_eq!(expected, stored);
assert_eq!(actual, computed);
assert_ne!(actual, 0);
}
other => panic!("expected CrcMismatch, got {other:?}"),
}
}
}