Skip to content

Commit 640f17e

Browse files
Build identifier tokens with a single String allocation
1 parent dc8f0c4 commit 640f17e

1 file changed

Lines changed: 32 additions & 20 deletions

File tree

‎src/tokenizer.rs‎

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,8 @@ impl<'a> Tokenizer<'a> {
968968
line: 1,
969969
col: 1,
970970
};
971+
// Tokens, whitespace included, average about 2.5 input bytes each.
972+
buf.reserve(self.query.len() / 2);
971973

972974
let mut location = state.location();
973975
while let Some(token) = self.next_token(&mut state, buf.last().map(|t| &t.token))? {
@@ -1043,7 +1045,6 @@ impl<'a> Tokenizer<'a> {
10431045
chars: &mut State,
10441046
) -> Result<Option<Token>, TokenizerError> {
10451047
chars.next(); // consume the first char
1046-
let ch: String = ch.into_iter().collect();
10471048
let word = self.tokenize_word(ch, chars);
10481049

10491050
// TODO: implement parsing of exponent here
@@ -1116,7 +1117,7 @@ impl<'a> Tokenizer<'a> {
11161117
}
11171118
_ => {
11181119
// regular identifier starting with an "b" or "B"
1119-
let s = self.tokenize_word(b, chars);
1120+
let s = self.tokenize_word([b], chars);
11201121
Ok(Some(Token::make_word_owned(s, None)))
11211122
}
11221123
}
@@ -1143,7 +1144,7 @@ impl<'a> Tokenizer<'a> {
11431144
),
11441145
_ => {
11451146
// regular identifier starting with an "r" or "R"
1146-
let s = self.tokenize_word(b, chars);
1147+
let s = self.tokenize_word([b], chars);
11471148
Ok(Some(Token::make_word_owned(s, None)))
11481149
}
11491150
}
@@ -1168,13 +1169,13 @@ impl<'a> Tokenizer<'a> {
11681169
self.tokenize_quote_delimited_string(chars, &[n, q])
11691170
.map(|s| Some(Token::NationalQuoteDelimitedStringLiteral(s)))
11701171
} else {
1171-
let s = self.tokenize_word(String::from_iter([n, q]), chars);
1172+
let s = self.tokenize_word([n, q], chars);
11721173
Ok(Some(Token::make_word_owned(s, None)))
11731174
}
11741175
}
11751176
_ => {
11761177
// regular identifier starting with an "N"
1177-
let s = self.tokenize_word(n, chars);
1178+
let s = self.tokenize_word([n], chars);
11781179
Ok(Some(Token::make_word_owned(s, None)))
11791180
}
11801181
}
@@ -1185,7 +1186,7 @@ impl<'a> Tokenizer<'a> {
11851186
self.tokenize_quote_delimited_string(chars, &[q])
11861187
.map(|s| Some(Token::QuoteDelimitedStringLiteral(s)))
11871188
} else {
1188-
let s = self.tokenize_word(q, chars);
1189+
let s = self.tokenize_word([q], chars);
11891190
Ok(Some(Token::make_word_owned(s, None)))
11901191
}
11911192
}
@@ -1201,7 +1202,7 @@ impl<'a> Tokenizer<'a> {
12011202
}
12021203
_ => {
12031204
// regular identifier starting with an "E" or "e"
1204-
let s = self.tokenize_word(x, chars);
1205+
let s = self.tokenize_word([x], chars);
12051206
Ok(Some(Token::make_word_owned(s, None)))
12061207
}
12071208
}
@@ -1220,7 +1221,7 @@ impl<'a> Tokenizer<'a> {
12201221
}
12211222
}
12221223
// regular identifier starting with an "U" or "u"
1223-
let s = self.tokenize_word(x, chars);
1224+
let s = self.tokenize_word([x], chars);
12241225
Ok(Some(Token::make_word_owned(s, None)))
12251226
}
12261227
// The spec only allows an uppercase 'X' to introduce a hex
@@ -1235,7 +1236,7 @@ impl<'a> Tokenizer<'a> {
12351236
}
12361237
_ => {
12371238
// regular identifier starting with an "X"
1238-
let s = self.tokenize_word(x, chars);
1239+
let s = self.tokenize_word([x], chars);
12391240
Ok(Some(Token::make_word_owned(s, None)))
12401241
}
12411242
}
@@ -2062,11 +2063,14 @@ impl<'a> Tokenizer<'a> {
20622063
}
20632064

20642065
/// Tokenize an identifier or keyword, after the first char is already consumed.
2065-
fn tokenize_word(&self, first_chars: impl Into<String>, chars: &mut State) -> String {
2066-
let mut s = first_chars.into();
2067-
s.push_str(&peeking_take_while(chars, |ch| {
2068-
self.dialect.is_identifier_part(ch)
2069-
}));
2066+
fn tokenize_word(
2067+
&self,
2068+
first_chars: impl IntoIterator<Item = char>,
2069+
chars: &mut State,
2070+
) -> String {
2071+
let mut s = String::new();
2072+
s.extend(first_chars);
2073+
peeking_take_while_into(chars, &mut s, |ch| self.dialect.is_identifier_part(ch));
20702074
s
20712075
}
20722076

@@ -2415,17 +2419,25 @@ impl<'a> Tokenizer<'a> {
24152419
/// Read from `chars` until `predicate` returns `false` or EOF is hit.
24162420
/// Return the characters read as String, and keep the first non-matching
24172421
/// char available as `chars.next()`.
2418-
fn peeking_take_while(chars: &mut State, mut predicate: impl FnMut(char) -> bool) -> String {
2422+
fn peeking_take_while(chars: &mut State, predicate: impl FnMut(char) -> bool) -> String {
24192423
let mut s = String::new();
2424+
peeking_take_while_into(chars, &mut s, predicate);
2425+
s
2426+
}
2427+
2428+
/// [`peeking_take_while`] appending into an existing buffer.
2429+
fn peeking_take_while_into(
2430+
chars: &mut State,
2431+
s: &mut String,
2432+
mut predicate: impl FnMut(char) -> bool,
2433+
) {
24202434
while let Some(&ch) = chars.peek() {
2421-
if predicate(ch) {
2422-
chars.next(); // consume
2423-
s.push(ch);
2424-
} else {
2435+
if !predicate(ch) {
24252436
break;
24262437
}
2438+
chars.next(); // consume
2439+
s.push(ch);
24272440
}
2428-
s
24292441
}
24302442

24312443
fn unescape_single_quoted_string(chars: &mut State<'_>) -> Option<String> {

0 commit comments

Comments
 (0)