use crate::{error::TdmsError, io::data_types::TdmsStorageType, meta_data::RawDataMeta};
#[derive(Debug)]
pub enum RecordEntryPlan<'a, T: 'a, I: Iterator<Item = &'a mut T>> {
Skip(i64),
Read {
output: I,
block_skip: u64,
},
}
impl<'a, T: TdmsStorageType, I: Iterator<Item = &'a mut T>> RecordEntryPlan<'a, T, I> {
fn entry_size_bytes(&self) -> Option<usize> {
match self {
RecordEntryPlan::Skip(bytes) => Some(*bytes as usize),
RecordEntryPlan::Read { .. } => Some(T::SIZE_BYTES),
}
}
}
#[derive(Debug)]
pub struct RecordEntry<'a, T: 'a> {
pub length: usize,
pub plan: RecordEntryPlan<'a, T, std::slice::IterMut<'a, T>>,
}
#[derive(Debug)]
pub struct RecordPlan<'a, T>(Vec<RecordEntry<'a, T>>);
impl<'o, 'b: 'o, T: TdmsStorageType> RecordPlan<'o, T> {
pub fn build_record_plan(
channels: &[RawDataMeta],
outputs: &'b mut [(usize, &'b mut [T])],
) -> Result<RecordPlan<'o, T>, TdmsError> {
let mut plan = Self::build_base_record(channels);
validate_types_match(outputs, channels)?;
plan.set_readable_records(outputs);
plan.compress_reads();
Ok(plan)
}
pub fn read_instructions<'a>(&'a mut self) -> &'a mut [RecordEntry<'o, T>] {
&mut self.0[..]
}
pub fn block_skips(&self) -> impl Iterator<Item = u64> + '_ {
self.0.iter().filter_map(|entry| {
if let RecordEntryPlan::Read { block_skip, .. } = &entry.plan {
Some(*block_skip)
} else {
None
}
})
}
pub fn block_skips_mut(&mut self) -> impl Iterator<Item = &mut u64> + '_ {
self.0.iter_mut().filter_map(|entry| {
if let RecordEntryPlan::Read { block_skip, .. } = &mut entry.plan {
Some(block_skip)
} else {
None
}
})
}
pub fn row_size(&self) -> usize {
self.0
.iter()
.map(|entry| match entry.plan.entry_size_bytes() {
Some(bytes) => bytes,
None => todo!("Variable length records not yet supported"),
})
.sum()
}
pub fn block_size(&self) -> usize {
self.0
.iter()
.map(|entry| {
entry
.plan
.entry_size_bytes()
.expect("Variable length records not yet supported")
* entry.length
})
.sum()
}
fn build_base_record(channels: &[RawDataMeta]) -> Self {
let mut plan = Vec::with_capacity(channels.len());
for channel in channels {
plan.push(RecordEntry {
length: channel.number_of_values as usize,
plan: RecordEntryPlan::Skip(channel.data_type.size() as i64),
})
}
Self(plan)
}
fn set_readable_records(&mut self, outputs: &'b mut [(usize, &'b mut [T])]) {
for output in outputs {
self.0[output.0].plan = RecordEntryPlan::Read {
output: output.1.iter_mut(),
block_skip: 0,
};
}
}
fn compress_reads(&mut self) {}
}
fn validate_types_match<T: TdmsStorageType>(
outputs: &[(usize, &mut [T])],
channels: &[RawDataMeta],
) -> Result<(), TdmsError> {
for (output_idx, _) in outputs.iter() {
if !T::SUPPORTED_TYPES.contains(&channels[*output_idx].data_type) {
return Err(TdmsError::DataTypeMismatch(
channels[*output_idx].data_type,
T::NATURAL_TYPE,
));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::{error::TdmsError, io::data_types::DataType};
use super::*;
#[test]
fn test_basic_record_structure_read_all() {
let channels = vec![
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
];
let mut out1 = vec![0.0; 1000];
let mut out2 = vec![0.0; 1000];
let mut outputs: Vec<(usize, &mut [f64])> = vec![(0, &mut out1), (1, &mut out2)];
let read_plan = RecordPlan::<f64>::build_record_plan(&channels, &mut outputs[..]).unwrap();
assert_eq!(read_plan.0.len(), 2);
assert_eq!(read_plan.0[0].length, 1000);
assert_eq!(read_plan.0[1].length, 1000);
assert!(matches!(read_plan.0[0].plan, RecordEntryPlan::Read { .. }));
assert!(matches!(read_plan.0[0].plan, RecordEntryPlan::Read { .. }));
}
#[test]
fn test_basic_record_structure_read_one() {
let channels = vec![
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
];
let mut out1 = vec![0.0; 1000];
let mut outputs: Vec<(usize, &mut [f64])> = vec![(1, &mut out1)];
let read_plan = RecordPlan::<f64>::build_record_plan(&channels, &mut outputs[..]).unwrap();
assert_eq!(read_plan.0.len(), 2);
assert_eq!(read_plan.0[1].length, 1000);
assert!(matches!(read_plan.0[0].plan, RecordEntryPlan::Skip(8)));
assert!(matches!(read_plan.0[1].plan, RecordEntryPlan::Read { .. }));
}
#[test]
fn test_error_on_type_mismatch() {
let channels = vec![
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
];
let mut out1 = vec![0; 1000];
let mut outputs: Vec<(usize, &mut [u32])> = vec![(1, &mut out1)];
let read_plan_result = RecordPlan::<u32>::build_record_plan(&channels, &mut outputs[..]);
assert!(matches!(
read_plan_result,
Err(TdmsError::DataTypeMismatch(
DataType::DoubleFloat,
DataType::U32
))
));
}
#[test]
fn test_does_not_error_on_different_but_compatible_types() {
let channels = vec![
RawDataMeta {
data_type: DataType::DoubleFloatWithUnit,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::DoubleFloatWithUnit,
number_of_values: 1000,
total_size_bytes: None,
},
];
let mut out1 = vec![0.0; 1000];
let mut outputs: Vec<(usize, &mut [f64])> = vec![(1, &mut out1)];
let read_plan_result = RecordPlan::<f64>::build_record_plan(&channels, &mut outputs[..]);
assert!(matches!(read_plan_result, Ok(_)));
}
#[ignore = "Not yet implemented"]
#[test]
fn test_compresses_similar_skips_for_performance() {
let channels = vec![
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::I32,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
];
let mut out1 = vec![0.0; 1000];
let mut outputs: Vec<(usize, &mut [f64])> = vec![(1, &mut out1)];
let read_plan = RecordPlan::<f64>::build_record_plan(&channels, &mut outputs[..]).unwrap();
assert_eq!(read_plan.0.len(), 2);
assert_eq!(read_plan.0[1].length, 1000);
assert!(matches!(read_plan.0[0].plan, RecordEntryPlan::Skip(12)));
assert!(matches!(read_plan.0[1].plan, RecordEntryPlan::Read { .. }));
}
#[test]
fn test_returns_length_of_single_records() {
let channels = vec![
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::I32,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
];
let mut out1 = vec![0i32; 1000];
let mut outputs: Vec<(usize, &mut [i32])> = vec![(1, &mut out1)];
let read_plan = RecordPlan::<i32>::build_record_plan(&channels, &mut outputs[..]).unwrap();
assert_eq!(read_plan.row_size(), 20);
}
#[test]
fn test_returns_length_of_write_block() {
let channels = vec![
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::I32,
number_of_values: 1000,
total_size_bytes: None,
},
RawDataMeta {
data_type: DataType::DoubleFloat,
number_of_values: 1000,
total_size_bytes: None,
},
];
let mut out1 = vec![0i32; 1000];
let mut outputs: Vec<(usize, &mut [i32])> = vec![(1, &mut out1)];
let read_plan = RecordPlan::<i32>::build_record_plan(&channels, &mut outputs[..]).unwrap();
assert_eq!(read_plan.block_size(), 20000);
}
}