11use std:: {
22 borrow:: Cow ,
3- hash:: { BuildHasherDefault , Hash , Hasher } ,
3+ hash:: { Hash , Hasher } ,
44 sync:: { Arc , OnceLock } ,
55} ;
66
7- use dashmap:: { mapref:: entry:: Entry , DashMap } ;
87use rustc_hash:: FxHasher ;
98
109use crate :: {
@@ -13,9 +12,16 @@ use crate::{
1312 stream_chunks_of_source_map, StreamChunks ,
1413 } ,
1514 rope:: Rope ,
16- MapOptions , Source , SourceMap ,
15+ BoxSource , MapOptions , Source , SourceExt , SourceMap ,
1716} ;
1817
18+ #[ derive( Default ) ]
19+ struct CachedData {
20+ hash : OnceLock < u64 > ,
21+ line_only_map : OnceLock < Option < SourceMap > > ,
22+ columns_map : OnceLock < Option < SourceMap > > ,
23+ }
24+
1925/// It tries to reused cached results from other methods to avoid calculations,
2026/// usually used after modify is finished.
2127///
@@ -49,30 +55,30 @@ use crate::{
4955/// "Hello World\nconsole.log('test');\nconsole.log('test2');\nHello2\n"
5056/// );
5157/// ```
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 > > > ,
58+ pub struct CachedSource {
59+ inner : BoxSource ,
60+ cache : Arc < CachedData > ,
5761}
5862
59- impl < T > CachedSource < T > {
63+ impl CachedSource {
6064 /// 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 ( ) ,
65+ pub fn new < T : SourceExt > ( inner : T ) -> Self {
66+ let box_source = inner. boxed ( ) ;
67+ // Check if it's already a BoxSource containing a CachedSource
68+ if let Some ( cached_source) =
69+ box_source. as_ref ( ) . as_any ( ) . downcast_ref :: < CachedSource > ( )
70+ {
71+ return cached_source. clone ( ) ;
6672 }
67- }
6873
69- /// Get the original [Source].
70- pub fn original ( & self ) -> & T {
71- & self . inner
74+ Self {
75+ inner : box_source,
76+ cache : Arc :: new ( CachedData :: default ( ) ) ,
77+ }
7278 }
7379}
7480
75- impl < T : Source + Hash + PartialEq + Eq + ' static > Source for CachedSource < T > {
81+ impl Source for CachedSource {
7682 fn source ( & self ) -> Cow < str > {
7783 self . inner . source ( )
7884 }
@@ -92,12 +98,18 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> Source for CachedSource<T> {
9298 }
9399
94100 fn map ( & self , options : & MapOptions ) -> Option < SourceMap > {
95- if let Some ( map) = self . cached_maps . get ( options) {
96- map. clone ( )
101+ if options. columns {
102+ self
103+ . cache
104+ . columns_map
105+ . get_or_init ( || self . inner . map ( options) )
106+ . clone ( )
97107 } else {
98- let map = self . inner . map ( options) ;
99- self . cached_maps . insert ( options. clone ( ) , map. clone ( ) ) ;
100- map
108+ self
109+ . cache
110+ . line_only_map
111+ . get_or_init ( || self . inner . map ( options) )
112+ . clone ( )
101113 }
102114 }
103115
@@ -106,29 +118,23 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> Source for CachedSource<T> {
106118 }
107119}
108120
109- impl < T : Source + Hash + PartialEq + Eq + ' static > StreamChunks
110- for CachedSource < T >
111- {
121+ impl StreamChunks for CachedSource {
112122 fn stream_chunks < ' a > (
113123 & ' a self ,
114124 options : & MapOptions ,
115125 on_chunk : crate :: helpers:: OnChunk < ' _ , ' a > ,
116126 on_source : crate :: helpers:: OnSource < ' _ , ' a > ,
117127 on_name : crate :: helpers:: OnName < ' _ , ' a > ,
118128 ) -> crate :: helpers:: GeneratedInfo {
119- let cached_map = self . cached_maps . entry ( options. clone ( ) ) ;
120- match cached_map {
121- Entry :: Occupied ( entry) => {
129+ let cell = if options. columns {
130+ & self . cache . columns_map
131+ } else {
132+ & self . cache . line_only_map
133+ } ;
134+ match cell. get ( ) {
135+ Some ( map) => {
122136 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) } ;
137+ if let Some ( map) = map {
132138 stream_chunks_of_source_map (
133139 source, map, on_chunk, on_source, on_name, options,
134140 )
@@ -138,34 +144,33 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> StreamChunks
138144 )
139145 }
140146 }
141- Entry :: Vacant ( entry ) => {
147+ None => {
142148 let ( generated_info, map) = stream_and_get_source_and_map (
143- & self . inner as & T ,
149+ & self . inner ,
144150 options,
145151 on_chunk,
146152 on_source,
147153 on_name,
148154 ) ;
149- entry . insert ( map) ;
155+ cell . get_or_init ( || map) ;
150156 generated_info
151157 }
152158 }
153159 }
154160}
155161
156- impl < T > Clone for CachedSource < T > {
162+ impl Clone for CachedSource {
157163 fn clone ( & self ) -> Self {
158164 Self {
159165 inner : self . inner . clone ( ) ,
160- cached_hash : self . cached_hash . clone ( ) ,
161- cached_maps : self . cached_maps . clone ( ) ,
166+ cache : self . cache . clone ( ) ,
162167 }
163168 }
164169}
165170
166- impl < T : Source + Hash + PartialEq + Eq + ' static > Hash for CachedSource < T > {
171+ impl Hash for CachedSource {
167172 fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
168- ( self . cached_hash . get_or_init ( || {
173+ ( self . cache . hash . get_or_init ( || {
169174 let mut hasher = FxHasher :: default ( ) ;
170175 self . inner . hash ( & mut hasher) ;
171176 hasher. finish ( )
@@ -174,15 +179,15 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> Hash for CachedSource<T> {
174179 }
175180}
176181
177- impl < T : PartialEq > PartialEq for CachedSource < T > {
182+ impl PartialEq for CachedSource {
178183 fn eq ( & self , other : & Self ) -> bool {
179- self . inner == other. inner
184+ self . inner . as_ref ( ) == other. inner . as_ref ( )
180185 }
181186}
182187
183- impl < T : Eq > Eq for CachedSource < T > { }
188+ impl Eq for CachedSource { }
184189
185- impl < T : std:: fmt:: Debug > std :: fmt :: Debug for CachedSource < T > {
190+ impl std:: fmt:: Debug for CachedSource {
186191 fn fmt (
187192 & self ,
188193 f : & mut std:: fmt:: Formatter < ' _ > ,
@@ -244,7 +249,7 @@ mod tests {
244249 source. map ( & map_options) ;
245250
246251 assert_eq ! (
247- * clone. cached_maps . get( & map_options ) . unwrap( ) . value ( ) ,
252+ * clone. cache . columns_map . get( ) . unwrap( ) ,
248253 source. map( & map_options)
249254 ) ;
250255 }
0 commit comments