use crate::hygiene::SyntaxContext;
use crate::GLOBALS;
use crate::{BytePos, SpanData};
use rustc_data_structures::fx::FxHashMap;
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub struct Span {
base_or_index: u32,
len_or_tag: u16,
ctxt_or_zero: u16,
}
const LEN_TAG: u16 = 0b1000_0000_0000_0000;
const MAX_LEN: u32 = 0b0111_1111_1111_1111;
const MAX_CTXT: u32 = 0b1111_1111_1111_1111;
pub const DUMMY_SP: Span = Span { base_or_index: 0, len_or_tag: 0, ctxt_or_zero: 0 };
impl Span {
#[inline]
pub fn new(mut lo: BytePos, mut hi: BytePos, ctxt: SyntaxContext) -> Self {
if lo > hi {
std::mem::swap(&mut lo, &mut hi);
}
let (base, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());
if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
Span { base_or_index: base, len_or_tag: len as u16, ctxt_or_zero: ctxt2 as u16 }
} else {
let index = with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt }));
Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_zero: 0 }
}
}
#[inline]
pub fn data(self) -> SpanData {
if self.len_or_tag != LEN_TAG {
debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
SpanData {
lo: BytePos(self.base_or_index),
hi: BytePos(self.base_or_index + self.len_or_tag as u32),
ctxt: SyntaxContext::from_u32(self.ctxt_or_zero as u32),
}
} else {
debug_assert!(self.ctxt_or_zero == 0);
let index = self.base_or_index;
with_span_interner(|interner| *interner.get(index))
}
}
}
#[derive(Default)]
pub struct SpanInterner {
spans: FxHashMap<SpanData, u32>,
span_data: Vec<SpanData>,
}
impl SpanInterner {
fn intern(&mut self, span_data: &SpanData) -> u32 {
if let Some(index) = self.spans.get(span_data) {
return *index;
}
let index = self.spans.len() as u32;
self.span_data.push(*span_data);
self.spans.insert(*span_data, index);
index
}
#[inline]
fn get(&self, index: u32) -> &SpanData {
&self.span_data[index as usize]
}
}
#[inline]
fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
GLOBALS.with(|globals| f(&mut *globals.span_interner.lock()))
}