Merge pull request #60 from epage/lazy

perf: Speed up identifier splitting
This commit is contained in:
Ed Page 2019-10-25 15:01:42 -06:00 committed by GitHub
commit af49b6af86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -238,30 +238,50 @@ impl WordMode {
} }
} }
fn split_ident(ident: &str, offset: usize) -> impl Iterator<Item = Word<'_>> { struct SplitIdent<'s> {
let mut result = vec![]; ident: &'s str,
offset: usize,
let mut char_indices = ident.char_indices().peekable(); char_indices: std::iter::Peekable<std::str::CharIndices<'s>>,
let mut start = 0; start: usize,
let mut start_mode = WordMode::Boundary; start_mode: WordMode,
let mut last_mode = WordMode::Boundary; last_mode: WordMode,
while let Some((i, c)) = char_indices.next() { }
impl<'s> SplitIdent<'s> {
fn new(ident: &'s str, offset: usize) -> Self {
Self {
ident,
offset,
char_indices: ident.char_indices().peekable(),
start: 0,
start_mode: WordMode::Boundary,
last_mode: WordMode::Boundary,
}
}
}
impl<'s> Iterator for SplitIdent<'s> {
type Item = Word<'s>;
fn next(&mut self) -> Option<Word<'s>> {
while let Some((i, c)) = self.char_indices.next() {
let cur_mode = WordMode::classify(c); let cur_mode = WordMode::classify(c);
if cur_mode == WordMode::Boundary { if cur_mode == WordMode::Boundary {
assert!(start_mode == WordMode::Boundary); assert!(self.start_mode == WordMode::Boundary);
continue; continue;
} }
if start_mode == WordMode::Boundary { if self.start_mode == WordMode::Boundary {
start_mode = cur_mode; self.start_mode = cur_mode;
start = i; self.start = i;
} }
if let Some(&(next_i, next)) = char_indices.peek() { if let Some(&(next_i, next)) = self.char_indices.peek() {
// The mode including the current character, assuming the current character does // The mode including the current character, assuming the current character does
// not result in a word boundary. // not result in a word boundary.
let next_mode = WordMode::classify(next); let next_mode = WordMode::classify(next);
match (last_mode, cur_mode, next_mode) { match (self.last_mode, cur_mode, next_mode) {
// cur_mode is last of current word // cur_mode is last of current word
(_, _, WordMode::Boundary) (_, _, WordMode::Boundary)
| (_, WordMode::Lowercase, WordMode::Number) | (_, WordMode::Lowercase, WordMode::Number)
@ -269,41 +289,49 @@ fn split_ident(ident: &str, offset: usize) -> impl Iterator<Item = Word<'_>> {
| (_, WordMode::Number, WordMode::Lowercase) | (_, WordMode::Number, WordMode::Lowercase)
| (_, WordMode::Number, WordMode::Uppercase) | (_, WordMode::Number, WordMode::Uppercase)
| (_, WordMode::Lowercase, WordMode::Uppercase) => { | (_, WordMode::Lowercase, WordMode::Uppercase) => {
let case = start_mode.case(cur_mode); let case = self.start_mode.case(cur_mode);
result.push(Word::new_unchecked( let result = Word::new_unchecked(
&ident[start..next_i], &self.ident[self.start..next_i],
case, case,
start + offset, self.start + self.offset,
)); );
start = next_i; self.start = next_i;
start_mode = WordMode::Boundary; self.start_mode = WordMode::Boundary;
last_mode = WordMode::Boundary; self.last_mode = WordMode::Boundary;
return Some(result);
} }
// cur_mode is start of next word // cur_mode is start of next word
(WordMode::Uppercase, WordMode::Uppercase, WordMode::Lowercase) => { (WordMode::Uppercase, WordMode::Uppercase, WordMode::Lowercase) => {
result.push(Word::new_unchecked( let result = Word::new_unchecked(
&ident[start..i], &self.ident[self.start..i],
Case::Scream, Case::Scream,
start + offset, self.start + self.offset,
)); );
start = i; self.start = i;
start_mode = cur_mode; self.start_mode = cur_mode;
last_mode = WordMode::Boundary; self.last_mode = WordMode::Boundary;
return Some(result);
} }
// No word boundary // No word boundary
(_, _, _) => { (_, _, _) => {
last_mode = cur_mode; self.last_mode = cur_mode;
} }
} }
} else { } else {
// Collect trailing characters as a word // Collect trailing characters as a word
let case = start_mode.case(cur_mode); let case = self.start_mode.case(cur_mode);
result.push(Word::new_unchecked(&ident[start..], case, start + offset)); let result =
break; Word::new_unchecked(&self.ident[self.start..], case, self.start + self.offset);
return Some(result);
} }
} }
result.into_iter() None
}
}
fn split_ident(ident: &str, offset: usize) -> impl Iterator<Item = Word<'_>> {
SplitIdent::new(ident, offset)
} }
#[cfg(test)] #[cfg(test)]