From 3965035a48f5ca57daf4b106e4785c6e2b8e1ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20L=C3=A4ufer?= Date: Thu, 31 Oct 2024 11:13:18 -0400 Subject: [PATCH] make it possible to read from anything that implements Read + Seek --- Cargo.toml | 2 +- pywellen/src/lib.rs | 2 +- wellen/examples/load_signals.rs | 3 +- wellen/src/fst.rs | 63 +++--------- wellen/src/ghw/mod.rs | 62 +++--------- wellen/src/signals.rs | 4 +- wellen/src/simple.rs | 15 ++- wellen/src/vcd.rs | 172 +++++++++++++++++++++----------- wellen/src/viewers.rs | 70 ++++++------- wellen/tests/diff_tests.rs | 36 +++++-- 10 files changed, 224 insertions(+), 205 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b079752..3c830a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = ["wellen", "pywellen"] default-members = ["wellen"] [workspace.package] -version = "0.12.2" +version = "0.13.0" edition = "2021" # we require the `div_ceil` method on integers rust-version = "1.73.0" diff --git a/pywellen/src/lib.rs b/pywellen/src/lib.rs index 27d5743..d923138 100644 --- a/pywellen/src/lib.rs +++ b/pywellen/src/lib.rs @@ -199,7 +199,7 @@ impl Waveform { multi_thread: multi_threaded, remove_scopes_with_empty_name, }; - let header_result = viewers::read_header(path.as_str(), &opts).toerr()?; + let header_result = viewers::read_header_from_file(path.as_str(), &opts).toerr()?; let hier = Hierarchy(Arc::new(header_result.hierarchy)); let body = viewers::read_body(header_result.body, &hier.0, None) diff --git a/wellen/examples/load_signals.rs b/wellen/examples/load_signals.rs index 814fbb3..799f6eb 100644 --- a/wellen/examples/load_signals.rs +++ b/wellen/examples/load_signals.rs @@ -72,7 +72,8 @@ fn main() { // load header let header_start = std::time::Instant::now(); - let header = viewers::read_header(&args.filename, &LOAD_OPTS).expect("Failed to load file!"); + let header = + viewers::read_header_from_file(&args.filename, &LOAD_OPTS).expect("Failed to load file!"); let header_load_duration = header_start.elapsed(); println!( "It took {:?} to load the header of {}", diff --git a/wellen/src/fst.rs b/wellen/src/fst.rs index e7e61f2..b40127c 100644 --- a/wellen/src/fst.rs +++ b/wellen/src/fst.rs @@ -15,58 +15,27 @@ use std::io::{BufRead, Seek}; pub type Result = std::result::Result; -pub fn read_header>( - filename: P, +pub fn read_header( + input: R, _options: &LoadOptions, -) -> Result<(Hierarchy, ReadBodyContinuation)> { - let input = std::fs::File::open(filename)?; - let mut reader = FstReader::open_and_read_time_table(std::io::BufReader::new(input))?; +) -> Result<(Hierarchy, ReadBodyContinuation)> { + let mut reader = FstReader::open_and_read_time_table(input)?; let hierarchy = read_hierarchy(&mut reader)?; - let cont = ReadBodyContinuation { - reader: Reader::File(reader), - }; - Ok((hierarchy, cont)) -} - -pub fn read_header_from_bytes( - bytes: Vec, - _options: &LoadOptions, -) -> Result<(Hierarchy, ReadBodyContinuation)> { - let mut reader = FstReader::open_and_read_time_table(std::io::Cursor::new(bytes))?; - let hierarchy = read_hierarchy(&mut reader)?; - let cont = ReadBodyContinuation { - reader: Reader::Bytes(reader), - }; + let cont = ReadBodyContinuation(reader); Ok((hierarchy, cont)) } - -pub fn read_body(data: ReadBodyContinuation) -> Result<(SignalSource, TimeTable)> { - match data.reader { - Reader::File(reader) => { - let time_table = reader.get_time_table().unwrap().to_vec(); - Ok(( - SignalSource::new(Box::new(FstWaveDatabase::new(reader))), - time_table, - )) - } - Reader::Bytes(reader) => { - let time_table = reader.get_time_table().unwrap().to_vec(); - Ok(( - SignalSource::new(Box::new(FstWaveDatabase::new(reader))), - time_table, - )) - } - } +pub fn read_body( + data: ReadBodyContinuation, +) -> Result<(SignalSource, TimeTable)> { + let time_table = data.0.get_time_table().unwrap().to_vec(); + let reader = data.0; + let db = FstWaveDatabase::new(reader); + let boxed_db = Box::new(db); + let source = SignalSource::new(boxed_db); + Ok((source, time_table)) } -pub struct ReadBodyContinuation { - reader: Reader, -} - -enum Reader { - File(FstReader>), - Bytes(FstReader>>), -} +pub struct ReadBodyContinuation(FstReader); struct FstWaveDatabase { reader: FstReader, @@ -78,7 +47,7 @@ impl FstWaveDatabase { } } -impl SignalSourceImplementation for FstWaveDatabase { +impl SignalSourceImplementation for FstWaveDatabase { fn load_signals( &mut self, ids: &[SignalRef], diff --git a/wellen/src/ghw/mod.rs b/wellen/src/ghw/mod.rs index c6b44b5..28bcbc5 100644 --- a/wellen/src/ghw/mod.rs +++ b/wellen/src/ghw/mod.rs @@ -24,68 +24,38 @@ pub fn is_ghw(input: &mut (impl BufRead + Seek)) -> bool { pub type Result = std::result::Result; -pub fn read_header>( - filename: P, +pub fn read_header( + mut input: R, options: &LoadOptions, -) -> Result<(Hierarchy, ReadBodyContinuation, u64)> { - let f = std::fs::File::open(filename)?; - let mut input = std::io::BufReader::new(f); +) -> Result<(Hierarchy, ReadBodyContinuation, u64)> { let (hierarchy, header, decode_info, body_len) = read_header_internal(&mut input, options)?; let cont = ReadBodyContinuation { header, decode_info, - input: Input::File(input), + input, }; Ok((hierarchy, cont, body_len)) } -pub fn read_header_from_bytes( - bytes: Vec, - options: &LoadOptions, -) -> Result<(Hierarchy, ReadBodyContinuation, u64)> { - let mut input = std::io::Cursor::new(bytes); - let (hierarchy, header, decode_info, body_len) = read_header_internal(&mut input, options)?; - let cont = ReadBodyContinuation { - header, - decode_info, - input: Input::Bytes(input), - }; - Ok((hierarchy, cont, body_len)) -} - -pub fn read_body( - data: ReadBodyContinuation, +pub fn read_body( + data: ReadBodyContinuation, hierarchy: &Hierarchy, progress: Option, ) -> Result<(SignalSource, TimeTable)> { - let (source, time_table) = match data.input { - Input::Bytes(mut input) => match progress { - Some(p) => { - let mut wrapped = ProgressTracker::new(input, p); - signals::read_signals(&data.header, data.decode_info, hierarchy, &mut wrapped)? - } - None => signals::read_signals(&data.header, data.decode_info, hierarchy, &mut input)?, - }, - Input::File(mut input) => match progress { - Some(p) => { - let mut wrapped = ProgressTracker::new(input, p); - signals::read_signals(&data.header, data.decode_info, hierarchy, &mut wrapped)? - } - None => signals::read_signals(&data.header, data.decode_info, hierarchy, &mut input)?, - }, - }; - Ok((source, time_table)) + let mut input = data.input; + match progress { + Some(p) => { + let mut wrapped = ProgressTracker::new(input, p); + signals::read_signals(&data.header, data.decode_info, hierarchy, &mut wrapped) + } + None => signals::read_signals(&data.header, data.decode_info, hierarchy, &mut input), + } } -pub struct ReadBodyContinuation { +pub struct ReadBodyContinuation { header: HeaderData, decode_info: GhwDecodeInfo, - input: Input, -} - -enum Input { - Bytes(std::io::Cursor>), - File(std::io::BufReader), + input: R, } fn read_header_internal( diff --git a/wellen/src/signals.rs b/wellen/src/signals.rs index fd6486a..c3cf7ae 100644 --- a/wellen/src/signals.rs +++ b/wellen/src/signals.rs @@ -620,7 +620,7 @@ impl SignalChangeData { } } -pub trait SignalSourceImplementation { +pub trait SignalSourceImplementation: Sync + Send { /// Loads new signals. /// Many implementations take advantage of loading multiple signals at a time. fn load_signals( @@ -634,7 +634,7 @@ pub trait SignalSourceImplementation { } pub struct SignalSource { - inner: Box, + inner: Box, } impl SignalSource { diff --git a/wellen/src/simple.rs b/wellen/src/simple.rs index 4c41338..e38629f 100644 --- a/wellen/src/simple.rs +++ b/wellen/src/simple.rs @@ -10,6 +10,7 @@ use crate::{ }; use std::collections::HashMap; use std::fmt::{Debug, Formatter}; +use std::io::{BufRead, Seek}; /// Read a waveform file with the default options. Reads in header and body at once. pub fn read>(filename: P) -> Result { @@ -21,7 +22,19 @@ pub fn read_with_options>( filename: P, options: &LoadOptions, ) -> Result { - let header = viewers::read_header(filename, options)?; + let header = viewers::read_header_from_file(filename, options)?; + let body = viewers::read_body(header.body, &header.hierarchy, None)?; + Ok(Waveform::new( + header.hierarchy, + body.source, + body.time_table, + )) +} + +/// Read from something that is not a file. +pub fn read_from_reader(input: R) -> Result { + let options = LoadOptions::default(); + let header = viewers::read_header(input, &options)?; let body = viewers::read_body(header.body, &header.hierarchy, None)?; Ok(Waveform::new( header.hierarchy, diff --git a/wellen/src/vcd.rs b/wellen/src/vcd.rs index ccef54a..f5bdb03 100644 --- a/wellen/src/vcd.rs +++ b/wellen/src/vcd.rs @@ -54,10 +54,14 @@ pub enum VcdParseError { pub type Result = std::result::Result; -pub fn read_header>( +pub fn read_header_from_file>( filename: P, options: &LoadOptions, -) -> Result<(Hierarchy, ReadBodyContinuation, u64)> { +) -> Result<( + Hierarchy, + ReadBodyContinuation>, + u64, +)> { let input_file = std::fs::File::open(filename)?; let mmap = unsafe { memmap2::Mmap::map(&input_file)? }; let (header_len, hierarchy, lookup) = @@ -67,59 +71,78 @@ pub fn read_header>( multi_thread: options.multi_thread, header_len, lookup, - input: Input::File(mmap), + input: Input::Mmap(mmap), }; Ok((hierarchy, cont, body_len)) } -pub fn read_header_from_bytes( - bytes: Vec, +pub fn read_header( + mut input: R, options: &LoadOptions, -) -> Result<(Hierarchy, ReadBodyContinuation, u64)> { - let (header_len, hierarchy, lookup) = - read_hierarchy(&mut std::io::Cursor::new(&bytes), options)?; - let body_len = (bytes.len() - header_len) as u64; +) -> Result<(Hierarchy, ReadBodyContinuation, u64)> { + // determine the length of the input + let start = input.stream_position()?; + input.seek(SeekFrom::End(0))?; + let end = input.stream_position()?; + input.seek(SeekFrom::Start(start))?; + let input_len = end - start; + + // actually read the header + let (header_len, hierarchy, lookup) = read_hierarchy(&mut input, options)?; + let body_len = input_len - header_len as u64; let cont = ReadBodyContinuation { multi_thread: options.multi_thread, header_len, lookup, - input: Input::Bytes(bytes), + input: Input::Reader(input), }; Ok((hierarchy, cont, body_len)) } -pub struct ReadBodyContinuation { +pub struct ReadBodyContinuation { multi_thread: bool, header_len: usize, lookup: IdLookup, - input: Input, + input: Input, } -enum Input { - Bytes(Vec), - File(memmap2::Mmap), +enum Input { + Reader(R), + Mmap(memmap2::Mmap), } -pub fn read_body( - data: ReadBodyContinuation, +pub fn read_body( + data: ReadBodyContinuation, hierarchy: &Hierarchy, progress: Option, ) -> Result<(SignalSource, TimeTable)> { let (source, time_table) = match data.input { - Input::Bytes(mmap) => read_values( + Input::Reader(mut input) => { + // determine binput length + let start = input.stream_position()?; + input.seek(SeekFrom::End(0))?; + let end = input.stream_position()?; + input.seek(SeekFrom::Start(start))?; + + // encode signals + let encoder = read_single_stream_of_values( + &mut input, + end as usize, // end_pos includes the size of the header + true, + true, + hierarchy, + &data.lookup, + progress, + )?; + encoder.finish() + } + Input::Mmap(mmap) => read_values( &mmap[data.header_len..], data.multi_thread, hierarchy, &data.lookup, progress, )?, - Input::File(bytes) => read_values( - &bytes[data.header_len..], - data.multi_thread, - hierarchy, - &data.lookup, - progress, - )?, }; Ok((source, time_table)) } @@ -958,7 +981,7 @@ fn read_values( ) -> Result<(SignalSource, TimeTable)> { if multi_thread { let chunks = determine_thread_chunks(input.len()); - let encoders: Vec = chunks + let encoders: Result> = chunks .par_iter() .map(|(start, len)| { let is_first = *start == 0; @@ -970,8 +993,9 @@ fn read_values( // TODO: deal with \n\r before == b'\n' }; + let mut inp = std::io::Cursor::new(&input[*start..]); read_single_stream_of_values( - &input[*start..], + &mut inp, *len - 1, is_first, starts_on_new_line, @@ -981,6 +1005,7 @@ fn read_values( ) }) .collect(); + let encoders = encoders?; // combine encoders let mut encoder_iter = encoders.into_iter(); @@ -990,51 +1015,63 @@ fn read_values( } Ok(encoder.finish()) } else { + let mut inp = std::io::Cursor::new(input); let encoder = read_single_stream_of_values( - input, + &mut inp, input.len() - 1, true, true, hierarchy, lookup, progress, - ); + )?; Ok(encoder.finish()) } } -fn read_single_stream_of_values( - input: &[u8], +fn read_single_stream_of_values( + input: &mut R, stop_pos: usize, is_first: bool, starts_on_new_line: bool, hierarchy: &Hierarchy, lookup: &IdLookup, progress: Option, -) -> crate::wavemem::Encoder { +) -> Result { let mut encoder = crate::wavemem::Encoder::new(hierarchy); - let (input2, offset) = if starts_on_new_line { - (input, 0) - } else { - advance_to_first_newline(input) - }; - let mut reader = BodyReader::new(input2); + if !starts_on_new_line { + // if we start in the middle of a line, we need to skip it + let mut dummy = Vec::new(); + input.read_until(b'\n', &mut dummy)?; + } + // We only start recording once we have encountered our first time step let mut found_first_time_step = false; // progress tracking let mut last_reported_pos = 0; - let report_increments = std::cmp::max(input2.len() as u64 / 1000, 512); + let report_increments = std::cmp::max(stop_pos / 1000, 512) as u64; + let mut lines_read = 0; loop { - if let Some((pos, cmd)) = reader.next() { - if (pos + offset) > stop_pos { + let start_pos = input.stream_position()? as usize; + // parse one buffer full of content + let buf = input.fill_buf()?; + if buf.is_empty() { + // did we reach the end of the input? + break; + } + let mut reader = BodyReader::new(buf, start_pos, lines_read); + let mut reached_end_of_time_step_after_stop = false; + while let Some((pos, cmd)) = reader.next() { + if pos > stop_pos { if let BodyCmd::Time(_to) = cmd { if let Some(p) = progress.as_ref() { let increment = (pos - last_reported_pos) as u64; p.fetch_add(increment, Ordering::SeqCst); } + reached_end_of_time_step_after_stop = true; break; // stop before the next time value when we go beyond the stop position } } @@ -1067,44 +1104,57 @@ fn read_single_stream_of_values( } } }; - } else { - if let Some(p) = progress.as_ref() { - let increment = (reader.pos - last_reported_pos) as u64; - p.fetch_add(increment, Ordering::SeqCst); - } + } + let res = reader.finish(); + + if reached_end_of_time_step_after_stop { break; // done, no more values to read + } else { + lines_read = res.lines_read; + debug_assert!(res.pos > 0, "not consuming anything!"); + input.consume(res.pos); } } - encoder -} - -#[inline] -fn advance_to_first_newline(input: &[u8]) -> (&[u8], usize) { - for (pos, byte) in input.iter().enumerate() { - if *byte == b'\n' { - return (&input[pos..], pos); - } + if let Some(p) = progress.as_ref() { + let pos = input.stream_position()?; + let increment = pos - last_reported_pos as u64; + p.fetch_add(increment, Ordering::SeqCst); } - (&[], 0) // no whitespaces found + + Ok(encoder) } struct BodyReader<'a> { input: &'a [u8], // state + pos_offset: usize, pos: usize, // statistics lines_read: usize, } +struct BodyReaderResult { + pos: usize, + lines_read: usize, +} + const ASCII_ZERO: &[u8] = b"0"; impl<'a> BodyReader<'a> { - fn new(input: &'a [u8]) -> Self { + fn new(input: &'a [u8], start_pos: usize, lines_read: usize) -> Self { BodyReader { input, + pos_offset: start_pos, pos: 0, - lines_read: 0, + lines_read, + } + } + + fn finish(self) -> BodyReaderResult { + BodyReaderResult { + pos: self.pos, + lines_read: self.lines_read, } } @@ -1229,7 +1279,7 @@ impl<'a> Iterator for BodyReader<'a> { if *b == b'\n' { self.lines_read += 1; } - return Some((start_pos, cmd)); + return Some((start_pos + self.pos_offset, cmd)); } } } @@ -1256,7 +1306,7 @@ impl<'a> Iterator for BodyReader<'a> { ) { None => {} Some(cmd) => { - return Some((start_pos, cmd)); + return Some((start_pos + self.pos_offset, cmd)); } } // now we are done @@ -1293,7 +1343,7 @@ mod tests { fn read_body_to_vec(input: &[u8]) -> Vec { let mut out = Vec::new(); - let reader = BodyReader::new(input); + let reader = BodyReader::new(input, 0, 0); for (_, cmd) in reader { let desc = match cmd { BodyCmd::Time(value) => { diff --git a/wellen/src/viewers.rs b/wellen/src/viewers.rs index 8987129..eab034e 100644 --- a/wellen/src/viewers.rs +++ b/wellen/src/viewers.rs @@ -25,24 +25,24 @@ impl From for WellenError { } } -pub struct HeaderResult { +pub struct HeaderResult { pub hierarchy: Hierarchy, pub file_format: FileFormat, /// Body length in bytes. pub body_len: u64, - pub body: ReadBodyContinuation, + pub body: ReadBodyContinuation, } -pub fn read_header>( +pub fn read_header_from_file>( filename: P, options: &LoadOptions, -) -> Result { +) -> Result>> { let file_format = open_and_detect_file_format(filename.as_ref()); match file_format { FileFormat::Unknown => Err(WellenError::UnknownFileFormat), FileFormat::Vcd => { - let (hierarchy, body, body_len) = crate::vcd::read_header(filename, options)?; - let body = ReadBodyContinuation::new(ReadBodyData::Vcd(body)); + let (hierarchy, body, body_len) = crate::vcd::read_header_from_file(filename, options)?; + let body = ReadBodyContinuation(ReadBodyData::Vcd(body)); Ok(HeaderResult { hierarchy, file_format, @@ -51,8 +51,9 @@ pub fn read_header>( }) } FileFormat::Ghw => { - let (hierarchy, body, body_len) = crate::ghw::read_header(filename, options)?; - let body = ReadBodyContinuation::new(ReadBodyData::Ghw(body)); + let input = std::io::BufReader::new(std::fs::File::open(filename)?); + let (hierarchy, body, body_len) = crate::ghw::read_header(input, options)?; + let body = ReadBodyContinuation(ReadBodyData::Ghw(body)); Ok(HeaderResult { hierarchy, file_format, @@ -61,8 +62,9 @@ pub fn read_header>( }) } FileFormat::Fst => { - let (hierarchy, body) = crate::fst::read_header(filename, options)?; - let body = ReadBodyContinuation::new(ReadBodyData::Fst(body)); + let input = std::io::BufReader::new(std::fs::File::open(filename)?); + let (hierarchy, body) = crate::fst::read_header(input, options)?; + let body = ReadBodyContinuation(ReadBodyData::Fst(body)); Ok(HeaderResult { hierarchy, file_format, @@ -73,16 +75,16 @@ pub fn read_header>( } } -pub fn read_header_from_bytes(bytes: Vec, options: &LoadOptions) -> Result { - let file_format = { - let mut cursor = &mut std::io::Cursor::new(&bytes); - detect_file_format(&mut cursor) - }; +pub fn read_header( + mut input: R, + options: &LoadOptions, +) -> Result> { + let file_format = detect_file_format(&mut input); match file_format { FileFormat::Unknown => Err(WellenError::UnknownFileFormat), FileFormat::Vcd => { - let (hierarchy, body, body_len) = crate::vcd::read_header_from_bytes(bytes, options)?; - let body = ReadBodyContinuation::new(ReadBodyData::Vcd(body)); + let (hierarchy, body, body_len) = crate::vcd::read_header(input, options)?; + let body = ReadBodyContinuation(ReadBodyData::Vcd(body)); Ok(HeaderResult { hierarchy, file_format, @@ -91,8 +93,8 @@ pub fn read_header_from_bytes(bytes: Vec, options: &LoadOptions) -> Result { - let (hierarchy, body, body_len) = crate::ghw::read_header_from_bytes(bytes, options)?; - let body = ReadBodyContinuation::new(ReadBodyData::Ghw(body)); + let (hierarchy, body, body_len) = crate::ghw::read_header(input, options)?; + let body = ReadBodyContinuation(ReadBodyData::Ghw(body)); Ok(HeaderResult { hierarchy, file_format, @@ -101,8 +103,8 @@ pub fn read_header_from_bytes(bytes: Vec, options: &LoadOptions) -> Result { - let (hierarchy, body) = crate::fst::read_header_from_bytes(bytes, options)?; - let body = ReadBodyContinuation::new(ReadBodyData::Fst(body)); + let (hierarchy, body) = crate::fst::read_header(input, options)?; + let body = ReadBodyContinuation(ReadBodyData::Fst(body)); Ok(HeaderResult { hierarchy, file_format, @@ -113,20 +115,12 @@ pub fn read_header_from_bytes(bytes: Vec, options: &LoadOptions) -> Result Self { - Self { data } - } -} +pub struct ReadBodyContinuation(ReadBodyData); -enum ReadBodyData { - Vcd(crate::vcd::ReadBodyContinuation), - Fst(crate::fst::ReadBodyContinuation), - Ghw(crate::ghw::ReadBodyContinuation), +enum ReadBodyData { + Vcd(crate::vcd::ReadBodyContinuation), + Fst(crate::fst::ReadBodyContinuation), + Ghw(crate::ghw::ReadBodyContinuation), } pub struct BodyResult { @@ -136,18 +130,18 @@ pub struct BodyResult { pub type ProgressCount = std::sync::Arc; -pub fn read_body( - body: ReadBodyContinuation, +pub fn read_body( + body: ReadBodyContinuation, hierarchy: &Hierarchy, progress: Option, ) -> Result { - match body.data { + match body.0 { ReadBodyData::Vcd(data) => { let (source, time_table) = crate::vcd::read_body(data, hierarchy, progress)?; Ok(BodyResult { source, time_table }) } ReadBodyData::Fst(data) => { - // fst does not support a progress count since it is no actually reading the body + // fst does not support a progress count since it is not actually reading the body let (source, time_table) = crate::fst::read_body(data)?; Ok(BodyResult { source, time_table }) } diff --git a/wellen/tests/diff_tests.rs b/wellen/tests/diff_tests.rs index 99158ac..64c30f2 100644 --- a/wellen/tests/diff_tests.rs +++ b/wellen/tests/diff_tests.rs @@ -8,27 +8,32 @@ use wellen::simple::*; use wellen::*; fn run_diff_test(vcd_filename: &str, fst_filename: &str) { - run_diff_test_internal(vcd_filename, Some(fst_filename), false); + run_diff_test_internal(vcd_filename, Some(fst_filename), false, false); +} + +fn run_diff_test_from_bytes(vcd_filename: &str, fst_filename: &str) { + run_diff_test_internal(vcd_filename, Some(fst_filename), false, true); } fn run_diff_test_vcd_only(vcd_filename: &str) { - run_diff_test_internal(vcd_filename, None, false); + run_diff_test_internal(vcd_filename, None, false, false); } /// Skips trying to load the content with the `vcd` library. This is important for files /// with 9-state values since these cannot be read by the `vcd` library. fn run_load_test(vcd_filename: &str, fst_filename: &str) { - run_diff_test_internal(vcd_filename, Some(fst_filename), true); + run_diff_test_internal(vcd_filename, Some(fst_filename), true, false); } fn run_load_test_vcd(vcd_filename: &str) { - run_diff_test_internal(vcd_filename, None, true); + run_diff_test_internal(vcd_filename, None, true, false); } fn run_diff_test_internal( vcd_filename: &str, fst_filename: Option<&str>, skip_content_comparison: bool, + load_from_bytes_instead_of_file: bool, ) { { let single_thread = LoadOptions { @@ -39,13 +44,25 @@ fn run_diff_test_internal( .expect("Failed to load VCD with a single thread"); diff_test_one(vcd_filename, wave, skip_content_comparison); } - { + if load_from_bytes_instead_of_file { + let bytes = std::io::Cursor::new(std::fs::read(vcd_filename).expect("failed")); + let wave = + read_from_reader(bytes).expect("Failed to load VCD with multiple threads from bytes"); + diff_test_one(vcd_filename, wave, skip_content_comparison); + } else { let wave = read(vcd_filename).expect("Failed to load VCD with multiple threads"); diff_test_one(vcd_filename, wave, skip_content_comparison); } if let Some(fst_filename) = fst_filename { - let wave = read(fst_filename).expect("Failed to load FST"); - diff_test_one(vcd_filename, wave, skip_content_comparison); + if load_from_bytes_instead_of_file { + let bytes = std::io::Cursor::new(std::fs::read(fst_filename).expect("failed")); + let wave = read_from_reader(bytes) + .expect("Failed to load FST with multiple threads from bytes"); + diff_test_one(fst_filename, wave, skip_content_comparison); + } else { + let wave = read(fst_filename).expect("Failed to load FST"); + diff_test_one(vcd_filename, wave, skip_content_comparison); + } } } @@ -470,6 +487,11 @@ fn diff_icarus_test1() { run_diff_test("inputs/icarus/test1.vcd", "inputs/icarus/test1.vcd.fst"); } +#[test] +fn diff_icarus_test1_from_bytes() { + run_diff_test_from_bytes("inputs/icarus/test1.vcd", "inputs/icarus/test1.vcd.fst"); +} + #[test] fn diff_model_sim_clkdiv2n_tb() { run_diff_test(