Skip to content

Commit 118f6ea

Browse files
committed
perf: ReplaceSourceRopeIterator
1 parent 34505cb commit 118f6ea

File tree

1 file changed

+96
-147
lines changed

1 file changed

+96
-147
lines changed

src/replace_source.rs

Lines changed: 96 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -906,13 +906,6 @@ impl PartialEq for ReplaceSource {
906906

907907
impl Eq for ReplaceSource {}
908908

909-
enum IteratorState {
910-
ProcessingChunk,
911-
EmittingReplacement,
912-
FinalReplacements,
913-
Done,
914-
}
915-
916909
/// Iterator for ReplaceSource rope that applies replacements on the fly
917910
pub struct ReplaceSourceRopeIterator<'a> {
918911
inner_chunks: Box<dyn Iterator<Item = &'a str> + 'a>,
@@ -924,15 +917,14 @@ pub struct ReplaceSourceRopeIterator<'a> {
924917
current_chunk: Option<&'a str>,
925918
current_chunk_start: usize,
926919
current_chunk_pos: usize,
927-
state: IteratorState,
928920
}
929921

930922
impl<'a> ReplaceSourceRopeIterator<'a> {
931923
fn new(
932924
inner_chunks: Box<dyn Iterator<Item = &'a str> + 'a>,
933925
replacements: &'a [Replacement],
934926
) -> Self {
935-
let mut iter = Self {
927+
Self {
936928
inner_chunks,
937929
replacements,
938930
pos: 0,
@@ -942,166 +934,123 @@ impl<'a> ReplaceSourceRopeIterator<'a> {
942934
current_chunk: None,
943935
current_chunk_start: 0,
944936
current_chunk_pos: 0,
945-
state: IteratorState::ProcessingChunk,
946-
};
947-
948-
iter.load_next_chunk();
949-
iter
950-
}
951-
952-
fn load_next_chunk(&mut self) {
953-
self.current_chunk = self.inner_chunks.next();
954-
self.current_chunk_pos = 0;
955-
if self.current_chunk.is_some() {
956-
self.current_chunk_start = self.pos;
957-
} else {
958-
self.state = IteratorState::FinalReplacements;
959937
}
960938
}
939+
}
961940

962-
fn skip_replaced_content(&mut self) -> bool {
963-
if let Some(replacement_end) = self.replacement_end {
964-
if replacement_end > self.pos {
965-
let chunk_end =
966-
self.current_chunk_start + self.current_chunk.unwrap().len();
941+
impl<'a> Iterator for ReplaceSourceRopeIterator<'a> {
942+
type Item = &'a str;
967943

968-
// Skip the entire chunk
969-
if replacement_end >= chunk_end {
970-
self.pos = chunk_end;
971-
self.load_next_chunk();
972-
return true;
944+
#[allow(unsafe_code)]
945+
fn next(&mut self) -> Option<Self::Item> {
946+
loop {
947+
// Load next chunk (if needed)
948+
if self.current_chunk.is_none() {
949+
self.current_chunk = self.inner_chunks.next();
950+
self.current_chunk_pos = 0;
951+
if self.current_chunk.is_some() {
952+
self.current_chunk_start = self.pos;
953+
} else {
954+
// No more chunks, handle remaining replacements
955+
return if self.replacement_idx < self.replacements.len() {
956+
let content = unsafe {
957+
&self
958+
.replacements
959+
.get_unchecked(self.replacement_idx)
960+
.content
961+
};
962+
self.replacement_idx += 1;
963+
Some(content)
964+
} else {
965+
None
966+
};
973967
}
974-
975-
// Partially skip the chunk
976-
let skip_len = replacement_end - self.pos;
977-
self.current_chunk_pos += skip_len;
978-
self.pos += skip_len;
979-
}
980-
}
981-
false
982-
}
983-
984-
fn process_current_chunk(&mut self) -> Option<&'a str> {
985-
let chunk = self.current_chunk?;
986-
let chunk_end = self.current_chunk_start + chunk.len();
987-
988-
// Check if there are replacements in the current chunk
989-
while let Some(next_repl_pos) =
990-
self.next_replacement.filter(|&pos| pos < chunk_end)
991-
{
992-
if next_repl_pos > self.pos {
993-
// Return content before replacement
994-
let offset = next_repl_pos - self.pos;
995-
let result =
996-
&chunk[self.current_chunk_pos..self.current_chunk_pos + offset];
997-
self.current_chunk_pos += offset;
998-
self.pos = next_repl_pos;
999-
return Some(result);
1000968
}
1001969

1002-
// Prepare to return replacement content
1003-
self.state = IteratorState::EmittingReplacement;
1004-
return None;
1005-
}
1006-
1007-
// Return remaining content of the chunk
1008-
if self.current_chunk_pos < chunk.len() {
1009-
let result = &chunk[self.current_chunk_pos..];
1010-
self.pos = chunk_end;
1011-
self.load_next_chunk();
1012-
Some(result)
1013-
} else {
1014-
self.load_next_chunk();
1015-
None
1016-
}
1017-
}
1018-
1019-
fn emit_replacement(&mut self) -> Option<&'a str> {
1020-
let replacement = &self.replacements[self.replacement_idx];
1021-
let content = &replacement.content;
1022-
1023-
// Update replacement state
1024-
self.replacement_end = Some(
1025-
self
1026-
.replacement_end
1027-
.map_or(replacement.end as usize, |end| {
1028-
end.max(replacement.end as usize)
1029-
}),
1030-
);
1031-
1032-
self.replacement_idx += 1;
1033-
self.next_replacement = self
1034-
.replacements
1035-
.get(self.replacement_idx)
1036-
.map(|r| r.start as usize);
970+
let chunk = self.current_chunk.unwrap();
971+
let chunk_end = self.current_chunk_start + chunk.len();
1037972

1038-
// Check if we need to skip replaced content
1039-
if let Some(replacement_end) = self.replacement_end {
1040-
if replacement_end > self.pos {
1041-
self.pos = replacement_end;
1042-
1043-
// If current chunk needs to be skipped, reload it
1044-
if let Some(chunk) = self.current_chunk {
1045-
let chunk_end = self.current_chunk_start + chunk.len();
973+
// Skip replaced content
974+
if let Some(replacement_end) = self.replacement_end {
975+
if replacement_end > self.pos {
1046976
if replacement_end >= chunk_end {
1047-
self.load_next_chunk();
977+
// Skip entire chunk
978+
self.pos = chunk_end;
979+
self.current_chunk = None;
980+
continue;
1048981
} else {
1049-
self.current_chunk_pos = replacement_end - self.current_chunk_start;
982+
// Partially skip chunk
983+
let skip_len = replacement_end - self.pos;
984+
self.current_chunk_pos += skip_len;
985+
self.pos += skip_len;
1050986
}
1051987
}
1052988
}
1053-
}
1054-
1055-
self.state = IteratorState::ProcessingChunk;
1056-
Some(content)
1057-
}
1058-
1059-
fn emit_final_replacements(&mut self) -> Option<&'a str> {
1060-
if self.replacement_idx < self.replacements.len() {
1061-
let content = &self.replacements[self.replacement_idx].content;
1062-
self.replacement_idx += 1;
1063-
Some(content)
1064-
} else {
1065-
self.state = IteratorState::Done;
1066-
None
1067-
}
1068-
}
1069-
}
1070-
1071-
impl<'a> Iterator for ReplaceSourceRopeIterator<'a> {
1072-
type Item = &'a str;
1073989

1074-
fn next(&mut self) -> Option<Self::Item> {
1075-
loop {
1076-
match self.state {
1077-
IteratorState::ProcessingChunk => {
1078-
if self.skip_replaced_content() {
1079-
continue;
1080-
}
990+
// Check if there are replacements in the current chunk
991+
if let Some(next_repl_pos) =
992+
self.next_replacement.filter(|&pos| pos < chunk_end)
993+
{
994+
if next_repl_pos > self.pos {
995+
// Return content before replacement
996+
let offset = next_repl_pos - self.pos;
997+
let result = unsafe {
998+
chunk.get_unchecked(
999+
self.current_chunk_pos..self.current_chunk_pos + offset,
1000+
)
1001+
};
1002+
self.current_chunk_pos += offset;
1003+
self.pos = next_repl_pos;
1004+
return Some(result);
1005+
}
10811006

1082-
if let Some(result) = self.process_current_chunk() {
1083-
return Some(result);
1084-
}
1007+
// Process replacement
1008+
let replacement =
1009+
unsafe { self.replacements.get_unchecked(self.replacement_idx) };
1010+
let content = &replacement.content;
10851011

1086-
if self.current_chunk.is_none() {
1087-
self.state = IteratorState::FinalReplacements;
1088-
}
1089-
}
1012+
// Move to next replacement
1013+
self.replacement_end = Some(
1014+
self
1015+
.replacement_end
1016+
.map_or(replacement.end as usize, |end| {
1017+
end.max(replacement.end as usize)
1018+
}),
1019+
);
10901020

1091-
IteratorState::EmittingReplacement => {
1092-
if let Some(result) = self.emit_replacement() {
1093-
return Some(result);
1021+
// Update position (skip replaced content)
1022+
self.replacement_idx += 1;
1023+
self.next_replacement = self
1024+
.replacements
1025+
.get(self.replacement_idx)
1026+
.map(|r| r.start as usize);
1027+
1028+
// Update position (skip replaced content)
1029+
if let Some(replacement_end) = self.replacement_end {
1030+
if replacement_end > self.pos {
1031+
self.pos = replacement_end;
1032+
// If current chunk needs to be skipped, reset it
1033+
if replacement_end >= chunk_end {
1034+
self.current_chunk = None;
1035+
} else {
1036+
self.current_chunk_pos =
1037+
replacement_end - self.current_chunk_start;
1038+
}
10941039
}
10951040
}
10961041

1097-
IteratorState::FinalReplacements => {
1098-
if let Some(result) = self.emit_final_replacements() {
1099-
return Some(result);
1100-
}
1101-
}
1042+
return Some(content);
1043+
}
11021044

1103-
IteratorState::Done => return None,
1045+
// Return remaining chunk content
1046+
if self.current_chunk_pos < chunk.len() {
1047+
let result = unsafe { chunk.get_unchecked(self.current_chunk_pos..) };
1048+
self.pos = chunk_end;
1049+
self.current_chunk = None;
1050+
return Some(result);
11041051
}
1052+
1053+
self.current_chunk = None;
11051054
}
11061055
}
11071056
}

0 commit comments

Comments
 (0)