Skip to content

Commit faf2138

Browse files
authored
perf: remove VecDeque in CharIndices (#179)
1 parent 4bc9e33 commit faf2138

File tree

3 files changed

+33
-50
lines changed

3 files changed

+33
-50
lines changed

Cargo.lock

Lines changed: 0 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ simd-json = "0.14.3"
4545

4646
[dev-dependencies]
4747
twox-hash = "2.1.0"
48-
base64-simd = "0.8.0"
4948
regex = "1.11.1"
5049
criterion = { version = "0.5.1", default-features = false }
5150

src/rope.rs

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
use std::{
44
borrow::Cow,
5-
collections::VecDeque,
65
hash::Hash,
76
ops::{Bound, RangeBounds},
87
rc::Rc,
@@ -136,7 +135,7 @@ impl<'a> Rope<'a> {
136135
}
137136

138137
/// Returns an iterator over the characters and their byte positions.
139-
pub fn char_indices(&self) -> CharIndices<'_, 'a> {
138+
pub fn char_indices(&self) -> CharIndices<'_> {
140139
match &self.repr {
141140
Repr::Light(s) => CharIndices {
142141
iter: CharIndicesEnum::Light {
@@ -146,8 +145,9 @@ impl<'a> Rope<'a> {
146145
Repr::Full(data) => CharIndices {
147146
iter: CharIndicesEnum::Full {
148147
chunks: data,
149-
char_indices: VecDeque::new(),
150148
chunk_index: 0,
149+
iter: data[0].0.char_indices(),
150+
start_pos: 0,
151151
},
152152
},
153153
}
@@ -729,51 +729,58 @@ impl<'a> Iterator for Lines<'_, 'a> {
729729
}
730730
}
731731

732-
enum CharIndicesEnum<'a, 'b> {
732+
enum CharIndicesEnum<'a> {
733733
Light {
734-
iter: std::str::CharIndices<'b>,
734+
iter: std::str::CharIndices<'a>,
735735
},
736736
Full {
737-
chunks: &'a [(&'b str, usize)],
738-
char_indices: VecDeque<(usize, char)>,
737+
chunks: &'a [(&'a str, usize)],
739738
chunk_index: usize,
739+
start_pos: usize,
740+
iter: std::str::CharIndices<'a>,
740741
},
741742
}
742743

743-
pub struct CharIndices<'a, 'b> {
744-
iter: CharIndicesEnum<'a, 'b>,
744+
pub struct CharIndices<'a> {
745+
iter: CharIndicesEnum<'a>,
745746
}
746747

747-
impl Iterator for CharIndices<'_, '_> {
748+
impl Iterator for CharIndices<'_> {
748749
type Item = (usize, char);
749750

750751
fn next(&mut self) -> Option<Self::Item> {
751752
match &mut self.iter {
752753
CharIndicesEnum::Light { iter } => iter.next(),
753754
CharIndicesEnum::Full {
754755
chunks,
755-
char_indices,
756756
chunk_index,
757+
iter,
758+
start_pos,
757759
} => {
758-
if let Some(item) = char_indices.pop_front() {
759-
return Some(item);
760+
if let Some((i, c)) = iter.next() {
761+
return Some((*start_pos + i, c));
760762
}
761-
762-
if *chunk_index >= chunks.len() {
763-
return None;
764-
}
765-
766-
// skip empty chunks
767-
while *chunk_index < chunks.len() && chunks[*chunk_index].0.is_empty() {
763+
loop {
764+
if *chunk_index == chunks.len() - 1 {
765+
return None;
766+
}
768767
*chunk_index += 1;
769-
}
768+
// SAFETY: We just checked bounds above
769+
let (chunk, next_start_pos) =
770+
unsafe { chunks.get_unchecked(*chunk_index) };
771+
772+
// Skip empty chunks without creating iterators
773+
if chunk.is_empty() {
774+
continue;
775+
}
770776

771-
let (chunk, start_pos) = chunks[*chunk_index];
777+
*iter = chunk.char_indices();
778+
*start_pos = *next_start_pos;
772779

773-
char_indices
774-
.extend(chunk.char_indices().map(|(i, c)| (start_pos + i, c)));
775-
*chunk_index += 1;
776-
char_indices.pop_front()
780+
if let Some((i, c)) = iter.next() {
781+
return Some((*start_pos + i, c));
782+
}
783+
}
777784
}
778785
}
779786
}

0 commit comments

Comments
 (0)