11use std:: {
22 borrow:: Cow ,
3- hash:: { Hash , Hasher } ,
4- sync:: OnceLock ,
3+ hash:: { BuildHasherDefault , Hash , Hasher } ,
4+ sync:: { Arc , OnceLock } ,
55} ;
66
7+ use dashmap:: { mapref:: entry:: Entry , DashMap } ;
78use rustc_hash:: FxHasher ;
89
910use crate :: {
@@ -12,7 +13,7 @@ use crate::{
1213 stream_chunks_of_source_map, StreamChunks ,
1314 } ,
1415 rope:: Rope ,
15- BoxSource , MapOptions , Source , SourceExt , SourceMap ,
16+ MapOptions , Source , SourceMap ,
1617} ;
1718
1819/// It tries to reused cached results from other methods to avoid calculations,
@@ -48,61 +49,36 @@ use crate::{
4849/// "Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n"
4950/// );
5051/// ```
51-
52- #[ derive( Debug ) ]
53- struct CachedSourceOwner {
54- inner : BoxSource ,
55- cached_hash : OnceLock < u64 > ,
56- }
57-
58- #[ derive( Debug ) ]
59- struct CachedSourceDependent < ' a > {
60- cached_colomns_map : OnceLock < Option < Cow < ' static , SourceMap < ' static > > > > ,
61- cached_line_only_map : OnceLock < Option < Cow < ' static , SourceMap < ' static > > > > ,
62- phantom : std:: marker:: PhantomData < & ' a ( ) > ,
52+ pub struct CachedSource < T > {
53+ inner : Arc < T > ,
54+ cached_hash : Arc < OnceLock < u64 > > ,
55+ cached_maps :
56+ Arc < DashMap < MapOptions , Option < SourceMap > , BuildHasherDefault < FxHasher > > > ,
6357}
6458
65- self_cell:: self_cell!(
66- struct CachedSourceCell {
67- owner: CachedSourceOwner ,
68-
69- #[ covariant]
70- dependent: CachedSourceDependent ,
71- }
72-
73- impl { Debug }
74- ) ;
75-
76- /// A wrapper around any [`Source`] that caches expensive computations to improve performance.
77- pub struct CachedSource ( CachedSourceCell ) ;
78-
79- impl CachedSource {
59+ impl < T > CachedSource < T > {
8060 /// Create a [CachedSource] with the original [Source].
81- pub fn new < T : SourceExt > ( inner : T ) -> Self {
82- let owner = CachedSourceOwner {
83- inner : inner. boxed ( ) ,
84- cached_hash : OnceLock :: new ( ) ,
85- } ;
86- Self ( CachedSourceCell :: new ( owner, |_| CachedSourceDependent {
87- cached_colomns_map : Default :: default ( ) ,
88- cached_line_only_map : Default :: default ( ) ,
89- phantom : std:: marker:: PhantomData ,
90- } ) )
61+ pub fn new ( inner : T ) -> Self {
62+ Self {
63+ inner : Arc :: new ( inner) ,
64+ cached_hash : Default :: default ( ) ,
65+ cached_maps : Default :: default ( ) ,
66+ }
9167 }
9268
9369 /// Get the original [Source].
94- pub fn original ( & self ) -> & BoxSource {
95- & self . 0 . borrow_owner ( ) . inner
70+ pub fn original ( & self ) -> & T {
71+ & self . inner
9672 }
9773}
9874
99- impl Source for CachedSource {
75+ impl < T : Source + Hash + PartialEq + Eq + ' static > Source for CachedSource < T > {
10076 fn source ( & self ) -> Cow < str > {
101- self . 0 . borrow_owner ( ) . inner . source ( )
77+ self . inner . source ( )
10278 }
10379
10480 fn rope ( & self ) -> Rope < ' _ > {
105- self . 0 . borrow_owner ( ) . inner . rope ( )
81+ self . inner . rope ( )
10682 }
10783
10884 fn buffer ( & self ) -> Cow < [ u8 ] > {
@@ -112,134 +88,101 @@ impl Source for CachedSource {
11288 }
11389
11490 fn size ( & self ) -> usize {
115- self . 0 . borrow_owner ( ) . inner . size ( )
91+ self . inner . size ( )
11692 }
11793
118- fn map < ' a > ( & ' a self , options : & MapOptions ) -> Option < Cow < ' a , SourceMap < ' a > > > {
119- fn get_or_init_cache < ' b > (
120- owner : & ' b CachedSourceOwner ,
121- cache : & ' b OnceLock < Option < Cow < ' static , SourceMap < ' static > > > > ,
122- options : & MapOptions ,
123- ) -> Option < Cow < ' b , SourceMap < ' b > > > {
124- cache
125- . get_or_init ( || {
126- let map = owner. inner . map ( options) ;
127- // SAFETY: This transmute is safe because:
128- // 1. BoxSource is an immutable wrapper around Arc<dyn Source>, ensuring the underlying
129- // data remains stable throughout the CachedSource's lifetime
130- // 2. The SourceMap references string data that lives in the BoxSource, which is owned
131- // by the CachedSourceOwner and guaranteed to outlive any cached references
132- // 3. The self_cell structure ensures that the dependent (cache) cannot outlive the
133- // owner (BoxSource), maintaining memory safety invariants
134- // 4. We're extending the lifetime to 'static for caching purposes, but the actual
135- // data lifetime is managed by the self-referential structure
136- #[ allow( unsafe_code) ]
137- unsafe { std:: mem:: transmute :: < _ , Option < Cow < ' static , SourceMap < ' static > > > > ( map) }
138- } )
139- . as_ref ( )
140- . map ( |map| {
141- Cow :: Borrowed ( map. as_ref ( ) )
142- } )
143- }
144-
145- if options. columns {
146- self . 0 . with_dependent ( |owner, dependent| {
147- get_or_init_cache ( owner, & dependent. cached_colomns_map , options)
148- } )
94+ fn map ( & self , options : & MapOptions ) -> Option < SourceMap > {
95+ if let Some ( map) = self . cached_maps . get ( options) {
96+ map. clone ( )
14997 } else {
150- self . 0 . with_dependent ( |owner , dependent| {
151- get_or_init_cache ( owner , & dependent . cached_line_only_map , options )
152- } )
98+ let map = self . inner . map ( options ) ;
99+ self . cached_maps . insert ( options . clone ( ) , map . clone ( ) ) ;
100+ map
153101 }
154102 }
155103
156104 fn to_writer ( & self , writer : & mut dyn std:: io:: Write ) -> std:: io:: Result < ( ) > {
157- self . 0 . borrow_owner ( ) . inner . to_writer ( writer)
105+ self . inner . to_writer ( writer)
158106 }
159107}
160108
161- impl StreamChunks for CachedSource {
109+ impl < T : Source + Hash + PartialEq + Eq + ' static > StreamChunks
110+ for CachedSource < T >
111+ {
162112 fn stream_chunks < ' a > (
163113 & ' a self ,
164114 options : & MapOptions ,
165115 on_chunk : crate :: helpers:: OnChunk < ' _ , ' a > ,
166116 on_source : crate :: helpers:: OnSource < ' _ , ' a > ,
167117 on_name : crate :: helpers:: OnName < ' _ , ' a > ,
168118 ) -> crate :: helpers:: GeneratedInfo {
169- let cached = if options. columns {
170- self . 0 . borrow_dependent ( ) . cached_colomns_map . get ( )
171- } else {
172- self . 0 . borrow_dependent ( ) . cached_line_only_map . get ( )
173- } ;
174- match cached {
175- Some ( Some ( map) ) => {
176- let source = self . 0 . borrow_owner ( ) . inner . rope ( ) ;
177- stream_chunks_of_source_map (
178- source, map, on_chunk, on_source, on_name, options,
179- )
180- }
181- Some ( None ) => {
182- let source = self . 0 . borrow_owner ( ) . inner . rope ( ) ;
183- stream_chunks_of_raw_source (
184- source, options, on_chunk, on_source, on_name,
185- )
186- }
187- None => {
188- if options. columns {
189- self . 0 . with_dependent ( |owner, dependent| {
190- let ( generated_info, map) = stream_and_get_source_and_map (
191- & owner. inner ,
192- options,
193- on_chunk,
194- on_source,
195- on_name,
196- ) ;
197- dependent. cached_colomns_map . get_or_init ( || {
198- unsafe { std:: mem:: transmute :: < Option < SourceMap > , Option < SourceMap < ' static > > > ( map) } . map ( Cow :: Owned )
199- } ) ;
200- generated_info
201- } )
119+ let cached_map = self . cached_maps . entry ( options. clone ( ) ) ;
120+ match cached_map {
121+ Entry :: Occupied ( entry) => {
122+ let source = self . rope ( ) ;
123+ if let Some ( map) = entry. get ( ) {
124+ #[ allow( unsafe_code) ]
125+ // SAFETY: We guarantee that once a `SourceMap` is stored in the cache, it will never be removed.
126+ // Therefore, even if we force its lifetime to be longer, the reference remains valid.
127+ // This is based on the following assumptions:
128+ // 1. `SourceMap` will be valid for the entire duration of the application.
129+ // 2. The cached `SourceMap` will not be manually removed or replaced, ensuring the reference's safety.
130+ let map =
131+ unsafe { std:: mem:: transmute :: < & SourceMap , & ' a SourceMap > ( map) } ;
132+ stream_chunks_of_source_map (
133+ source, map, on_chunk, on_source, on_name, options,
134+ )
202135 } else {
203- self . 0 . with_dependent ( |owner, dependent| {
204- let ( generated_info, map) = stream_and_get_source_and_map (
205- & owner. inner ,
136+ stream_chunks_of_raw_source (
137+ source, options, on_chunk, on_source, on_name,
138+ )
139+ }
140+ }
141+ Entry :: Vacant ( entry) => {
142+ let ( generated_info, map) = stream_and_get_source_and_map (
143+ & self . inner as & T ,
206144 options,
207145 on_chunk,
208146 on_source,
209147 on_name,
210148 ) ;
211- dependent. cached_line_only_map . get_or_init ( || {
212- unsafe { std:: mem:: transmute :: < Option < SourceMap > , Option < SourceMap < ' static > > > ( map) } . map ( Cow :: Owned )
213- } ) ;
149+ entry. insert ( map) ;
214150 generated_info
215- } )
216- }
217151 }
218152 }
219153 }
220154}
221155
222- impl Hash for CachedSource {
156+ impl < T > Clone for CachedSource < T > {
157+ fn clone ( & self ) -> Self {
158+ Self {
159+ inner : self . inner . clone ( ) ,
160+ cached_hash : self . cached_hash . clone ( ) ,
161+ cached_maps : self . cached_maps . clone ( ) ,
162+ }
163+ }
164+ }
165+
166+ impl < T : Source + Hash + PartialEq + Eq + ' static > Hash for CachedSource < T > {
223167 fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
224- let owner = self . 0 . borrow_owner ( ) ;
225- ( owner. cached_hash . get_or_init ( || {
168+ ( self . cached_hash . get_or_init ( || {
226169 let mut hasher = FxHasher :: default ( ) ;
227- owner . inner . hash ( & mut hasher) ;
170+ self . inner . hash ( & mut hasher) ;
228171 hasher. finish ( )
229172 } ) )
230173 . hash ( state) ;
231174 }
232175}
233176
234- impl PartialEq for CachedSource {
177+ impl < T : PartialEq > PartialEq for CachedSource < T > {
235178 fn eq ( & self , other : & Self ) -> bool {
236- & self . 0 . borrow_owner ( ) . inner == & other. 0 . borrow_owner ( ) . inner
179+ self . inner == other. inner
237180 }
238181}
239182
240- impl Eq for CachedSource { }
183+ impl < T : Eq > Eq for CachedSource < T > { }
241184
242- impl std:: fmt:: Debug for CachedSource {
185+ impl < T : std:: fmt:: Debug > std :: fmt :: Debug for CachedSource < T > {
243186 fn fmt (
244187 & self ,
245188 f : & mut std:: fmt:: Formatter < ' _ > ,
@@ -251,7 +194,7 @@ impl std::fmt::Debug for CachedSource {
251194 writeln ! (
252195 f,
253196 "{indent_str}{:indent$?}" ,
254- self . 0 . borrow_owner ( ) . inner,
197+ self . inner,
255198 indent = indent + 2
256199 ) ?;
257200 write ! ( f, "{indent_str}).boxed()" )
@@ -287,6 +230,25 @@ mod tests {
287230 assert_eq ! ( map. mappings( ) , ";;AACA" ) ;
288231 }
289232
233+ #[ test]
234+ fn should_allow_to_store_and_share_cached_data ( ) {
235+ let original = OriginalSource :: new ( "Hello World" , "test.txt" ) ;
236+ let source = CachedSource :: new ( original) ;
237+ let clone = source. clone ( ) ;
238+
239+ // fill up cache
240+ let map_options = MapOptions :: default ( ) ;
241+ source. source ( ) ;
242+ source. buffer ( ) ;
243+ source. size ( ) ;
244+ source. map ( & map_options) ;
245+
246+ assert_eq ! (
247+ * clone. cached_maps. get( & map_options) . unwrap( ) . value( ) ,
248+ source. map( & map_options)
249+ ) ;
250+ }
251+
290252 #[ test]
291253 fn should_return_the_correct_size_for_binary_files ( ) {
292254 let source = OriginalSource :: new (
0 commit comments