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(
13 Debug,
14 PartialEq,
15 Eq,
16 Clone,
17 Hash,
18 PartialOrd,
19 Ord,
20 Serialize,
21 Deserialize,
22 Drive,
23 DriveMut,
24 DriveTwo,
25)]
26pub enum FileName {
27 Virtual(PathBuf),
29 Local(PathBuf),
31 NotReal(String),
33}
34
35#[derive(
36 Debug,
37 PartialEq,
38 Eq,
39 Clone,
40 Hash,
41 PartialOrd,
42 Ord,
43 Serialize,
44 Deserialize,
45 Drive,
46 DriveMut,
47 DriveTwo,
48)]
49pub struct File {
50 #[cfg_attr(feature = "charon_on_charon", charon::opaque)]
52 pub id: FileId,
53 pub name: FileName,
55 pub crate_name: String,
57 pub contents: Option<String>,
60}
61
62#[derive(
63 Debug,
64 Copy,
65 Clone,
66 PartialEq,
67 Eq,
68 PartialOrd,
69 Ord,
70 Hash,
71 Serialize,
72 Deserialize,
73 Drive,
74 DriveMut,
75 DriveTwo,
76)]
77pub struct Loc {
78 pub line: u32,
80 pub col: u32,
82}
83
84#[derive(
86 Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Drive, DriveMut, DriveTwo,
87)]
88pub struct SpanData {
89 #[cfg_attr(feature = "charon_on_charon", charon::rename("file"))]
90 pub file_id: FileId,
91 #[cfg_attr(feature = "charon_on_charon", charon::rename("beg_loc"))]
92 pub beg: Loc,
93 #[cfg_attr(feature = "charon_on_charon", charon::rename("end_loc"))]
94 pub end: Loc,
95}
96
97#[derive(Copy, Clone, PartialEq, Eq, Hash)]
133pub struct Span(u64);
134
135mod pack {
137 pub const WIDE_FLAG: u64 = 1 << 63;
139 pub const FILE_BITS: u32 = 16;
140 pub const LINE_BITS: u32 = 20;
141 pub const COL_BITS: u32 = 10;
142 pub const NLINES_BITS: u32 = 7;
143
144 pub const END_COL_SHIFT: u32 = 0;
145 pub const NLINES_SHIFT: u32 = END_COL_SHIFT + COL_BITS;
146 pub const BEG_COL_SHIFT: u32 = NLINES_SHIFT + NLINES_BITS;
147 pub const BEG_LINE_SHIFT: u32 = BEG_COL_SHIFT + COL_BITS;
148 pub const FILE_SHIFT: u32 = BEG_LINE_SHIFT + LINE_BITS;
149
150 #[inline]
152 pub fn get(x: u64, shift: u32, bits: u32) -> u32 {
153 ((x >> shift) & ((1 << bits) - 1)) as u32
154 }
155
156 #[inline]
158 pub fn put(x: u32, shift: u32, bits: u32) -> Option<u64> {
159 (u64::from(x) < (1 << bits)).then_some(u64::from(x) << shift)
160 }
161}
162
163#[derive(
166 Debug,
167 Copy,
168 Clone,
169 PartialEq,
170 Eq,
171 PartialOrd,
172 Ord,
173 Hash,
174 Serialize,
175 Deserialize,
176 SerializeState,
177 DeserializeState,
178 Drive,
179 DriveMut,
180 DriveTwo,
181)]
182#[cfg_attr(feature = "charon_on_charon", charon::rename("Span"))]
183#[serde_state(stateless)]
184pub struct SerializedSpan {
185 pub data: SpanData,
188 pub generated_from_span: Option<SpanData>,
190}
191
192static WIDE_SPANS: LazyLock<Mutex<WideSpans>> = LazyLock::new(Default::default);
198
199#[derive(Default)]
200struct WideSpans {
201 spans: Vec<SerializedSpan>,
202 indices: HashMap<SerializedSpan, u64>,
203}
204
205impl Span {
206 #[inline]
207 pub fn new(data: SpanData, generated_from_span: Option<SpanData>) -> Self {
208 Self::from_unpacked(SerializedSpan {
209 data,
210 generated_from_span,
211 })
212 }
213
214 #[inline]
217 pub fn data(self) -> SpanData {
218 self.unpack().data
219 }
220
221 #[inline]
223 pub fn generated_from_span(self) -> Option<SpanData> {
224 self.unpack().generated_from_span
225 }
226
227 fn from_unpacked(span: SerializedSpan) -> Self {
228 match Self::pack(span) {
229 Some(packed) => packed,
230 None => Self::store_wide(span),
231 }
232 }
233
234 fn pack(span: SerializedSpan) -> Option<Self> {
235 use pack::*;
236 if span.generated_from_span.is_some() {
237 return None;
238 }
239 let data = span.data;
240 let nb_lines = data.end.line.checked_sub(data.beg.line)?;
241 let bits = put(data.file_id.index() as u32, FILE_SHIFT, FILE_BITS)?
242 | put(data.beg.line, BEG_LINE_SHIFT, LINE_BITS)?
243 | put(data.beg.col, BEG_COL_SHIFT, COL_BITS)?
244 | put(nb_lines, NLINES_SHIFT, NLINES_BITS)?
245 | put(data.end.col, END_COL_SHIFT, COL_BITS)?;
246 Some(Span(bits))
247 }
248
249 fn unpack(self) -> SerializedSpan {
250 use pack::*;
251 if self.0 & WIDE_FLAG != 0 {
252 return WIDE_SPANS.lock().unwrap().spans[(self.0 ^ WIDE_FLAG) as usize];
253 }
254 let beg_line = get(self.0, BEG_LINE_SHIFT, LINE_BITS);
255 let data = SpanData {
256 file_id: FileId::from_raw(get(self.0, FILE_SHIFT, FILE_BITS)),
257 beg: Loc {
258 line: beg_line,
259 col: get(self.0, BEG_COL_SHIFT, COL_BITS),
260 },
261 end: Loc {
262 line: beg_line + get(self.0, NLINES_SHIFT, NLINES_BITS),
263 col: get(self.0, END_COL_SHIFT, COL_BITS),
264 },
265 };
266 SerializedSpan {
267 data,
268 generated_from_span: None,
269 }
270 }
271
272 #[cold]
273 fn store_wide(span: SerializedSpan) -> Self {
274 let mut wide_spans = WIDE_SPANS.lock().unwrap();
275 let index = match wide_spans.indices.get(&span) {
276 Some(index) => *index,
277 None => {
278 let index = wide_spans.spans.len() as u64;
279 assert!(index & pack::WIDE_FLAG == 0, "too many wide spans");
280 wide_spans.spans.push(span);
281 wide_spans.indices.insert(span, index);
282 index
283 }
284 };
285 Span(index | pack::WIDE_FLAG)
286 }
287}
288
289impl Ord for Span {
290 fn cmp(&self, other: &Self) -> Ordering {
291 if (self.0 | other.0) & pack::WIDE_FLAG == 0 {
292 self.0.cmp(&other.0)
295 } else {
296 self.unpack().cmp(&other.unpack())
297 }
298 }
299}
300impl PartialOrd for Span {
301 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
302 Some(self.cmp(other))
303 }
304}
305
306impl Serialize for Span {
307 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
308 SerDedup::Untagged(self.unpack()).serialize(serializer)
309 }
310}
311impl<'de> Deserialize<'de> for Span {
312 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
313 use serde::de::Error;
314 match SerDedup::<SerializedSpan>::deserialize(deserializer)? {
315 SerDedup::Untagged(span) => Ok(Span::from_unpacked(span)),
316 SerDedup::Value { .. } | SerDedup::Deduplicated { .. } => {
317 Err(D::Error::custom(stateless_deserialize_error::<Span>()))
318 }
319 }
320 }
321}
322impl<State: DedupSerializerState> SerializeState<State> for Span {
323 fn serialize_state<S: serde::Serializer>(
324 &self,
325 state: &State,
326 serializer: S,
327 ) -> Result<S::Ok, S::Error> {
328 serialize_dedup(self, self.unpack(), state, serializer)
329 }
330}
331impl<'de, State: DedupSerializerState> DeserializeState<'de, State> for Span {
332 fn deserialize_state<D: serde::Deserializer<'de>>(
333 state: &State,
334 deserializer: D,
335 ) -> Result<Self, D::Error> {
336 deserialize_dedup(state, deserializer, Span::from_unpacked)
337 }
338}
339
340impl std::fmt::Debug for Span {
341 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342 let span = self.unpack();
343 f.debug_struct("Span")
344 .field("data", &span.data)
345 .field("generated_from_span", &span.generated_from_span)
346 .finish()
347 }
348}
349
350impl<'s, V> Drive<'s, V> for Span
351where
352 V: for<'a> Visit<'a, SerializedSpan> + for<'a> Visit<'a, Option<SerializedSpan>>,
353{
354 fn drive_inner(&'s self, v: &mut V) -> ControlFlow<V::Break> {
355 v.visit(&self.unpack())
356 }
357}
358impl<'s, V> DriveMut<'s, V> for Span
359where
360 V: for<'a> VisitMut<'a, SerializedSpan> + for<'a> VisitMut<'a, Option<SerializedSpan>>,
361{
362 fn drive_inner_mut(&'s mut self, v: &mut V) -> ControlFlow<V::Break> {
363 let mut span = self.unpack();
364 let res = v.visit(&mut span);
365 *self = Span::from_unpacked(span);
366 res
367 }
368}
369impl<'s, V> DriveTwo<'s, V> for Span
370where
371 V: for<'a> VisitTwo<'a, SerializedSpan> + for<'a> VisitTwo<'a, Option<SerializedSpan>>,
372{
373 fn drive_two_inner(&'s self, other: &'s Self, v: &mut V) -> ControlFlow<V::Break> {
374 v.visit(&self.unpack(), &other.unpack())
375 }
376}
377
378fn line_to_start_byte(source: &str, line_nbr: usize) -> usize {
382 let mut cur_byte = 0;
383 for (i, line) in source.split_inclusive('\n').enumerate() {
384 if line_nbr == i + 1 {
385 break;
386 }
387 cur_byte += line.len();
388 }
389 cur_byte
390}
391
392impl Loc {
393 const fn dummy() -> Self {
394 Loc { line: 0, col: 0 }
395 }
396
397 fn min(l0: &Loc, l1: &Loc) -> Loc {
398 match l0.line.cmp(&l1.line) {
399 Ordering::Equal => Loc {
400 line: l0.line,
401 col: std::cmp::min(l0.col, l1.col),
402 },
403 Ordering::Less => *l0,
404 Ordering::Greater => *l1,
405 }
406 }
407
408 fn max(l0: &Loc, l1: &Loc) -> Loc {
409 match l0.line.cmp(&l1.line) {
410 Ordering::Equal => Loc {
411 line: l0.line,
412 col: std::cmp::max(l0.col, l1.col),
413 },
414 Ordering::Greater => *l0,
415 Ordering::Less => *l1,
416 }
417 }
418
419 pub fn to_byte(self, source: &str) -> usize {
420 line_to_start_byte(source, self.line as usize) + self.col as usize
421 }
422}
423
424impl SpanData {
425 pub const fn dummy() -> Self {
426 SpanData {
427 file_id: FileId::ZERO,
428 beg: Loc::dummy(),
429 end: Loc::dummy(),
430 }
431 }
432
433 fn sort_key(&self) -> impl Ord {
435 (self.file_id, self.beg, self.end)
436 }
437
438 pub fn to_byte_range(self, source: &str) -> Range<usize> {
439 self.beg.to_byte(source)..self.end.to_byte(source)
440 }
441}
442
443impl PartialOrd for SpanData {
445 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
446 Some(self.cmp(other))
447 }
448}
449impl Ord for SpanData {
450 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
451 self.sort_key().cmp(&other.sort_key())
452 }
453}
454
455impl Span {
456 pub const fn dummy() -> Self {
457 Span(0)
460 }
461}
462
463pub fn combine_span(m0: &Span, m1: &Span) -> Span {
466 let (d0, d1) = (m0.data(), m1.data());
467 if d0.file_id == d1.file_id {
469 let data = SpanData {
470 file_id: d0.file_id,
471 beg: Loc::min(&d0.beg, &d1.beg),
472 end: Loc::max(&d0.end, &d1.end),
473 };
474
475 Span::new(data, None)
479 } else {
480 *m0
483 }
484}
485
486pub fn combine_span_iter<'a, T: Iterator<Item = &'a Span>>(mut ms: T) -> Span {
488 let mut mc: Span = ms.next().copied().unwrap_or_default();
490 for m in ms {
491 mc = combine_span(&mc, m);
492 }
493
494 mc
495}
496
497impl FileName {
498 pub fn to_string(&self) -> Cow<'_, str> {
499 match self {
500 FileName::Virtual(path_buf) | FileName::Local(path_buf) => path_buf.to_string_lossy(),
501 FileName::NotReal(path) => Cow::Borrowed(path),
502 }
503 }
504}
505
506impl Default for Span {
507 fn default() -> Self {
508 Self::dummy()
509 }
510}
511
512#[test]
514fn span_is_small() {
515 assert_eq!(size_of::<Span>(), 8);
516}
517
518#[test]
520fn span_dummy_is_zero() {
521 assert_eq!(Span::dummy(), Span::new(SpanData::dummy(), None));
522 assert_eq!(Span::dummy().data(), SpanData::dummy());
523}
524
525#[test]
528fn span_roundtrip() {
529 let data = |file: usize, beg: (u32, u32), end: (u32, u32)| SpanData {
530 file_id: FileId::from_usize(file),
531 beg: Loc {
532 line: beg.0,
533 col: beg.1,
534 },
535 end: Loc {
536 line: end.0,
537 col: end.1,
538 },
539 };
540 let packed = data(12, (34, 56), (78, 90));
541 let huge_file = data(1 << 20, (34, 56), (78, 90));
542 let long_line = data(12, (34, 5678), (78, 90));
543 let backwards = data(12, (78, 56), (34, 90));
544 for (d, generated) in [
545 (packed, None),
546 (packed, Some(packed)),
547 (huge_file, None),
548 (long_line, None),
549 (backwards, None),
550 ] {
551 let span = Span::new(d, generated);
552 assert_eq!(span.data(), d);
553 assert_eq!(span.generated_from_span(), generated);
554 }
555 assert!(Span::new(packed, None).0 & pack::WIDE_FLAG == 0);
557 assert_eq!(Span::new(backwards, None), Span::new(backwards, None));
559}