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        match res.status {
399            Ok(status) => {
400                // move bytes that were not consumed to the start of the buffer and update the length
401                buf_in.copy_within(res.bytes_consumed..buf_in_remaining, 0);
402                buf_in_remaining -= res.bytes_consumed;
403
404                // write decompressed output
405                let out_bytes = std::cmp::min(res.bytes_written, remaining);
406                remaining -= out_bytes;
407                target.write_all(&buf_out[..out_bytes])?;
408
409                match status {
410                    miniz_oxide::MZStatus::Ok => {
411                        // nothing to do
412                    }
413                    miniz_oxide::MZStatus::StreamEnd => {
414                        debug_assert_eq!(remaining, 0);
415                        return Ok(());
416                    }
417                    miniz_oxide::MZStatus::NeedDict => {
418                        todo!("hande NeedDict status");
419                    }
420                }
421            }
422            Err(err) => {
423                return Err(ReaderError::GZipBody(format!("{err:?}")));
424            }
425        }
426    }
427
428    Ok(())
429}
430
431#[derive(Debug)]
432struct MetaData {
433    header: Header,
434    signals: Vec<SignalInfo>,
435    #[allow(dead_code)]
436    blackouts: Vec<BlackoutData>,
437    data_sections: Vec<DataSectionInfo>,
438    float_endian: FloatingPointEndian,
439    hierarchy_compression: HierarchyCompression,
440    hierarchy_offset: u64,
441    time_table: Option<Vec<u64>>,
442}
443
444pub type Result<T> = std::result::Result<T, ReaderError>;
445
446struct HeaderReader<R: Read + Seek> {
447    input: R,
448    header: Option<Header>,
449    signals: Option<Vec<SignalInfo>>,
450    blackouts: Option<Vec<BlackoutData>>,
451    data_sections: Vec<DataSectionInfo>,
452    float_endian: FloatingPointEndian,
453    hierarchy: Option<(HierarchyCompression, u64)>,
454    time_table: Option<Vec<u64>>,
455    is_incomplete: bool,
456}
457
458impl<R: Read + Seek> HeaderReader<R> {
459    fn new(input: R) -> Self {
460        HeaderReader {
461            input,
462            header: None,
463            signals: None,
464            blackouts: None,
465            data_sections: Vec::default(),
466            float_endian: FloatingPointEndian::Little,
467            hierarchy: None,
468            time_table: None,
469            is_incomplete: false,
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) = self.header.as_mut() {
507            if self.data_sections.is_empty() {
508                self.is_incomplete = header.start_time == 0 && header.end_time == 0;
509                if self.is_incomplete {
510                    header.start_time = start_time;
511                }
512            }
513            if self.is_incomplete {
514                header.end_time = end_time;
515            }
516        }
517
518        self.data_sections.push(info);
519        Ok(())
520    }
521
522    fn skip(&mut self, section_length: u64, already_read: i64) -> Result<u64> {
523        Ok(self
524            .input
525            .seek(SeekFrom::Current((section_length as i64) - already_read))?)
526    }
527
528    fn read_hierarchy(&mut self, compression: HierarchyCompression) -> Result<()> {
529        let file_offset = self.input.stream_position()?;
530        // this is the data section
531        let section_length = read_u64(&mut self.input)?;
532        self.skip(section_length, 8)?;
533        assert!(
534            self.hierarchy.is_none(),
535            "Only a single hierarchy block is expected!"
536        );
537        self.hierarchy = Some((compression, file_offset));
538        Ok(())
539    }
540
541    // The geometry block contains the types and lengths of variables (see [`SignalInfo`]).
542    // In case this block is missing from the FST file, it can be reconstructed from the hierarchy.
543    fn reconstruct_geometry(&mut self, hierarchy: &mut (impl BufRead + Seek)) -> Result<()> {
544        hierarchy.seek(SeekFrom::Start(0))?;
545        let bytes = read_hierarchy_bytes(hierarchy, HierarchyCompression::Uncompressed)?;
546        let mut input = bytes.as_slice();
547        let mut handle_count = 0u32;
548        let mut signals: Vec<SignalInfo> = Vec::new();
549        while let Some(entry) = read_hierarchy_entry(&mut input, &mut handle_count)? {
550            match entry {
551                FstHierarchyEntry::Var {
552                    tpe,
553                    length,
554                    is_alias,
555                    ..
556                } if !is_alias => {
557                    // Check variable type to correctly identify Real signals
558                    // (length alone is not sufficient - Real signals have length=64 but should be SignalInfo::Real)
559                    let signal_info = if tpe.is_real() {
560                        SignalInfo::Real
561                    } else {
562                        SignalInfo::from_file_format(length)
563                    };
564                    signals.push(signal_info);
565                }
566                _ => {}
567            }
568        }
569        self.signals = Some(signals);
570        Ok(())
571    }
572
573    fn read(&mut self, read_time_table: bool) -> Result<()> {
574        if read_time_table {
575            self.time_table = Some(Vec::new());
576        }
577        loop {
578            let block_tpe = match read_block_tpe(&mut self.input) {
579                Err(ReaderError::Io(_)) => {
580                    break;
581                }
582                Err(other) => return Err(other),
583                Ok(tpe) => tpe,
584            };
585
586            match block_tpe {
587                BlockType::Header => {
588                    let (header, endian) = read_header(&mut self.input)?;
589                    self.header = Some(header);
590                    self.float_endian = endian;
591                }
592                BlockType::VcData => self.read_data(&block_tpe)?,
593                BlockType::VcDataDynamicAlias => self.read_data(&block_tpe)?,
594                BlockType::VcDataDynamicAlias2 => self.read_data(&block_tpe)?,
595                BlockType::Blackout => {
596                    self.blackouts = Some(read_blackout(&mut self.input)?);
597                }
598                BlockType::Geometry => {
599                    self.signals = Some(read_geometry(&mut self.input)?);
600                }
601                BlockType::Hierarchy => self.read_hierarchy(HierarchyCompression::ZLib)?,
602                BlockType::HierarchyLZ4 => self.read_hierarchy(HierarchyCompression::Lz4)?,
603                BlockType::HierarchyLZ4Duo => self.read_hierarchy(HierarchyCompression::Lz4Duo)?,
604                BlockType::GZipWrapper => panic!("GZip Wrapper should have been handled earlier!"),
605                BlockType::Skip => {
606                    let section_length = read_u64(&mut self.input)?;
607                    if section_length == 0 {
608                        break;
609                    }
610                    self.skip(section_length, 8)?;
611                }
612            };
613        }
614
615        if self.signals.is_none() {
616            return Err(ReaderError::MissingGeometry());
617        }
618
619        if self.hierarchy.is_none() {
620            return Err(ReaderError::MissingHierarchy());
621        }
622
623        Ok(())
624    }
625
626    fn into_input_and_meta_data(mut self) -> Result<(R, MetaData)> {
627        self.input.seek(SeekFrom::Start(0))?;
628        let meta = MetaData {
629            header: self.header.unwrap(),
630            signals: self.signals.unwrap(),
631            blackouts: self.blackouts.unwrap_or_default(),
632            data_sections: self.data_sections,
633            float_endian: self.float_endian,
634            hierarchy_compression: self.hierarchy.unwrap().0,
635            hierarchy_offset: self.hierarchy.unwrap().1,
636            time_table: self.time_table,
637        };
638        Ok((self.input, meta))
639    }
640}
641
642struct DataReader<'a, R: Read + Seek, F: FnMut(u64, FstSignalHandle, FstSignalValue)> {
643    input: &'a mut R,
644    meta: &'a MetaData,
645    filter: &'a DataFilter,
646    callback: &'a mut F,
647}
648
649impl<R: Read + Seek, F: FnMut(u64, FstSignalHandle, FstSignalValue)> DataReader<'_, R, F> {
650    fn read_value_changes(
651        &mut self,
652        section_kind: DataSectionKind,
653        section_start: u64,
654        section_length: u64,
655        time_section_length: u64,
656        time_table: &[u64],
657    ) -> Result<()> {
658        let (max_handle, _) = read_variant_u64(&mut self.input)?;
659        let vc_start = self.input.stream_position()?;
660        let packtpe = ValueChangePackType::from_u8(read_u8(&mut self.input)?);
661        // the chain length is right in front of the time section
662        let chain_len_offset = section_start + section_length - time_section_length - 8;
663        let signal_offsets = read_signal_locs(
664            &mut self.input,
665            chain_len_offset,
666            section_kind,
667            max_handle,
668            vc_start,
669        )?;
670
671        // read data and create a bunch of pointers
672        let mut mu: Vec<u8> = Vec::new();
673        let mut head_pointer = vec![0u32; max_handle as usize];
674        let mut length_remaining = vec![0u32; max_handle as usize];
675        let mut scatter_pointer = vec![0u32; max_handle as usize];
676        let mut tc_head = vec![0u32; std::cmp::max(1, time_table.len())];
677
678        for entry in signal_offsets.iter() {
679            // is the signal supposed to be included?
680            if self.filter.signals.is_set(entry.signal_idx) {
681                // read all signal values
682                self.input.seek(SeekFrom::Start(vc_start + entry.offset))?;
683                let mut bytes =
684                    read_packed_signal_value_bytes(&mut self.input, entry.len, packtpe)?;
685
686                // read first time delta
687                let len = self.meta.signals[entry.signal_idx].len();
688                let tdelta = if len == 1 {
689                    read_one_bit_signal_time_delta(&bytes, 0)?
690                } else {
691                    read_multi_bit_signal_time_delta(&bytes, 0)?
692                };
693
694                // remember where we stored the signal data and how long it is
695                head_pointer[entry.signal_idx] = mu.len() as u32;
696                length_remaining[entry.signal_idx] = bytes.len() as u32;
697                mu.append(&mut bytes);
698
699                // remember at what time step we will read this signal
700                scatter_pointer[entry.signal_idx] = tc_head[tdelta];
701                tc_head[tdelta] = entry.signal_idx as u32 + 1; // index to handle
702            }
703        }
704
705        let mut buffer = Vec::new();
706
707        for (time_id, time) in time_table.iter().enumerate() {
708            // while we cannot ignore signal changes before the start of the window
709            // (since the signal might retain values for multiple cycles),
710            // signal changes after our window are completely useless
711            if *time > self.filter.end {
712                break;
713            }
714
715            let eof_error = || {
716                ReaderError::Io(std::io::Error::new(
717                    std::io::ErrorKind::UnexpectedEof,
718                    "unexpected eof",
719                ))
720            };
721
722            // handles cannot be zero
723            while tc_head[time_id] != 0 {
724                let signal_id = (tc_head[time_id] - 1) as usize; // convert handle to index
725                let mut mu_slice = &mu.as_slice()[head_pointer[signal_id] as usize..];
726                let (vli, skiplen) = read_variant_u32(&mut mu_slice)?;
727                let signal_len = self.meta.signals[signal_id].len();
728                let signal_handle = FstSignalHandle::from_index(signal_id);
729                let len = match signal_len {
730                    1 => {
731                        let value = one_bit_signal_value_to_char(vli);
732                        let value_buf = [value];
733                        (self.callback)(*time, signal_handle, FstSignalValue::String(&value_buf));
734                        0 // no additional bytes consumed
735                    }
736                    0 => {
737                        let (len, skiplen2) = read_variant_u32(&mut mu_slice)?;
738                        let value = mu_slice.get(..len as usize).ok_or_else(eof_error)?;
739                        (self.callback)(*time, signal_handle, FstSignalValue::String(value));
740                        len + skiplen2
741                    }
742                    len => {
743                        let signal_len = len as usize;
744                        if !self.meta.signals[signal_id].is_real() {
745                            let (value, len) = if (vli & 1) == 0 {
746                                // if bit0 is zero -> 2-state
747                                let read_len = signal_len.div_ceil(8);
748                                let bytes = mu_slice.get(..read_len).ok_or_else(eof_error)?;
749                                multi_bit_digital_signal_to_chars(bytes, signal_len, &mut buffer);
750                                (buffer.as_slice(), read_len as u32)
751                            } else {
752                                let value = mu_slice.get(..signal_len).ok_or_else(eof_error)?;
753                                (value, len)
754                            };
755                            (self.callback)(*time, signal_handle, FstSignalValue::String(value));
756                            len
757                        } else {
758                            assert_eq!(vli & 1, 1, "TODO: implement support for rare packed case");
759                            let value = read_f64(&mut mu_slice, self.meta.float_endian)?;
760                            (self.callback)(*time, signal_handle, FstSignalValue::Real(value));
761                            8
762                        }
763                    }
764                };
765
766                // update pointers
767                let total_skiplen = skiplen + len;
768                // advance "slice" for signal values
769                head_pointer[signal_id] += total_skiplen;
770                length_remaining[signal_id] -= total_skiplen;
771                // find the next signal to read in this time step
772                tc_head[time_id] = scatter_pointer[signal_id];
773                // invalidate pointer
774                scatter_pointer[signal_id] = 0;
775
776                // is there more data for this signal in the current block?
777                if length_remaining[signal_id] > 0 {
778                    let tdelta = if signal_len == 1 {
779                        read_one_bit_signal_time_delta(&mu, head_pointer[signal_id])?
780                    } else {
781                        read_multi_bit_signal_time_delta(&mu, head_pointer[signal_id])?
782                    };
783
784                    // point to the next time step
785                    scatter_pointer[signal_id] = tc_head[time_id + tdelta];
786                    tc_head[time_id + tdelta] = (signal_id + 1) as u32; // store handle
787                }
788            }
789        }
790
791        Ok(())
792    }
793
794    fn read(&mut self) -> Result<()> {
795        let sections = self.meta.data_sections.clone();
796        // filter out any sections which are not in our time window
797        let relevant_sections = sections
798            .iter()
799            .filter(|s| self.filter.end >= s.start_time && s.end_time >= self.filter.start);
800        for (sec_num, section) in relevant_sections.enumerate() {
801            // skip to section
802            self.input.seek(SeekFrom::Start(section.file_offset))?;
803            let section_length = read_u64(&mut self.input)?;
804
805            // verify meta-data
806            let start_time = read_u64(&mut self.input)?;
807            let end_time = read_u64(&mut self.input)?;
808            assert_eq!(start_time, section.start_time);
809            assert_eq!(end_time, section.end_time);
810            let is_first_section = sec_num == 0;
811
812            // read the time table
813            let (time_section_length, time_table) =
814                read_time_table(&mut self.input, section.file_offset, section_length)?;
815
816            // only read frame if this is the first section and there is no other data for
817            // the start time
818            if is_first_section && (time_table.is_empty() || time_table[0] > start_time) {
819                read_frame(
820                    &mut self.input,
821                    section.file_offset,
822                    section_length,
823                    &self.meta.signals,
824                    &self.filter.signals,
825                    self.meta.float_endian,
826                    start_time,
827                    self.callback,
828                )?;
829            } else {
830                skip_frame(&mut self.input, section.file_offset)?;
831            }
832
833            self.read_value_changes(
834                section.kind,
835                section.file_offset,
836                section_length,
837                time_section_length,
838                &time_table,
839            )?;
840        }
841
842        Ok(())
843    }
844}