Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: refactor TypeSpecifier parsing #230

Merged
merged 2 commits into from
Aug 14, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 74 additions & 55 deletions crates/sol-type-parser/src/type_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use core::num::NonZeroUsize;
/// ```
/// # use alloy_sol_type_parser::TypeSpecifier;
/// # use core::num::NonZeroUsize;
/// let spec = TypeSpecifier::try_from("uint256[2][]")?;
/// let spec = TypeSpecifier::parse("uint256[2][]")?;
/// assert_eq!(spec.span(), "uint256[2][]");
/// assert_eq!(spec.stem.span(), "uint256");
/// // The sizes are in innermost-to-outermost order.
Expand Down Expand Up @@ -79,43 +79,62 @@ impl<'a> TypeSpecifier<'a> {
/// Parse a type specifier from a string.
pub fn parse(span: &'a str) -> Result<Self> {
let span = span.trim();
let err = || Error::invalid_type_string(span);

// i is the start of the array sizes
let (i, is_tuple) = if let Some(i) = span.rfind(')') {
// ')' is 1 byte
(i + 1, true)
} else {
(span.find('[').unwrap_or(span.len()), false)
};
// spit_at_unchecked(i)
let (l, r) = unsafe { (span.get_unchecked(..i), span.get_unchecked(i..)) };
// avoids double check in `TypeStem::parse`
let stem = if is_tuple {
l.try_into().map(TypeStem::Tuple)
} else {
l.try_into().map(TypeStem::Root)
}?;

let mut root = span;
let mut sizes = vec![];

// an iterator of string slices split by `[`
for s in root.rsplit('[') {
// we've reached a root tuple so we need to include the closing
// paren
if s.contains(')') {
let idx = span.rfind(')').unwrap();
root = &span[..=idx];
break
}
// we've reached a root type that is not a tuple or array
if !s.contains(']') {
root = s;
break
let mut chars = r.char_indices();
while let Some((i, next)) = chars.next() {
match next {
'[' => {
let mut j = 0;
let mut closed = false;
for (idx, c) in chars.by_ref() {
match c {
']' => {
closed = true;
break
}
c if c.is_ascii_digit() => j = idx,
c if c.is_whitespace() => continue,
_ => return Err(err()),
}
}
if !closed {
return Err(err())
}
let size = if j == 0 {
None
} else {
// i and j are the index of '[' and the last digit respectively,
// '[' and ASCII digits are 1 byte
let s = unsafe { r.get_unchecked(i + 1..j + 1) };
// end is trimmed in the loop above
Some(s.trim_start().parse().map_err(|_| err())?)
};
sizes.push(size);
}
c if c.is_whitespace() => continue,
_ => return Err(err()),
}

let s = s
.trim()
.strip_suffix(']')
.ok_or_else(|| Error::invalid_type_string(span))?;
let size = if s.is_empty() {
None
} else {
Some(s.parse().map_err(|_| Error::invalid_type_string(span))?)
};
sizes.push(size);
}

sizes.reverse();
Ok(Self {
span,
stem: root.try_into()?,
sizes,
})
Ok(Self { span, stem, sizes })
}

/// Returns the type stem as a string.
Expand Down Expand Up @@ -145,71 +164,71 @@ mod test {
#[test]
fn parse_test() {
assert_eq!(
super::TypeSpecifier::try_from("uint256").unwrap(),
super::TypeSpecifier {
TypeSpecifier::parse("uint256").unwrap(),
TypeSpecifier {
span: "uint256",
stem: TypeStem::try_from("uint256").unwrap(),
stem: TypeStem::parse("uint256").unwrap(),
sizes: vec![],
}
);

assert_eq!(
super::TypeSpecifier::try_from("uint256[2]").unwrap(),
super::TypeSpecifier {
TypeSpecifier::parse("uint256[2]").unwrap(),
TypeSpecifier {
span: "uint256[2]",
stem: TypeStem::try_from("uint256").unwrap(),
stem: TypeStem::parse("uint256").unwrap(),
sizes: vec![NonZeroUsize::new(2)],
}
);

assert_eq!(
super::TypeSpecifier::try_from("uint256[2][]").unwrap(),
super::TypeSpecifier {
TypeSpecifier::parse("uint256[2][]").unwrap(),
TypeSpecifier {
span: "uint256[2][]",
stem: TypeStem::try_from("uint256").unwrap(),
stem: TypeStem::parse("uint256").unwrap(),
sizes: vec![NonZeroUsize::new(2), None],
}
);

assert_eq!(
super::TypeSpecifier::try_from("(uint256,uint256)").unwrap(),
super::TypeSpecifier {
TypeSpecifier::parse("(uint256,uint256)").unwrap(),
TypeSpecifier {
span: "(uint256,uint256)",
stem: TypeStem::Tuple(TupleSpecifier::try_from("(uint256,uint256)").unwrap()),
stem: TypeStem::Tuple(TupleSpecifier::parse("(uint256,uint256)").unwrap()),
sizes: vec![],
}
);

assert_eq!(
super::TypeSpecifier::try_from("(uint256,uint256)[2]").unwrap(),
super::TypeSpecifier {
TypeSpecifier::parse("(uint256,uint256)[2]").unwrap(),
TypeSpecifier {
span: "(uint256,uint256)[2]",
stem: TypeStem::Tuple(TupleSpecifier::try_from("(uint256,uint256)").unwrap()),
stem: TypeStem::Tuple(TupleSpecifier::parse("(uint256,uint256)").unwrap()),
sizes: vec![NonZeroUsize::new(2)],
}
);

assert_eq!(
super::TypeSpecifier::try_from("MyStruct").unwrap(),
super::TypeSpecifier {
TypeSpecifier::parse("MyStruct").unwrap(),
TypeSpecifier {
span: "MyStruct",
stem: TypeStem::try_from("MyStruct").unwrap(),
stem: TypeStem::parse("MyStruct").unwrap(),
sizes: vec![],
}
);

assert_eq!(
super::TypeSpecifier::try_from("MyStruct[2]").unwrap(),
super::TypeSpecifier {
TypeSpecifier::parse("MyStruct[2]").unwrap(),
TypeSpecifier {
span: "MyStruct[2]",
stem: TypeStem::try_from("MyStruct").unwrap(),
stem: TypeStem::parse("MyStruct").unwrap(),
sizes: vec![NonZeroUsize::new(2)],
}
);
}

#[test]
fn a_type_named_tuple() {
TypeSpecifier::try_from("tuple").unwrap();
TypeSpecifier::parse("tuple").unwrap();
}
}
Loading