11use std:: {
22 borrow:: Cow ,
3- cell:: OnceCell ,
43 hash:: { Hash , Hasher } ,
54 sync:: { Arc , OnceLock } ,
65} ;
@@ -21,6 +20,7 @@ use crate::{
2120struct CachedData {
2221 hash : OnceLock < u64 > ,
2322 size : OnceLock < usize > ,
23+ chunks : OnceLock < Vec < & ' static str > > ,
2424 columns_map : OnceLock < Option < SourceMap > > ,
2525 line_only_map : OnceLock < Option < SourceMap > > ,
2626}
@@ -79,19 +79,50 @@ impl CachedSource {
7979 cache : Arc :: new ( CachedData :: default ( ) ) ,
8080 }
8181 }
82+
83+ fn get_or_init_chunks ( & self ) -> & [ & str ] {
84+ self . cache . chunks . get_or_init ( || {
85+ let mut chunks = Vec :: new ( ) ;
86+ self . inner . rope ( & mut |chunk| {
87+ chunks. push ( chunk) ;
88+ } ) ;
89+ #[ allow( unsafe_code) ]
90+ // SAFETY: CachedSource guarantees that the underlying source outlives the cache,
91+ // so transmuting Vec<&str> to Vec<&'static str> is safe in this context.
92+ // This allows us to store string slices in the cache without additional allocations.
93+ unsafe {
94+ std:: mem:: transmute :: < Vec < & str > , Vec < & ' static str > > ( chunks)
95+ }
96+ } )
97+ }
8298}
8399
84100impl Source for CachedSource {
85101 fn source ( & self ) -> SourceValue {
86- self . inner . source ( )
102+ let chunks = self . get_or_init_chunks ( ) ;
103+ let mut string = String :: with_capacity ( self . size ( ) ) ;
104+ for chunk in chunks {
105+ string. push_str ( chunk) ;
106+ }
107+ SourceValue :: String ( Cow :: Owned ( string) )
108+ }
109+
110+ fn rope < ' a > ( & ' a self , on_chunk : & mut dyn FnMut ( & ' a str ) ) {
111+ let chunks = self . get_or_init_chunks ( ) ;
112+ chunks. iter ( ) . for_each ( |chunk| on_chunk ( chunk) ) ;
87113 }
88114
89115 fn buffer ( & self ) -> Cow < [ u8 ] > {
90116 self . inner . buffer ( )
91117 }
92118
93119 fn size ( & self ) -> usize {
94- * self . cache . size . get_or_init ( || self . inner . size ( ) )
120+ * self . cache . size . get_or_init ( || {
121+ if let Some ( chunks) = self . cache . chunks . get ( ) {
122+ return chunks. iter ( ) . fold ( 0 , |acc, chunk| acc + chunk. len ( ) ) ;
123+ }
124+ self . inner . size ( )
125+ } )
95126 }
96127
97128 fn map (
@@ -114,10 +145,6 @@ impl Source for CachedSource {
114145 }
115146 }
116147
117- fn write_to_string ( & self , string : & mut String ) {
118- self . inner . write_to_string ( string) ;
119- }
120-
121148 fn to_writer ( & self , writer : & mut dyn std:: io:: Write ) -> std:: io:: Result < ( ) > {
122149 self . inner . to_writer ( writer)
123150 }
@@ -126,17 +153,17 @@ impl Source for CachedSource {
126153struct CachedSourceChunks < ' source > {
127154 chunks : Box < dyn Chunks + ' source > ,
128155 cache : Arc < CachedData > ,
129- inner : & ' source dyn Source ,
130- source : OnceCell < Cow < ' source , str > > ,
156+ source : Cow < ' source , str > ,
131157}
132158
133159impl < ' a > CachedSourceChunks < ' a > {
134160 fn new ( cache_source : & ' a CachedSource ) -> Self {
161+ let source = cache_source. source ( ) . into_string_lossy ( ) ;
162+
135163 Self {
136164 chunks : cache_source. inner . stream_chunks ( ) ,
137165 cache : cache_source. cache . clone ( ) ,
138- inner : & cache_source. inner ,
139- source : OnceCell :: new ( ) ,
166+ source,
140167 }
141168 }
142169}
@@ -157,22 +184,19 @@ impl Chunks for CachedSourceChunks<'_> {
157184 } ;
158185 match cell. get ( ) {
159186 Some ( map) => {
160- let source = self
161- . source
162- . get_or_init ( || self . inner . source ( ) . into_string_lossy ( ) ) ;
163187 if let Some ( map) = map {
164188 stream_chunks_of_source_map (
165189 options,
166190 object_pool,
167- source. as_ref ( ) ,
191+ self . source . as_ref ( ) ,
168192 map,
169193 on_chunk,
170194 on_source,
171195 on_name,
172196 )
173197 } else {
174198 stream_chunks_of_raw_source (
175- source. as_ref ( ) ,
199+ self . source . as_ref ( ) ,
176200 options,
177201 on_chunk,
178202 on_source,
0 commit comments