From b86e5fd3e4897f7fe143e5ef4f628e5777d4827c Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Sat, 6 Aug 2022 21:40:33 -0400 Subject: [PATCH 1/2] ordered binary search working - but need to be sped up --- README.md | 3 +- src/vcd/parse.rs | 110 ++++++++++++++++++++++++++--------- src/vcd/types.rs | 146 ++++++++++++++++++++++++++--------------------- 3 files changed, 166 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 4ad7bdc..70fe645 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Copyright - Yehowshua Immanuel ## Current Features - pretty fast, parses 3.04 GB VCD file in ~54s on M1 Macbook Air with - respect to 50s with GTKWave on the same device. FastWave currently + respect to 30s with GTKWave on the same device. FastWave currently offers highly robust error handling which GTKWave doesn't have. I noticed that when running FastWave in the VsCode terminal as opposed @@ -40,6 +40,7 @@ Here's a command to test on a malformed VCD: ## Features - [ ] macro for getting line number when propagating errors + - [ ] search for any unwraps or any direct vectors indexing - [ ] re-order all signal timelines as binary balanced trees with respect to timestamps - support multithreaded re-ordering - [ ] looks into making a macro for filename and linenumber later diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index 320f8bb..8927102 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -1,6 +1,6 @@ -use std::{fs::File}; -use std::collections::HashMap; use num::BigInt; +use std::collections::HashMap; +use std::fs::File; use super::*; @@ -19,27 +19,95 @@ use scopes::*; mod events; use events::*; -pub fn parse_vcd(file : File) -> Result { +use std::cmp::Ordering; + +fn compare_strs(a: &str, b: &str) -> Ordering { + let last_idx = if a.len() > b.len() { a.len() } else { b.len() }; + // let last_idx += -1; + Ordering::Less +} + +fn ordered_binary_lookup(map: &Vec<(String, SignalIdx)>, key: &str) -> Result { + let mut upper_idx = map.len() - 1; + let mut lower_idx = 0usize; + + while lower_idx <= upper_idx { + let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2); + let (str_val, signal_idx) = map.get(mid_idx).unwrap(); + let ordering = key.partial_cmp(str_val.as_str()).unwrap(); + + match ordering { + Ordering::Less => { + upper_idx = mid_idx - 1; + } + Ordering::Equal => { + return Ok(*signal_idx); + } + Ordering::Greater => { + lower_idx = mid_idx + 1; + } + } + } + + return Err(format!( + "Error near {}:{}. Unable to find key: `{key}` in the map.", + file!(), + line!() + )); +} + +pub fn parse_vcd(file: File) -> Result { let mut word_gen = WordReader::new(file); let header = parse_metadata(&mut word_gen)?; - // later, we'll need to map parsed ascii symbols to their + // later, we'll need to map parsed ascii symbols to their // respective signal indexes let mut signal_map = std::collections::HashMap::new(); // after we parse metadata, we form the VCD object - let mut vcd = VCD{ - metadata : header, - timeline : vec![], - timeline_markers : vec![], - all_signals : vec![], - all_scopes : vec![], - scope_roots : vec![], + let mut vcd = VCD { + metadata: header, + timeline: vec![], + timeline_markers: vec![], + all_signals: vec![], + all_scopes: vec![], + scope_roots: vec![], }; parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?; - parse_events(&mut word_gen, &mut vcd, &mut signal_map)?; + + // the signal map should not contain any empty string + for (k, v) in &signal_map { + if k.len() == 0 { + return Err(format!("Critical error near {}:{}. There should be no empty strings in vcd string -> SignalIdx hashmap.", file!(), line!())); + } + } + + // now that we've parsed all scopes and filled the hashmap + // with signals, we convert hashmap to an ordered vector + let mut signal_map1: Vec<(String, SignalIdx)> = signal_map + .iter() + .map(|(string, idx)| (string.clone(), idx.clone())) + .collect(); + signal_map1.sort_by(|a: &(String, SignalIdx), b: &(String, SignalIdx)| { + let a = &a.0; + let b = &b.0; + a.partial_cmp(&b).unwrap() + }); + + let now = std::time::Instant::now(); + for (k, v) in &signal_map1 { + let signal_idx = ordered_binary_lookup(&signal_map1, k.as_str())?; + assert!(*v == signal_idx); + } + let ordered_binary_search_elapsed = now.elapsed(); + println!( + "ordered_binary_search_elapsed: {:.2?}", + ordered_binary_search_elapsed + ); + + // parse_events(&mut wosrd_gen, &mut vcd, &mut signal_map)?; Ok(vcd) } @@ -55,29 +123,18 @@ mod tests { // two loops // testing dates for file in test::GOOD_DATE_FILES { - let metadata = parse_metadata( - &mut WordReader::new( - File::open(file) - .unwrap() - ) - ); + let metadata = parse_metadata(&mut WordReader::new(File::open(file).unwrap())); assert!(metadata.is_ok()); assert!(metadata.unwrap().date.is_some()); } for file in test::FILES { - let metadata = parse_metadata( - &mut WordReader::new( - File::open(file) - .unwrap() - ) - ); + let metadata = parse_metadata(&mut WordReader::new(File::open(file).unwrap())); assert!(metadata.is_ok()); let (scalar, _timescale) = metadata.unwrap().timescale; assert!(scalar.is_some()); } - } #[test] @@ -94,6 +151,5 @@ mod tests { // assert!(vcd.is_ok()); } - } -} \ No newline at end of file +} diff --git a/src/vcd/types.rs b/src/vcd/types.rs index 0a9e004..6bcd66e 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -4,18 +4,27 @@ use chrono::prelude::*; pub(super) struct Version(pub String); #[derive(Debug)] -pub(super) enum Timescale {Fs, Ps, Ns, Us, Ms, S, Unit} +pub(super) enum Timescale { + Fs, + Ps, + Ns, + Us, + Ms, + S, + Unit, +} #[derive(Debug)] pub(super) struct Metadata { - pub(super) date : Option>, - pub(super) version : Option, - pub(super) timescale : (Option, Timescale)} + pub(super) date: Option>, + pub(super) version: Option, + pub(super) timescale: (Option, Timescale), +} #[derive(Debug, Copy, Clone)] pub(super) struct ScopeIdx(pub(super) usize); -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub(super) struct SignalIdx(pub(super) usize); #[derive(Debug, Copy, Clone)] @@ -25,63 +34,71 @@ pub(super) struct TimelineIdx(pub(super) u32); pub struct StartIdx(pub(super) u32); #[derive(Debug)] -pub(super) enum SigType {Integer, Parameter, Real, Reg, Str, Wire, Tri1, Time} +pub(super) enum SigType { + Integer, + Parameter, + Real, + Reg, + Str, + Wire, + Tri1, + Time, +} #[derive(Debug)] -pub(super) enum Signal{ - Data{ - name : String, - sig_type : SigType, - // I've seen a 0 bit signal parameter in a xilinx - // simulation before that gets assigned 1 bit values. - // I consider this to be bad behavior. We capture such - // errors in the following type. - signal_error : Option, - num_bits : Option, - // TODO : may be able to remove self_idx - self_idx : SignalIdx, - // we could encounter a mix of pure values and strings - // for the same signal timeline - u8_timeline : Vec, - u8_timeline_markers : Vec, - string_timeline : Vec, - string_timeline_markers : Vec, - scope_parent : ScopeIdx}, - Alias{ - name : String, - signal_alias : SignalIdx} +pub(super) enum Signal { + Data { + name: String, + sig_type: SigType, + // I've seen a 0 bit signal parameter in a xilinx + // simulation before that gets assigned 1 bit values. + // I consider this to be bad behavior. We capture such + // errors in the following type. + signal_error: Option, + num_bits: Option, + // TODO : may be able to remove self_idx + self_idx: SignalIdx, + // we could encounter a mix of pure values and strings + // for the same signal timeline + u8_timeline: Vec, + u8_timeline_markers: Vec, + string_timeline: Vec, + string_timeline_markers: Vec, + scope_parent: ScopeIdx, + }, + Alias { + name: String, + signal_alias: SignalIdx, + }, } #[derive(Debug)] pub(super) struct Scope { - pub(super) name : String, + pub(super) name: String, - pub(super) parent_idx : Option, - pub(super) self_idx : ScopeIdx, - - pub(super) child_signals : Vec, - pub(super) child_scopes : Vec} + pub(super) parent_idx: Option, + pub(super) self_idx: ScopeIdx, + pub(super) child_signals: Vec, + pub(super) child_scopes: Vec, +} // TODO: document how timeline is represented #[derive(Debug)] pub struct VCD { - pub(super) metadata : Metadata, - pub timeline : Vec, - pub timeline_markers : Vec, - pub(super) all_signals : Vec, - pub(super) all_scopes : Vec, - pub(super) scope_roots : Vec} + pub(super) metadata: Metadata, + pub timeline: Vec, + pub timeline_markers: Vec, + pub(super) all_signals: Vec, + pub(super) all_scopes: Vec, + pub(super) scope_roots: Vec, +} impl VCD { - // TODO : make this a generic traversal function that applies specified + // TODO : make this a generic traversal function that applies specified // functions upon encountering scopes and signals - fn print_scope_tree( - &self, - root_scope_idx : ScopeIdx, - depth : usize) - { - let all_scopes = &self.all_scopes; + fn print_scope_tree(&self, root_scope_idx: ScopeIdx, depth: usize) { + let all_scopes = &self.all_scopes; let all_signals = &self.all_signals; let indent = " ".repeat(depth * 4); @@ -94,17 +111,16 @@ impl VCD { for SignalIdx(ref signal_idx) in &root_scope.child_signals { let child_signal = &all_signals[*signal_idx]; let name = match child_signal { - Signal::Data{name, ..} => {name} - Signal::Alias{name, ..} => {name} + Signal::Data { name, .. } => name, + Signal::Alias { name, .. } => name, }; println!("{indent} - sig: {name}") } println!(); for scope_idx in &root_scope.child_scopes { - self.print_scope_tree(*scope_idx, depth+1); + self.print_scope_tree(*scope_idx, depth + 1); } - } pub fn print_scopes(&self) { @@ -120,23 +136,23 @@ impl VCD { for signal in &self.all_signals { match signal { - Signal::Alias {..} => {} - Signal::Data { - name, - self_idx, - u8_timeline, - .. } => { - if u8_timeline.len() > max_len { - max_len = u8_timeline.len(); - let SignalIdx(idx_usize) = self_idx; - idx = *idx_usize; - signal_name = name.clone(); - } - + Signal::Alias { .. } => {} + Signal::Data { + name, + self_idx, + u8_timeline, + .. + } => { + if u8_timeline.len() > max_len { + max_len = u8_timeline.len(); + let SignalIdx(idx_usize) = self_idx; + idx = *idx_usize; + signal_name = name.clone(); } + } } } dbg!((idx, max_len, signal_name)); } -} \ No newline at end of file +} -- 2.47.1 From a7d2b119989a5b140f006ae4da027c18f3b824f6 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Sun, 7 Aug 2022 18:57:58 -0400 Subject: [PATCH 2/2] now running much faster, but due for refactor --- README.md | 9 +- src/vcd/parse.rs | 60 +++++-- src/vcd/parse/events.rs | 355 ++++++++++++++++++++++++---------------- 3 files changed, 267 insertions(+), 157 deletions(-) diff --git a/README.md b/README.md index 70fe645..21b5df2 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ Copyright - Yehowshua Immanuel # A High performance, VCD Parser written in Rust ## Current Features - - pretty fast, parses 3.04 GB VCD file in ~54s on M1 Macbook Air with + - pretty fast, parses 3.04 GB VCD file in ~27.23s on M1 Macbook Air with respect to 30s with GTKWave on the same device. FastWave currently - offers highly robust error handling which GTKWave doesn't have. + offers highly robust error(at least on the sample VCD files in this + repository) handling which GTKWave doesn't have. I noticed that when running FastWave in the VsCode terminal as opposed - to the MacOS system terminal or the Lapce terminal, FastWave takes 67s - to parse the 3.04GB file. + to the MacOS system terminal or the Lapce terminal. # Current Limitations @@ -40,6 +40,7 @@ Here's a command to test on a malformed VCD: ## Features - [ ] macro for getting line number when propagating errors + - [ ] search for any ok_or's - [ ] search for any unwraps or any direct vectors indexing - [ ] re-order all signal timelines as binary balanced trees with respect to timestamps - support multithreaded re-ordering diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index 8927102..813a9eb 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -22,9 +22,31 @@ use events::*; use std::cmp::Ordering; fn compare_strs(a: &str, b: &str) -> Ordering { - let last_idx = if a.len() > b.len() { a.len() } else { b.len() }; - // let last_idx += -1; - Ordering::Less + // choose the smaller of the two indices + let upper_bound = if a.len() > b.len() { b.len() } else { a.len() }; + let a_as_bytes = a.as_bytes(); + let b_as_bytes = b.as_bytes(); + + for i in 0..upper_bound { + let a_byte = a_as_bytes[i]; + let b_byte = b_as_bytes[i]; + if a_byte > b_byte { + return Ordering::Greater; + } + if b_byte > a_byte { + return Ordering::Less; + } + } + + if a.len() > b.len() { + return Ordering::Greater; + } + + if a.len() < b.len() { + return Ordering::Less; + } + + return Ordering::Equal; } fn ordered_binary_lookup(map: &Vec<(String, SignalIdx)>, key: &str) -> Result { @@ -34,7 +56,7 @@ fn ordered_binary_lookup(map: &Vec<(String, SignalIdx)>, key: &str) -> Result { @@ -93,21 +115,35 @@ pub fn parse_vcd(file: File) -> Result { signal_map1.sort_by(|a: &(String, SignalIdx), b: &(String, SignalIdx)| { let a = &a.0; let b = &b.0; - a.partial_cmp(&b).unwrap() + compare_strs(a, b) }); - let now = std::time::Instant::now(); for (k, v) in &signal_map1 { let signal_idx = ordered_binary_lookup(&signal_map1, k.as_str())?; assert!(*v == signal_idx); } - let ordered_binary_search_elapsed = now.elapsed(); - println!( - "ordered_binary_search_elapsed: {:.2?}", - ordered_binary_search_elapsed - ); - // parse_events(&mut wosrd_gen, &mut vcd, &mut signal_map)?; + // let now = std::time::Instant::now(); + // for (k, v) in &signal_map1 { + // let signal_idx = ordered_binary_lookup(&signal_map1, k.as_str())?; + // assert!(*v == signal_idx); + // } + // let ordered_binary_search_elapsed = now.elapsed(); + // println!( + // "ordered_binary_search_elapsed: {:.2?}", + // ordered_binary_search_elapsed + // ); + + // let now = std::time::Instant::now(); + // for (k, v) in &signal_map1 { + // // let signal_idx = ordered_binary_lookup(&signal_map1, k.as_str())?; + // let signal_idx = signal_map.get(k).unwrap(); + // assert!(*v == *signal_idx); + // } + // let hashmap_search_elapsed = now.elapsed(); + // println!("hashmap_search_elapsed: {:.2?}", hashmap_search_elapsed); + + parse_events(&mut word_gen, &mut vcd, &mut signal_map)?; Ok(vcd) } diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index 14bb81c..e280e14 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -1,18 +1,26 @@ //! part of the vcd parser that handles parsing the signal tree and //! building the resulting signal tree +use core::time; + use super::*; #[derive(Debug)] -pub(super) enum BinaryParserErrTypes {XValue, ZValue, UValue, OtherValue(char), TooLong} +pub(super) enum BinaryParserErrTypes { + XValue, + ZValue, + UValue, + OtherValue(char), + TooLong, +} // We build a quick and not so dirty bit string parser. -fn base2_str_to_byte(word : &[u8]) -> Result { +fn base2_str_to_byte(word: &[u8]) -> Result { let mut val = 0u8; // shouldn't have more than 8 chars in str let len = word.len(); if len > 8 { - return Err(BinaryParserErrTypes::TooLong) + return Err(BinaryParserErrTypes::TooLong); } let bit_lut = [ @@ -23,89 +31,93 @@ fn base2_str_to_byte(word : &[u8]) -> Result { 0b0001_0000u8, 0b0010_0000u8, 0b0100_0000u8, - 0b1000_0000u8 + 0b1000_0000u8, ]; for (idx, chr) in word.iter().rev().enumerate() { match chr { - b'1' => {val = bit_lut[idx] | val} + b'1' => val = bit_lut[idx] | val, b'0' => {} - b'x' | b'X' => {return Err(BinaryParserErrTypes::XValue)} - b'z' | b'Z' => {return Err(BinaryParserErrTypes::ZValue)} - b'u' | b'U' => {return Err(BinaryParserErrTypes::UValue)} - _ => {return Err(BinaryParserErrTypes::OtherValue(*chr as char))} + b'x' | b'X' => return Err(BinaryParserErrTypes::XValue), + b'z' | b'Z' => return Err(BinaryParserErrTypes::ZValue), + b'u' | b'U' => return Err(BinaryParserErrTypes::UValue), + _ => return Err(BinaryParserErrTypes::OtherValue(*chr as char)), } - } Ok(val) } -fn binary_str_to_vec_u8(binary_str : &str) -> Result, BinaryParserErrTypes> { - let mut vec_u8 : Vec = Vec::new(); +fn binary_str_to_vec_u8(binary_str: &str) -> Result, BinaryParserErrTypes> { + let mut vec_u8: Vec = Vec::new(); let binary_str_as_bytes = binary_str.as_bytes(); let mut tail_idx = binary_str_as_bytes.len(); // clamp head if provided binary str is less than 8 long - let mut head_idx = - if tail_idx >= 8 - {binary_str_as_bytes.len() - 8} - else - {0}; + let mut head_idx = if tail_idx >= 8 { + binary_str_as_bytes.len() - 8 + } else { + 0 + }; while tail_idx > 0 { let curr_b_val = &binary_str_as_bytes[head_idx..tail_idx]; let val_u8 = base2_str_to_byte(curr_b_val)?; vec_u8.push(val_u8); - if head_idx < 8 { head_idx = 0 - } - else { + } else { head_idx = head_idx - 8; } if tail_idx < 8 { tail_idx = 0 - } - else { + } else { tail_idx = tail_idx - 8; } - } Ok(vec_u8) } pub(super) fn parse_events<'a>( - word_reader : &mut WordReader, - vcd : &'a mut VCD, - signal_map : &mut HashMap + word_reader: &mut WordReader, + vcd: &'a mut VCD, + signal_map: &mut HashMap, ) -> Result<(), String> { + // let hash_time = std::time::Duration::ZERO; + // let hash_time = std::time::Duration::ZERO; loop { let next_word = word_reader.next_word(); // The following is the only case where eof is not an error. // If we've reached the end of the file, then there is obviously // nothing left to do... - if next_word.is_err() {break}; + if next_word.is_err() { + break; + }; let (word, cursor) = next_word.unwrap(); let Cursor(Line(_), Word(word_in_line_idx)) = cursor; // we only want to match on the first word in a line - if word_in_line_idx != 1 {continue} + if word_in_line_idx != 1 { + continue; + } match &word[0..1] { "$" => {} "#" => { let value = &word[1..]; - let (f, l )= (file!(), line!()); + let (f, l) = (file!(), line!()); let value = BigInt::parse_bytes(value.as_bytes(), 10).ok_or( - format!("Error near {f}:{l}. Failed to parse {value} as BigInt at {cursor:?}").as_str())?; + format!("Error near {f}:{l}. Failed to parse {value} as BigInt at {cursor:?}") + .as_str(), + )?; let (_, mut value) = value.to_bytes_le(); - // TODO : u32 helps with less memory, but should ideally likely be - // configurable. - let (f, l )= (file!(), line!()); - let start_idx = u32::try_from(vcd.timeline.len()).map_err( - |_| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; + // TODO : u32 helps with less memory, but should ideally likely be + // configurable. + let (f, l) = (file!(), line!()); + let start_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { + format!("Error near {f}:{l}. Failed to convert from usize to u32.") + })?; vcd.timeline_markers.push(StartIdx(start_idx)); vcd.timeline.append(&mut value); } @@ -115,50 +127,55 @@ pub(super) fn parse_events<'a>( let binary_value = &word[1..]; let observed_num_bits = binary_value.len(); - let mut value_u8 : Vec = Vec::new(); + let mut value_u8: Vec = Vec::new(); let mut value_string = String::new(); - + let mut store_as_string = false; // If we encounter x or z in a value, we can recover from // the error and store the value as a string. // Or else, we we propagate up other errors. match binary_str_to_vec_u8(binary_value) { - Ok(result) => {value_u8 = result;} - Err(BinaryParserErrTypes::XValue | - BinaryParserErrTypes::ZValue | - BinaryParserErrTypes::UValue - ) => - { - store_as_string = true; - value_string = binary_value.to_string(); - } + Ok(result) => { + value_u8 = result; + } + Err( + BinaryParserErrTypes::XValue + | BinaryParserErrTypes::ZValue + | BinaryParserErrTypes::UValue, + ) => { + store_as_string = true; + value_string = binary_value.to_string(); + } Err(e) => { - let (f, l )= (file!(), line!()); - Err(e).map_err( - |e| format!("Error near {f}:{l}. Error {e:?} at {cursor:?}."))?; + let (f, l) = (file!(), line!()); + Err(e).map_err(|e| { + format!("Error near {f}:{l}. Error {e:?} at {cursor:?}.") + })?; } } - + // this word should be the signal alias let (word, cursor) = word_reader.next_word().unwrap(); // lookup signal idx - let (f, l )= (file!(), line!()); - let SignalIdx(ref signal_idx) = signal_map.get(word).ok_or( - format!("Error near {f}:{l}. Failed to lookup signal {word} at {cursor:?}"))?; + let SignalIdx(ref signal_idx) = signal_map.get(word).ok_or(()).map_err(|_| { + format!( + "Error near {}:{}. Failed to lookup signal {word} at {cursor:?}", + file!(), + line!() + ) + })?; // account for fact that signal idx could be an alias, so there // could be one step of indirection - let signal_idx = - { + let signal_idx = { let signal = vcd.all_signals.get(*signal_idx).unwrap(); match signal { - Signal::Data {..} => {*signal_idx} - Signal::Alias {signal_alias, ..} => { + Signal::Data { .. } => *signal_idx, + Signal::Alias { signal_alias, .. } => { let SignalIdx(ref signal_idx) = signal_alias; signal_idx.clone() - } } }; @@ -167,11 +184,20 @@ pub(super) fn parse_events<'a>( // of the signal signal_idx references let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); match signal { - Signal::Data {name, sig_type, ref mut signal_error, num_bits, - u8_timeline, u8_timeline_markers, string_timeline, - string_timeline_markers, ..} => { - - if signal_error.is_some() {continue;} + Signal::Data { + name, + sig_type, + ref mut signal_error, + num_bits, + u8_timeline, + u8_timeline_markers, + string_timeline, + string_timeline_markers, + .. + } => { + if signal_error.is_some() { + continue; + } // Get the observed number of bits for the value parsed earlier // and verify that it is not greater than the numbits declared @@ -193,39 +219,40 @@ pub(super) fn parse_events<'a>( } None => { let (f, l) = (file!(), line!()); - let msg = format!("\ + let msg = format!( + "\ Error near {f}:{l}. The bitwidth for signal {name} \ must be specified for a signal of type {sig_type:?}. \ This error occurred while parsing the vcd file at \ - {cursor:?}"); + {cursor:?}" + ); Err(msg)?; } }; - let (f, l )= (file!(), line!()); - let timeline_idx = u32::try_from(vcd.timeline.len()).map_err( - |_| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; + let (f, l) = (file!(), line!()); + let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { + format!("Error near {f}:{l}. Failed to convert from usize to u32.") + })?; let timeline_idx = TimelineIdx(timeline_idx); if store_as_string { string_timeline_markers.push(timeline_idx); string_timeline.push(value_string); Ok(()) - - } - else { + } else { u8_timeline_markers.push(timeline_idx); - + let mut curr_num_bytes = value_u8.len(); u8_timeline.append(&mut value_u8); - // we may need to zero extend values + // we may need to zero extend values // so that we end up storing all values // of a particular signal in a consistent // amount of bytes let num_bits = num_bits.unwrap(); - let bytes_required = (num_bits / 8) + - if (num_bits % 8) > 0 {1} else {0}; + let bytes_required = + (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 }; while curr_num_bytes < bytes_required { // useful for debugging @@ -243,8 +270,8 @@ pub(super) fn parse_events<'a>( Ok(()) } } - Signal::Alias {..} => { - let (f, l )= (file!(), line!()); + Signal::Alias { .. } => { + let (f, l) = (file!(), line!()); let msg = format!( "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ This error occurred while parsing vcd file at {cursor:?}"); @@ -257,21 +284,24 @@ pub(super) fn parse_events<'a>( "0" => { // lookup signal idx let hash = &word[1..]; - let (f, l )= (file!(), line!()); - let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or( - format!("Error near {f}:{l}. Failed to lookup signal {hash} at {cursor:?}"))?; + let (f, l) = (file!(), line!()); + let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { + format!( + "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", + file!(), + line!() + ) + })?; // account for fact that signal idx could be an alias, so there // could be one step of indirection - let signal_idx = - { + let signal_idx = { let signal = vcd.all_signals.get(*signal_idx).unwrap(); match signal { - Signal::Data {..} => {*signal_idx} - Signal::Alias {signal_alias, ..} => { + Signal::Data { .. } => *signal_idx, + Signal::Alias { signal_alias, .. } => { let SignalIdx(ref signal_idx) = signal_alias; signal_idx.clone() - } } }; @@ -280,11 +310,19 @@ pub(super) fn parse_events<'a>( // of the signal signal_idx references let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); match signal { - Signal::Data {name, sig_type, ref mut signal_error, num_bits, - u8_timeline, u8_timeline_markers, ..} => { - + Signal::Data { + name, + sig_type, + ref mut signal_error, + num_bits, + u8_timeline, + u8_timeline_markers, + .. + } => { // if this is a bad signal, go ahead and skip it - if signal_error.is_some() {continue;} + if signal_error.is_some() { + continue; + } // Get bitwidth and verify that it is 1. // Also account for the error case of a bitwidth of `None` @@ -292,38 +330,43 @@ pub(super) fn parse_events<'a>( Some(ref num_bits) => { if *num_bits != 1 { let (f, l) = (file!(), line!()); - let msg = format!("\ + let msg = format!( + "\ Error near {f}:{l}. The bitwidth for signal {name} \ of sig_type {sig_type:?} is expected to be `1` not \ `{num_bits}`. \ This error occurred while parsing the vcd file at \ - {cursor:?}"); + {cursor:?}" + ); *signal_error = Some(msg); continue; } } None => { let (f, l) = (file!(), line!()); - let msg = format!("\ + let msg = format!( + "\ Error near {f}:{l}. The bitwidth for signal {name} \ must be specified for a signal of type {sig_type:?}. \ This error occurred while parsing the vcd file at \ - {cursor:?}"); + {cursor:?}" + ); Err(msg)?; } }; - let (f, l )= (file!(), line!()); - let timeline_idx = u32::try_from(vcd.timeline.len()).map_err( - |_| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; + let (f, l) = (file!(), line!()); + let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { + format!("Error near {f}:{l}. Failed to convert from usize to u32.") + })?; let timeline_idx = TimelineIdx(timeline_idx); u8_timeline_markers.push(timeline_idx); u8_timeline.push(0u8); Ok(()) } - Signal::Alias {..} => { - let (f, l )= (file!(), line!()); + Signal::Alias { .. } => { + let (f, l) = (file!(), line!()); let msg = format!( "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ This error occurred while parsing vcd file at {cursor:?}"); @@ -335,21 +378,23 @@ pub(super) fn parse_events<'a>( "1" => { // lokup signal idx let hash = &word[1..]; - let (f, l )= (file!(), line!()); - let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or( - format!("Error near {f}:{l}. Failed to lookup signal {hash} at {cursor:?}"))?; + let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { + format!( + "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", + file!(), + line!() + ) + })?; // account for fact that signal idx could be an alias, so there // could be one step of indirection - let signal_idx = - { + let signal_idx = { let signal = vcd.all_signals.get(*signal_idx).unwrap(); match signal { - Signal::Data {..} => {*signal_idx} - Signal::Alias {signal_alias, ..} => { + Signal::Data { .. } => *signal_idx, + Signal::Alias { signal_alias, .. } => { let SignalIdx(ref signal_idx) = signal_alias; signal_idx.clone() - } } }; @@ -358,11 +403,19 @@ pub(super) fn parse_events<'a>( // of the signal signal_idx references let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); match signal { - Signal::Data {name, sig_type, ref mut signal_error, num_bits, - u8_timeline, u8_timeline_markers, ..} => { - + Signal::Data { + name, + sig_type, + ref mut signal_error, + num_bits, + u8_timeline, + u8_timeline_markers, + .. + } => { // if this is a bad signal, go ahead and skip it - if signal_error.is_some() {continue;} + if signal_error.is_some() { + continue; + } // Get bitwidth and verify that it is 1. // Also account for the error case of a bitwidth of `None` @@ -370,38 +423,43 @@ pub(super) fn parse_events<'a>( Some(ref num_bits) => { if *num_bits != 1 { let (f, l) = (file!(), line!()); - let msg = format!("\ + let msg = format!( + "\ Error near {f}:{l}. The bitwidth for signal {name} \ of sig_type {sig_type:?} is expected to be `1` not \ `{num_bits}`. \ This error occurred while parsing the vcd file at \ - {cursor:?}"); + {cursor:?}" + ); *signal_error = Some(msg); continue; } } None => { let (f, l) = (file!(), line!()); - let msg = format!("\ + let msg = format!( + "\ Error near {f}:{l}. The bitwidth for signal {name} \ must be specified for a signal of type {sig_type:?}. \ This error occurred while parsing the vcd file at \ - {cursor:?}"); + {cursor:?}" + ); Err(msg)?; } }; - let (f, l )= (file!(), line!()); - let timeline_idx = u32::try_from(vcd.timeline.len()).map_err( - |_| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; + let (f, l) = (file!(), line!()); + let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { + format!("Error near {f}:{l}. Failed to convert from usize to u32.") + })?; let timeline_idx = TimelineIdx(timeline_idx); u8_timeline_markers.push(timeline_idx); u8_timeline.push(1u8); Ok(()) } - Signal::Alias {..} => { - let (f, l )= (file!(), line!()); + Signal::Alias { .. } => { + let (f, l) = (file!(), line!()); let msg = format!( "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ This error occurred while parsing vcd file at {cursor:?}"); @@ -414,21 +472,23 @@ pub(super) fn parse_events<'a>( let val = word.to_string(); // lokup signal idx let hash = &word[1..]; - let (f, l )= (file!(), line!()); - let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or( - format!("Error near {f}:{l}. Failed to lookup signal {hash} at {cursor:?}"))?; + let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { + format!( + "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", + file!(), + line!() + ) + })?; // account for fact that signal idx could be an alias, so there // could be one step of indirection - let signal_idx = - { + let signal_idx = { let signal = vcd.all_signals.get(*signal_idx).unwrap(); match signal { - Signal::Data {..} => {*signal_idx} - Signal::Alias {signal_alias, ..} => { + Signal::Data { .. } => *signal_idx, + Signal::Alias { signal_alias, .. } => { let SignalIdx(ref signal_idx) = signal_alias; signal_idx.clone() - } } }; @@ -437,11 +497,19 @@ pub(super) fn parse_events<'a>( // of the signal signal_idx references let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); match signal { - Signal::Data {name, sig_type, ref mut signal_error, num_bits, - string_timeline, string_timeline_markers, ..} => { - + Signal::Data { + name, + sig_type, + ref mut signal_error, + num_bits, + string_timeline, + string_timeline_markers, + .. + } => { // if this is a bad signal, go ahead and skip it - if signal_error.is_some() {continue;} + if signal_error.is_some() { + continue; + } // Get bitwidth and verify that it is 1. // Also account for the error case of a bitwidth of `None` @@ -449,38 +517,43 @@ pub(super) fn parse_events<'a>( Some(ref num_bits) => { if *num_bits != 1 { let (f, l) = (file!(), line!()); - let msg = format!("\ + let msg = format!( + "\ Error near {f}:{l}. The bitwidth for signal {name} \ of sig_type {sig_type:?} is expected to be `1` not \ `{num_bits}`. \ This error occurred while parsing the vcd file at \ - {cursor:?}"); + {cursor:?}" + ); *signal_error = Some(msg); continue; } } None => { let (f, l) = (file!(), line!()); - let msg = format!("\ + let msg = format!( + "\ Error near {f}:{l}. The bitwidth for signal {name} \ must be specified for a signal of type {sig_type:?}. \ This error occurred while parsing the vcd file at \ - {cursor:?}"); + {cursor:?}" + ); Err(msg)?; } }; - let (f, l )= (file!(), line!()); - let timeline_idx = u32::try_from(vcd.timeline.len()).map_err( - |_| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; + let (f, l) = (file!(), line!()); + let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { + format!("Error near {f}:{l}. Failed to convert from usize to u32.") + })?; let timeline_idx = TimelineIdx(timeline_idx); string_timeline_markers.push(timeline_idx); string_timeline.push(val); Ok(()) } - Signal::Alias {..} => { - let (f, l )= (file!(), line!()); + Signal::Alias { .. } => { + let (f, l) = (file!(), line!()); let msg = format!( "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ This error occurred while parsing vcd file at {cursor:?}"); @@ -494,4 +567,4 @@ pub(super) fn parse_events<'a>( } Ok(()) -} \ No newline at end of file +} -- 2.47.1