1use std::ops::Range;
2use std::sync::Arc;
3
4use crate::source_map::SourceMap;
5use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData};
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<(Arc<SourceFile>, 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 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;
132 }
133
134 (
135 Arc::clone(&lo.file),
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 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 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;
163 }
164
165 Some(new_file_and_idx)
166 } else {
167 let file = &self.line_cache[oldest].file;
168 if !file_contains(file, span_data.hi) {
169 return None;
170 }
171
172 None
173 };
174
175 let (lo_idx, hi_idx) = match (lo_cache_idx, hi_cache_idx) {
177 (-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((Arc::clone(&lo.file), 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 (-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 (_, -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 "the case of neither value being equal to -1 was handled above and the function returns."
211 );
212 }
213 };
214
215 let lo = &self.line_cache[lo_idx];
216 let hi = &self.line_cache[hi_idx];
217
218 assert!(span_data.lo >= lo.line.start);
221 assert!(span_data.lo <= lo.line.end);
222 assert!(span_data.hi >= hi.line.start);
223 assert!(span_data.hi <= hi.line.end);
224 assert!(lo.file.contains(span_data.lo));
225 assert!(lo.file.contains(span_data.hi));
226 assert_eq!(lo.file_index, hi.file_index);
227
228 Some((
229 Arc::clone(&lo.file),
230 lo.line_number,
231 span_data.lo - lo.line.start,
232 hi.line_number,
233 span_data.hi - hi.line.start,
234 ))
235 }
236
237 fn cache_entry_index(&self, pos: BytePos) -> isize {
238 for (idx, cache_entry) in self.line_cache.iter().enumerate() {
239 if cache_entry.line.contains(&pos) {
240 return idx as isize;
241 }
242 }
243
244 -1
245 }
246
247 fn oldest_cache_entry_index(&self) -> usize {
248 let mut oldest = 0;
249
250 for idx in 1..self.line_cache.len() {
251 if self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp {
252 oldest = idx;
253 }
254 }
255
256 oldest
257 }
258
259 fn oldest_cache_entry_index_avoid(&self, avoid_idx: usize) -> usize {
260 let mut oldest = if avoid_idx != 0 { 0 } else { 1 };
261
262 for idx in 0..self.line_cache.len() {
263 if idx != avoid_idx
264 && self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp
265 {
266 oldest = idx;
267 }
268 }
269
270 oldest
271 }
272
273 fn file_for_position(&self, pos: BytePos) -> Option<(Arc<SourceFile>, usize)> {
274 if !self.source_map.files().is_empty() {
275 let file_idx = self.source_map.lookup_source_file_idx(pos);
276 let file = &self.source_map.files()[file_idx];
277
278 if file_contains(file, pos) {
279 return Some((Arc::clone(file), file_idx));
280 }
281 }
282
283 None
284 }
285}
286
287#[inline]
288fn file_contains(file: &SourceFile, pos: BytePos) -> bool {
289 file.contains(pos) && !file.is_empty()
295}