Performance improvement.

The refactoring added `next_is` which takes a `&str` as parameter, while
we only use it with strings of lengths 2 and 3. Replacing this by 2
dedicated methods (which can be added to the trait interface and only
specialized if needed) removes almost all the overhead that was added by
`Input`.
This commit is contained in:
Ethiraric 2024-04-18 19:11:40 +02:00
parent d9bb7a1693
commit f8b6d849d3
3 changed files with 23 additions and 14 deletions

View file

@ -90,10 +90,4 @@ impl<T: Iterator<Item = char>> Input for BufferedInput<T> {
fn peek_nth(&self, n: usize) -> char {
self.buffer[n]
}
#[inline]
fn next_is(&self, pat: &str) -> bool {
assert!(self.buffer.len() >= pat.len());
self.buffer.iter().zip(pat.chars()).all(|(a, b)| *a == b)
}
}

View file

@ -102,10 +102,25 @@ pub trait Input {
self.peek_nth(n) == c
}
/// Return whether the next characters in the input source match the given pattern.
/// Return whether the next 2 characters in the input source match the given characters.
///
/// This function assumes that the next `pat.len()` characters in the input has already been
/// fetched through [`Input::lookahead`].
/// This function assumes that the next 2 characters in the input has already been fetched
/// through [`Input::lookahead`].
#[inline]
#[must_use]
fn next_is(&self, pat: &str) -> bool;
fn next_2_are(&self, c1: char, c2: char) -> bool {
assert!(self.buflen() >= 2);
self.peek() == c1 && self.peek_nth(1) == c2
}
/// Return whether the next 3 characters in the input source match the given characters.
///
/// This function assumes that the next 3 characters in the input has already been fetched
/// through [`Input::lookahead`].
#[inline]
#[must_use]
fn next_3_are(&self, c1: char, c2: char, c3: char) -> bool {
assert!(self.buflen() >= 3);
self.peek() == c1 && self.peek_nth(1) == c2 && self.peek_nth(2) == c3
}
}

View file

@ -528,7 +528,7 @@ impl<T: Input> Scanner<T> {
/// Consume a linebreak (either CR, LF or CRLF), if any. Do nothing if there's none.
#[inline]
fn skip_linebreak(&mut self) {
if self.input.next_is("\r\n") {
if self.input.next_2_are('\r', '\n') {
// While technically not a blank, this does not matter as `self.leading_whitespace`
// will be reset by `skip_nl`.
self.skip_blank();
@ -597,7 +597,7 @@ impl<T: Input> Scanner<T> {
/// [`Self::lookahead`] must have been called before calling this function.
fn next_is_document_start(&self) -> bool {
assert!(self.input.buflen() >= 4);
self.input.next_is("---") && is_blank_or_breakz(self.input.peek_nth(3))
self.input.next_3_are('-', '-', '-') && is_blank_or_breakz(self.input.peek_nth(3))
}
/// Check whether the next characters correspond to an end of document.
@ -605,7 +605,7 @@ impl<T: Input> Scanner<T> {
/// [`Self::lookahead`] must have been called before calling this function.
fn next_is_document_end(&self) -> bool {
assert!(self.input.buflen() >= 4);
self.input.next_is("...") && is_blank_or_breakz(self.input.peek_nth(3))
self.input.next_3_are('.', '.', '.') && is_blank_or_breakz(self.input.peek_nth(3))
}
/// Check whether the next characters correspond to a document indicator.
@ -615,7 +615,7 @@ impl<T: Input> Scanner<T> {
fn next_is_document_indicator(&self) -> bool {
assert!(self.input.buflen() >= 4);
is_blank_or_breakz(self.input.peek_nth(3))
&& (self.input.next_is("...") || self.input.next_is("---"))
&& (self.input.next_3_are('.', '.', '.') || self.input.next_3_are('-', '-', '-'))
}
/// Insert a token at the given position.