fst_reader/
reader.rs

1// Copyright 2023 The Regents of the University of California
2// Copyright 2024 Cornell University
3// released under BSD 3-Clause License
4// author: Kevin Laeufer <laeufer@cornell.edu>
5
6use crate::io::*;
7use crate::types::*;
8use std::io::{BufRead, Read, Seek, SeekFrom, Write};
9
10trait BufReadSeek: BufRead + Seek {}
11impl<T> BufReadSeek for T where T: BufRead + Seek {}
12
13/// Reads in a FST file.
14pub struct FstReader<R: BufRead + Seek> {
15    input: InputVariant<R>,
16    meta: MetaData,
17}
18
19enum InputVariant<R: BufRead + Seek> {
20    Original(R),
21    Incomplete(R, Box<dyn BufReadSeek + Sync + Send>),
22    UncompressedInMem(std::io::Cursor<Vec<u8>>),
23    IncompleteUncompressedInMem(std::io::Cursor<Vec<u8>>, Box<dyn BufReadSeek + Sync + Send>),
24}
25
26/// Filter the changes by time and/or signals
27///
28/// The time filter is inclusive, i.e. it includes all changes in `start..=end`.
29pub struct FstFilter {
30    pub start: u64,
31    pub end: Option<u64>,
32    pub include: Option<Vec<FstSignalHandle>>,
33}
34
35impl FstFilter {
36    pub fn all() -> Self {
37        FstFilter {
38            start: 0,
39            end: None,
40            include: None,
41        }
42    }
43
44    pub fn new(start: u64, end: u64, signals: Vec<FstSignalHandle>) -> Self {
45        FstFilter {
46            start,
47            end: Some(end),
48            include: Some(signals),
49        }
50    }
51
52    pub fn filter_time(start: u64, end: u64) -> Self {
53        FstFilter {
54            start,
55            end: Some(end),
56            include: None,
57        }
58    }
59
60    pub fn filter_signals(signals: Vec<FstSignalHandle>) -> Self {
61        FstFilter {
62            start: 0,
63            end: None,
64            include: Some(signals),
65        }
66    }
67}
68
69#[derive(Debug, Clone, PartialEq)]
70pub struct FstHeader {
71    /// time of first sample
72    pub start_time: u64,
73    /// time of last sample
74    pub end_time: u64,
75    /// number of variables in the design
76    pub var_count: u64,
77    /// the highest signal handle; indicates the number of unique signals
78    pub max_handle: u64,
79    /// human readable version string
80    pub version: String,
81    /// human readable times stamp
82    pub date: String,
83    /// the exponent of the timescale; timescale will be 10^(exponent) seconds
84    pub timescale_exponent: i8,
85}
86
87impl<R: BufRead + Seek> FstReader<R> {
88    /// Reads in the FST file meta-data.
89    pub fn open(input: R) -> Result<Self> {
90        Self::open_internal(input, false)
91    }
92
93    pub fn open_and_read_time_table(input: R) -> Result<Self> {
94        Self::open_internal(input, true)
95    }
96
97    fn open_internal(mut input: R, read_time_table: bool) -> Result<Self> {
98        let uncompressed_input = uncompress_gzip_wrapper(&mut input)?;
99        match uncompressed_input {
100            UncompressGzipWrapper::None => {
101                let mut header_reader = HeaderReader::new(input);
102                header_reader.read(read_time_table)?;
103                let (input, meta) = header_reader.into_input_and_meta_data().unwrap();
104                Ok(FstReader {
105                    input: InputVariant::Original(input),
106                    meta,
107                })
108            }
109            UncompressGzipWrapper::InMemory(uc) => {
110                let mut header_reader = HeaderReader::new(uc);
111                header_reader.read(read_time_table)?;
112                let (uc2, meta) = header_reader.into_input_and_meta_data().unwrap();
113                Ok(FstReader {
114                    input: InputVariant::UncompressedInMem(uc2),
115                    meta,
116                })
117            }
118        }
119    }
120
121    /// Reads in the meta-data of an incomplete FST file with external `.hier` file.
122    ///
123    /// When the creation of an FST file using `fstlib` is interrupted, some information
124    /// such as a geometry block or hierarchy block may be missing (as indicated by
125    /// [`ReaderError::MissingGeometry`] and [`ReaderError::MissingHierarchy`]).
126    ///
127    /// This function tries to reconstruct these missing blocks from an external `.hier`
128    /// file, which is commonly generated while outputting FST files.
129    pub fn open_incomplete<H: BufRead + Seek + Sync + Send + 'static>(
130        input: R,
131        hierarchy: H,
132    ) -> Result<Self> {
133        Self::open_incomplete_internal(input, hierarchy, false)
134    }
135
136    pub fn open_incomplete_and_read_time_table<H: BufRead + Seek + Sync + Send + 'static>(
137        input: R,
138        hierarchy: H,
139    ) -> Result<Self> {
140        Self::open_incomplete_internal(input, hierarchy, true)
141    }
142
143    fn open_incomplete_internal<H: BufRead + Seek + Sync + Send + 'static>(
144        mut input: R,
145        mut hierarchy: H,
146        read_time_table: bool,
147    ) -> Result<Self> {
148        let uncompressed_input = uncompress_gzip_wrapper(&mut input)?;
149        match uncompressed_input {
150            UncompressGzipWrapper::None => {
151                let (input, meta) = Self::open_incomplete_internal_uncompressed(
152                    input,
153                    &mut hierarchy,
154                    read_time_table,
155                )?;
156                Ok(FstReader {
157                    input: InputVariant::Incomplete(input, Box::new(hierarchy)),
158                    meta,
159                })
160            }
161            UncompressGzipWrapper::InMemory(uc) => {
162                let (uc2, meta) = Self::open_incomplete_internal_uncompressed(
163                    uc,
164                    &mut hierarchy,
165                    read_time_table,
166                )?;
167                Ok(FstReader {
168                    input: InputVariant::IncompleteUncompressedInMem(uc2, Box::new(hierarchy)),
169                    meta,
170                })
171            }
172        }
173    }
174
175    fn open_incomplete_internal_uncompressed<I: BufRead + Seek, H: BufRead + Seek>(
176        input: I,
177        hierarchy: &mut H,
178        read_time_table: bool,
179    ) -> Result<(I, MetaData)> {
180        let mut header_reader = HeaderReader::new(input);
181        match header_reader.read(read_time_table) {
182            Ok(_) => {}
183            Err(ReaderError::MissingGeometry() | ReaderError::MissingHierarchy()) => {
184                header_reader
185                    .hierarchy
186                    .get_or_insert((HierarchyCompression::Uncompressed, 0));
187                header_reader.reconstruct_geometry(hierarchy)?;
188            }
189            Err(e) => return Err(e),
190        };
191        Ok(header_reader.into_input_and_meta_data().unwrap())
192    }
193
194    pub fn get_header(&self) -> FstHeader {
195        FstHeader {
196            start_time: self.meta.header.start_time,
197            end_time: self.meta.header.end_time,
198            var_count: self.meta.header.var_count,
199            max_handle: self.meta.header.max_var_id_code,
200            version: self.meta.header.version.clone(),
201            date: self.meta.header.date.clone(),
202            timescale_exponent: self.meta.header.timescale_exponent,
203        }
204    }
205
206    pub fn get_time_table(&self) -> Option<&[u64]> {
207        match &self.meta.time_table {
208            Some(table) => Some(table),
209            None => None,
210        }
211    }
212
213    /// Reads the hierarchy and calls callback for every item.
214    pub fn read_hierarchy(&mut self, callback: impl FnMut(FstHierarchyEntry)) -> Result<()> {
215        match &mut self.input {
216            InputVariant::Original(input) => read_hierarchy(input, &self.meta, callback),
217            InputVariant::Incomplete(_, input) => read_hierarchy(input, &self.meta, callback),
218            InputVariant::UncompressedInMem(input) => read_hierarchy(input, &self.meta, callback),
219            InputVariant::IncompleteUncompressedInMem(_, input) => {
220                read_hierarchy(input, &self.meta, callback)
221            }
222        }
223    }
224
225    /// Read signal values for a specific time interval.
226    pub fn read_signals(
227        &mut self,
228        filter: &FstFilter,
229        callback: impl FnMut(u64, FstSignalHandle, FstSignalValue),
230    ) -> Result<()> {
231        // convert user filters
232        let signal_count = self.meta.signals.len();
233        let signal_mask = if let Some(signals) = &filter.include {
234            let mut signal_mask = BitMask::repeat(false, signal_count);
235            for sig in signals {
236                let signal_idx = sig.get_index();
237                signal_mask.set(signal_idx, true);
238            }
239            signal_mask
240        } else {
241            // include all
242            BitMask::repeat(true, signal_count)
243        };
244        let data_filter = DataFilter {
245            start: filter.start,
246            end: filter.end.unwrap_or(self.meta.header.end_time),
247            signals: signal_mask,
248        };
249
250        // build and run reader
251        match &mut self.input {
252            InputVariant::Original(input) => {
253                read_signals(input, &self.meta, &data_filter, callback)
254            }
255            InputVariant::Incomplete(input, _) => {
256                read_signals(input, &self.meta, &data_filter, callback)
257            }
258            InputVariant::UncompressedInMem(input) => {
259                read_signals(input, &self.meta, &data_filter, callback)
260            }
261            InputVariant::IncompleteUncompressedInMem(input, _) => {
262                read_signals(input, &self.meta, &data_filter, callback)
263            }
264        }
265    }
266}
267
268pub enum FstSignalValue<'a> {
269    String(&'a [u8]),
270    Real(f64),
271}
272
273/// Quickly scans an input to see if it could be a FST file.
274pub fn is_fst_file(input: &mut (impl Read + Seek)) -> bool {
275    let is_fst = matches!(internal_check_fst_file(input), Ok(true));
276    // try to reset input
277    let _ = input.seek(SeekFrom::Start(0));
278    is_fst
279}
280
281/// Returns an error or false if not an fst. Returns Ok(true) only if we think it is an fst.
282fn internal_check_fst_file(input: &mut (impl Read + Seek)) -> Result<bool> {
283    let mut seen_header = false;
284
285    // try to iterate over all blocks
286    loop {
287        let block_tpe = match read_block_tpe(input) {
288            Err(ReaderError::Io(_)) => {
289                break;
290            }
291            Err(other) => return Err(other),
292            Ok(tpe) => tpe,
293        };
294        let section_length = read_u64(input)?;
295        match block_tpe {
296            BlockType::GZipWrapper => return Ok(true),
297            BlockType::Header => {
298                seen_header = true;
299            }
300            BlockType::Skip if section_length == 0 => {
301                break;
302            }
303            _ => {}
304        }
305        input.seek(SeekFrom::Current((section_length as i64) - 8))?;
306    }
307    Ok(seen_header)
308}
309
310fn read_hierarchy(
311    input: &mut (impl Read + Seek),
312    meta: &MetaData,
313    mut callback: impl FnMut(FstHierarchyEntry),
314) -> Result<()> {
315    input.seek(SeekFrom::Start(meta.hierarchy_offset))?;
316    let bytes = read_hierarchy_bytes(input, meta.hierarchy_compression)?;
317    let mut input = bytes.as_slice();
318    let mut handle_count = 0u32;
319    while let Some(entry) = read_hierarchy_entry(&mut input, &mut handle_count)? {
320        callback(entry);
321    }
322    Ok(())
323}
324
325fn read_signals(
326    input: &mut (impl Read + Seek),
327    meta: &MetaData,
328    filter: &DataFilter,
329    mut callback: impl FnMut(u64, FstSignalHandle, FstSignalValue),
330) -> Result<()> {
331    let mut reader = DataReader {
332        input,
333        meta,
334        filter,
335        callback: &mut callback,
336    };
337    reader.read()
338}
339
340enum UncompressGzipWrapper {
341    None,
342    // TempFile(BufReader<std::fs::File>),
343    InMemory(std::io::Cursor<Vec<u8>>),
344}
345
346/// Checks to see if the whole file is compressed in which case it is decompressed
347/// to a temp file which is returned.
348fn uncompress_gzip_wrapper(input: &mut (impl Read + Seek)) -> Result<UncompressGzipWrapper> {
349    let block_tpe = read_block_tpe(input)?;
350    if block_tpe != BlockType::GZipWrapper {
351        // no gzip wrapper
352        input.seek(SeekFrom::Start(0))?;
353        Ok(UncompressGzipWrapper::None)
354    } else {
355        // uncompress
356        let section_length = read_u64(input)?;
357        let uncompress_length = read_u64(input)? as usize;
358        if section_length == 0 {
359            return Err(ReaderError::NotFinishedCompressing());
360        }
361
362        // TODO: add back the ability to uncompress to a temporary file without adding a dependency
363        // we always decompress into memory
364        let mut target = vec![];
365        decompress_gz_in_chunks(input, uncompress_length, &mut target)?;
366        let new_input = std::io::Cursor::new(target);
367        Ok(UncompressGzipWrapper::InMemory(new_input))
368    }
369}
370
371fn decompress_gz_in_chunks(
372    input: &mut (impl Read + Seek),
373    mut remaining: usize,
374    target: &mut impl Write,
375) -> Result<()> {
376    read_gzip_header(input)?;
377    let mut buf_in = vec![0u8; 32768 / 2]; // FST_GZIO_LEN
378    let mut buf_out = vec![0u8; 32768 * 2]; // FST_GZIO_LEN
379
380    let mut state = miniz_oxide::inflate::stream::InflateState::new(miniz_oxide::DataFormat::Raw);
381    let mut buf_in_remaining = 0;
382    while remaining > 0 {
383        // load more bytes into the input buffer
384        buf_in_remaining += input.read(&mut buf_in[buf_in_remaining..])?;
385        debug_assert!(
386            buf_in_remaining > 0,
387            "ran out of input data while gzip decompressing"
388        );
389
390        // decompress them
391        let res = miniz_oxide::inflate::stream::inflate(
392            &mut state,
393            &buf_in[0..buf_in_remaining],
394            buf_out.as_mut_slice(),
395            miniz_oxide::MZFlush::None,
396        );
397
398        println!("{res:?}\nremaining={remaining}");
399
400        match res.status {
401            Ok(status) => {
402                // move bytes that were not consumed to the start of the buffer and update the length
403                buf_in.copy_within(res.bytes_consumed..buf_in_remaining, 0);
404                buf_in_remaining -= res.bytes_consumed;
405
406                // write decompressed output
407                let out_bytes = std::cmp::min(res.bytes_written, remaining);
408                remaining -= out_bytes;
409                target.write_all(&buf_out[..out_bytes])?;
410
411                match status {
412                    miniz_oxide::MZStatus::Ok => {
413                        // nothing to do
414                    }
415                    miniz_oxide::MZStatus::StreamEnd => {
416                        debug_assert_eq!(remaining, 0);
417                        return Ok(());
418                    }
419                    miniz_oxide::MZStatus::NeedDict => {
420                        todo!("hande NeedDict status");
421                    }
422                }
423            }
424            Err(err) => {
425                return Err(ReaderError::GZipBody(format!("{err:?}")));
426            }
427        }
428    }
429
430    Ok(())
431}
432
433#[derive(Debug)]
434struct MetaData {
435    header: Header,
436    signals: Vec<SignalInfo>,
437    #[allow(dead_code)]
438    blackouts: Vec<BlackoutData>,
439    data_sections: Vec<DataSectionInfo>,
440    float_endian: FloatingPointEndian,
441    hierarchy_compression: HierarchyCompression,
442    hierarchy_offset: u64,
443    time_table: Option<Vec<u64>>,
444}
445
446pub type Result<T> = std::result::Result<T, ReaderError>;
447
448struct HeaderReader<R: Read + Seek> {
449    input: R,
450    header: Option<Header>,
451    signals: Option<Vec<SignalInfo>>,
452    blackouts: Option<Vec<BlackoutData>>,
453    data_sections: Vec<DataSectionInfo>,
454    float_endian: FloatingPointEndian,
455    hierarchy: Option<(HierarchyCompression, u64)>,
456    time_table: Option<Vec<u64>>,
457}
458
459impl<R: Read + Seek> HeaderReader<R> {
460    fn new(input: R) -> Self {
461        HeaderReader {
462            input,
463            header: None,
464            signals: None,
465            blackouts: None,
466            data_sections: Vec::default(),
467            float_endian: FloatingPointEndian::Little,
468            hierarchy: None,
469            time_table: None,
470        }
471    }
472
473    fn read_data(&mut self, tpe: &BlockType) -> Result<()> {
474        let file_offset = self.input.stream_position()?;
475        // this is the data section
476        let section_length = read_u64(&mut self.input)?;
477        let start_time = read_u64(&mut self.input)?;
478        let end_time = read_u64(&mut self.input)?;
479        let mem_required_for_traversal = read_u64(&mut self.input)?;
480
481        // optional: read the time table
482        if let Some(table) = &mut self.time_table {
483            let (_, mut time_chain) =
484                read_time_table(&mut self.input, file_offset, section_length)?;
485            // in the first section, we might need to include the start time
486            let is_first_section = table.is_empty();
487            if is_first_section && time_chain[0] > start_time {
488                table.push(start_time);
489            }
490            table.append(&mut time_chain);
491            self.input.seek(SeekFrom::Start(file_offset + 4 * 8))?;
492        }
493        // go to the end of the section
494        self.skip(section_length, 4 * 8)?;
495        let kind = DataSectionKind::from_block_type(tpe).unwrap();
496        let info = DataSectionInfo {
497            file_offset,
498            start_time,
499            end_time,
500            kind,
501            mem_required_for_traversal,
502        };
503
504        // incomplete fst files may have start_time and end_time set to 0,
505        // in which case we can infer it from the data
506        if let Some(Header {
507            start_time: header_start,
508            end_time: header_end,
509            ..
510        }) = self.header.as_mut()
511        {
512            if *header_start == 0 && *header_end == 0 {
513                *header_end = end_time;
514                if self.data_sections.is_empty() {
515                    *header_start = start_time;
516                }
517            }
518        }
519
520        self.data_sections.push(info);
521        Ok(())
522    }
523
524    fn skip(&mut self, section_length: u64, already_read: i64) -> Result<u64> {
525        Ok(self
526            .input
527            .seek(SeekFrom::Current((section_length as i64) - already_read))?)
528    }
529
530    fn read_hierarchy(&mut self, compression: HierarchyCompression) -> Result<()> {
531        let file_offset = self.input.stream_position()?;
532        // this is the data section
533        let section_length = read_u64(&mut self.input)?;
534        self.skip(section_length, 8)?;
535        assert!(
536            self.hierarchy.is_none(),
537            "Only a single hierarchy block is expected!"
538        );
539        self.hierarchy = Some((compression, file_offset));
540        Ok(())
541    }
542
543    // The geometry block contains the types and lengths of variables (see [`SignalInfo`]).
544    // In case this block is missing from the FST file, it can be reconstructed from the hierarchy.
545    fn reconstruct_geometry(&mut self, hierarchy: &mut (impl BufRead + Seek)) -> Result<()> {
546        hierarchy.seek(SeekFrom::Start(0))?;
547        let bytes = read_hierarchy_bytes(hierarchy, HierarchyCompression::Uncompressed)?;
548        let mut input = bytes.as_slice();
549        let mut handle_count = 0u32;
550        let mut signals: Vec<SignalInfo> = Vec::new();
551        while let Some(entry) = read_hierarchy_entry(&mut input, &mut handle_count)? {
552            match entry {
553                FstHierarchyEntry::Var {
554                    length, is_alias, ..
555                } if !is_alias => {
556                    signals.push(SignalInfo::from_file_format(length));
557                }
558                _ => {}
559            }
560        }
561        self.signals = Some(signals);
562        Ok(())
563    }
564
565    fn read(&mut self, read_time_table: bool) -> Result<()> {
566        if read_time_table {
567            self.time_table = Some(Vec::new());
568        }
569        loop {
570            let block_tpe = match read_block_tpe(&mut self.input) {
571                Err(ReaderError::Io(_)) => {
572                    break;
573                }
574                Err(other) => return Err(other),
575                Ok(tpe) => tpe,
576            };
577
578            match block_tpe {
579                BlockType::Header => {
580                    let (header, endian) = read_header(&mut self.input)?;
581                    self.header = Some(header);
582                    self.float_endian = endian;
583                }
584                BlockType::VcData => self.read_data(&block_tpe)?,
585                BlockType::VcDataDynamicAlias => self.read_data(&block_tpe)?,
586                BlockType::VcDataDynamicAlias2 => self.read_data(&block_tpe)?,
587                BlockType::Blackout => {
588                    self.blackouts = Some(read_blackout(&mut self.input)?);
589                }
590                BlockType::Geometry => {
591                    self.signals = Some(read_geometry(&mut self.input)?);
592                }
593                BlockType::Hierarchy => self.read_hierarchy(HierarchyCompression::ZLib)?,
594                BlockType::HierarchyLZ4 => self.read_hierarchy(HierarchyCompression::Lz4)?,
595                BlockType::HierarchyLZ4Duo => self.read_hierarchy(HierarchyCompression::Lz4Duo)?,
596                BlockType::GZipWrapper => panic!("GZip Wrapper should have been handled earlier!"),
597                BlockType::Skip => {
598                    let section_length = read_u64(&mut self.input)?;
599                    if section_length == 0 {
600                        break;
601                    }
602                    self.skip(section_length, 8)?;
603                }
604            };
605        }
606
607        if self.signals.is_none() {
608            return Err(ReaderError::MissingGeometry());
609        }
610
611        if self.hierarchy.is_none() {
612            return Err(ReaderError::MissingHierarchy());
613        }
614
615        Ok(())
616    }
617
618    fn into_input_and_meta_data(mut self) -> Result<(R, MetaData)> {
619        self.input.seek(SeekFrom::Start(0))?;
620        let meta = MetaData {
621            header: self.header.unwrap(),
622            signals: self.signals.unwrap(),
623            blackouts: self.blackouts.unwrap_or_default(),
624            data_sections: self.data_sections,
625            float_endian: self.float_endian,
626            hierarchy_compression: self.hierarchy.unwrap().0,
627            hierarchy_offset: self.hierarchy.unwrap().1,
628            time_table: self.time_table,
629        };
630        Ok((self.input, meta))
631    }
632}
633
634struct DataReader<'a, R: Read + Seek, F: FnMut(u64, FstSignalHandle, FstSignalValue)> {
635    input: &'a mut R,
636    meta: &'a MetaData,
637    filter: &'a DataFilter,
638    callback: &'a mut F,
639}
640
641impl<R: Read + Seek, F: FnMut(u64, FstSignalHandle, FstSignalValue)> DataReader<'_, R, F> {
642    fn read_value_changes(
643        &mut self,
644        section_kind: DataSectionKind,
645        section_start: u64,
646        section_length: u64,
647        time_section_length: u64,
648        time_table: &[u64],
649    ) -> Result<()> {
650        let (max_handle, _) = read_variant_u64(&mut self.input)?;
651        let vc_start = self.input.stream_position()?;
652        let packtpe = ValueChangePackType::from_u8(read_u8(&mut self.input)?);
653        // the chain length is right in front of the time section
654        let chain_len_offset = section_start + section_length - time_section_length - 8;
655        let signal_offsets = read_signal_locs(
656            &mut self.input,
657            chain_len_offset,
658            section_kind,
659            max_handle,
660            vc_start,
661        )?;
662
663        // read data and create a bunch of pointers
664        let mut mu: Vec<u8> = Vec::new();
665        let mut head_pointer = vec![0u32; max_handle as usize];
666        let mut length_remaining = vec![0u32; max_handle as usize];
667        let mut scatter_pointer = vec![0u32; max_handle as usize];
668        let mut tc_head = vec![0u32; std::cmp::max(1, time_table.len())];
669
670        for entry in signal_offsets.iter() {
671            // is the signal supposed to be included?
672            if self.filter.signals.is_set(entry.signal_idx) {
673                // read all signal values
674                self.input.seek(SeekFrom::Start(vc_start + entry.offset))?;
675                let mut bytes =
676                    read_packed_signal_value_bytes(&mut self.input, entry.len, packtpe)?;
677
678                // read first time delta
679                let len = self.meta.signals[entry.signal_idx].len();
680                let tdelta = if len == 1 {
681                    read_one_bit_signal_time_delta(&bytes, 0)?
682                } else {
683                    read_multi_bit_signal_time_delta(&bytes, 0)?
684                };
685
686                // remember where we stored the signal data and how long it is
687                head_pointer[entry.signal_idx] = mu.len() as u32;
688                length_remaining[entry.signal_idx] = bytes.len() as u32;
689                mu.append(&mut bytes);
690
691                // remember at what time step we will read this signal
692                scatter_pointer[entry.signal_idx] = tc_head[tdelta];
693                tc_head[tdelta] = entry.signal_idx as u32 + 1; // index to handle
694            }
695        }
696
697        let mut buffer = Vec::new();
698
699        for (time_id, time) in time_table.iter().enumerate() {
700            // while we cannot ignore signal changes before the start of the window
701            // (since the signal might retain values for multiple cycles),
702            // signal changes after our window are completely useless
703            if *time > self.filter.end {
704                break;
705            }
706
707            let eof_error = || {
708                ReaderError::Io(std::io::Error::new(
709                    std::io::ErrorKind::UnexpectedEof,
710                    "unexpected eof",
711                ))
712            };
713
714            // handles cannot be zero
715            while tc_head[time_id] != 0 {
716                let signal_id = (tc_head[time_id] - 1) as usize; // convert handle to index
717                let mut mu_slice = &mu.as_slice()[head_pointer[signal_id] as usize..];
718                let (vli, skiplen) = read_variant_u32(&mut mu_slice)?;
719                let signal_len = self.meta.signals[signal_id].len();
720                let signal_handle = FstSignalHandle::from_index(signal_id);
721                let len = match signal_len {
722                    1 => {
723                        let value = one_bit_signal_value_to_char(vli);
724                        let value_buf = [value];
725                        (self.callback)(*time, signal_handle, FstSignalValue::String(&value_buf));
726                        0 // no additional bytes consumed
727                    }
728                    0 => {
729                        let (len, skiplen2) = read_variant_u32(&mut mu_slice)?;
730                        let value = mu_slice.get(..len as usize).ok_or_else(eof_error)?;
731                        (self.callback)(*time, signal_handle, FstSignalValue::String(value));
732                        len + skiplen2
733                    }
734                    len => {
735                        let signal_len = len as usize;
736                        if !self.meta.signals[signal_id].is_real() {
737                            let (value, len) = if (vli & 1) == 0 {
738                                // if bit0 is zero -> 2-state
739                                let read_len = signal_len.div_ceil(8);
740                                let bytes = mu_slice.get(..read_len).ok_or_else(eof_error)?;
741                                multi_bit_digital_signal_to_chars(bytes, signal_len, &mut buffer);
742                                (buffer.as_slice(), read_len as u32)
743                            } else {
744                                let value = mu_slice.get(..signal_len).ok_or_else(eof_error)?;
745                                (value, len)
746                            };
747                            (self.callback)(*time, signal_handle, FstSignalValue::String(value));
748                            len
749                        } else {
750                            assert_eq!(vli & 1, 1, "TODO: implement support for rare packed case");
751                            let value = read_f64(&mut mu_slice, self.meta.float_endian)?;
752                            (self.callback)(*time, signal_handle, FstSignalValue::Real(value));
753                            8
754                        }
755                    }
756                };
757
758                // update pointers
759                let total_skiplen = skiplen + len;
760                // advance "slice" for signal values
761                head_pointer[signal_id] += total_skiplen;
762                length_remaining[signal_id] -= total_skiplen;
763                // find the next signal to read in this time step
764                tc_head[time_id] = scatter_pointer[signal_id];
765                // invalidate pointer
766                scatter_pointer[signal_id] = 0;
767
768                // is there more data for this signal in the current block?
769                if length_remaining[signal_id] > 0 {
770                    let tdelta = if signal_len == 1 {
771                        read_one_bit_signal_time_delta(&mu, head_pointer[signal_id])?
772                    } else {
773                        read_multi_bit_signal_time_delta(&mu, head_pointer[signal_id])?
774                    };
775
776                    // point to the next time step
777                    scatter_pointer[signal_id] = tc_head[time_id + tdelta];
778                    tc_head[time_id + tdelta] = (signal_id + 1) as u32; // store handle
779                }
780            }
781        }
782
783        Ok(())
784    }
785
786    fn read(&mut self) -> Result<()> {
787        let sections = self.meta.data_sections.clone();
788        // filter out any sections which are not in our time window
789        let relevant_sections = sections
790            .iter()
791            .filter(|s| self.filter.end >= s.start_time && s.end_time >= self.filter.start);
792        for (sec_num, section) in relevant_sections.enumerate() {
793            // skip to section
794            self.input.seek(SeekFrom::Start(section.file_offset))?;
795            let section_length = read_u64(&mut self.input)?;
796
797            // verify meta-data
798            let start_time = read_u64(&mut self.input)?;
799            let end_time = read_u64(&mut self.input)?;
800            assert_eq!(start_time, section.start_time);
801            assert_eq!(end_time, section.end_time);
802            let is_first_section = sec_num == 0;
803
804            // read the time table
805            let (time_section_length, time_table) =
806                read_time_table(&mut self.input, section.file_offset, section_length)?;
807
808            // only read frame if this is the first section and there is no other data for
809            // the start time
810            if is_first_section && (time_table.is_empty() || time_table[0] > start_time) {
811                read_frame(
812                    &mut self.input,
813                    section.file_offset,
814                    section_length,
815                    &self.meta.signals,
816                    &self.filter.signals,
817                    self.meta.float_endian,
818                    start_time,
819                    self.callback,
820                )?;
821            } else {
822                skip_frame(&mut self.input, section.file_offset)?;
823            }
824
825            self.read_value_changes(
826                section.kind,
827                section.file_offset,
828                section_length,
829                time_section_length,
830                &time_table,
831            )?;
832        }
833
834        Ok(())
835    }
836}