@@ -161,14 +161,11 @@ impl ReplaceSource {
161161
162162impl Source for ReplaceSource {
163163 fn source ( & self ) -> SourceValue {
164- let ( chunks, len) = self . rope ( ) ;
165- if chunks. len ( ) == 1 {
166- SourceValue :: String ( Cow :: Borrowed ( chunks[ 0 ] ) )
164+ if self . replacements . len ( ) == 0 {
165+ self . inner . source ( )
167166 } else {
168- let mut string = String :: with_capacity ( len) ;
169- for chunk in chunks {
170- string. push_str ( chunk) ;
171- }
167+ let mut string = String :: with_capacity ( self . size ( ) ) ;
168+ self . write_to_string ( & mut string) ;
172169 SourceValue :: String ( Cow :: Owned ( string) )
173170 }
174171 }
@@ -339,9 +336,103 @@ impl Source for ReplaceSource {
339336 get_map ( & ObjectPool :: default ( ) , chunks. as_ref ( ) , options)
340337 }
341338
339+ #[ allow( unsafe_code) ]
342340 fn write_to_string ( & self , string : & mut String ) {
343- for chunk in self . rope ( ) . 0 {
344- string. push_str ( chunk) ;
341+ let ( inner_chunks, _) = self . inner . rope ( ) ;
342+
343+ let mut pos: usize = 0 ;
344+ let mut replacement_idx: usize = 0 ;
345+ let mut replacement_end: Option < usize > = None ;
346+ let mut next_replacement: Option < usize > = ( replacement_idx
347+ < self . replacements . len ( ) )
348+ . then ( || self . replacements [ replacement_idx] . start as usize ) ;
349+
350+ ' chunk_loop: for chunk in inner_chunks {
351+ let mut chunk_pos = 0 ;
352+ let end_pos = pos + chunk. len ( ) ;
353+
354+ // Skip over when it has been replaced
355+ if let Some ( replacement_end) =
356+ replacement_end. filter ( |replacement_end| * replacement_end > pos)
357+ {
358+ // Skip over the whole chunk
359+ if replacement_end >= end_pos {
360+ pos = end_pos;
361+ continue ;
362+ }
363+ // Partially skip over chunk
364+ chunk_pos = replacement_end - pos;
365+ pos += chunk_pos;
366+ }
367+
368+ // Is a replacement in the chunk?
369+ while let Some ( next_replacement_pos) = next_replacement
370+ . filter ( |next_replacement_pos| * next_replacement_pos < end_pos)
371+ {
372+ if next_replacement_pos > pos {
373+ // Emit chunk until replacement
374+ let offset = next_replacement_pos - pos;
375+ let chunk_slice =
376+ unsafe { & chunk. get_unchecked ( chunk_pos..( chunk_pos + offset) ) } ;
377+ string. push_str ( chunk_slice) ;
378+ chunk_pos += offset;
379+ pos = next_replacement_pos;
380+ }
381+ // Insert replacement content split into chunks by lines
382+ let replacement =
383+ unsafe { & self . replacements . get_unchecked ( replacement_idx) } ;
384+ string. push_str ( & replacement. content ) ;
385+
386+ // Remove replaced content by settings this variable
387+ replacement_end = if let Some ( replacement_end) = replacement_end {
388+ Some ( replacement_end. max ( replacement. end as usize ) )
389+ } else {
390+ Some ( replacement. end as usize )
391+ } ;
392+
393+ // Move to next replacement
394+ replacement_idx += 1 ;
395+ next_replacement = if replacement_idx < self . replacements . len ( ) {
396+ Some ( unsafe {
397+ self . replacements . get_unchecked ( replacement_idx) . start as usize
398+ } )
399+ } else {
400+ None
401+ } ;
402+
403+ // Skip over when it has been replaced
404+ let offset = chunk. len ( ) as i64 - end_pos as i64
405+ + replacement_end. unwrap ( ) as i64
406+ - chunk_pos as i64 ;
407+ if offset > 0 {
408+ // Skip over whole chunk
409+ if replacement_end
410+ . is_some_and ( |replacement_end| replacement_end >= end_pos)
411+ {
412+ pos = end_pos;
413+ continue ' chunk_loop;
414+ }
415+
416+ // Partially skip over chunk
417+ chunk_pos += offset as usize ;
418+ pos += offset as usize ;
419+ }
420+ }
421+
422+ // Emit remaining chunk
423+ if chunk_pos < chunk. len ( ) {
424+ let chunk = unsafe { & chunk. get_unchecked ( chunk_pos..) } ;
425+ string. push_str ( chunk) ;
426+ }
427+ pos = end_pos;
428+ }
429+
430+ // Handle remaining replacements one by one
431+ while replacement_idx < self . replacements . len ( ) {
432+ let content =
433+ unsafe { & self . replacements . get_unchecked ( replacement_idx) . content } ;
434+ string. push_str ( content) ;
435+ replacement_idx += 1 ;
345436 }
346437 }
347438
0 commit comments