@@ -161,34 +161,150 @@ impl ReplaceSource {
161161
162162impl Source for ReplaceSource {
163163 fn source ( & self ) -> SourceValue {
164- let inner_source_code = self . inner . source ( ) . into_string_lossy ( ) ;
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 ( "" ) ) )
169+ }
170+ }
171+
172+ fn rope ( & self ) -> Vec < & str > {
173+ let inner_source_code = self . inner . rope ( ) ;
165174
166- // mut_string_push_str is faster that vec join
167- // concatenate strings benchmark, see https://github.com/hoodie/concatenation_benchmarks-rs
168175 if self . replacements . is_empty ( ) {
169- return SourceValue :: String ( inner_source_code) ;
176+ return inner_source_code;
170177 }
171- let capacity = self . size ( ) ;
172- let mut source_code = String :: with_capacity ( capacity) ;
173- let mut inner_pos = 0 ;
174- for replacement in & self . replacements {
175- if inner_pos < replacement. start {
176- let end_pos = ( replacement. start as usize ) . min ( inner_source_code. len ( ) ) ;
177- source_code. push_str ( & inner_source_code[ inner_pos as usize ..end_pos] ) ;
178+
179+ let mut result = Vec :: new ( ) ;
180+ let mut pos: u32 = 0 ;
181+ let mut chunk_index = 0 ;
182+ let mut chunk_pos = 0 ; // Position within current chunk
183+ let mut replacement_index = 0 ;
184+
185+ // Calculate total length to determine positions
186+ let mut chunk_start_positions = Vec :: new ( ) ;
187+ let mut total_pos = 0 ;
188+ for chunk in & inner_source_code {
189+ chunk_start_positions. push ( total_pos) ;
190+ total_pos += chunk. len ( ) as u32 ;
191+ }
192+
193+ while replacement_index < self . replacements . len ( )
194+ || chunk_index < inner_source_code. len ( )
195+ {
196+ let next_replacement = self . replacements . get ( replacement_index) ;
197+
198+ // Process chunks until we hit a replacement or finish
199+ while chunk_index < inner_source_code. len ( ) {
200+ let chunk = inner_source_code[ chunk_index] ;
201+ let chunk_start = chunk_start_positions[ chunk_index] ;
202+ let chunk_end = chunk_start + chunk. len ( ) as u32 ;
203+
204+ // Check if there's a replacement that starts within this chunk
205+ if let Some ( replacement) = next_replacement {
206+ if replacement. start >= chunk_start && replacement. start < chunk_end {
207+ // Replacement starts within this chunk
208+ let offset_in_chunk = ( replacement. start - chunk_start) as usize ;
209+
210+ // Add the part of chunk before replacement
211+ if offset_in_chunk > chunk_pos {
212+ result. push ( & chunk[ chunk_pos..offset_in_chunk] ) ;
213+ }
214+
215+ // Add replacement content
216+ result. push ( & replacement. content ) ;
217+
218+ // Update positions
219+ pos = replacement. end ;
220+ replacement_index += 1 ;
221+
222+ // Find where to continue after replacement
223+ let mut found_continue_pos = false ;
224+ for ( idx, & chunk_start_pos) in
225+ chunk_start_positions. iter ( ) . enumerate ( )
226+ {
227+ let chunk_end_pos =
228+ chunk_start_pos + inner_source_code[ idx] . len ( ) as u32 ;
229+
230+ if pos >= chunk_start_pos && pos < chunk_end_pos {
231+ // Continue from within this chunk
232+ chunk_index = idx;
233+ chunk_pos = ( pos - chunk_start_pos) as usize ;
234+ found_continue_pos = true ;
235+ break ;
236+ } else if pos <= chunk_start_pos {
237+ // Continue from the start of this chunk
238+ chunk_index = idx;
239+ chunk_pos = 0 ;
240+ found_continue_pos = true ;
241+ break ;
242+ }
243+ }
244+
245+ if !found_continue_pos {
246+ // Replacement goes beyond all chunks
247+ chunk_index = inner_source_code. len ( ) ;
248+ }
249+
250+ break ;
251+ } else if replacement. start < chunk_start {
252+ // Replacement starts before this chunk
253+ result. push ( & replacement. content ) ;
254+ replacement_index += 1 ;
255+
256+ // Skip chunks that are replaced
257+ pos = replacement. end ;
258+ while chunk_index < inner_source_code. len ( ) {
259+ let current_chunk_start = chunk_start_positions[ chunk_index] ;
260+ let current_chunk_end = current_chunk_start
261+ + inner_source_code[ chunk_index] . len ( ) as u32 ;
262+
263+ if pos <= current_chunk_start {
264+ // Start from beginning of this chunk
265+ chunk_pos = 0 ;
266+ break ;
267+ } else if pos < current_chunk_end {
268+ // Start from middle of this chunk
269+ chunk_pos = ( pos - current_chunk_start) as usize ;
270+ break ;
271+ } else {
272+ // Skip this entire chunk
273+ chunk_index += 1 ;
274+ }
275+ }
276+ break ;
277+ }
278+ }
279+
280+ // No replacement affecting this chunk, add the remaining part
281+ if chunk_pos == 0
282+ && ( next_replacement. is_none ( )
283+ || next_replacement. unwrap ( ) . start > chunk_end)
284+ {
285+ // Add entire chunk
286+ result. push ( chunk) ;
287+ } else if chunk_pos < chunk. len ( ) {
288+ // Add remaining part of chunk
289+ result. push ( & chunk[ chunk_pos..] ) ;
290+ }
291+
292+ chunk_index += 1 ;
293+ chunk_pos = 0 ;
294+ pos = chunk_end;
178295 }
179- source_code. push_str ( & replacement. content ) ;
180- #[ allow( clippy:: manual_clamp) ]
181- {
182- inner_pos = inner_pos
183- . max ( replacement. end )
184- . min ( inner_source_code. len ( ) as u32 ) ;
296+
297+ // Handle remaining replacements that are beyond all chunks
298+ while replacement_index < self . replacements . len ( ) {
299+ let replacement = & self . replacements [ replacement_index] ;
300+ if replacement. start >= pos {
301+ result. push ( & replacement. content ) ;
302+ }
303+ replacement_index += 1 ;
185304 }
186305 }
187- source_code. push_str (
188- & inner_source_code[ inner_pos as usize ..inner_source_code. len ( ) ] ,
189- ) ;
190306
191- SourceValue :: String ( Cow :: Owned ( source_code ) )
307+ result
192308 }
193309
194310 fn buffer ( & self ) -> Cow < [ u8 ] > {
@@ -248,10 +364,6 @@ impl Source for ReplaceSource {
248364 get_map ( & ObjectPool :: default ( ) , chunks. as_ref ( ) , options)
249365 }
250366
251- fn write_to_string ( & self , string : & mut String ) {
252- string. push_str ( & self . source ( ) . into_string_lossy ( ) ) ;
253- }
254-
255367 fn to_writer ( & self , writer : & mut dyn std:: io:: Write ) -> std:: io:: Result < ( ) > {
256368 writer. write_all ( self . source ( ) . as_bytes ( ) )
257369 }
0 commit comments