1
1
use crate :: source_map:: SourceMap ;
2
- use crate :: { BytePos , SourceFile } ;
2
+ use crate :: { BytePos , SourceFile , SpanData } ;
3
3
use rustc_data_structures:: sync:: Lrc ;
4
4
use std:: ops:: Range ;
5
5
@@ -24,6 +24,32 @@ struct CacheEntry {
24
24
file_index : usize ,
25
25
}
26
26
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
+
27
53
#[ derive( Clone ) ]
28
54
pub struct CachingSourceMapView < ' sm > {
29
55
source_map : & ' sm SourceMap ,
@@ -57,59 +83,202 @@ impl<'sm> CachingSourceMapView<'sm> {
57
83
self . time_stamp += 1 ;
58
84
59
85
// 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 ) ;
63
90
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
+ ) ) ;
70
96
}
71
97
72
98
// 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
+ } ;
79
107
80
108
let cache_entry = & mut self . line_cache [ oldest] ;
109
+ cache_entry. update ( new_file_and_idx, pos, self . time_stamp ) ;
81
110
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 ;
95
132
}
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 ;
98
163
}
99
164
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 ) {
101
169
return None ;
102
170
}
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
+ }
103
252
}
104
253
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
+ }
107
256
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 } ;
111
259
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
113
282
}
114
283
}
115
284
0 commit comments