1use crate::utils::dedup::*;
2use derive_generic_visitor::{ControlFlow, Drive, DriveMut, DriveTwo, Visit, VisitMut, VisitTwo};
3use serde::{Deserialize, Serialize};
4use serde_state::{DeserializeState, SerializeState};
5use std::collections::HashMap;
6use std::sync::{LazyLock, Mutex};
7use std::{borrow::Cow, cmp::Ordering, ops::Range, path::PathBuf};
8
9generate_index_type!(FileId);
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[derive(Serialize, Deserialize, Drive, DriveMut, DriveTwo)]
14pub enum FileName {
15 Virtual(PathBuf),
17 Local(PathBuf),
19 NotReal(String),
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[derive(Serialize, Deserialize, Drive, DriveMut, DriveTwo)]
25pub struct File {
26 #[cfg_attr(feature = "charon_on_charon", charon::opaque)]
28 pub id: FileId,
29 pub name: FileName,
31 pub crate_name: String,
33 pub contents: Option<String>,
36}
37
38#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39#[derive(Serialize, Deserialize, Drive, DriveMut, DriveTwo)]
40pub struct Loc {
41 pub line: u32,
43 pub col: u32,
45}
46
47#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
49#[derive(Serialize, Deserialize, Drive, DriveMut, DriveTwo)]
50pub struct SpanData {
51 #[cfg_attr(feature = "charon_on_charon", charon::rename("file"))]
52 pub file_id: FileId,
53 #[cfg_attr(feature = "charon_on_charon", charon::rename("beg_loc"))]
54 pub beg: Loc,
55 #[cfg_attr(feature = "charon_on_charon", charon::rename("end_loc"))]
56 pub end: Loc,
57}
58
59#[derive(Copy, Clone, PartialEq, Eq, Hash)]
95pub struct Span(u64);
96
97mod pack {
99 pub const WIDE_FLAG: u64 = 1 << 63;
101 pub const FILE_BITS: u32 = 16;
102 pub const LINE_BITS: u32 = 20;
103 pub const COL_BITS: u32 = 10;
104 pub const NLINES_BITS: u32 = 7;
105
106 pub const END_COL_SHIFT: u32 = 0;
107 pub const NLINES_SHIFT: u32 = END_COL_SHIFT + COL_BITS;
108 pub const BEG_COL_SHIFT: u32 = NLINES_SHIFT + NLINES_BITS;
109 pub const BEG_LINE_SHIFT: u32 = BEG_COL_SHIFT + COL_BITS;
110 pub const FILE_SHIFT: u32 = BEG_LINE_SHIFT + LINE_BITS;
111
112 #[inline]
114 pub fn get(x: u64, shift: u32, bits: u32) -> u32 {
115 ((x >> shift) & ((1 << bits) - 1)) as u32
116 }
117
118 #[inline]
120 pub fn put(x: u32, shift: u32, bits: u32) -> Option<u64> {
121 (u64::from(x) < (1 << bits)).then_some(u64::from(x) << shift)
122 }
123}
124
125#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128#[derive(
129 Serialize,
130 Deserialize,
131 SerializeState,
132 DeserializeState,
133 Drive,
134 DriveMut,
135 DriveTwo
136)]
137#[cfg_attr(feature = "charon_on_charon", charon::rename("Span"))]
138#[serde_state(stateless)]
139pub struct SerializedSpan {
140 pub data: SpanData,
143 pub generated_from_span: Option<SpanData>,
145}
146
147static WIDE_SPANS: LazyLock<Mutex<WideSpans>> = LazyLock::new(Default::default);
153
154#[derive(Default)]
155struct WideSpans {
156 spans: Vec<SerializedSpan>,
157 indices: HashMap<SerializedSpan, u64>,
158}
159
160impl Span {
161 #[inline]
162 pub fn new(data: SpanData, generated_from_span: Option<SpanData>) -> Self {
163 Self::from_unpacked(SerializedSpan {
164 data,
165 generated_from_span,
166 })
167 }
168
169 #[inline]
172 pub fn data(self) -> SpanData {
173 self.unpack().data
174 }
175
176 #[inline]
178 pub fn generated_from_span(self) -> Option<SpanData> {
179 self.unpack().generated_from_span
180 }
181
182 fn from_unpacked(span: SerializedSpan) -> Self {
183 match Self::pack(span) {
184 Some(packed) => packed,
185 None => Self::store_wide(span),
186 }
187 }
188
189 fn pack(span: SerializedSpan) -> Option<Self> {
190 use pack::*;
191 if span.generated_from_span.is_some() {
192 return None;
193 }
194 let data = span.data;
195 let nb_lines = data.end.line.checked_sub(data.beg.line)?;
196 let bits = put(data.file_id.index() as u32, FILE_SHIFT, FILE_BITS)?
197 | put(data.beg.line, BEG_LINE_SHIFT, LINE_BITS)?
198 | put(data.beg.col, BEG_COL_SHIFT, COL_BITS)?
199 | put(nb_lines, NLINES_SHIFT, NLINES_BITS)?
200 | put(data.end.col, END_COL_SHIFT, COL_BITS)?;
201 Some(Span(bits))
202 }
203
204 fn unpack(self) -> SerializedSpan {
205 use pack::*;
206 if self.0 & WIDE_FLAG != 0 {
207 return WIDE_SPANS.lock().unwrap().spans[(self.0 ^ WIDE_FLAG) as usize];
208 }
209 let beg_line = get(self.0, BEG_LINE_SHIFT, LINE_BITS);
210 let data = SpanData {
211 file_id: FileId::from_raw(get(self.0, FILE_SHIFT, FILE_BITS)),
212 beg: Loc {
213 line: beg_line,
214 col: get(self.0, BEG_COL_SHIFT, COL_BITS),
215 },
216 end: Loc {
217 line: beg_line + get(self.0, NLINES_SHIFT, NLINES_BITS),
218 col: get(self.0, END_COL_SHIFT, COL_BITS),
219 },
220 };
221 SerializedSpan {
222 data,
223 generated_from_span: None,
224 }
225 }
226
227 #[cold]
228 fn store_wide(span: SerializedSpan) -> Self {
229 let mut wide_spans = WIDE_SPANS.lock().unwrap();
230 let index = match wide_spans.indices.get(&span) {
231 Some(index) => *index,
232 None => {
233 let index = wide_spans.spans.len() as u64;
234 assert!(index & pack::WIDE_FLAG == 0, "too many wide spans");
235 wide_spans.spans.push(span);
236 wide_spans.indices.insert(span, index);
237 index
238 }
239 };
240 Span(index | pack::WIDE_FLAG)
241 }
242}
243
244impl Ord for Span {
245 fn cmp(&self, other: &Self) -> Ordering {
246 if (self.0 | other.0) & pack::WIDE_FLAG == 0 {
247 self.0.cmp(&other.0)
250 } else {
251 self.unpack().cmp(&other.unpack())
252 }
253 }
254}
255impl PartialOrd for Span {
256 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
257 Some(self.cmp(other))
258 }
259}
260
261impl Serialize for Span {
262 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
263 SerDedup::Untagged(self.unpack()).serialize(serializer)
264 }
265}
266impl<'de> Deserialize<'de> for Span {
267 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
268 use serde::de::Error;
269 match SerDedup::<SerializedSpan>::deserialize(deserializer)? {
270 SerDedup::Untagged(span) => Ok(Span::from_unpacked(span)),
271 SerDedup::Value { .. } | SerDedup::Deduplicated { .. } => {
272 Err(D::Error::custom(stateless_deserialize_error::<Span>()))
273 }
274 }
275 }
276}
277impl<State: DedupSerializerState> SerializeState<State> for Span {
278 fn serialize_state<S: serde::Serializer>(
279 &self,
280 state: &State,
281 serializer: S,
282 ) -> Result<S::Ok, S::Error> {
283 serialize_dedup(self, self.unpack(), state, serializer)
284 }
285}
286impl<'de, State: DedupSerializerState> DeserializeState<'de, State> for Span {
287 fn deserialize_state<D: serde::Deserializer<'de>>(
288 state: &State,
289 deserializer: D,
290 ) -> Result<Self, D::Error> {
291 deserialize_dedup(state, deserializer, Span::from_unpacked)
292 }
293}
294
295impl std::fmt::Debug for Span {
296 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297 let span = self.unpack();
298 f.debug_struct("Span")
299 .field("data", &span.data)
300 .field("generated_from_span", &span.generated_from_span)
301 .finish()
302 }
303}
304
305impl<'s, V> Drive<'s, V> for Span
306where
307 V: for<'a> Visit<'a, SerializedSpan> + for<'a> Visit<'a, Option<SerializedSpan>>,
308{
309 fn drive_inner(&'s self, v: &mut V) -> ControlFlow<V::Break> {
310 v.visit(&self.unpack())
311 }
312}
313impl<'s, V> DriveMut<'s, V> for Span
314where
315 V: for<'a> VisitMut<'a, SerializedSpan> + for<'a> VisitMut<'a, Option<SerializedSpan>>,
316{
317 fn drive_inner_mut(&'s mut self, v: &mut V) -> ControlFlow<V::Break> {
318 let mut span = self.unpack();
319 let res = v.visit(&mut span);
320 *self = Span::from_unpacked(span);
321 res
322 }
323}
324impl<'s, V> DriveTwo<'s, V> for Span
325where
326 V: for<'a> VisitTwo<'a, SerializedSpan> + for<'a> VisitTwo<'a, Option<SerializedSpan>>,
327{
328 fn drive_two_inner(&'s self, other: &'s Self, v: &mut V) -> ControlFlow<V::Break> {
329 v.visit(&self.unpack(), &other.unpack())
330 }
331}
332
333fn line_to_start_byte(source: &str, line_nbr: usize) -> usize {
337 let mut cur_byte = 0;
338 for (i, line) in source.split_inclusive('\n').enumerate() {
339 if line_nbr == i + 1 {
340 break;
341 }
342 cur_byte += line.len();
343 }
344 cur_byte
345}
346
347impl Loc {
348 const fn dummy() -> Self {
349 Loc { line: 0, col: 0 }
350 }
351
352 fn min(l0: &Loc, l1: &Loc) -> Loc {
353 match l0.line.cmp(&l1.line) {
354 Ordering::Equal => Loc {
355 line: l0.line,
356 col: std::cmp::min(l0.col, l1.col),
357 },
358 Ordering::Less => *l0,
359 Ordering::Greater => *l1,
360 }
361 }
362
363 fn max(l0: &Loc, l1: &Loc) -> Loc {
364 match l0.line.cmp(&l1.line) {
365 Ordering::Equal => Loc {
366 line: l0.line,
367 col: std::cmp::max(l0.col, l1.col),
368 },
369 Ordering::Greater => *l0,
370 Ordering::Less => *l1,
371 }
372 }
373
374 pub fn to_byte(self, source: &str) -> usize {
375 line_to_start_byte(source, self.line as usize) + self.col as usize
376 }
377}
378
379impl SpanData {
380 pub const fn dummy() -> Self {
381 SpanData {
382 file_id: FileId::ZERO,
383 beg: Loc::dummy(),
384 end: Loc::dummy(),
385 }
386 }
387
388 fn sort_key(&self) -> impl Ord {
390 (self.file_id, self.beg, self.end)
391 }
392
393 pub fn to_byte_range(self, source: &str) -> Range<usize> {
394 self.beg.to_byte(source)..self.end.to_byte(source)
395 }
396}
397
398impl PartialOrd for SpanData {
400 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
401 Some(self.cmp(other))
402 }
403}
404impl Ord for SpanData {
405 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
406 self.sort_key().cmp(&other.sort_key())
407 }
408}
409
410impl Span {
411 pub const fn dummy() -> Self {
412 Span(0)
415 }
416}
417
418pub fn combine_span(m0: &Span, m1: &Span) -> Span {
421 let (d0, d1) = (m0.data(), m1.data());
422 if d0.file_id == d1.file_id {
424 let data = SpanData {
425 file_id: d0.file_id,
426 beg: Loc::min(&d0.beg, &d1.beg),
427 end: Loc::max(&d0.end, &d1.end),
428 };
429
430 Span::new(data, None)
434 } else {
435 *m0
438 }
439}
440
441pub fn combine_span_iter<'a, T: Iterator<Item = &'a Span>>(mut ms: T) -> Span {
443 let mut mc: Span = ms.next().copied().unwrap_or_default();
445 for m in ms {
446 mc = combine_span(&mc, m);
447 }
448
449 mc
450}
451
452impl FileName {
453 pub fn to_string(&self) -> Cow<'_, str> {
454 match self {
455 FileName::Virtual(path_buf) | FileName::Local(path_buf) => path_buf.to_string_lossy(),
456 FileName::NotReal(path) => Cow::Borrowed(path),
457 }
458 }
459}
460
461impl Default for Span {
462 fn default() -> Self {
463 Self::dummy()
464 }
465}
466
467#[test]
469fn span_is_small() {
470 assert_eq!(size_of::<Span>(), 8);
471}
472
473#[test]
475fn span_dummy_is_zero() {
476 assert_eq!(Span::dummy(), Span::new(SpanData::dummy(), None));
477 assert_eq!(Span::dummy().data(), SpanData::dummy());
478}
479
480#[test]
483fn span_roundtrip() {
484 let data = |file: usize, beg: (u32, u32), end: (u32, u32)| SpanData {
485 file_id: FileId::from_usize(file),
486 beg: Loc {
487 line: beg.0,
488 col: beg.1,
489 },
490 end: Loc {
491 line: end.0,
492 col: end.1,
493 },
494 };
495 let packed = data(12, (34, 56), (78, 90));
496 let huge_file = data(1 << 20, (34, 56), (78, 90));
497 let long_line = data(12, (34, 5678), (78, 90));
498 let backwards = data(12, (78, 56), (34, 90));
499 for (d, generated) in [
500 (packed, None),
501 (packed, Some(packed)),
502 (huge_file, None),
503 (long_line, None),
504 (backwards, None),
505 ] {
506 let span = Span::new(d, generated);
507 assert_eq!(span.data(), d);
508 assert_eq!(span.generated_from_span(), generated);
509 }
510 assert!(Span::new(packed, None).0 & pack::WIDE_FLAG == 0);
512 assert_eq!(Span::new(backwards, None), Span::new(backwards, None));
514}