use crate::{ IdCode, InvalidData };
use std::io::{ self, Read };
use linereader::LineReader;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum FastFlowToken<'i> {
Timestamp(u64),
Value(FFValueChange<'i>)
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FFValueChange<'i> {
pub id: IdCode,
pub bits: &'i [u8]
}
pub struct FastFlow<R: Read> {
line_reader: LineReader<R>,
bytes_read: usize
}
impl<R: Read> FastFlow<R> {
pub fn new(source: R, buf_size: usize) -> FastFlow<R> {
FastFlow {
line_reader: LineReader::with_capacity(buf_size, source),
bytes_read: 0
}
}
pub fn bytes_read(&self) -> usize {
self.bytes_read
}
#[inline]
pub fn next_line<'i>(&'i mut self) -> io::Result<Option<&'i [u8]>> {
match self.line_reader.next_line() {
None => Ok(None),
Some(Err(e)) => Err(e),
Some(Ok(line)) => {
self.bytes_read += line.len();
Ok(Some(&line[..line.len() - 1]))
}
}
}
#[inline]
pub fn skip_line(&mut self) -> io::Result<()> {
let _ = self.next_line()?;
Ok(())
}
pub fn next_token<'i>(&'i mut self) -> io::Result<Option<FastFlowToken<'i>>> {
while let Some(line) = unsafe {
&mut *(self as *mut FastFlow<R>) }.next_line()? {
let line: &'i [u8] = line;
if line.len() == 0 { continue }
return Ok(Some(match line[0] {
b'#' => FastFlowToken::Timestamp(
atoi_radix10::parse(&line[1..]).map_err(
|_| io::Error::from(InvalidData("parse timestamp failed")))?
),
b'0' | b'1' | b'x' | b'z' => FastFlowToken::Value(
FFValueChange { id: IdCode::new(&line[1..])?,
bits: &line[0..1] }
),
b'b' => match line.iter().rposition(|c| *c == b' ') {
Some(i) => FastFlowToken::Value(FFValueChange {
id: IdCode::new(&line[i + 1..])?,
bits: &line[1..i]
}),
None => return Err(
InvalidData("vec value w/o space").into())
},
b'$' | b'\t' | b' ' => continue,
_ => {
return Err(InvalidData(
"unexpected line in vcd, which is unrecognized \
by FastFlow. please try normal parser \
instead.").into())
}
}))
}
Ok(None) }
pub fn first_timestamp(&mut self) -> io::Result<Option<u64>> {
self.next_line()?;
while let Some(line) = self.next_line()? {
if line.len() == 0 || line[0] != b'#' { continue }
return Ok(Some(atoi_radix10::parse::<u64>(&line[1..]).map_err(
|_| io::Error::from(InvalidData("parse first timestamp failed")))?))
}
Ok(None) }
pub fn into_inner(self) -> R {
self.line_reader.into_inner()
}
}
#[test]
fn test_fastflow() {
let buf = br###"
$enddefinitions $end
#0
$dumpvars
0$Q
0"#o
#250
1!
b00000000000000000000000000000000 #0
bxxxx $o
"###;
let mut f = FastFlow::new(&buf[..], 64);
assert_eq!(f.first_timestamp().unwrap(), Some(0));
assert_eq!(f.next_token().unwrap(),
Some(FastFlowToken::Value(FFValueChange {
id: IdCode::new(&b"$Q"[..]).unwrap(), bits: b"0"
})));
assert_eq!(f.next_token().unwrap(),
Some(FastFlowToken::Value(FFValueChange {
id: IdCode::new(&b"\"#o"[..]).unwrap(), bits: b"0"
})));
assert_eq!(f.next_token().unwrap(),
Some(FastFlowToken::Timestamp(250)));
assert_eq!(f.next_token().unwrap(),
Some(FastFlowToken::Value(FFValueChange {
id: IdCode::new(&b"!"[..]).unwrap(), bits: b"1"
})));
assert_eq!(f.next_token().unwrap(),
Some(FastFlowToken::Value(FFValueChange {
id: IdCode::new(&b"#0"[..]).unwrap(), bits: b"00000000000000000000000000000000"
})));
assert_eq!(f.next_token().unwrap(),
Some(FastFlowToken::Value(FFValueChange {
id: IdCode::new(&b"$o"[..]).unwrap(), bits: b"xxxx"
})));
assert_eq!(f.next_token().unwrap(), None);
assert_eq!(f.bytes_read(), buf.len());
}