wellen/
viewers.rs

1// Copyright 2023-2024 The Regents of the University of California
2// Copyright 2024-2025 Cornell University
3// released under BSD 3-Clause License
4// author: Kevin Laeufer <laeufer@cornell.edu>
5//
6// Interface for waveform viewers
7
8use crate::{FileFormat, Hierarchy, LoadOptions, Result, SignalSource, TimeTable, WellenError};
9use std::io::{BufRead, Seek};
10
11impl From<crate::ghw::GhwParseError> for WellenError {
12    fn from(value: crate::ghw::GhwParseError) -> Self {
13        WellenError::FailedToLoad(FileFormat::Ghw, value.to_string())
14    }
15}
16
17impl From<crate::vcd::VcdParseError> for WellenError {
18    fn from(value: crate::vcd::VcdParseError) -> Self {
19        WellenError::FailedToLoad(FileFormat::Vcd, value.to_string())
20    }
21}
22
23impl From<fst_reader::ReaderError> for WellenError {
24    fn from(value: fst_reader::ReaderError) -> Self {
25        WellenError::FailedToLoad(FileFormat::Fst, value.to_string())
26    }
27}
28
29pub struct HeaderResult<R: BufRead + Seek> {
30    pub hierarchy: Hierarchy,
31    pub file_format: FileFormat,
32    /// Body length in bytes.
33    pub body_len: u64,
34    pub body: ReadBodyContinuation<R>,
35}
36
37pub fn read_header_from_file<P: AsRef<std::path::Path>>(
38    filename: P,
39    options: &LoadOptions,
40) -> Result<HeaderResult<std::io::BufReader<std::fs::File>>> {
41    let file_format = open_and_detect_file_format(filename.as_ref());
42    match file_format {
43        FileFormat::Unknown => Err(WellenError::UnknownFileFormat),
44        FileFormat::Vcd => {
45            let (hierarchy, body, body_len) = crate::vcd::read_header_from_file(filename, options)?;
46            let body = ReadBodyContinuation(ReadBodyData::Vcd(Box::new(body)));
47            Ok(HeaderResult {
48                hierarchy,
49                file_format,
50                body_len,
51                body,
52            })
53        }
54        FileFormat::Ghw => {
55            let input = std::io::BufReader::new(std::fs::File::open(filename)?);
56            let (hierarchy, body, body_len) = crate::ghw::read_header(input, options)?;
57            let body = ReadBodyContinuation(ReadBodyData::Ghw(Box::new(body)));
58            Ok(HeaderResult {
59                hierarchy,
60                file_format,
61                body_len,
62                body,
63            })
64        }
65        FileFormat::Fst => {
66            let (hierarchy, body) = crate::fst::read_header_from_file(filename, options)?;
67            let body = ReadBodyContinuation(ReadBodyData::Fst(Box::new(body)));
68            Ok(HeaderResult {
69                hierarchy,
70                file_format,
71                body_len: 0, // fst never reads the full body (unless all signals are displayed)
72                body,
73            })
74        }
75    }
76}
77
78pub fn read_header<R: BufRead + Seek>(
79    mut input: R,
80    options: &LoadOptions,
81) -> Result<HeaderResult<R>> {
82    let file_format = detect_file_format(&mut input);
83    match file_format {
84        FileFormat::Unknown => Err(WellenError::UnknownFileFormat),
85        FileFormat::Vcd => {
86            let (hierarchy, body, body_len) = crate::vcd::read_header(input, options)?;
87            let body = ReadBodyContinuation(ReadBodyData::Vcd(Box::new(body)));
88            Ok(HeaderResult {
89                hierarchy,
90                file_format,
91                body_len,
92                body,
93            })
94        }
95        FileFormat::Ghw => {
96            let (hierarchy, body, body_len) = crate::ghw::read_header(input, options)?;
97            let body = ReadBodyContinuation(ReadBodyData::Ghw(Box::new(body)));
98            Ok(HeaderResult {
99                hierarchy,
100                file_format,
101                body_len,
102                body,
103            })
104        }
105        FileFormat::Fst => {
106            let (hierarchy, body) = crate::fst::read_header(input, options)?;
107            let body = ReadBodyContinuation(ReadBodyData::Fst(Box::new(body)));
108            Ok(HeaderResult {
109                hierarchy,
110                file_format,
111                body_len: 0, // fst never reads the full body (unless all signals are displayed)
112                body,
113            })
114        }
115    }
116}
117
118pub struct ReadBodyContinuation<R: BufRead + Seek>(ReadBodyData<R>);
119
120pub(crate) enum ReadBodyData<R: BufRead + Seek> {
121    Vcd(Box<crate::vcd::ReadBodyContinuation<R>>),
122    Fst(Box<crate::fst::ReadBodyContinuation<R>>),
123    Ghw(Box<crate::ghw::ReadBodyContinuation<R>>),
124}
125
126pub struct BodyResult {
127    pub source: SignalSource,
128    pub time_table: TimeTable,
129}
130
131pub type ProgressCount = std::sync::Arc<std::sync::atomic::AtomicU64>;
132
133pub fn read_body<R: BufRead + Seek + Sync + Send + 'static>(
134    body: ReadBodyContinuation<R>,
135    hierarchy: &Hierarchy,
136    progress: Option<ProgressCount>,
137) -> Result<BodyResult> {
138    match body.0 {
139        ReadBodyData::Vcd(data) => {
140            let (source, time_table) = crate::vcd::read_body(*data, hierarchy, progress)?;
141            Ok(BodyResult { source, time_table })
142        }
143        ReadBodyData::Fst(data) => {
144            // fst does not support a progress count since it is not actually reading the body
145            let (source, time_table) = crate::fst::read_body(*data)?;
146            Ok(BodyResult { source, time_table })
147        }
148        ReadBodyData::Ghw(data) => {
149            let (source, time_table) = crate::ghw::read_body(*data, hierarchy, progress)?;
150            Ok(BodyResult { source, time_table })
151        }
152    }
153}
154
155/// Tries to guess the format of the file.
156pub fn open_and_detect_file_format<P: AsRef<std::path::Path>>(filename: P) -> FileFormat {
157    let input_file = std::fs::File::open(filename).expect("failed to open input file!");
158    let mut reader = std::io::BufReader::new(input_file);
159    detect_file_format(&mut reader)
160}
161
162/// Tries to guess the file format used by the input.
163pub fn detect_file_format(input: &mut (impl BufRead + Seek)) -> FileFormat {
164    if crate::vcd::is_vcd(input) {
165        FileFormat::Vcd
166    } else if fst_reader::is_fst_file(input) {
167        FileFormat::Fst
168    } else if crate::ghw::is_ghw(input) {
169        FileFormat::Ghw
170    } else {
171        FileFormat::Unknown
172    }
173}