1use std::ops::Range;
2use std::sync::Arc;
3
4use crate::source_map::SourceMap;
5use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData, StableSourceFileId};
6
7#[derive(Clone)]
8struct CacheEntry {
9 time_stamp: usize,
10 line_number: usize,
11 line: Range<BytePos>,
24 file: Arc<SourceFile>,
25 file_index: usize,
26}
27
28impl CacheEntry {
29 #[inline]
30 fn update(
31 &mut self,
32 new_file_and_idx: Option<(Arc<SourceFile>, usize)>,
33 pos: BytePos,
34 time_stamp: usize,
35 ) {
36 if let Some((file, file_idx)) = new_file_and_idx {
37 self.file = file;
38 self.file_index = file_idx;
39 }
40
41 let pos = self.file.relative_position(pos);
42 let line_index = self.file.lookup_line(pos).unwrap();
43 let line_bounds = self.file.line_bounds(line_index);
44 self.line_number = line_index + 1;
45 self.line = line_bounds;
46 self.touch(time_stamp);
47 }
48
49 #[inline]
50 fn touch(&mut self, time_stamp: usize) {
51 self.time_stamp = time_stamp;
52 }
53}
54
55#[derive(Clone)]
56pub struct CachingSourceMapView<'sm> {
57 source_map: &'sm SourceMap,
58 line_cache: [CacheEntry; 3],
59 time_stamp: usize,
60}
61
62impl<'sm> CachingSourceMapView<'sm> {
63 pub fn new(source_map: &'sm SourceMap) -> CachingSourceMapView<'sm> {
64 let files = source_map.files();
65 let first_file = Arc::clone(&files[0]);
66 let entry = CacheEntry {
67 time_stamp: 0,
68 line_number: 0,
69 line: BytePos(0)..BytePos(0),
70 file: first_file,
71 file_index: 0,
72 };
73
74 CachingSourceMapView {
75 source_map,
76 line_cache: [entry.clone(), entry.clone(), entry],
77 time_stamp: 0,
78 }
79 }
80
81 pub fn byte_pos_to_line_and_col(
82 &mut self,
83 pos: BytePos,
84 ) -> Option<(Arc<SourceFile>, usize, RelativeBytePos)> {
85 self.time_stamp += 1;
86
87 let cache_idx = self.cache_entry_index(pos);
89 if cache_idx != -1 {
90 let cache_entry = &mut self.line_cache[cache_idx as usize];
91 cache_entry.touch(self.time_stamp);
92
93 let col = RelativeBytePos(pos.to_u32() - cache_entry.line.start.to_u32());
94 return Some((Arc::clone(&cache_entry.file), cache_entry.line_number, col));
95 }
96
97 let oldest = self.oldest_cache_entry_index();
99
100 let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, pos) {
102 Some(self.file_for_position(pos)?)
103 } else {
104 None
105 };
106
107 let cache_entry = &mut self.line_cache[oldest];
108 cache_entry.update(new_file_and_idx, pos, self.time_stamp);
109
110 let col = RelativeBytePos(pos.to_u32() - cache_entry.line.start.to_u32());
111 Some((Arc::clone(&cache_entry.file), cache_entry.line_number, col))
112 }
113
114 pub fn span_data_to_lines_and_cols(
115 &mut self,
116 span_data: &SpanData,
117 ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)> {
118 self.time_stamp += 1;
119
120 let lo_cache_idx: isize = 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 let lo_file_index = self.line_cache[lo_cache_idx as usize].file_index;
127 let hi_file_index = self.line_cache[hi_cache_idx as usize].file_index;
128
129 if lo_file_index != hi_file_index {
130 return None;
131 }
132
133 self.line_cache[lo_cache_idx as usize].touch(self.time_stamp);
134 self.line_cache[hi_cache_idx as usize].touch(self.time_stamp);
135
136 let lo = &self.line_cache[lo_cache_idx as usize];
137 let hi = &self.line_cache[hi_cache_idx as usize];
138 return Some((
139 lo.file.stable_id,
140 lo.line_number,
141 span_data.lo - lo.line.start,
142 hi.line_number,
143 span_data.hi - hi.line.start,
144 ));
145 }
146
147 let oldest = if lo_cache_idx != -1 || hi_cache_idx != -1 {
149 let avoid_idx = if lo_cache_idx != -1 { lo_cache_idx } else { hi_cache_idx };
150 self.oldest_cache_entry_index_avoid(avoid_idx as usize)
151 } else {
152 self.oldest_cache_entry_index()
153 };
154
155 let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, span_data.lo) {
158 let new_file_and_idx = self.file_for_position(span_data.lo)?;
159 if !file_contains(&new_file_and_idx.0, span_data.hi) {
160 return None;
161 }
162
163 Some(new_file_and_idx)
164 } else {
165 let file = &self.line_cache[oldest].file;
166 if !file_contains(file, span_data.hi) {
167 return None;
168 }
169
170 None
171 };
172
173 let (lo_idx, hi_idx) = match (lo_cache_idx, hi_cache_idx) {
175 (-1, -1) => {
177 let lo = &mut self.line_cache[oldest];
178 lo.update(new_file_and_idx, span_data.lo, self.time_stamp);
179
180 if !lo.line.contains(&span_data.hi) {
181 let new_file_and_idx = Some((Arc::clone(&lo.file), lo.file_index));
182 let next_oldest = self.oldest_cache_entry_index_avoid(oldest);
183 let hi = &mut self.line_cache[next_oldest];
184 hi.update(new_file_and_idx, span_data.hi, self.time_stamp);
185 (oldest, next_oldest)
186 } else {
187 (oldest, oldest)
188 }
189 }
190 (-1, _) => {
192 let lo = &mut self.line_cache[oldest];
193 lo.update(new_file_and_idx, span_data.lo, self.time_stamp);
194 let hi = &mut self.line_cache[hi_cache_idx as usize];
195 hi.touch(self.time_stamp);
196 (oldest, hi_cache_idx as usize)
197 }
198 (_, -1) => {
200 let hi = &mut self.line_cache[oldest];
201 hi.update(new_file_and_idx, span_data.hi, self.time_stamp);
202 let lo = &mut self.line_cache[lo_cache_idx as usize];
203 lo.touch(self.time_stamp);
204 (lo_cache_idx as usize, oldest)
205 }
206 _ => {
207 panic!(
208 "the case of neither value being equal to -1 was handled above and the function returns."
209 );
210 }
211 };
212
213 let lo = &self.line_cache[lo_idx];
214 let hi = &self.line_cache[hi_idx];
215
216 assert!(span_data.lo >= lo.line.start);
219 assert!(span_data.lo <= lo.line.end);
220 assert!(span_data.hi >= hi.line.start);
221 assert!(span_data.hi <= hi.line.end);
222 assert!(lo.file.contains(span_data.lo));
223 assert!(lo.file.contains(span_data.hi));
224 assert_eq!(lo.file_index, hi.file_index);
225
226 Some((
227 lo.file.stable_id,
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 }
252 }
253
254 oldest
255 }
256
257 fn oldest_cache_entry_index_avoid(&self, avoid_idx: usize) -> usize {
258 let mut oldest = if avoid_idx != 0 { 0 } else { 1 };
259
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<(Arc<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((Arc::clone(file), file_idx));
278 }
279 }
280
281 None
282 }
283}
284
285#[inline]
286fn file_contains(file: &SourceFile, pos: BytePos) -> bool {
287 file.contains(pos) && !file.is_empty()
293}