11use crate :: source_map:: SourceMap ;
2- use crate :: { BytePos , SourceFile } ;
2+ use crate :: { BytePos , SourceFile , SpanData } ;
33use rustc_data_structures:: sync:: Lrc ;
44use std:: ops:: Range ;
55
@@ -24,6 +24,32 @@ struct CacheEntry {
2424 file_index : usize ,
2525}
2626
27+ impl CacheEntry {
28+ #[ inline]
29+ fn update (
30+ & mut self ,
31+ new_file_and_idx : Option < ( Lrc < SourceFile > , usize ) > ,
32+ pos : BytePos ,
33+ time_stamp : usize ,
34+ ) {
35+ if let Some ( ( file, file_idx) ) = new_file_and_idx {
36+ self . file = file;
37+ self . file_index = file_idx;
38+ }
39+
40+ let line_index = self . file . lookup_line ( pos) . unwrap ( ) ;
41+ let line_bounds = self . file . line_bounds ( line_index) ;
42+ self . line_number = line_index + 1 ;
43+ self . line = line_bounds;
44+ self . touch ( time_stamp) ;
45+ }
46+
47+ #[ inline]
48+ fn touch ( & mut self , time_stamp : usize ) {
49+ self . time_stamp = time_stamp;
50+ }
51+ }
52+
2753#[ derive( Clone ) ]
2854pub struct CachingSourceMapView < ' sm > {
2955 source_map : & ' sm SourceMap ,
@@ -57,59 +83,202 @@ impl<'sm> CachingSourceMapView<'sm> {
5783 self . time_stamp += 1 ;
5884
5985 // Check if the position is in one of the cached lines
60- for cache_entry in self . line_cache . iter_mut ( ) {
61- if cache_entry. line . contains ( & pos) {
62- cache_entry. time_stamp = self . time_stamp ;
86+ let cache_idx = self . cache_entry_index ( pos) ;
87+ if cache_idx != -1 {
88+ let cache_entry = & mut self . line_cache [ cache_idx as usize ] ;
89+ cache_entry. touch ( self . time_stamp ) ;
6390
64- return Some ( (
65- cache_entry. file . clone ( ) ,
66- cache_entry. line_number ,
67- pos - cache_entry. line . start ,
68- ) ) ;
69- }
91+ return Some ( (
92+ cache_entry. file . clone ( ) ,
93+ cache_entry. line_number ,
94+ pos - cache_entry. line . start ,
95+ ) ) ;
7096 }
7197
7298 // No cache hit ...
73- let mut oldest = 0 ;
74- for index in 1 ..self . line_cache . len ( ) {
75- if self . line_cache [ index] . time_stamp < self . line_cache [ oldest] . time_stamp {
76- oldest = index;
77- }
78- }
99+ let oldest = self . oldest_cache_entry_index ( ) ;
100+
101+ // If the entry doesn't point to the correct file, get the new file and index.
102+ let new_file_and_idx = if !file_contains ( & self . line_cache [ oldest] . file , pos) {
103+ Some ( self . file_for_position ( pos) ?)
104+ } else {
105+ None
106+ } ;
79107
80108 let cache_entry = & mut self . line_cache [ oldest] ;
109+ cache_entry. update ( new_file_and_idx, pos, self . time_stamp ) ;
81110
82- // If the entry doesn't point to the correct file, fix it up
83- if !file_contains ( & cache_entry. file , pos) {
84- let file_valid;
85- if self . source_map . files ( ) . len ( ) > 0 {
86- let file_index = self . source_map . lookup_source_file_idx ( pos) ;
87- let file = self . source_map . files ( ) [ file_index] . clone ( ) ;
88-
89- if file_contains ( & file, pos) {
90- cache_entry. file = file;
91- cache_entry. file_index = file_index;
92- file_valid = true ;
93- } else {
94- file_valid = false ;
111+ Some ( ( cache_entry. file . clone ( ) , cache_entry. line_number , pos - cache_entry. line . start ) )
112+ }
113+
114+ pub fn span_data_to_lines_and_cols (
115+ & mut self ,
116+ span_data : & SpanData ,
117+ ) -> Option < ( Lrc < SourceFile > , usize , BytePos , usize , BytePos ) > {
118+ self . time_stamp += 1 ;
119+
120+ // Check if lo and hi are in the cached lines.
121+ let lo_cache_idx = self . cache_entry_index ( span_data. lo ) ;
122+ let hi_cache_idx = self . cache_entry_index ( span_data. hi ) ;
123+
124+ if lo_cache_idx != -1 && hi_cache_idx != -1 {
125+ // Cache hit for span lo and hi. Check if they belong to the same file.
126+ let result = {
127+ let lo = & self . line_cache [ lo_cache_idx as usize ] ;
128+ let hi = & self . line_cache [ hi_cache_idx as usize ] ;
129+
130+ if lo. file_index != hi. file_index {
131+ return None ;
95132 }
96- } else {
97- file_valid = false ;
133+
134+ (
135+ lo. file . clone ( ) ,
136+ lo. line_number ,
137+ span_data. lo - lo. line . start ,
138+ hi. line_number ,
139+ span_data. hi - hi. line . start ,
140+ )
141+ } ;
142+
143+ self . line_cache [ lo_cache_idx as usize ] . touch ( self . time_stamp ) ;
144+ self . line_cache [ hi_cache_idx as usize ] . touch ( self . time_stamp ) ;
145+
146+ return Some ( result) ;
147+ }
148+
149+ // No cache hit or cache hit for only one of span lo and hi.
150+ let oldest = if lo_cache_idx != -1 || hi_cache_idx != -1 {
151+ let avoid_idx = if lo_cache_idx != -1 { lo_cache_idx } else { hi_cache_idx } ;
152+ self . oldest_cache_entry_index_avoid ( avoid_idx as usize )
153+ } else {
154+ self . oldest_cache_entry_index ( )
155+ } ;
156+
157+ // If the entry doesn't point to the correct file, get the new file and index.
158+ // Return early if the file containing beginning of span doesn't contain end of span.
159+ let new_file_and_idx = if !file_contains ( & self . line_cache [ oldest] . file , span_data. lo ) {
160+ let new_file_and_idx = self . file_for_position ( span_data. lo ) ?;
161+ if !file_contains ( & new_file_and_idx. 0 , span_data. hi ) {
162+ return None ;
98163 }
99164
100- if !file_valid {
165+ Some ( new_file_and_idx)
166+ } else {
167+ let file = & self . line_cache [ oldest] . file ;
168+ if !file_contains ( & file, span_data. hi ) {
101169 return None ;
102170 }
171+
172+ None
173+ } ;
174+
175+ // Update the cache entries.
176+ let ( lo_idx, hi_idx) = match ( lo_cache_idx, hi_cache_idx) {
177+ // Oldest cache entry is for span_data.lo line.
178+ ( -1 , -1 ) => {
179+ let lo = & mut self . line_cache [ oldest] ;
180+ lo. update ( new_file_and_idx, span_data. lo , self . time_stamp ) ;
181+
182+ if !lo. line . contains ( & span_data. hi ) {
183+ let new_file_and_idx = Some ( ( lo. file . clone ( ) , lo. file_index ) ) ;
184+ let next_oldest = self . oldest_cache_entry_index_avoid ( oldest) ;
185+ let hi = & mut self . line_cache [ next_oldest] ;
186+ hi. update ( new_file_and_idx, span_data. hi , self . time_stamp ) ;
187+ ( oldest, next_oldest)
188+ } else {
189+ ( oldest, oldest)
190+ }
191+ }
192+ // Oldest cache entry is for span_data.lo line.
193+ ( -1 , _) => {
194+ let lo = & mut self . line_cache [ oldest] ;
195+ lo. update ( new_file_and_idx, span_data. lo , self . time_stamp ) ;
196+ let hi = & mut self . line_cache [ hi_cache_idx as usize ] ;
197+ hi. touch ( self . time_stamp ) ;
198+ ( oldest, hi_cache_idx as usize )
199+ }
200+ // Oldest cache entry is for span_data.hi line.
201+ ( _, -1 ) => {
202+ let hi = & mut self . line_cache [ oldest] ;
203+ hi. update ( new_file_and_idx, span_data. hi , self . time_stamp ) ;
204+ let lo = & mut self . line_cache [ lo_cache_idx as usize ] ;
205+ lo. touch ( self . time_stamp ) ;
206+ ( lo_cache_idx as usize , oldest)
207+ }
208+ _ => {
209+ panic ! ( ) ;
210+ }
211+ } ;
212+
213+ let lo = & self . line_cache [ lo_idx] ;
214+ let hi = & self . line_cache [ hi_idx] ;
215+
216+ // Span lo and hi may equal line end when last line doesn't
217+ // end in newline, hence the inclusive upper bounds below.
218+ debug_assert ! ( span_data. lo >= lo. line. start) ;
219+ debug_assert ! ( span_data. lo <= lo. line. end) ;
220+ debug_assert ! ( span_data. hi >= hi. line. start) ;
221+ debug_assert ! ( span_data. hi <= hi. line. end) ;
222+ debug_assert ! ( lo. file. contains( span_data. lo) ) ;
223+ debug_assert ! ( lo. file. contains( span_data. hi) ) ;
224+ debug_assert_eq ! ( lo. file_index, hi. file_index) ;
225+
226+ Some ( (
227+ lo. file . clone ( ) ,
228+ lo. line_number ,
229+ span_data. lo - lo. line . start ,
230+ hi. line_number ,
231+ span_data. hi - hi. line . start ,
232+ ) )
233+ }
234+
235+ fn cache_entry_index ( & self , pos : BytePos ) -> isize {
236+ for ( idx, cache_entry) in self . line_cache . iter ( ) . enumerate ( ) {
237+ if cache_entry. line . contains ( & pos) {
238+ return idx as isize ;
239+ }
240+ }
241+
242+ -1
243+ }
244+
245+ fn oldest_cache_entry_index ( & self ) -> usize {
246+ let mut oldest = 0 ;
247+
248+ for idx in 1 ..self . line_cache . len ( ) {
249+ if self . line_cache [ idx] . time_stamp < self . line_cache [ oldest] . time_stamp {
250+ oldest = idx;
251+ }
103252 }
104253
105- let line_index = cache_entry . file . lookup_line ( pos ) . unwrap ( ) ;
106- let line_bounds = cache_entry . file . line_bounds ( line_index ) ;
254+ oldest
255+ }
107256
108- cache_entry. line_number = line_index + 1 ;
109- cache_entry. line = line_bounds;
110- cache_entry. time_stamp = self . time_stamp ;
257+ fn oldest_cache_entry_index_avoid ( & self , avoid_idx : usize ) -> usize {
258+ let mut oldest = if avoid_idx != 0 { 0 } else { 1 } ;
111259
112- Some ( ( cache_entry. file . clone ( ) , cache_entry. line_number , pos - cache_entry. line . start ) )
260+ for idx in 0 ..self . line_cache . len ( ) {
261+ if idx != avoid_idx
262+ && self . line_cache [ idx] . time_stamp < self . line_cache [ oldest] . time_stamp
263+ {
264+ oldest = idx;
265+ }
266+ }
267+
268+ oldest
269+ }
270+
271+ fn file_for_position ( & self , pos : BytePos ) -> Option < ( Lrc < SourceFile > , usize ) > {
272+ if !self . source_map . files ( ) . is_empty ( ) {
273+ let file_idx = self . source_map . lookup_source_file_idx ( pos) ;
274+ let file = & self . source_map . files ( ) [ file_idx] ;
275+
276+ if file_contains ( file, pos) {
277+ return Some ( ( file. clone ( ) , file_idx) ) ;
278+ }
279+ }
280+
281+ None
113282 }
114283}
115284
0 commit comments