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
10/// Reads in a FST file.
11pub struct FstReader<R: BufRead + Seek, H = std::io::Cursor<Vec<u8>>> {
12    input: InputVariant<R, H>,
13    meta: MetaData,
14}
15
16enum InputVariant<R: BufRead + Seek, H = std::io::Cursor<Vec<u8>>> {
17    Original(R),
18    Incomplete(R, H),
19    // Uncompressed(BufReader<std::fs::File>),
20    UncompressedInMem(std::io::Cursor<Vec<u8>>),
21    IncompleteUncompressedInMem(std::io::Cursor<Vec<u8>>, H),
22}
23
24/// Filter the changes by time and/or signals
25///
26/// The time filter is inclusive, i.e. it includes all changes in `start..=end`.
27pub struct FstFilter {
28    pub start: u64,
29    pub end: Option<u64>,
30    pub include: Option<Vec<FstSignalHandle>>,
31}
32
33impl FstFilter {
34    pub fn all() -> Self {
35        FstFilter {
36            start: 0,
37            end: None,
38            include: None,
39        }
40    }
41
42    pub fn new(start: u64, end: u64, signals: Vec<FstSignalHandle>) -> Self {
43        FstFilter {
44            start,
45            end: Some(end),
46            include: Some(signals),
47        }
48    }
49
50    pub fn filter_time(start: u64, end: u64) -> Self {
51        FstFilter {
52            start,
53            end: Some(end),
54            include: None,
55        }
56    }
57
58    pub fn filter_signals(signals: Vec<FstSignalHandle>) -> Self {
59        FstFilter {
60            start: 0,
61            end: None,
62            include: Some(signals),
63        }
64    }
65}
66
67#[derive(Debug, Clone, PartialEq)]
68pub struct FstHeader {
69    /// time of first sample
70    pub start_time: u64,
71    /// time of last sample
72    pub end_time: u64,
73    /// number of variables in the design
74    pub var_count: u64,
75    /// the highest signal handle; indicates the number of unique signals
76    pub max_handle: u64,
77    /// human readable version string
78    pub version: String,
79    /// human readable times stamp
80    pub date: String,
81    /// the exponent of the timescale; timescale will be 10^(exponent) seconds
82    pub timescale_exponent: i8,
83}
84
85impl<R: BufRead + Seek> FstReader<R> {
86    /// Reads in the FST file meta-data.
87    pub fn open(input: R) -> Result<Self> {
88        Self::open_internal(input, false)
89    }
90
91    pub fn open_and_read_time_table(input: R) -> Result<Self> {
92        Self::open_internal(input, true)
93    }
94
95    fn open_internal(mut input: R, read_time_table: bool) -> Result<Self> {
96        let uncompressed_input = uncompress_gzip_wrapper(&mut input)?;
97        match uncompressed_input {
98            UncompressGzipWrapper::None => {
99                let mut header_reader = HeaderReader::new(input);
100                header_reader.read(read_time_table)?;
101                let (input, meta) = header_reader.into_input_and_meta_data().unwrap();
102                Ok(FstReader {
103                    input: InputVariant::Original(input),
104                    meta,
105                })
106            }
107            UncompressGzipWrapper::InMemory(uc) => {
108                let mut header_reader = HeaderReader::new(uc);
109                header_reader.read(read_time_table)?;
110                let (uc2, meta) = header_reader.into_input_and_meta_data().unwrap();
111                Ok(FstReader {
112                    input: InputVariant::UncompressedInMem(uc2),
113                    meta,
114                })
115            }
116        }
117    }
118}
119
120impl<R: BufRead + Seek, H: BufRead + Seek> FstReader<R, H> {
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(input: R, hierarchy: H) -> Result<Self> {
130        Self::open_incomplete_internal(input, hierarchy, false)
131    }
132
133    pub fn open_incomplete_and_read_time_table(input: R, hierarchy: H) -> Result<Self> {
134        Self::open_incomplete_internal(input, hierarchy, true)
135    }
136
137    fn open_incomplete_internal(
138        mut input: R,
139        mut hierarchy: H,
140        read_time_table: bool,
141    ) -> Result<Self> {
142        let uncompressed_input = uncompress_gzip_wrapper(&mut input)?;
143        match uncompressed_input {
144            UncompressGzipWrapper::None => {
145                let (input, meta) = Self::open_incomplete_internal_uncompressed(
146                    input,
147                    &mut hierarchy,
148                    read_time_table,
149                )?;
150                Ok(FstReader {
151                    input: InputVariant::Incomplete(input, hierarchy),
152                    meta,
153                })
154            }
155            UncompressGzipWrapper::InMemory(uc) => {
156                let (uc2, meta) = Self::open_incomplete_internal_uncompressed(
157                    uc,
158                    &mut hierarchy,
159                    read_time_table,
160                )?;
161                Ok(FstReader {
162                    input: InputVariant::IncompleteUncompressedInMem(uc2, hierarchy),
163                    meta,
164                })
165            }
166        }
167    }
168
169    fn open_incomplete_internal_uncompressed<I: BufRead + Seek>(
170        input: I,
171        hierarchy: &mut H,
172        read_time_table: bool,
173    ) -> Result<(I, MetaData)> {
174        let mut header_reader = HeaderReader::new(input);
175        match header_reader.read(read_time_table) {
176            Ok(_) => {}
177            Err(ReaderError::MissingGeometry() | ReaderError::MissingHierarchy()) => {
178                header_reader
179                    .hierarchy
180                    .get_or_insert((HierarchyCompression::Uncompressed, 0));
181                header_reader.reconstruct_geometry(hierarchy)?;
182            }
183            Err(e) => return Err(e),
184        };
185        Ok(header_reader.into_input_and_meta_data().unwrap())
186    }
187}
188
189impl<R: BufRead + Seek, H: BufRead + Seek> FstReader<R, H> {
190    pub fn get_header(&self) -> FstHeader {
191        FstHeader {
192            start_time: self.meta.header.start_time,
193            end_time: self.meta.header.end_time,
194            var_count: self.meta.header.var_count,
195            max_handle: self.meta.header.max_var_id_code,
196            version: self.meta.header.version.clone(),
197            date: self.meta.header.date.clone(),
198            timescale_exponent: self.meta.header.timescale_exponent,
199        }
200    }
201
202    pub fn get_time_table(&self) -> Option<&[u64]> {
203        match &self.meta.time_table {
204            Some(table) => Some(table),
205            None => None,
206        }
207    }
208
209    /// Reads the hierarchy and calls callback for every item.
210    pub fn read_hierarchy(&mut self, callback: impl FnMut(FstHierarchyEntry)) -> Result<()> {
211        match &mut self.input {
212            InputVariant::Original(input) => read_hierarchy(input, &self.meta, callback),
213            InputVariant::Incomplete(_, input) => read_hierarchy(input, &self.meta, callback),
214            InputVariant::UncompressedInMem(input) => read_hierarchy(input, &self.meta, callback),
215            InputVariant::IncompleteUncompressedInMem(_, input) => {
216                read_hierarchy(input, &self.meta, callback)
217            }
218        }
219    }
220
221    /// Read signal values for a specific time interval.
222    pub fn read_signals(
223        &mut self,
224        filter: &FstFilter,
225        callback: impl FnMut(u64, FstSignalHandle, FstSignalValue),
226    ) -> Result<()> {
227        // convert user filters
228        let signal_count = self.meta.signals.len();
229        let signal_mask = if let Some(signals) = &filter.include {
230            let mut signal_mask = BitMask::repeat(false, signal_count);
231            for sig in signals {
232                let signal_idx = sig.get_index();
233                signal_mask.set(signal_idx, true);
234            }
235            signal_mask
236        } else {
237            // include all
238            BitMask::repeat(true, signal_count)
239        };
240        let data_filter = DataFilter {
241            start: filter.start,
242            end: filter.end.unwrap_or(self.meta.header.end_time),
243            signals: signal_mask,
244        };
245
246        // build and run reader
247        match &mut self.input {
248            InputVariant::Original(input) => {
249                read_signals(input, &self.meta, &data_filter, callback)
250            }
251            InputVariant::Incomplete(input, _) => {
252                read_signals(input, &self.meta, &data_filter, callback)
253            }
254            InputVariant::UncompressedInMem(input) => {
255                read_signals(input, &self.meta, &data_filter, callback)
256            }
257            InputVariant::IncompleteUncompressedInMem(input, _) => {
258                read_signals(input, &self.meta, &data_filter, callback)
259            }
260        }
261    }
262}
263
264pub enum FstSignalValue<'a> {
265    String(&'a [u8]),
266    Real(f64),
267}
268
269/// Quickly scans an input to see if it could be a FST file.
270pub fn is_fst_file(input: &mut (impl Read + Seek)) -> bool {
271    let is_fst = matches!(internal_check_fst_file(input), Ok(true));
272    // try to reset input
273    let _ = input.seek(SeekFrom::Start(0));
274    is_fst
275}
276
277/// Returns an error or false if not an fst. Returns Ok(true) only if we think it is an fst.
278fn internal_check_fst_file(input: &mut (impl Read + Seek)) -> Result<bool> {
279    let mut seen_header = false;
280
281    // try to iterate over all blocks
282    loop {
283        let block_tpe = match read_block_tpe(input) {
284            Err(ReaderError::Io(_)) => {
285                break;
286            }
287            Err(other) => return Err(other),
288            Ok(tpe) => tpe,
289        };
290        let section_length = read_u64(input)?;
291        match block_tpe {
292            BlockType::GZipWrapper => return Ok(true),
293            BlockType::Header => {
294                seen_header = true;
295            }
296            BlockType::Skip if section_length == 0 => {
297                break;
298            }
299            _ => {}
300        }
301        input.seek(SeekFrom::Current((section_length as i64) - 8))?;
302    }
303    Ok(seen_header)
304}
305
306fn read_hierarchy(
307    input: &mut (impl Read + Seek),
308    meta: &MetaData,
309    mut callback: impl FnMut(FstHierarchyEntry),
310) -> Result<()> {
311    input.seek(SeekFrom::Start(meta.hierarchy_offset))?;
312    let bytes = read_hierarchy_bytes(input, meta.hierarchy_compression)?;
313    let mut input = bytes.as_slice();
314    let mut handle_count = 0u32;
315    while let Some(entry) = read_hierarchy_entry(&mut input, &mut handle_count)? {
316        callback(entry);
317    }
318    Ok(())
319}
320
321fn read_signals(
322    input: &mut (impl Read + Seek),
323    meta: &MetaData,
324    filter: &DataFilter,
325    mut callback: impl FnMut(u64, FstSignalHandle, FstSignalValue),
326) -> Result<()> {
327    let mut reader = DataReader {
328        input,
329        meta,
330        filter,
331        callback: &mut callback,
332    };
333    reader.read()
334}
335
336enum UncompressGzipWrapper {
337    None,
338    // TempFile(BufReader<std::fs::File>),
339    InMemory(std::io::Cursor<Vec<u8>>),
340}
341
342/// Checks to see if the whole file is compressed in which case it is decompressed
343/// to a temp file which is returned.
344fn uncompress_gzip_wrapper(input: &mut (impl Read + Seek)) -> Result<UncompressGzipWrapper> {
345    let block_tpe = read_block_tpe(input)?;
346    if block_tpe != BlockType::GZipWrapper {
347        // no gzip wrapper
348        input.seek(SeekFrom::Start(0))?;
349        Ok(UncompressGzipWrapper::None)
350    } else {
351        // uncompress
352        let section_length = read_u64(input)?;
353        let uncompress_length = read_u64(input)? as usize;
354        if section_length == 0 {
355            return Err(ReaderError::NotFinishedCompressing());
356        }
357
358        // TODO: add back the ability to uncompress to a temporary file without adding a dependency
359        // we always decompress into memory
360        let mut target = vec![];
361        decompress_gz_in_chunks(input, uncompress_length, &mut target)?;
362        let new_input = std::io::Cursor::new(target);
363        Ok(UncompressGzipWrapper::InMemory(new_input))
364    }
365}
366
367fn decompress_gz_in_chunks(
368    input: &mut (impl Read + Seek),
369    mut remaining: usize,
370    target: &mut impl Write,
371) -> Result<()> {
372    read_gzip_header(input)?;
373    let mut buf_in = vec![0u8; 32768 / 2]; // FST_GZIO_LEN
374    let mut buf_out = vec![0u8; 32768 * 2]; // FST_GZIO_LEN
375
376    let mut state = miniz_oxide::inflate::stream::InflateState::new(miniz_oxide::DataFormat::Raw);
377    let mut buf_in_remaining = 0;
378    while remaining > 0 {
379        // load more bytes into the input buffer
380        buf_in_remaining += input.read(&mut buf_in[buf_in_remaining..])?;
381        debug_assert!(
382            buf_in_remaining > 0,
383            "ran out of input data while gzip decompressing"
384        );
385
386        // decompress them
387        let res = miniz_oxide::inflate::stream::inflate(
388            &mut state,
389            &buf_in[0..buf_in_remaining],
390            buf_out.as_mut_slice(),
391            miniz_oxide::MZFlush::None,
392        );
393
394        println!("{res:?}\nremaining={remaining}");
395
396        match res.status {
397            Ok(status) => {
398                // move bytes that were not consumed to the start of the buffer and update the length
399                buf_in.copy_within(res.bytes_consumed..buf_in_remaining, 0);
400                buf_in_remaining -= res.bytes_consumed;
401
402                // write decompressed output
403                let out_bytes = std::cmp::min(res.bytes_written, remaining);
404                remaining -= out_bytes;
405                target.write_all(&buf_out[..out_bytes])?;
406
407                match status {
408                    miniz_oxide::MZStatus::Ok => {
409                        // nothing to do
410                    }
411                    miniz_oxide::MZStatus::StreamEnd => {
412                        debug_assert_eq!(remaining, 0);
413                        return Ok(());
414                    }
415                    miniz_oxide::MZStatus::NeedDict => {
416                        todo!("hande NeedDict status");
417                    }
418                }
419            }
420            Err(err) => {
421                return Err(ReaderError::GZipBody(format!("{err:?}")));
422            }
423        }
424    }
425
426    Ok(())
427}
428
429#[derive(Debug)]
430struct MetaData {
431    header: Header,
432    signals: Vec<SignalInfo>,
433    #[allow(dead_code)]
434    blackouts: Vec<BlackoutData>,
435    data_sections: Vec<DataSectionInfo>,
436    float_endian: FloatingPointEndian,
437    hierarchy_compression: HierarchyCompression,
438    hierarchy_offset: u64,
439    time_table: Option<Vec<u64>>,
440}
441
442pub type Result<T> = std::result::Result<T, ReaderError>;
443
444struct HeaderReader<R: Read + Seek> {
445    input: R,
446    header: Option<Header>,
447    signals: Option<Vec<SignalInfo>>,
448    blackouts: Option<Vec<BlackoutData>>,
449    data_sections: Vec<DataSectionInfo>,
450    float_endian: FloatingPointEndian,
451    hierarchy: Option<(HierarchyCompression, u64)>,
452    time_table: Option<Vec<u64>>,
453}
454
455impl<R: Read + Seek> HeaderReader<R> {
456    fn new(input: R) -> Self {
457        HeaderReader {
458            input,
459            header: None,
460            signals: None,
461            blackouts: None,
462            data_sections: Vec::default(),
463            float_endian: FloatingPointEndian::Little,
464            hierarchy: None,
465            time_table: None,
466        }
467    }
468
469    fn read_data(&mut self, tpe: &BlockType) -> Result<()> {
470        let file_offset = self.input.stream_position()?;
471        // this is the data section
472        let section_length = read_u64(&mut self.input)?;
473        let start_time = read_u64(&mut self.input)?;
474        let end_time = read_u64(&mut self.input)?;
475        let mem_required_for_traversal = read_u64(&mut self.input)?;
476
477        // optional: read the time table
478        if let Some(table) = &mut self.time_table {
479            let (_, mut time_chain) =
480                read_time_table(&mut self.input, file_offset, section_length)?;
481            // in the first section, we might need to include the start time
482            let is_first_section = table.is_empty();
483            if is_first_section && time_chain[0] > start_time {
484                table.push(start_time);
485            }
486            table.append(&mut time_chain);
487            self.input.seek(SeekFrom::Start(file_offset + 4 * 8))?;
488        }
489        // go to the end of the section
490        self.skip(section_length, 4 * 8)?;
491        let kind = DataSectionKind::from_block_type(tpe).unwrap();
492        let info = DataSectionInfo {
493            file_offset,
494            start_time,
495            end_time,
496            kind,
497            mem_required_for_traversal,
498        };
499
500        // incomplete fst files may have start_time and end_time set to 0,
501        // in which case we can infer it from the data
502        if let Some(Header {
503            start_time: header_start,
504            end_time: header_end,
505            ..
506        }) = self.header.as_mut()
507        {
508            if *header_start == 0 && *header_end == 0 {
509                *header_end = end_time;
510                if self.data_sections.is_empty() {
511                    *header_start = start_time;
512                }
513            }
514        }
515
516        self.data_sections.push(info);
517        Ok(())
518    }
519
520    fn skip(&mut self, section_length: u64, already_read: i64) -> Result<u64> {
521        Ok(self
522            .input
523            .seek(SeekFrom::Current((section_length as i64) - already_read))?)
524    }
525
526    fn read_hierarchy(&mut self, compression: HierarchyCompression) -> Result<()> {
527        let file_offset = self.input.stream_position()?;
528        // this is the data section
529        let section_length = read_u64(&mut self.input)?;
530        self.skip(section_length, 8)?;
531        assert!(
532            self.hierarchy.is_none(),
533            "Only a single hierarchy block is expected!"
534        );
535        self.hierarchy = Some((compression, file_offset));
536        Ok(())
537    }
538
539    // The geometry block contains the types and lengths of variables (see [`SignalInfo`]).
540    // In case this block is missing from the FST file, it can be reconstructed from the hierarchy.
541    fn reconstruct_geometry(&mut self, hierarchy: &mut (impl BufRead + Seek)) -> Result<()> {
542        hierarchy.seek(SeekFrom::Start(0))?;
543        let bytes = read_hierarchy_bytes(hierarchy, HierarchyCompression::Uncompressed)?;
544        let mut input = bytes.as_slice();
545        let mut handle_count = 0u32;
546        let mut signals: Vec<SignalInfo> = Vec::new();
547        while let Some(entry) = read_hierarchy_entry(&mut input, &mut handle_count)? {
548            match entry {
549                FstHierarchyEntry::Var {
550                    length, is_alias, ..
551                } if !is_alias => {
552                    signals.push(SignalInfo::from_file_format(length));
553                }
554                _ => {}
555            }
556        }
557        self.signals = Some(signals);
558        Ok(())
559    }
560
561    fn read(&mut self, read_time_table: bool) -> Result<()> {
562        if read_time_table {
563            self.time_table = Some(Vec::new());
564        }
565        loop {
566            let block_tpe = match read_block_tpe(&mut self.input) {
567                Err(ReaderError::Io(_)) => {
568                    break;
569                }
570                Err(other) => return Err(other),
571                Ok(tpe) => tpe,
572            };
573
574            dbg!(&block_tpe);
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}