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}
456
457impl<R: Read + Seek> HeaderReader<R> {
458    fn new(input: R) -> Self {
459        HeaderReader {
460            input,
461            header: None,
462            signals: None,
463            blackouts: None,
464            data_sections: Vec::default(),
465            float_endian: FloatingPointEndian::Little,
466            hierarchy: None,
467            time_table: None,
468        }
469    }
470
471    fn read_data(&mut self, tpe: &BlockType) -> Result<()> {
472        let file_offset = self.input.stream_position()?;
473        // this is the data section
474        let section_length = read_u64(&mut self.input)?;
475        let start_time = read_u64(&mut self.input)?;
476        let end_time = read_u64(&mut self.input)?;
477        let mem_required_for_traversal = read_u64(&mut self.input)?;
478
479        // optional: read the time table
480        if let Some(table) = &mut self.time_table {
481            let (_, mut time_chain) =
482                read_time_table(&mut self.input, file_offset, section_length)?;
483            // in the first section, we might need to include the start time
484            let is_first_section = table.is_empty();
485            if is_first_section && time_chain[0] > start_time {
486                table.push(start_time);
487            }
488            table.append(&mut time_chain);
489            self.input.seek(SeekFrom::Start(file_offset + 4 * 8))?;
490        }
491        // go to the end of the section
492        self.skip(section_length, 4 * 8)?;
493        let kind = DataSectionKind::from_block_type(tpe).unwrap();
494        let info = DataSectionInfo {
495            file_offset,
496            start_time,
497            end_time,
498            kind,
499            mem_required_for_traversal,
500        };
501
502        // incomplete fst files may have start_time and end_time set to 0,
503        // in which case we can infer it from the data
504        if let Some(Header {
505            start_time: header_start,
506            end_time: header_end,
507            ..
508        }) = self.header.as_mut()
509            && *header_start == 0
510            && *header_end == 0
511        {
512            *header_end = end_time;
513            if self.data_sections.is_empty() {
514                *header_start = start_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                    length, is_alias, ..
553                } if !is_alias => {
554                    signals.push(SignalInfo::from_file_format(length));
555                }
556                _ => {}
557            }
558        }
559        self.signals = Some(signals);
560        Ok(())
561    }
562
563    fn read(&mut self, read_time_table: bool) -> Result<()> {
564        if read_time_table {
565            self.time_table = Some(Vec::new());
566        }
567        loop {
568            let block_tpe = match read_block_tpe(&mut self.input) {
569                Err(ReaderError::Io(_)) => {
570                    break;
571                }
572                Err(other) => return Err(other),
573                Ok(tpe) => tpe,
574            };
575
576            match block_tpe {
577                BlockType::Header => {
578                    let (header, endian) = read_header(&mut self.input)?;
579                    self.header = Some(header);
580                    self.float_endian = endian;
581                }
582                BlockType::VcData => self.read_data(&block_tpe)?,
583                BlockType::VcDataDynamicAlias => self.read_data(&block_tpe)?,
584                BlockType::VcDataDynamicAlias2 => self.read_data(&block_tpe)?,
585                BlockType::Blackout => {
586                    self.blackouts = Some(read_blackout(&mut self.input)?);
587                }
588                BlockType::Geometry => {
589                    self.signals = Some(read_geometry(&mut self.input)?);
590                }
591                BlockType::Hierarchy => self.read_hierarchy(HierarchyCompression::ZLib)?,
592                BlockType::HierarchyLZ4 => self.read_hierarchy(HierarchyCompression::Lz4)?,
593                BlockType::HierarchyLZ4Duo => self.read_hierarchy(HierarchyCompression::Lz4Duo)?,
594                BlockType::GZipWrapper => panic!("GZip Wrapper should have been handled earlier!"),
595                BlockType::Skip => {
596                    let section_length = read_u64(&mut self.input)?;
597                    if section_length == 0 {
598                        break;
599                    }
600                    self.skip(section_length, 8)?;
601                }
602            };
603        }
604
605        if self.signals.is_none() {
606            return Err(ReaderError::MissingGeometry());
607        }
608
609        if self.hierarchy.is_none() {
610            return Err(ReaderError::MissingHierarchy());
611        }
612
613        Ok(())
614    }
615
616    fn into_input_and_meta_data(mut self) -> Result<(R, MetaData)> {
617        self.input.seek(SeekFrom::Start(0))?;
618        let meta = MetaData {
619            header: self.header.unwrap(),
620            signals: self.signals.unwrap(),
621            blackouts: self.blackouts.unwrap_or_default(),
622            data_sections: self.data_sections,
623            float_endian: self.float_endian,
624            hierarchy_compression: self.hierarchy.unwrap().0,
625            hierarchy_offset: self.hierarchy.unwrap().1,
626            time_table: self.time_table,
627        };
628        Ok((self.input, meta))
629    }
630}
631
632struct DataReader<'a, R: Read + Seek, F: FnMut(u64, FstSignalHandle, FstSignalValue)> {
633    input: &'a mut R,
634    meta: &'a MetaData,
635    filter: &'a DataFilter,
636    callback: &'a mut F,
637}
638
639impl<R: Read + Seek, F: FnMut(u64, FstSignalHandle, FstSignalValue)> DataReader<'_, R, F> {
640    fn read_value_changes(
641        &mut self,
642        section_kind: DataSectionKind,
643        section_start: u64,
644        section_length: u64,
645        time_section_length: u64,
646        time_table: &[u64],
647    ) -> Result<()> {
648        let (max_handle, _) = read_variant_u64(&mut self.input)?;
649        let vc_start = self.input.stream_position()?;
650        let packtpe = ValueChangePackType::from_u8(read_u8(&mut self.input)?);
651        // the chain length is right in front of the time section
652        let chain_len_offset = section_start + section_length - time_section_length - 8;
653        let signal_offsets = read_signal_locs(
654            &mut self.input,
655            chain_len_offset,
656            section_kind,
657            max_handle,
658            vc_start,
659        )?;
660
661        // read data and create a bunch of pointers
662        let mut mu: Vec<u8> = Vec::new();
663        let mut head_pointer = vec![0u32; max_handle as usize];
664        let mut length_remaining = vec![0u32; max_handle as usize];
665        let mut scatter_pointer = vec![0u32; max_handle as usize];
666        let mut tc_head = vec![0u32; std::cmp::max(1, time_table.len())];
667
668        for entry in signal_offsets.iter() {
669            // is the signal supposed to be included?
670            if self.filter.signals.is_set(entry.signal_idx) {
671                // read all signal values
672                self.input.seek(SeekFrom::Start(vc_start + entry.offset))?;
673                let mut bytes =
674                    read_packed_signal_value_bytes(&mut self.input, entry.len, packtpe)?;
675
676                // read first time delta
677                let len = self.meta.signals[entry.signal_idx].len();
678                let tdelta = if len == 1 {
679                    read_one_bit_signal_time_delta(&bytes, 0)?
680                } else {
681                    read_multi_bit_signal_time_delta(&bytes, 0)?
682                };
683
684                // remember where we stored the signal data and how long it is
685                head_pointer[entry.signal_idx] = mu.len() as u32;
686                length_remaining[entry.signal_idx] = bytes.len() as u32;
687                mu.append(&mut bytes);
688
689                // remember at what time step we will read this signal
690                scatter_pointer[entry.signal_idx] = tc_head[tdelta];
691                tc_head[tdelta] = entry.signal_idx as u32 + 1; // index to handle
692            }
693        }
694
695        let mut buffer = Vec::new();
696
697        for (time_id, time) in time_table.iter().enumerate() {
698            // while we cannot ignore signal changes before the start of the window
699            // (since the signal might retain values for multiple cycles),
700            // signal changes after our window are completely useless
701            if *time > self.filter.end {
702                break;
703            }
704
705            let eof_error = || {
706                ReaderError::Io(std::io::Error::new(
707                    std::io::ErrorKind::UnexpectedEof,
708                    "unexpected eof",
709                ))
710            };
711
712            // handles cannot be zero
713            while tc_head[time_id] != 0 {
714                let signal_id = (tc_head[time_id] - 1) as usize; // convert handle to index
715                let mut mu_slice = &mu.as_slice()[head_pointer[signal_id] as usize..];
716                let (vli, skiplen) = read_variant_u32(&mut mu_slice)?;
717                let signal_len = self.meta.signals[signal_id].len();
718                let signal_handle = FstSignalHandle::from_index(signal_id);
719                let len = match signal_len {
720                    1 => {
721                        let value = one_bit_signal_value_to_char(vli);
722                        let value_buf = [value];
723                        (self.callback)(*time, signal_handle, FstSignalValue::String(&value_buf));
724                        0 // no additional bytes consumed
725                    }
726                    0 => {
727                        let (len, skiplen2) = read_variant_u32(&mut mu_slice)?;
728                        let value = mu_slice.get(..len as usize).ok_or_else(eof_error)?;
729                        (self.callback)(*time, signal_handle, FstSignalValue::String(value));
730                        len + skiplen2
731                    }
732                    len => {
733                        let signal_len = len as usize;
734                        if !self.meta.signals[signal_id].is_real() {
735                            let (value, len) = if (vli & 1) == 0 {
736                                // if bit0 is zero -> 2-state
737                                let read_len = signal_len.div_ceil(8);
738                                let bytes = mu_slice.get(..read_len).ok_or_else(eof_error)?;
739                                multi_bit_digital_signal_to_chars(bytes, signal_len, &mut buffer);
740                                (buffer.as_slice(), read_len as u32)
741                            } else {
742                                let value = mu_slice.get(..signal_len).ok_or_else(eof_error)?;
743                                (value, len)
744                            };
745                            (self.callback)(*time, signal_handle, FstSignalValue::String(value));
746                            len
747                        } else {
748                            assert_eq!(vli & 1, 1, "TODO: implement support for rare packed case");
749                            let value = read_f64(&mut mu_slice, self.meta.float_endian)?;
750                            (self.callback)(*time, signal_handle, FstSignalValue::Real(value));
751                            8
752                        }
753                    }
754                };
755
756                // update pointers
757                let total_skiplen = skiplen + len;
758                // advance "slice" for signal values
759                head_pointer[signal_id] += total_skiplen;
760                length_remaining[signal_id] -= total_skiplen;
761                // find the next signal to read in this time step
762                tc_head[time_id] = scatter_pointer[signal_id];
763                // invalidate pointer
764                scatter_pointer[signal_id] = 0;
765
766                // is there more data for this signal in the current block?
767                if length_remaining[signal_id] > 0 {
768                    let tdelta = if signal_len == 1 {
769                        read_one_bit_signal_time_delta(&mu, head_pointer[signal_id])?
770                    } else {
771                        read_multi_bit_signal_time_delta(&mu, head_pointer[signal_id])?
772                    };
773
774                    // point to the next time step
775                    scatter_pointer[signal_id] = tc_head[time_id + tdelta];
776                    tc_head[time_id + tdelta] = (signal_id + 1) as u32; // store handle
777                }
778            }
779        }
780
781        Ok(())
782    }
783
784    fn read(&mut self) -> Result<()> {
785        let sections = self.meta.data_sections.clone();
786        // filter out any sections which are not in our time window
787        let relevant_sections = sections
788            .iter()
789            .filter(|s| self.filter.end >= s.start_time && s.end_time >= self.filter.start);
790        for (sec_num, section) in relevant_sections.enumerate() {
791            // skip to section
792            self.input.seek(SeekFrom::Start(section.file_offset))?;
793            let section_length = read_u64(&mut self.input)?;
794
795            // verify meta-data
796            let start_time = read_u64(&mut self.input)?;
797            let end_time = read_u64(&mut self.input)?;
798            assert_eq!(start_time, section.start_time);
799            assert_eq!(end_time, section.end_time);
800            let is_first_section = sec_num == 0;
801
802            // read the time table
803            let (time_section_length, time_table) =
804                read_time_table(&mut self.input, section.file_offset, section_length)?;
805
806            // only read frame if this is the first section and there is no other data for
807            // the start time
808            if is_first_section && (time_table.is_empty() || time_table[0] > start_time) {
809                read_frame(
810                    &mut self.input,
811                    section.file_offset,
812                    section_length,
813                    &self.meta.signals,
814                    &self.filter.signals,
815                    self.meta.float_endian,
816                    start_time,
817                    self.callback,
818                )?;
819            } else {
820                skip_frame(&mut self.input, section.file_offset)?;
821            }
822
823            self.read_value_changes(
824                section.kind,
825                section.file_offset,
826                section_length,
827                time_section_length,
828                &time_table,
829            )?;
830        }
831
832        Ok(())
833    }
834}