@@ -13,7 +13,7 @@ use crate::{
1313 stream_chunks_of_source_map, StreamChunks ,
1414 } ,
1515 rope:: Rope ,
16- MapOptions , Source , SourceMap ,
16+ BoxSource , MapOptions , Source , SourceExt , SourceMap ,
1717} ;
1818
1919/// It tries to reused cached results from other methods to avoid calculations,
@@ -49,36 +49,61 @@ use crate::{
4949/// "Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n"
5050/// );
5151/// ```
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 > > > ,
52+
53+ #[ derive( Debug ) ]
54+ struct CachedSourceOwner {
55+ inner : BoxSource ,
56+ cached_hash : OnceLock < u64 > ,
57+ }
58+
59+ #[ derive( Debug ) ]
60+ struct CachedSourceDependent < ' a > {
61+ cached_colomns_map : OnceLock < Option < SourceMap < ' static > > > ,
62+ cached_line_only_map : OnceLock < Option < SourceMap < ' static > > > ,
63+ phantom : std:: marker:: PhantomData < & ' a ( ) > ,
5764}
5865
59- impl < T > CachedSource < T > {
66+ self_cell:: self_cell!(
67+ struct CachedSourceCell {
68+ owner: CachedSourceOwner ,
69+
70+ #[ covariant]
71+ dependent: CachedSourceDependent ,
72+ }
73+
74+ impl { Debug }
75+ ) ;
76+
77+ /// A wrapper around any [`Source`] that caches expensive computations to improve performance.
78+ pub struct CachedSource ( CachedSourceCell ) ;
79+
80+ impl CachedSource {
6081 /// Create a [CachedSource] with the original [Source].
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- }
82+ pub fn new < T : SourceExt > ( inner : T ) -> Self {
83+ let owner = CachedSourceOwner {
84+ inner : inner. boxed ( ) ,
85+ cached_hash : OnceLock :: new ( ) ,
86+ } ;
87+ Self ( CachedSourceCell :: new ( owner, |_| CachedSourceDependent {
88+ cached_colomns_map : Default :: default ( ) ,
89+ cached_line_only_map : Default :: default ( ) ,
90+ phantom : std:: marker:: PhantomData ,
91+ } ) )
6792 }
6893
6994 /// Get the original [Source].
70- pub fn original ( & self ) -> & T {
71- & self . inner
95+ pub fn original ( & self ) -> & BoxSource {
96+ & self . 0 . borrow_owner ( ) . inner
7297 }
7398}
7499
75- impl < T : Source + Hash + PartialEq + Eq + ' static > Source for CachedSource < T > {
100+ impl Source for CachedSource {
76101 fn source ( & self ) -> Cow < str > {
77- self . inner . source ( )
102+ self . 0 . borrow_owner ( ) . inner . source ( )
78103 }
79104
80105 fn rope ( & self ) -> Rope < ' _ > {
81- self . inner . rope ( )
106+ self . 0 . borrow_owner ( ) . inner . rope ( )
82107 }
83108
84109 fn buffer ( & self ) -> Cow < [ u8 ] > {
@@ -88,101 +113,118 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> Source for CachedSource<T> {
88113 }
89114
90115 fn size ( & self ) -> usize {
91- self . inner . size ( )
116+ self . 0 . borrow_owner ( ) . inner . size ( )
92117 }
93118
94119 fn map ( & self , options : & MapOptions ) -> Option < SourceMap > {
95- if let Some ( map) = self . cached_maps . get ( options) {
96- map. clone ( )
120+ if options. columns {
121+ self . 0 . with_dependent ( |owner, dependent| {
122+ dependent. cached_colomns_map . get_or_init ( || {
123+ let map = owner. inner . map ( options) ;
124+ unsafe { std:: mem:: transmute :: < Option < SourceMap > , Option < SourceMap < ' static > > > ( map) }
125+ } )
126+ . as_ref ( )
127+ . map ( |m| m. as_borrowed ( ) )
128+ } )
97129 } else {
98- let map = self . inner . map ( options) ;
99- self . cached_maps . insert ( options. clone ( ) , map. clone ( ) ) ;
100- map
130+ self . 0 . with_dependent ( |owner, dependent| {
131+ dependent. cached_line_only_map . get_or_init ( || {
132+ let map = owner. inner . map ( options) ;
133+ unsafe { std:: mem:: transmute :: < Option < SourceMap > , Option < SourceMap < ' static > > > ( map) }
134+ } )
135+ . as_ref ( )
136+ . map ( |m| m. as_borrowed ( ) )
137+ } )
101138 }
102139 }
103140
104141 fn to_writer ( & self , writer : & mut dyn std:: io:: Write ) -> std:: io:: Result < ( ) > {
105- self . inner . to_writer ( writer)
142+ self . 0 . borrow_owner ( ) . inner . to_writer ( writer)
106143 }
107144}
108145
109- impl < T : Source + Hash + PartialEq + Eq + ' static > StreamChunks
110- for CachedSource < T >
111- {
146+ impl StreamChunks for CachedSource {
112147 fn stream_chunks < ' a > (
113148 & ' a self ,
114149 options : & MapOptions ,
115150 on_chunk : crate :: helpers:: OnChunk < ' _ , ' a > ,
116151 on_source : crate :: helpers:: OnSource < ' _ , ' a > ,
117152 on_name : crate :: helpers:: OnName < ' _ , ' a > ,
118153 ) -> crate :: helpers:: GeneratedInfo {
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- )
135- } else {
136- stream_chunks_of_raw_source (
137- source, options, on_chunk, on_source, on_name,
138- )
139- }
154+ let cached = if options. columns {
155+ self . 0 . borrow_dependent ( ) . cached_colomns_map . get ( )
156+ } else {
157+ self . 0 . borrow_dependent ( ) . cached_line_only_map . get ( )
158+ } ;
159+ match cached {
160+ Some ( Some ( map) ) => {
161+ let source = self . 0 . borrow_owner ( ) . inner . rope ( ) ;
162+ stream_chunks_of_source_map (
163+ source, map, on_chunk, on_source, on_name, options,
164+ )
165+ }
166+ Some ( None ) => {
167+ let source = self . 0 . borrow_owner ( ) . inner . rope ( ) ;
168+ stream_chunks_of_raw_source (
169+ source, options, on_chunk, on_source, on_name,
170+ )
140171 }
141- Entry :: Vacant ( entry) => {
142- let ( generated_info, map) = stream_and_get_source_and_map (
143- & self . inner as & T ,
172+ None => {
173+ if options. columns {
174+ self . 0 . with_dependent ( |owner, dependent| {
175+ let ( generated_info, map) = stream_and_get_source_and_map (
176+ & owner. inner ,
144177 options,
145178 on_chunk,
146179 on_source,
147180 on_name,
148181 ) ;
149- entry. insert ( map) ;
182+ dependent. cached_colomns_map . get_or_init ( || {
183+ unsafe { std:: mem:: transmute :: < Option < SourceMap > , Option < SourceMap < ' static > > > ( map) }
184+ } ) ;
185+ generated_info
186+ } )
187+ } else {
188+ self . 0 . with_dependent ( |owner, dependent| {
189+ let ( generated_info, map) = stream_and_get_source_and_map (
190+ & owner. inner ,
191+ options,
192+ on_chunk,
193+ on_source,
194+ on_name,
195+ ) ;
196+ dependent. cached_line_only_map . get_or_init ( || {
197+ unsafe { std:: mem:: transmute :: < Option < SourceMap > , Option < SourceMap < ' static > > > ( map) }
198+ } ) ;
150199 generated_info
200+ } )
201+ }
151202 }
152203 }
153204 }
154205}
155206
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 > {
207+ impl Hash for CachedSource {
167208 fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
168- ( self . cached_hash . get_or_init ( || {
209+ let owner = self . 0 . borrow_owner ( ) ;
210+ ( owner. cached_hash . get_or_init ( || {
169211 let mut hasher = FxHasher :: default ( ) ;
170- self . inner . hash ( & mut hasher) ;
212+ owner . inner . hash ( & mut hasher) ;
171213 hasher. finish ( )
172214 } ) )
173215 . hash ( state) ;
174216 }
175217}
176218
177- impl < T : PartialEq > PartialEq for CachedSource < T > {
219+ impl PartialEq for CachedSource {
178220 fn eq ( & self , other : & Self ) -> bool {
179- self . inner == other. inner
221+ & self . 0 . borrow_owner ( ) . inner == & other. 0 . borrow_owner ( ) . inner
180222 }
181223}
182224
183- impl < T : Eq > Eq for CachedSource < T > { }
225+ impl Eq for CachedSource { }
184226
185- impl < T : std:: fmt:: Debug > std :: fmt :: Debug for CachedSource < T > {
227+ impl std:: fmt:: Debug for CachedSource {
186228 fn fmt (
187229 & self ,
188230 f : & mut std:: fmt:: Formatter < ' _ > ,
@@ -194,7 +236,7 @@ impl<T: std::fmt::Debug> std::fmt::Debug for CachedSource<T> {
194236 writeln ! (
195237 f,
196238 "{indent_str}{:indent$?}" ,
197- self . inner,
239+ self . 0 . borrow_owner ( ) . inner,
198240 indent = indent + 2
199241 ) ?;
200242 write ! ( f, "{indent_str}).boxed()" )
@@ -230,25 +272,6 @@ mod tests {
230272 assert_eq ! ( map. mappings( ) , ";;AACA" ) ;
231273 }
232274
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-
252275 #[ test]
253276 fn should_return_the_correct_size_for_binary_files ( ) {
254277 let source = OriginalSource :: new (
0 commit comments