@@ -161,109 +161,24 @@ impl ReplaceSource {
161161
162162impl Source for ReplaceSource {
163163 fn source ( & self ) -> SourceValue {
164- let rope = self . rope ( ) ;
165- if rope. len ( ) == 1 {
166- SourceValue :: String ( Cow :: Borrowed ( rope[ 0 ] ) )
167- } else {
168- SourceValue :: String ( Cow :: Owned ( rope. join ( "" ) ) )
164+ if self . replacements . is_empty ( ) {
165+ return self . inner . source ( ) ;
169166 }
170- }
171-
172- fn rope ( & self ) -> Vec < & str > {
173- let inner_rope = self . inner . rope ( ) ;
174- let mut rope =
175- Vec :: with_capacity ( inner_rope. len ( ) + self . replacements . len ( ) * 2 ) ;
176-
177- let mut pos: usize = 0 ;
178- let mut replacement_idx: usize = 0 ;
179- let mut replacement_end: Option < usize > = None ;
180- let mut next_replacement: Option < usize > = ( replacement_idx
181- < self . replacements . len ( ) )
182- . then ( || self . replacements [ replacement_idx] . start as usize ) ;
183-
184- ' chunk_loop: for chunk in inner_rope {
185- let mut chunk_pos = 0 ;
186- let end_pos = pos + chunk. len ( ) ;
187-
188- // Skip over when it has been replaced
189- if let Some ( replacement_end) =
190- replacement_end. filter ( |replacement_end| * replacement_end > pos)
191- {
192- // Skip over the whole chunk
193- if replacement_end >= end_pos {
194- pos = end_pos;
195- continue ;
196- }
197- // Partially skip over chunk
198- chunk_pos = replacement_end - pos;
199- pos += chunk_pos;
200- }
201-
202- // Is a replacement in the chunk?
203- while let Some ( next_replacement_pos) = next_replacement
204- . filter ( |next_replacement_pos| * next_replacement_pos < end_pos)
205- {
206- if next_replacement_pos > pos {
207- // Emit chunk until replacement
208- let offset = next_replacement_pos - pos;
209- let chunk_slice = & chunk[ chunk_pos..( chunk_pos + offset) ] ;
210- rope. push ( chunk_slice) ;
211- chunk_pos += offset;
212- pos = next_replacement_pos;
213- }
214- // Insert replacement content split into chunks by lines
215- let replacement = & self . replacements [ replacement_idx] ;
216- rope. push ( & replacement. content ) ;
217-
218- // Remove replaced content by settings this variable
219- replacement_end = if let Some ( replacement_end) = replacement_end {
220- Some ( replacement_end. max ( replacement. end as usize ) )
221- } else {
222- Some ( replacement. end as usize )
223- } ;
224-
225- // Move to next replacement
226- replacement_idx += 1 ;
227- next_replacement = if replacement_idx < self . replacements . len ( ) {
228- Some ( self . replacements [ replacement_idx] . start as usize )
229- } else {
230- None
231- } ;
232-
233- // Skip over when it has been replaced
234- let offset = chunk. len ( ) as i64 - end_pos as i64
235- + replacement_end. unwrap ( ) as i64
236- - chunk_pos as i64 ;
237- if offset > 0 {
238- // Skip over whole chunk
239- if replacement_end
240- . is_some_and ( |replacement_end| replacement_end >= end_pos)
241- {
242- pos = end_pos;
243- continue ' chunk_loop;
244- }
245-
246- // Partially skip over chunk
247- chunk_pos += offset as usize ;
248- pos += offset as usize ;
249- }
250- }
251-
252- // Emit remaining chunk
253- if chunk_pos < chunk. len ( ) {
254- rope. push ( & chunk[ chunk_pos..] ) ;
255- }
256- pos = end_pos;
167+ let mut string = String :: with_capacity ( self . size ( ) ) ;
168+ for chunk in self . rope ( ) {
169+ string. push_str ( chunk) ;
257170 }
171+ SourceValue :: String ( Cow :: Owned ( string) )
172+ }
258173
259- // Handle remaining replacements one by one
260- while replacement_idx < self . replacements . len ( ) {
261- let content = & self . replacements [ replacement_idx] . content ;
262- rope. push ( content) ;
263- replacement_idx += 1 ;
174+ fn rope ( & self ) -> Box < dyn Iterator < Item = & str > + ' _ > {
175+ if self . replacements . is_empty ( ) {
176+ return self . inner . rope ( ) ;
264177 }
265-
266- rope
178+ Box :: new ( ReplaceSourceRopeIterator :: new (
179+ self . inner . rope ( ) ,
180+ & self . replacements ,
181+ ) )
267182 }
268183
269184 fn buffer ( & self ) -> Cow < [ u8 ] > {
@@ -897,6 +812,191 @@ impl PartialEq for ReplaceSource {
897812
898813impl Eq for ReplaceSource { }
899814
815+ /// Iterator for ReplaceSource rope that applies replacements on the fly
816+ pub struct ReplaceSourceRopeIterator < ' a > {
817+ inner_rope : Box < dyn Iterator < Item = & ' a str > + ' a > ,
818+ replacements : & ' a [ Replacement ] ,
819+
820+ // Current state
821+ pos : usize ,
822+ replacement_idx : usize ,
823+ replacement_end : Option < usize > ,
824+
825+ // Current chunk state
826+ current_chunk : Option < & ' a str > ,
827+ chunk_pos : usize ,
828+ chunk_start_pos : usize ,
829+
830+ // Buffer for remaining replacements
831+ remaining_replacements : std:: collections:: VecDeque < & ' a str > ,
832+
833+ // Flag to indicate if we're done with inner rope
834+ inner_exhausted : bool ,
835+ }
836+
837+ impl < ' a > ReplaceSourceRopeIterator < ' a > {
838+ fn new (
839+ inner_rope : Box < dyn Iterator < Item = & ' a str > + ' a > ,
840+ replacements : & ' a [ Replacement ] ,
841+ ) -> Self {
842+ Self {
843+ inner_rope,
844+ replacements,
845+ pos : 0 ,
846+ replacement_idx : 0 ,
847+ replacement_end : None ,
848+ current_chunk : None ,
849+ chunk_pos : 0 ,
850+ chunk_start_pos : 0 ,
851+ remaining_replacements : std:: collections:: VecDeque :: new ( ) ,
852+ inner_exhausted : false ,
853+ }
854+ }
855+
856+ fn get_next_replacement_pos ( & self ) -> Option < usize > {
857+ if self . replacement_idx < self . replacements . len ( ) {
858+ Some ( self . replacements [ self . replacement_idx ] . start as usize )
859+ } else {
860+ None
861+ }
862+ }
863+
864+ fn advance_to_next_chunk ( & mut self ) -> bool {
865+ if let Some ( chunk) = self . inner_rope . next ( ) {
866+ self . current_chunk = Some ( chunk) ;
867+ self . chunk_pos = 0 ;
868+ self . chunk_start_pos = self . pos ;
869+ true
870+ } else {
871+ self . inner_exhausted = true ;
872+ false
873+ }
874+ }
875+
876+ fn process_current_chunk ( & mut self ) -> Option < & ' a str > {
877+ let chunk = self . current_chunk ?;
878+ let chunk_end_pos = self . chunk_start_pos + chunk. len ( ) ;
879+
880+ // Skip over when it has been replaced
881+ if let Some ( replacement_end) =
882+ self . replacement_end . filter ( |& end| end > self . pos )
883+ {
884+ if replacement_end >= chunk_end_pos {
885+ // Skip entire chunk
886+ self . pos = chunk_end_pos;
887+ self . current_chunk = None ;
888+ return None ;
889+ }
890+
891+ // Partially skip chunk
892+ let skip_amount = replacement_end - self . pos ;
893+ self . chunk_pos += skip_amount;
894+ self . pos += skip_amount;
895+ }
896+
897+ // Check for replacements in current chunk
898+ if let Some ( next_replacement_pos) = self
899+ . get_next_replacement_pos ( )
900+ . filter ( |& pos| pos < chunk_end_pos)
901+ {
902+ if next_replacement_pos > self . pos {
903+ // Emit chunk until replacement
904+ let offset = next_replacement_pos - self . pos ;
905+ let chunk_slice = & chunk[ self . chunk_pos ..( self . chunk_pos + offset) ] ;
906+ self . chunk_pos += offset;
907+ self . pos = next_replacement_pos;
908+ return Some ( chunk_slice) ;
909+ }
910+
911+ // Add replacement content
912+ let replacement = & self . replacements [ self . replacement_idx ] ;
913+ self . remaining_replacements . push_back ( & replacement. content ) ;
914+
915+ // Update replacement_end
916+ self . replacement_end = Some (
917+ self
918+ . replacement_end
919+ . map ( |end| end. max ( replacement. end as usize ) )
920+ . unwrap_or ( replacement. end as usize ) ,
921+ ) ;
922+
923+ // Move to next replacement
924+ self . replacement_idx += 1 ;
925+
926+ // Skip replaced content
927+ let offset = chunk. len ( ) as i64 - chunk_end_pos as i64
928+ + self . replacement_end . unwrap ( ) as i64
929+ - self . chunk_pos as i64 ;
930+
931+ if offset > 0 {
932+ if self . replacement_end . is_some_and ( |end| end >= chunk_end_pos) {
933+ // Skip entire remaining chunk
934+ self . pos = chunk_end_pos;
935+ self . current_chunk = None ;
936+ return self . remaining_replacements . pop_front ( ) ;
937+ }
938+
939+ // Partially skip chunk
940+ self . chunk_pos += offset as usize ;
941+ self . pos += offset as usize ;
942+ }
943+
944+ // Return the replacement content
945+ return self . remaining_replacements . pop_front ( ) ;
946+ }
947+
948+ // Emit remaining chunk
949+ if self . chunk_pos < chunk. len ( ) {
950+ let remaining = & chunk[ self . chunk_pos ..] ;
951+ self . pos = chunk_end_pos;
952+ self . current_chunk = None ;
953+ Some ( remaining)
954+ } else {
955+ self . pos = chunk_end_pos;
956+ self . current_chunk = None ;
957+ None
958+ }
959+ }
960+ }
961+
962+ impl < ' a > Iterator for ReplaceSourceRopeIterator < ' a > {
963+ type Item = & ' a str ;
964+
965+ fn next ( & mut self ) -> Option < Self :: Item > {
966+ // First, check if we have buffered replacement content
967+ if let Some ( replacement) = self . remaining_replacements . pop_front ( ) {
968+ return Some ( replacement) ;
969+ }
970+
971+ // Process current chunk if we have one
972+ if self . current_chunk . is_some ( ) {
973+ if let Some ( result) = self . process_current_chunk ( ) {
974+ return Some ( result) ;
975+ }
976+ }
977+
978+ // Advance to next chunk if inner rope is not exhausted
979+ while !self . inner_exhausted {
980+ if !self . advance_to_next_chunk ( ) {
981+ break ;
982+ }
983+
984+ if let Some ( result) = self . process_current_chunk ( ) {
985+ return Some ( result) ;
986+ }
987+ }
988+
989+ // Handle remaining replacements when inner rope is exhausted
990+ if self . replacement_idx < self . replacements . len ( ) {
991+ let replacement = & self . replacements [ self . replacement_idx ] ;
992+ self . replacement_idx += 1 ;
993+ Some ( & replacement. content )
994+ } else {
995+ None
996+ }
997+ }
998+ }
999+
9001000#[ cfg( test) ]
9011001mod tests {
9021002 use rustc_hash:: FxHasher ;
0 commit comments