Split skip into more specific variants.

This function is a hotpath and sometimes removing the conditional jump
to detect line breaks saves a bunch of instructions.
This commit is contained in:
Ethiraric 2024-01-24 20:20:52 +01:00
parent 666965ef4f
commit 7a51c3dfca

View file

@ -430,32 +430,58 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
} }
/// Consume the next character, remove it from the buffer and update the mark. /// Consume the next character. It is assumed the next character is a blank.
#[inline] #[inline]
fn skip(&mut self) { fn skip_blank(&mut self) {
let c = self.buffer.pop_front().unwrap(); self.buffer.pop_front();
self.mark.index += 1; self.mark.index += 1;
if c == '\n' { self.mark.col += 1;
self.leading_whitespace = true; }
self.mark.line += 1;
self.mark.col = 0; /// Consume the next character. It is assumed the next character is not a blank.
} else { #[inline]
if self.leading_whitespace && !is_blank(c) { fn skip_non_blank(&mut self) {
self.leading_whitespace = false; self.buffer.pop_front();
}
self.mark.col += 1; self.mark.index += 1;
self.mark.col += 1;
self.leading_whitespace = false;
}
/// Consume the next characters. It is assumed none of the next characters are blanks.
#[inline]
fn skip_n_non_blank(&mut self, n: usize) {
for _ in 0..n {
self.buffer.pop_front();
} }
self.mark.index += n;
self.mark.col += n;
self.leading_whitespace = false;
}
/// Consume the next character. It is assumed the next character is a newline.
#[inline]
fn skip_nl(&mut self) {
self.buffer.pop_front();
self.mark.index += 1;
self.mark.col = 0;
self.mark.line += 1;
self.leading_whitespace = true;
} }
/// Consume a linebreak (either CR, LF or CRLF), if any. Do nothing if there's none. /// Consume a linebreak (either CR, LF or CRLF), if any. Do nothing if there's none.
#[inline] #[inline]
fn skip_line(&mut self) { fn skip_line(&mut self) {
if self.buffer[0] == '\r' && self.buffer[1] == '\n' { if self.buffer[0] == '\r' && self.buffer[1] == '\n' {
self.skip(); // While technically not a blank, this does not matter as `self.leading_whitespace`
self.skip(); // will be reset by `skip_nl`.
self.skip_blank();
self.skip_nl();
} else if is_break(self.buffer[0]) { } else if is_break(self.buffer[0]) {
self.skip(); self.skip_nl();
} }
} }
@ -477,16 +503,6 @@ impl<T: Iterator<Item = char>> Scanner<T> {
self.ch() self.ch()
} }
/// Consume and return the next character.
///
/// Equivalent to calling [`Self::ch`] and [`Self::skip`].
#[inline]
fn ch_skip(&mut self) -> char {
let ret = self.ch();
self.skip();
ret
}
/// Return whether the next character is `c`. /// Return whether the next character is `c`.
#[inline] #[inline]
fn ch_is(&self, c: char) -> bool { fn ch_is(&self, c: char) -> bool {
@ -517,11 +533,12 @@ impl<T: Iterator<Item = char>> Scanner<T> {
#[inline] #[inline]
fn read_break(&mut self, s: &mut String) { fn read_break(&mut self, s: &mut String) {
let c = self.buffer[0]; let c = self.buffer[0];
let nc = self.buffer[1];
debug_assert!(is_break(c)); debug_assert!(is_break(c));
self.skip(); if c == '\r' && nc == '\n' {
if c == '\r' && self.buffer[0] == '\n' { self.skip_blank();
self.skip();
} }
self.skip_nl();
s.push('\n'); s.push('\n');
} }
@ -731,7 +748,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
loop { loop {
// TODO(chenyh) BOM // TODO(chenyh) BOM
match self.look_ch() { match self.look_ch() {
' ' => self.skip(), ' ' => self.skip_blank(),
// Tabs may not be used as indentation. // Tabs may not be used as indentation.
// "Indentation" only exists as long as a block is started, but does not exist // "Indentation" only exists as long as a block is started, but does not exist
// inside of flow-style constructs. Tabs are allowed as part of leading // inside of flow-style constructs. Tabs are allowed as part of leading
@ -751,7 +768,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
)); ));
} }
} }
'\t' => self.skip(), '\t' => self.skip_blank(),
'\n' | '\r' => { '\n' | '\r' => {
self.lookahead(2); self.lookahead(2);
self.skip_line(); self.skip_line();
@ -760,9 +777,8 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
} }
'#' => { '#' => {
while !is_breakz(self.ch()) { while !is_breakz(self.look_ch()) {
self.skip(); self.skip_non_blank();
self.lookahead(1);
} }
} }
_ => break, _ => break,
@ -780,7 +796,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
loop { loop {
match self.look_ch() { match self.look_ch() {
' ' => { ' ' => {
self.skip(); self.skip_blank();
need_whitespace = false; need_whitespace = false;
} }
@ -793,9 +809,8 @@ impl<T: Iterator<Item = char>> Scanner<T> {
need_whitespace = false; need_whitespace = false;
} }
'#' => { '#' => {
while !is_breakz(self.ch()) { while !is_breakz(self.look_ch()) {
self.skip(); self.skip_non_blank();
self.lookahead(1);
} }
} }
_ => break, _ => break,
@ -817,11 +832,11 @@ impl<T: Iterator<Item = char>> Scanner<T> {
match self.look_ch() { match self.look_ch() {
' ' => { ' ' => {
has_yaml_ws = true; has_yaml_ws = true;
self.skip(); self.skip_blank();
} }
'\t' if skip_tabs != SkipTabs::No => { '\t' if skip_tabs != SkipTabs::No => {
encountered_tab = true; encountered_tab = true;
self.skip(); self.skip_blank();
} }
// YAML comments must be preceded by whitespace. // YAML comments must be preceded by whitespace.
'#' if !encountered_tab && !has_yaml_ws => { '#' if !encountered_tab && !has_yaml_ws => {
@ -832,7 +847,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
'#' => { '#' => {
while !is_breakz(self.look_ch()) { while !is_breakz(self.look_ch()) {
self.skip(); self.skip_non_blank();
} }
} }
_ => break, _ => break,
@ -893,7 +908,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
fn scan_directive(&mut self) -> Result<Token, ScanError> { fn scan_directive(&mut self) -> Result<Token, ScanError> {
let start_mark = self.mark; let start_mark = self.mark;
self.skip(); self.skip_non_blank();
let name = self.scan_directive_name()?; let name = self.scan_directive_name()?;
let tok = match name.as_ref() { let tok = match name.as_ref() {
@ -902,10 +917,8 @@ impl<T: Iterator<Item = char>> Scanner<T> {
// XXX This should be a warning instead of an error // XXX This should be a warning instead of an error
_ => { _ => {
// skip current line // skip current line
self.lookahead(1); while !is_breakz(self.look_ch()) {
while !is_breakz(self.ch()) { self.skip_non_blank();
self.skip();
self.lookahead(1);
} }
// XXX return an empty TagDirective token // XXX return an empty TagDirective token
Token( Token(
@ -916,7 +929,6 @@ impl<T: Iterator<Item = char>> Scanner<T> {
// "while scanning a directive, found unknown directive name")) // "while scanning a directive, found unknown directive name"))
} }
}; };
self.lookahead(1);
self.skip_ws_to_eol(SkipTabs::Yes)?; self.skip_ws_to_eol(SkipTabs::Yes)?;
@ -937,11 +949,8 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token, ScanError> { fn scan_version_directive_value(&mut self, mark: &Marker) -> Result<Token, ScanError> {
self.lookahead(1); while is_blank(self.look_ch()) {
self.skip_blank();
while is_blank(self.ch()) {
self.skip();
self.lookahead(1);
} }
let major = self.scan_version_directive_number(mark)?; let major = self.scan_version_directive_number(mark)?;
@ -952,8 +961,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
"while scanning a YAML directive, did not find expected digit or '.' character", "while scanning a YAML directive, did not find expected digit or '.' character",
)); ));
} }
self.skip_non_blank();
self.skip();
let minor = self.scan_version_directive_number(mark)?; let minor = self.scan_version_directive_number(mark)?;
@ -963,11 +971,9 @@ impl<T: Iterator<Item = char>> Scanner<T> {
fn scan_directive_name(&mut self) -> Result<String, ScanError> { fn scan_directive_name(&mut self) -> Result<String, ScanError> {
let start_mark = self.mark; let start_mark = self.mark;
let mut string = String::new(); let mut string = String::new();
self.lookahead(1); while is_alpha(self.look_ch()) {
while is_alpha(self.ch()) {
string.push(self.ch()); string.push(self.ch());
self.skip(); self.skip_non_blank();
self.lookahead(1);
} }
if string.is_empty() { if string.is_empty() {
@ -999,7 +1005,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
length += 1; length += 1;
val = val * 10 + digit; val = val * 10 + digit;
self.skip(); self.skip_non_blank();
} }
if length == 0 { if length == 0 {
@ -1013,17 +1019,15 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
fn scan_tag_directive_value(&mut self, mark: &Marker) -> Result<Token, ScanError> { fn scan_tag_directive_value(&mut self, mark: &Marker) -> Result<Token, ScanError> {
self.lookahead(1);
/* Eat whitespaces. */ /* Eat whitespaces. */
while is_blank(self.ch()) { while is_blank(self.look_ch()) {
self.skip(); self.skip_blank();
self.lookahead(1);
} }
let handle = self.scan_tag_handle(true, mark)?; let handle = self.scan_tag_handle(true, mark)?;
/* Eat whitespaces. */ /* Eat whitespaces. */
while is_blank(self.look_ch()) { while is_blank(self.look_ch()) {
self.skip(); self.skip_blank();
} }
let prefix = self.scan_tag_prefix(mark)?; let prefix = self.scan_tag_prefix(mark)?;
@ -1100,15 +1104,18 @@ impl<T: Iterator<Item = char>> Scanner<T> {
)); ));
} }
string.push(self.ch_skip()); string.push(self.ch());
self.skip_non_blank();
while is_alpha(self.look_ch()) { while is_alpha(self.look_ch()) {
string.push(self.ch_skip()); string.push(self.ch());
self.skip_non_blank();
} }
// Check if the trailing character is '!' and copy it. // Check if the trailing character is '!' and copy it.
if self.ch() == '!' { if self.ch() == '!' {
string.push(self.ch_skip()); string.push(self.ch());
self.skip_non_blank();
} else if directive && string != "!" { } else if directive && string != "!" {
// It's either the '!' tag or not really a tag handle. If it's a %TAG // It's either the '!' tag or not really a tag handle. If it's a %TAG
// directive, it's an error. If it's a tag token, it must be a part of // directive, it's an error. If it's a tag token, it must be a part of
@ -1131,7 +1138,8 @@ impl<T: Iterator<Item = char>> Scanner<T> {
if self.look_ch() == '!' { if self.look_ch() == '!' {
// If we have a local tag, insert and skip `!`. // If we have a local tag, insert and skip `!`.
string.push(self.ch_skip()); string.push(self.ch());
self.skip_non_blank();
} else if !is_tag_char(self.ch()) { } else if !is_tag_char(self.ch()) {
// Otherwise, check if the first global tag character is valid. // Otherwise, check if the first global tag character is valid.
return Err(ScanError::new(*start_mark, "invalid global tag character")); return Err(ScanError::new(*start_mark, "invalid global tag character"));
@ -1140,14 +1148,16 @@ impl<T: Iterator<Item = char>> Scanner<T> {
string.push(self.scan_uri_escapes(start_mark)?); string.push(self.scan_uri_escapes(start_mark)?);
} else { } else {
// Otherwise, push the first character. // Otherwise, push the first character.
string.push(self.ch_skip()); string.push(self.ch());
self.skip_non_blank();
} }
while is_uri_char(self.look_ch()) { while is_uri_char(self.look_ch()) {
if self.ch() == '%' { if self.ch() == '%' {
string.push(self.scan_uri_escapes(start_mark)?); string.push(self.scan_uri_escapes(start_mark)?);
} else { } else {
string.push(self.ch_skip()); string.push(self.ch());
self.skip_non_blank();
} }
} }
@ -1159,15 +1169,16 @@ impl<T: Iterator<Item = char>> Scanner<T> {
/// The prefixing `!<` must _not_ have been skipped. /// The prefixing `!<` must _not_ have been skipped.
fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> { fn scan_verbatim_tag(&mut self, start_mark: &Marker) -> Result<String, ScanError> {
// Eat `!<` // Eat `!<`
self.skip(); self.skip_non_blank();
self.skip(); self.skip_non_blank();
let mut string = String::new(); let mut string = String::new();
while is_uri_char(self.look_ch()) { while is_uri_char(self.look_ch()) {
if self.ch() == '%' { if self.ch() == '%' {
string.push(self.scan_uri_escapes(start_mark)?); string.push(self.scan_uri_escapes(start_mark)?);
} else { } else {
string.push(self.ch_skip()); string.push(self.ch());
self.skip_non_blank();
} }
} }
@ -1177,7 +1188,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
"while scanning a verbatim tag, did not find the expected '>'", "while scanning a verbatim tag, did not find the expected '>'",
)); ));
} }
self.skip(); self.skip_non_blank();
Ok(string) Ok(string)
} }
@ -1204,7 +1215,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
string.push(self.scan_uri_escapes(mark)?); string.push(self.scan_uri_escapes(mark)?);
} else { } else {
string.push(self.ch()); string.push(self.ch());
self.skip(); self.skip_non_blank();
} }
length += 1; length += 1;
@ -1258,9 +1269,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
code = (code << 8) + octet; code = (code << 8) + octet;
} }
self.skip(); self.skip_n_non_blank(3);
self.skip();
self.skip();
width -= 1; width -= 1;
if width == 0 { if width == 0 {
@ -1292,10 +1301,10 @@ impl<T: Iterator<Item = char>> Scanner<T> {
let mut string = String::new(); let mut string = String::new();
let start_mark = self.mark; let start_mark = self.mark;
self.skip(); self.skip_non_blank();
while is_anchor_char(self.look_ch()) { while is_anchor_char(self.look_ch()) {
string.push(self.ch()); string.push(self.ch());
self.skip(); self.skip_non_blank();
} }
if string.is_empty() { if string.is_empty() {
@ -1319,7 +1328,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
self.allow_simple_key(); self.allow_simple_key();
let start_mark = self.mark; let start_mark = self.mark;
self.skip(); self.skip_non_blank();
if tok == TokenType::FlowMappingStart { if tok == TokenType::FlowMappingStart {
self.flow_mapping_started = true; self.flow_mapping_started = true;
@ -1340,7 +1349,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
self.end_implicit_mapping(self.mark); self.end_implicit_mapping(self.mark);
let start_mark = self.mark; let start_mark = self.mark;
self.skip(); self.skip_non_blank();
self.skip_ws_to_eol(SkipTabs::Yes)?; self.skip_ws_to_eol(SkipTabs::Yes)?;
// A flow collection within a flow mapping can be a key. In that case, the value may be // A flow collection within a flow mapping can be a key. In that case, the value may be
@ -1364,7 +1373,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
self.end_implicit_mapping(self.mark); self.end_implicit_mapping(self.mark);
let start_mark = self.mark; let start_mark = self.mark;
self.skip(); self.skip_non_blank();
self.skip_ws_to_eol(SkipTabs::Yes)?; self.skip_ws_to_eol(SkipTabs::Yes)?;
self.tokens self.tokens
@ -1418,7 +1427,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
// Skip over the `-`. // Skip over the `-`.
let mark = self.mark; let mark = self.mark;
self.skip(); self.skip_non_blank();
// generate BLOCK-SEQUENCE-START if indented // generate BLOCK-SEQUENCE-START if indented
self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark); self.roll_indent(mark.col, None, TokenType::BlockSequenceStart, mark);
@ -1452,9 +1461,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
let mark = self.mark; let mark = self.mark;
self.skip(); self.skip_n_non_blank(3);
self.skip();
self.skip();
self.tokens.push_back(Token(mark, t)); self.tokens.push_back(Token(mark, t));
Ok(()) Ok(())
@ -1489,7 +1496,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
let mut chomping_break = String::new(); let mut chomping_break = String::new();
// skip '|' or '>' // skip '|' or '>'
self.skip(); self.skip_non_blank();
self.unroll_non_block_indents(); self.unroll_non_block_indents();
if self.look_ch() == '+' || self.ch() == '-' { if self.look_ch() == '+' || self.ch() == '-' {
@ -1498,7 +1505,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} else { } else {
chomping = Chomping::Strip; chomping = Chomping::Strip;
} }
self.skip(); self.skip_non_blank();
if is_digit(self.look_ch()) { if is_digit(self.look_ch()) {
if self.ch() == '0' { if self.ch() == '0' {
return Err(ScanError::new( return Err(ScanError::new(
@ -1507,7 +1514,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
)); ));
} }
increment = (self.ch() as usize) - ('0' as usize); increment = (self.ch() as usize) - ('0' as usize);
self.skip(); self.skip_non_blank();
} }
} else if is_digit(self.ch()) { } else if is_digit(self.ch()) {
if self.ch() == '0' { if self.ch() == '0' {
@ -1518,7 +1525,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
increment = (self.ch() as usize) - ('0' as usize); increment = (self.ch() as usize) - ('0' as usize);
self.skip(); self.skip_non_blank();
self.lookahead(1); self.lookahead(1);
if self.ch() == '+' || self.ch() == '-' { if self.ch() == '+' || self.ch() == '-' {
if self.ch() == '+' { if self.ch() == '+' {
@ -1526,7 +1533,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} else { } else {
chomping = Chomping::Strip; chomping = Chomping::Strip;
} }
self.skip(); self.skip_non_blank();
} }
} }
@ -1622,10 +1629,14 @@ impl<T: Iterator<Item = char>> Scanner<T> {
leading_blank = is_blank(self.ch()); leading_blank = is_blank(self.ch());
while !is_breakz(self.ch()) { while !is_breakz(self.look_ch()) {
string.push(self.ch()); string.push(self.ch());
self.skip(); // We may technically skip non-blank characters. However, the only distinction is
self.lookahead(1); // to determine what is leading whitespace and what is not. Here, we read the
// contents of the line until either eof or a linebreak. We know we will not read
// `self.leading_whitespace` until the end of the line, where it will be reset.
// This allows us to call a slightly less expensive function.
self.skip_blank();
} }
// break on EOF // break on EOF
if is_z(self.ch()) { if is_z(self.ch()) {
@ -1663,7 +1674,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
self.lookahead(indent + 2); self.lookahead(indent + 2);
// Consume all spaces. Tabs cannot be used as indentation. // Consume all spaces. Tabs cannot be used as indentation.
while self.mark.col < indent && self.ch() == ' ' { while self.mark.col < indent && self.ch() == ' ' {
self.skip(); self.skip_blank();
} }
// If our current line is empty, skip over the break and continue looping. // If our current line is empty, skip over the break and continue looping.
@ -1685,14 +1696,14 @@ impl<T: Iterator<Item = char>> Scanner<T> {
loop { loop {
// Consume all spaces. Tabs cannot be used as indentation. // Consume all spaces. Tabs cannot be used as indentation.
while self.look_ch() == ' ' { while self.look_ch() == ' ' {
self.skip(); self.skip_blank();
} }
if self.mark.col > max_indent { if self.mark.col > max_indent {
max_indent = self.mark.col; max_indent = self.mark.col;
} }
if is_break(self.look_ch()) { if is_break(self.ch()) {
// If our current line is empty, skip over the break and continue looping. // If our current line is empty, skip over the break and continue looping.
self.lookahead(2); self.lookahead(2);
self.read_break(breaks); self.read_break(breaks);
@ -1742,7 +1753,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
let mut leading_blanks; let mut leading_blanks;
/* Eat the left quote. */ /* Eat the left quote. */
self.skip(); self.skip_non_blank();
loop { loop {
/* Check for a document indicator. */ /* Check for a document indicator. */
@ -1800,10 +1811,10 @@ impl<T: Iterator<Item = char>> Scanner<T> {
"tab cannot be used as indentation", "tab cannot be used as indentation",
)); ));
} }
self.skip(); self.skip_blank();
} else { } else {
whitespaces.push(self.ch()); whitespaces.push(self.ch());
self.skip(); self.skip_blank();
} }
} else { } else {
self.lookahead(2); self.lookahead(2);
@ -1842,7 +1853,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} // loop } // loop
// Eat the right quote. // Eat the right quote.
self.skip(); self.skip_non_blank();
// Ensure there is no invalid trailing content. // Ensure there is no invalid trailing content.
self.skip_ws_to_eol(SkipTabs::Yes)?; self.skip_ws_to_eol(SkipTabs::Yes)?;
match self.ch() { match self.ch() {
@ -1892,8 +1903,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
// Check for an escaped single quote. // Check for an escaped single quote.
'\'' if self.buffer[1] == '\'' && single => { '\'' if self.buffer[1] == '\'' && single => {
string.push('\''); string.push('\'');
self.skip(); self.skip_n_non_blank(2);
self.skip();
} }
// Check for the right quote. // Check for the right quote.
'\'' if single => break, '\'' if single => break,
@ -1901,7 +1911,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
// Check for an escaped line break. // Check for an escaped line break.
'\\' if !single && is_break(self.buffer[1]) => { '\\' if !single && is_break(self.buffer[1]) => {
self.lookahead(3); self.lookahead(3);
self.skip(); self.skip_non_blank();
self.skip_line(); self.skip_line();
*leading_blanks = true; *leading_blanks = true;
break; break;
@ -1912,7 +1922,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
c => { c => {
string.push(c); string.push(c);
self.skip(); self.skip_non_blank();
} }
} }
self.lookahead(2); self.lookahead(2);
@ -1965,8 +1975,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
)) ))
} }
} }
self.skip(); self.skip_n_non_blank(2);
self.skip();
// Consume an arbitrary escape code. // Consume an arbitrary escape code.
if code_length > 0 { if code_length > 0 {
@ -1990,9 +1999,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
}; };
ret = ch; ret = ch;
for _ in 0..code_length { self.skip_n_non_blank(code_length);
self.skip();
}
} }
Ok(ret) Ok(ret)
} }
@ -2086,7 +2093,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
} }
string.push(self.ch()); string.push(self.ch());
self.skip(); self.skip_non_blank();
self.lookahead(2); self.lookahead(2);
} }
// is the end? // is the end?
@ -2112,7 +2119,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
if !leading_blanks { if !leading_blanks {
whitespaces.push(self.ch()); whitespaces.push(self.ch());
} }
self.skip(); self.skip_blank();
} else { } else {
self.lookahead(2); self.lookahead(2);
// Check if it is a first line break // Check if it is a first line break
@ -2171,7 +2178,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
self.disallow_simple_key(); self.disallow_simple_key();
} }
self.skip(); self.skip_non_blank();
self.skip_yaml_whitespace()?; self.skip_yaml_whitespace()?;
if self.ch() == '\t' { if self.ch() == '\t' {
return Err(ScanError::new( return Err(ScanError::new(
@ -2190,7 +2197,7 @@ impl<T: Iterator<Item = char>> Scanner<T> {
self.implicit_flow_mapping = self.flow_level > 0 && !self.flow_mapping_started; self.implicit_flow_mapping = self.flow_level > 0 && !self.flow_mapping_started;
// Skip over ':'. // Skip over ':'.
self.skip(); self.skip_non_blank();
if self.look_ch() == '\t' if self.look_ch() == '\t'
&& !self.skip_ws_to_eol(SkipTabs::Yes)?.has_valid_yaml_ws() && !self.skip_ws_to_eol(SkipTabs::Yes)?.has_valid_yaml_ws()
&& (self.ch() == '-' || is_alpha(self.ch())) && (self.ch() == '-' || is_alpha(self.ch()))