fst_writer/
writer.rs

1// Copyright 2024 Cornell University
2// released under BSD 3-Clause License
3// author: Kevin Laeufer <laeufer@cornell.edu>
4
5use crate::buffer::SignalBuffer;
6use crate::io::{
7    HeaderFinishInfo, update_header, write_geometry, write_header_meta_data, write_hierarchy_bytes,
8    write_hierarchy_scope, write_hierarchy_up_scope, write_hierarchy_var,
9};
10use crate::{
11    FstInfo, FstScopeType, FstSignalId, FstSignalType, FstVarDirection, FstVarType, Result,
12};
13
14pub fn open_fst<P: AsRef<std::path::Path>>(
15    path: P,
16    info: &FstInfo,
17) -> Result<FstHeaderWriter<std::io::BufWriter<std::fs::File>>> {
18    FstHeaderWriter::open(path, info)
19}
20
21pub struct FstHeaderWriter<W: std::io::Write + std::io::Seek> {
22    out: W,
23    /// collect hierarchy section before compressing it
24    hierarchy_buf: std::io::Cursor<Vec<u8>>,
25    signals: Vec<FstSignalType>,
26    scope_depth: u64,
27    var_count: u64,
28    scope_count: u64,
29}
30
31impl FstHeaderWriter<std::io::BufWriter<std::fs::File>> {
32    fn open<P: AsRef<std::path::Path>>(path: P, info: &FstInfo) -> Result<Self> {
33        let f = std::fs::File::create(path)?;
34        let mut out = std::io::BufWriter::new(f);
35        write_header_meta_data(&mut out, info)?;
36        Ok(Self {
37            out,
38            hierarchy_buf: std::io::Cursor::new(Vec::new()),
39            signals: vec![],
40            scope_depth: 0,
41            var_count: 0,
42            scope_count: 0,
43        })
44    }
45}
46
47impl<W: std::io::Write + std::io::Seek> FstHeaderWriter<W> {
48    pub fn scope(
49        &mut self,
50        name: impl AsRef<str>,
51        component: impl AsRef<str>,
52        tpe: FstScopeType,
53    ) -> Result<()> {
54        self.scope_depth += 1;
55        self.scope_count += 1;
56        write_hierarchy_scope(&mut self.hierarchy_buf, name, component, tpe)
57    }
58    pub fn up_scope(&mut self) -> Result<()> {
59        debug_assert!(self.scope_depth > 0, "no scope to pop");
60        self.scope_depth -= 1;
61        write_hierarchy_up_scope(&mut self.hierarchy_buf)
62    }
63
64    pub fn var(
65        &mut self,
66        name: impl AsRef<str>,
67        signal_tpe: FstSignalType,
68        tpe: FstVarType,
69        dir: FstVarDirection,
70        alias: Option<FstSignalId>,
71    ) -> Result<FstSignalId> {
72        self.var_count += 1;
73        write_hierarchy_var(&mut self.hierarchy_buf, tpe, dir, name, signal_tpe, alias)?;
74        if let Some(alias) = alias {
75            debug_assert!(alias.to_index() <= self.signals.len() as u32);
76            Ok(alias)
77        } else {
78            self.signals.push(signal_tpe);
79            let id = FstSignalId::from_index(self.signals.len() as u32);
80            Ok(id)
81        }
82    }
83
84    pub fn finish(mut self) -> Result<FstBodyWriter<W>> {
85        debug_assert_eq!(
86            self.scope_depth, 0,
87            "missing calls to up-scope to close all scopes!"
88        );
89        write_hierarchy_bytes(&mut self.out, &self.hierarchy_buf.into_inner())?;
90        write_geometry(&mut self.out, &self.signals)?;
91        let buffer = SignalBuffer::new(&self.signals)?;
92        let finish_info = HeaderFinishInfo {
93            end_time: 0, // currently unknown
94            scope_count: self.scope_count,
95            var_count: self.var_count,
96            num_signals: self.signals.len() as u64,
97            num_value_change_sections: 0, // currently unknown
98        };
99        let next = FstBodyWriter {
100            out: self.out,
101            buffer,
102            finish_info,
103        };
104        Ok(next)
105    }
106}
107
108pub struct FstBodyWriter<W: std::io::Write + std::io::Seek> {
109    out: W,
110    buffer: SignalBuffer,
111    finish_info: HeaderFinishInfo,
112}
113
114impl<W: std::io::Write + std::io::Seek> FstBodyWriter<W> {
115    pub fn time_change(&mut self, time: u64) -> Result<()> {
116        self.buffer.time_change(time)
117    }
118
119    pub fn signal_change(&mut self, signal_id: FstSignalId, value: &[u8]) -> Result<()> {
120        self.buffer.signal_change(signal_id, value)
121    }
122
123    /// flushes all value change data to disk
124    pub fn flush(&mut self) -> Result<()> {
125        self.buffer.flush(&mut self.out)?;
126        self.finish_info.num_value_change_sections += 1;
127        Ok(())
128    }
129
130    /// Returns the estimated size of all data structures that grow over time.
131    pub fn size(&self) -> usize {
132        self.buffer.size()
133    }
134
135    pub fn finish(mut self) -> Result<()> {
136        // write value change section
137        let end_time = self.buffer.flush(&mut self.out)?;
138
139        // update info
140        self.finish_info.num_value_change_sections += 1;
141        self.finish_info.end_time = end_time;
142        update_header(&mut self.out, &self.finish_info)?;
143
144        Ok(())
145    }
146}