Skip to content

Commit

Permalink
change VCD index parsing code
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Oct 9, 2024
1 parent 250bc39 commit 4540d7f
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = ["wellen"]
default-members = ["wellen"]

[workspace.package]
version = "0.11.4"
version = "0.11.5"
edition = "2021"
# we require the `div_ceil` method on integers
rust-version = "1.73.0"
Expand Down
122 changes: 73 additions & 49 deletions wellen/src/vcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,45 +383,89 @@ fn read_hierarchy_inner(
Ok(((end - start) as usize, hierarchy, lookup))
}

/// Tries to extract an index expression from the end of `value`. Ignores spaces.
/// Returns the index and the remaining bytes of `value` before the parsed index.
fn extract_suffix_index(value: &[u8]) -> (&[u8], Option<VarIndex>) {
use ExtractSuffixIndexState as St;
let mut state = St::SearchingForClosingBracket;

for (ii, cc) in value.iter().enumerate().rev() {
// skip whitespace
if *cc == b' ' {
continue;
}

state = match state {
St::SearchingForClosingBracket => {
if *cc == b']' {
St::ParsingLsb(ii, 0, 1)
} else {
// our value does not end in `]`
return (&value[0..ii + 1], None);
}
}
St::ParsingLsb(end, num, factor) => {
if cc.is_ascii_digit() {
let digit = (*cc - b'0') as i64;
St::ParsingLsb(end, num + digit * factor, factor * 10)
} else if *cc == b'-' {
St::ParsingLsb(end, -num, factor)
} else if *cc == b':' {
St::ParsingMsb(end, num, 0, 1)
} else if *cc == b'[' {
St::LookingForName(VarIndex::new(num, num))
} else {
// not a valid number, give up
return (&value[0..end + 1], None);
}
}
St::ParsingMsb(end, lsb, num, factor) => {
if cc.is_ascii_digit() {
let digit = (*cc - b'0') as i64;
St::ParsingMsb(end, lsb, num + digit * factor, factor * 10)
} else if *cc == b'-' {
St::ParsingMsb(end, lsb, -num, factor)
} else if *cc == b'[' {
St::LookingForName(VarIndex::new(num, lsb))
} else {
// not a valid number, give up
return (&value[0..end + 1], None);
}
}
St::LookingForName(index) => {
// any non-space character means that we found the name
return (&value[0..ii + 1], Some(index));
}
};
}

// wasn't able to parse any index
(value, None)
}

#[derive(Debug, Copy, Clone)]
enum ExtractSuffixIndexState {
SearchingForClosingBracket,
ParsingLsb(usize, i64, i64),
ParsingMsb(usize, i64, i64, i64),
LookingForName(VarIndex),
}

/// Splits a full name into:
/// 1. the variable name
/// 2. the bit index
/// 3. any extra scopes generated by a multidimensional arrays
pub fn parse_name(name: &[u8]) -> Result<(String, Option<VarIndex>, Vec<String>)> {
let last = match name.last() {
// special case for empty name
None => return Ok(("".to_string(), None, vec![])),
Some(l) => *l,
};
debug_assert!(
last != b' ',
"we assume that the final character is not a space!"
);
debug_assert!(
name[0] != b' ',
"we assume that the first character is not a space!"
);
if name.is_empty() {
return Ok(("".to_string(), None, vec![]));
}
debug_assert!(
name[0] != b'[',
"we assume that the first character is not `[`!"
);

// find the bit index from the back
let (mut name, index) = if last == b']' {
let index_start = match find_last(name, b'[') {
Some(s) => s,
None => {
return Err(VcdParseError::VcdVarNameParsing(
String::from_utf8_lossy(name).to_string(),
))
}
};
let inner_index = &name[index_start + 1..(name.len() - 1)];
let remaining_name = trim_right(&name[..index_start]);
(remaining_name, parse_inner_index(inner_index))
} else {
(name, None)
};
let (mut name, index) = extract_suffix_index(name);

// see if there are any other indices from multidimensional arrays
let mut indices = vec![];
Expand Down Expand Up @@ -469,27 +513,6 @@ fn find_last(haystack: &[u8], needle: u8) -> Option<usize> {
Some(haystack.len() - from_back - 1)
}

#[inline]
fn parse_inner_index(index: &[u8]) -> Option<VarIndex> {
let sep = index.iter().position(|b| *b == b':');
match sep {
None => {
let inner_str = std::str::from_utf8(index).unwrap();
let bit = inner_str.parse::<i64>().unwrap();
Some(VarIndex::new(bit, bit))
}
Some(pos) => {
let msb_bytes = &index[0..pos];
let msb_str = std::str::from_utf8(msb_bytes).unwrap();
let msb = msb_str.parse::<i64>().unwrap();
let lsb_bytes = &index[(pos + 1)..index.len()];
let lsb_str = std::str::from_utf8(lsb_bytes).unwrap();
let lsb = lsb_str.parse::<i64>().unwrap();
Some(VarIndex::new(msb, lsb))
}
}
}

fn convert_timescale_unit(name: &[u8]) -> TimescaleUnit {
match name {
b"fs" => TimescaleUnit::FemtoSeconds,
Expand Down Expand Up @@ -1397,5 +1420,6 @@ x%i"
Some((0, 0)),
&["test", "[0]", "[3]"],
);
do_test_parse_name("test [10:0]", "test", Some((10, 0)), &[]);
}
}

0 comments on commit 4540d7f

Please sign in to comment.