use super::records::{RecordEntryPlan, RecordPlan};
use crate::io::reader::TdmsReader;
use crate::{error::TdmsError, io::data_types::TdmsStorageType};
use std::num::NonZeroU64;
use std::{
io::{Read, Seek},
marker::PhantomData,
};
pub struct MultiChannelInterleavedReader<R: Read + Seek, T: TdmsReader<R>> {
reader: T,
_marker: PhantomData<R>,
block_size: NonZeroU64,
block_start: u64,
}
impl<R: Read + Seek, T: TdmsReader<R>> MultiChannelInterleavedReader<R, T> {
pub fn new(reader: T, block_start: u64, block_size: NonZeroU64) -> Self {
Self {
reader,
_marker: PhantomData,
block_size,
block_start,
}
}
pub fn read<D: TdmsStorageType>(
&mut self,
mut channels: RecordPlan<D>,
) -> Result<usize, TdmsError> {
self.reader.to_file_position(self.block_start)?;
let total_row_count = self.block_size.get() / channels.row_size() as u64;
let min_skip = channels.block_skips().min().unwrap_or(0);
if min_skip > 0 {
let skip_bytes = min_skip as i64 * channels.row_size() as i64;
self.reader.move_position(skip_bytes)?;
}
for skip in channels.block_skips_mut() {
*skip = skip.saturating_sub(min_skip);
}
let rows_to_process = total_row_count.saturating_sub(min_skip);
let mut samples_read = 0;
for row in 0..rows_to_process {
let mut any_values_read = false;
for read_instruction in channels.read_instructions().iter_mut() {
match &mut read_instruction.plan {
RecordEntryPlan::Read { output, block_skip } => {
let read_value = self.reader.read_value()?;
if row >= *block_skip
&& let Some(value) = output.next()
{
*value = read_value;
any_values_read = true;
}
}
RecordEntryPlan::Skip(bytes) => {
self.reader.move_position(*bytes)?;
}
};
}
if !any_values_read {
break;
}
samples_read += 1;
}
Ok(samples_read)
}
pub fn read_from<D: TdmsStorageType>(
&mut self,
mut channels: RecordPlan<D>,
samples_to_skip: u64,
) -> Result<usize, TdmsError> {
for channel_skip in channels.block_skips_mut() {
*channel_skip = samples_to_skip;
}
self.read(channels)
}
}
#[cfg(test)]
mod tests {
use crate::{io::data_types::DataType, io::reader::BigEndianReader, meta_data::RawDataMeta};
use super::*;
use std::io::{Cursor, Write};
fn create_test_buffer() -> Cursor<Vec<u8>> {
let buffer = Vec::with_capacity(1024);
let mut cursor = Cursor::new(buffer);
for index in 0..100 {
let value = index as f64;
cursor.write(&value.to_be_bytes()).unwrap();
}
cursor
}
fn create_test_meta_data(columns: usize) -> Vec<RawDataMeta> {
let rows = 1024 / columns;
let mut meta_data = Vec::with_capacity(columns);
for _ in 0..columns {
meta_data.push(RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: rows as u64,
total_size_bytes: None,
});
}
meta_data
}
#[test]
fn read_data_interleaved_single() {
let mut buffer = create_test_buffer();
let meta = create_test_meta_data(2);
let mut reader = MultiChannelInterleavedReader::<_, _>::new(
BigEndianReader::from_reader(&mut buffer),
0,
800.try_into().unwrap(),
);
let mut output: Vec<f64> = vec![0.0; 3];
let mut channels = vec![(0usize, &mut output[..])];
let read_plan = RecordPlan::<f64>::build_record_plan(&meta, &mut channels[..]).unwrap();
reader.read(read_plan).unwrap();
assert_eq!(output, vec![0.0, 2.0, 4.0]);
}
#[test]
fn read_data_interleaved_multi() {
let mut buffer = create_test_buffer();
let meta = create_test_meta_data(4);
let mut reader = MultiChannelInterleavedReader::<_, _>::new(
BigEndianReader::from_reader(&mut buffer),
0,
800.try_into().unwrap(),
);
let mut output_1: Vec<f64> = vec![0.0; 3];
let mut output_2: Vec<f64> = vec![0.0; 3];
let mut channels = vec![(0usize, &mut output_1[..]), (2usize, &mut output_2[..])];
let read_plan = RecordPlan::<f64>::build_record_plan(&meta, &mut channels[..]).unwrap();
reader.read(read_plan).unwrap();
assert_eq!(output_1, vec![0.0, 4.0, 8.0]);
assert_eq!(output_2, vec![2.0, 6.0, 10.0]);
}
#[test]
fn read_data_interleaved_multi_different_lengths() {
let mut buffer = create_test_buffer();
let meta = create_test_meta_data(4);
let mut reader = MultiChannelInterleavedReader::<_, _>::new(
BigEndianReader::from_reader(&mut buffer),
0,
800.try_into().unwrap(),
);
let mut output_1: Vec<f64> = vec![0.0; 3];
let mut output_2: Vec<f64> = vec![0.0; 2];
let mut channels = vec![(0usize, &mut output_1[..]), (2usize, &mut output_2[..])];
let read_plan = RecordPlan::<f64>::build_record_plan(&meta, &mut channels[..]).unwrap();
reader.read(read_plan).unwrap();
assert_eq!(output_1, vec![0.0, 4.0, 8.0]);
assert_eq!(output_2, vec![2.0, 6.0]);
}
#[test]
fn read_data_interleaved_with_skip() {
let mut buffer = create_test_buffer();
let meta = create_test_meta_data(4);
let mut reader = MultiChannelInterleavedReader::<_, _>::new(
BigEndianReader::from_reader(&mut buffer),
0,
800.try_into().unwrap(),
);
let mut output_1: Vec<f64> = vec![0.0; 3];
let mut output_2: Vec<f64> = vec![0.0; 3];
let mut channels = vec![(0usize, &mut output_1[..]), (2usize, &mut output_2[..])];
let read_plan = RecordPlan::<f64>::build_record_plan(&meta, &mut channels[..]).unwrap();
let rows_read = reader.read_from(read_plan, 2).unwrap();
assert_eq!(output_1, vec![8.0, 12.0, 16.0]);
assert_eq!(output_2, vec![10.0, 14.0, 18.0]);
assert_eq!(rows_read, 3);
}
}