Skip to main content

vcd_ng/
fastflow.rs

1//! Fast Flow: an ultrafast way to parse VCD signal parts.
2//!
3//! This module provides [`FastFlow`], a Reader-like iterator interface
4//! which is very similar to [`vcd_ng::Parser`], but faster.
5//!
6//! The magic behind this module is the reuse of buffer spaces to
7//! represent long signals, and the elimination of temporary memory
8//! allocations.
9//!
10//! Some compatibility has been sacrificed in return for speed. Notably:
11//! 0. We only support timestamps and value changes. All other operations
12//!    are intentionally ignored and do not produce tokens.
13//! 1. We assert for newline characters after each timestamp `#xxx`
14//!    as well as bit change lines.
15//!    Previously, both newline and other whitespaces could be used.
16//!    This helps us to identify timestamp from the middle of an input.
17//! 2. We do not support lines that are itself too long (which means
18//!    EXTREMELY long, longer than the whole buffer size).
19//!    This should not be a problem with a large buffer like 1MB,
20//!    unless one user becomes insane and defines a million-sized
21//!    bit vector.
22
23use crate::{ IdCode, InvalidData };
24use std::io::{ self, Read };
25use linereader::LineReader;
26
27/// An enum of tokens that fast flow supports.
28#[derive(Debug, PartialEq, Eq, Clone)]
29pub enum FastFlowToken<'i> {
30    Timestamp(u64),
31    Value(FFValueChange<'i>)
32}
33
34/// A value change token.
35///
36/// It uses a reference to the internal buffer of the [`FastFlow`]
37/// parser object to speed up the retrieval of signals.
38#[derive(Debug, PartialEq, Eq, Clone)]
39pub struct FFValueChange<'i> {
40    /// The symbolic index of the signal to change
41    pub id: IdCode,
42    /// A byte slice of signal changes. Each byte can be
43    /// `0`, `1`, `x`, or `z`.
44    pub bits: &'i [u8]
45}
46
47/// Fast token stream of timestamp and value changes.
48/// See the module-level documentation for details.
49pub struct FastFlow<R: Read> {
50    /// The line reader inner object
51    line_reader: LineReader<R>,
52    /// The bytes already read since the start.
53    bytes_read: usize
54}
55
56impl<R: Read> FastFlow<R> {
57    /// Create a new FastFlow from a Read object and a buffer size.
58    ///
59    /// It is recommended that we do NOT use BufReaders here, because
60    /// we have built-in buffers in FastFlow. Doubling the buffer
61    /// introduces unnecessary overhead.
62    pub fn new(source: R, buf_size: usize) -> FastFlow<R> {
63        FastFlow {
64            line_reader: LineReader::with_capacity(buf_size, source),
65            bytes_read: 0
66        }
67    }
68
69    /// Get number of bytes that have been read.
70    pub fn bytes_read(&self) -> usize {
71        self.bytes_read
72    }
73
74    /// Read a line.
75    /// This records the number of bytes read.
76    #[inline]
77    pub fn next_line<'i>(&'i mut self) -> io::Result<Option<&'i [u8]>> {
78        match self.line_reader.next_line() {
79            None => Ok(None),
80            Some(Err(e)) => Err(e),
81            Some(Ok(line)) => {
82                self.bytes_read += line.len();
83                Ok(Some(&line[..line.len() - 1]))
84            }
85        }
86    }
87
88    /// Skip a line.
89    #[inline]
90    pub fn skip_line(&mut self) -> io::Result<()> {
91        let _ = self.next_line()?;
92        Ok(())
93    }
94
95    /// Read a token.
96    pub fn next_token<'i>(&'i mut self) -> io::Result<Option<FastFlowToken<'i>>> {
97        while let Some(line) = unsafe {
98            // The following unsafe transform is NEEDED.
99            // If we use &'i mut self here, it will leave a footprint
100            // that marks lifetime 'i as *mutated* (exclusively used)
101            // inside the loop.
102            // As a result, we cannot return the value to outside.
103            // This is known as a limitation of current Rust borrow checker.
104            // See https://github.com/rust-lang/rust/issues/68117.
105            &mut *(self as *mut FastFlow<R>)  // kicks off the lifetime
106            // We are safe as long as the reference next_line() returns
107            // can outlive 'i **in reality**.
108        }.next_line()? {
109            let line: &'i [u8] = line;
110            // ok, we are safe now with the assumption above.
111            
112            if line.len() == 0 { continue }
113            return Ok(Some(match line[0] {
114                b'#' => FastFlowToken::Timestamp(
115                    atoi_radix10::parse(&line[1..]).map_err(
116                        |_| io::Error::from(InvalidData("parse timestamp failed")))?
117                ),
118                b'0' | b'1' | b'x' | b'z' => FastFlowToken::Value(
119                    FFValueChange { id: IdCode::new(&line[1..])?,
120                                    bits: &line[0..1] }
121                ),
122                b'b' => match line.iter().rposition(|c| *c == b' ') {
123                    Some(i) => FastFlowToken::Value(FFValueChange {
124                        id: IdCode::new(&line[i + 1..])?,
125                        bits: &line[1..i]
126                    }),
127                    None => return Err(
128                        InvalidData("vec value w/o space").into())
129                },
130                b'$' | b'\t' | b' ' => continue,
131                _ => {
132                    return Err(InvalidData(
133                        "unexpected line in vcd, which is unrecognized \
134                         by FastFlow. please try normal parser \
135                         instead.").into())
136                }
137            }))
138        }
139        Ok(None) // EOF
140    }
141
142    /// Read the first complete timestamp token, skipping all other tokens
143    /// around it.
144    ///
145    /// This function looks for a "\n#" pattern. Thus, it will skip at
146    /// least one line in the input byte stream.
147    pub fn first_timestamp(&mut self) -> io::Result<Option<u64>> {
148        self.next_line()?;
149        while let Some(line) = self.next_line()? {
150            if line.len() == 0 || line[0] != b'#' { continue }
151            return Ok(Some(atoi_radix10::parse::<u64>(&line[1..]).map_err(
152                |_| io::Error::from(InvalidData("parse first timestamp failed")))?))
153        }
154        Ok(None) // EOF before even a first timestamp.
155    }
156
157    /// Unwraps this `FastFlow` and returns the underlying reader.
158    /// All unread buffered lines will be discarded.
159    pub fn into_inner(self) -> R {
160        self.line_reader.into_inner()
161    }
162}
163
164#[test]
165fn test_fastflow() {
166    let buf = br###"
167$enddefinitions $end
168
169#0
170$dumpvars
1710$Q
1720"#o
173#250
1741!
175b00000000000000000000000000000000 #0
176bxxxx $o
177"###;
178    let mut f = FastFlow::new(&buf[..], 64);
179    assert_eq!(f.first_timestamp().unwrap(), Some(0));
180    assert_eq!(f.next_token().unwrap(),
181               Some(FastFlowToken::Value(FFValueChange {
182                   id: IdCode::new(&b"$Q"[..]).unwrap(), bits: b"0"
183               })));
184    assert_eq!(f.next_token().unwrap(),
185               Some(FastFlowToken::Value(FFValueChange {
186                   id: IdCode::new(&b"\"#o"[..]).unwrap(), bits: b"0"
187               })));
188    assert_eq!(f.next_token().unwrap(),
189               Some(FastFlowToken::Timestamp(250)));
190    assert_eq!(f.next_token().unwrap(),
191               Some(FastFlowToken::Value(FFValueChange {
192                   id: IdCode::new(&b"!"[..]).unwrap(), bits: b"1"
193               })));
194    assert_eq!(f.next_token().unwrap(),
195               Some(FastFlowToken::Value(FFValueChange {
196                   id: IdCode::new(&b"#0"[..]).unwrap(), bits: b"00000000000000000000000000000000"
197               })));
198    assert_eq!(f.next_token().unwrap(),
199               Some(FastFlowToken::Value(FFValueChange {
200                   id: IdCode::new(&b"$o"[..]).unwrap(), bits: b"xxxx"
201               })));
202    assert_eq!(f.next_token().unwrap(), None);
203    assert_eq!(f.bytes_read(), buf.len());
204}