use crate::parser::SbfParser;
use crate::Messages;
use std::io::Read;
const BUFFER_SIZE: usize = 1024 * 8;
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub struct SbfReader<R: Read> {
reader: R,
parser: SbfParser,
drain_internal: bool,
}
impl<R: Read> SbfReader<R> {
pub fn new(reader: R) -> Self {
Self {
reader,
parser: SbfParser::new(),
drain_internal: false,
}
}
}
impl<R: Read> Iterator for SbfReader<R> {
type Item = Result<Messages, std::io::Error>;
fn next(&mut self) -> Option<Self::Item> {
let mut buffer = [0u8; BUFFER_SIZE];
loop {
tracing::debug!("Trying to read from reader");
let (bytes_read, is_eof) = {
if self.drain_internal {
(0, false)
} else {
match self.reader.read(&mut buffer) {
Ok(br) => {
tracing::debug!("Successfully read {br} bytes from reader");
(br, br == 0)
}
Err(e) => {
return Some(Err(e));
}
}
}
};
match self.parser.consume(&buffer[..bytes_read]) {
Some(msg) => {
self.drain_internal = true;
return Some(Ok(msg));
}
None => {
self.drain_internal = false;
}
}
if is_eof {
return None;
}
}
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use libsbf::{reader::SbfReader, Messages};
use std::io::{BufRead, Read};
#[test]
fn test_random_data_consumption() {
struct TrackingReader {
data: Vec<u8>,
position: usize,
}
impl TrackingReader {
fn new(size: usize) -> Self {
let data: Vec<_> = (0..size).map(|i| (i % 256) as u8).collect();
Self { data, position: 0 }
}
fn bytes_read(&self) -> usize {
self.position
}
fn total_bytes(&self) -> usize {
self.data.len()
}
}
impl Read for TrackingReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let remaining = self.data.len() - self.position;
let to_read = buf.len().min(remaining);
if to_read > 0 {
buf[..to_read]
.copy_from_slice(&self.data[self.position..self.position + to_read]);
self.position += to_read;
}
Ok(to_read)
}
}
let test_sizes = vec![100, 1024, 8192, 16384, 100000];
for size in test_sizes {
let mut reader = TrackingReader::new(size);
let total_bytes = reader.total_bytes();
let sbf_reader = SbfReader::new(&mut reader);
let mut message_count = 0;
let mut error_count = 0;
for result in sbf_reader {
match result {
Ok(_) => message_count += 1,
Err(_) => error_count += 1,
}
}
assert_eq!(
reader.bytes_read(),
total_bytes,
"SbfReader did not consume all bytes. Read {} out of {} bytes",
reader.bytes_read(),
total_bytes
);
println!(
"Test passed for {} bytes: {} messages parsed, {} errors",
size, message_count, error_count
);
}
}
#[test]
fn sbf_correct_parse() -> Result<()> {
let input_stream = std::fs::File::open("test-files/sbf_binary.log")?;
let correct_file = std::fs::File::open("test-files/correct_sbf_output.log")?;
let mut cf_lines = std::io::BufReader::new(correct_file).lines();
let sbf_reader = SbfReader::new(input_stream);
for m in sbf_reader {
match m? {
Messages::INSNavGeod(ins_nav_geod) => {
let parsed = format!("{:?}", ins_nav_geod);
let expected = cf_lines.next().unwrap()?;
assert!(
parsed == expected,
"parsed line: {} did not match expected line: {}",
parsed,
expected
);
}
Messages::AttEuler(att_euler) => {
let parsed = format!("{:?}", att_euler);
let expected = cf_lines.next().unwrap()?;
assert!(
parsed == expected,
"parsed line: {} did not match expected line: {}",
parsed,
expected
);
}
Messages::ExtSensorMeas(ext_sensor_meas) => {
let parsed = format!("{:?}", ext_sensor_meas);
let expected = cf_lines.next().unwrap()?;
assert!(
parsed == expected,
"parsed line: {} did not match expected line: {}",
parsed,
expected
);
}
_ => continue,
}
}
Ok(())
}
}