use std::fmt::{self, Debug, Formatter};
use std::num::{NonZeroU16, NonZeroU32, NonZeroU64};
use std::ops::Range;
use ecow::{EcoString, eco_format};
use crate::FileId;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Span(NonZeroU64);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SpanNumber(pub(crate) u64);
#[derive(Debug)]
pub enum SpanKind {
Detached,
Number { id: FileId, num: SpanNumber },
Range { id: FileId, range: Range<usize> },
}
impl Span {
pub(crate) const FULL: Range<u64> = 2..(1 << 47);
const DETACHED: Self = Self(NonZeroU64::new(1).unwrap());
const NUMBER_BITS: usize = 48;
const RANGE_BITS: usize = 46;
const FILE_ID_SHIFT: usize = Self::NUMBER_BITS;
const NUMBER_MASK: u64 = (1 << Self::NUMBER_BITS) - 1;
const EXTERNAL_BASE: u64 = Self::FULL.end;
const EXTERNAL_VALUE_MAX: u64 = (1 << Self::RANGE_BITS) - 1;
const RANGE_BASE: u64 = Self::EXTERNAL_BASE + (1 << Self::RANGE_BITS);
const RANGE_VALUE_BITS: usize = 23;
const RANGE_VALUE_MAX: u64 = (1 << Self::RANGE_VALUE_BITS) - 1;
pub const fn detached() -> Self {
Self::DETACHED
}
pub(crate) const fn from_number(id: FileId, SpanNumber(number): SpanNumber) -> Self {
debug_assert!(Self::FULL.start <= number);
debug_assert!(number < Self::FULL.end);
Self::pack(id, number)
}
pub(crate) const fn from_range(id: FileId, range: Range<usize>) -> Self {
let start = saturate(range.start, Self::RANGE_VALUE_MAX);
let end = saturate(range.end, Self::RANGE_VALUE_MAX);
let number = (start << Self::RANGE_VALUE_BITS) | end;
Self::pack(id, Self::RANGE_BASE + number)
}
pub const fn from_raw(v: NonZeroU64) -> Self {
Self(v)
}
const fn pack(id: FileId, low: u64) -> Self {
let bits = ((id.into_raw().get() as u64) << Self::FILE_ID_SHIFT) | low;
Self(NonZeroU64::new(bits).unwrap())
}
pub const fn is_detached(self) -> bool {
self.0.get() == Self::DETACHED.0.get()
}
pub const fn id(self) -> Option<FileId> {
match NonZeroU16::new((self.0.get() >> Self::FILE_ID_SHIFT) as u16) {
Some(v) => Some(FileId::from_raw(v)),
None => None,
}
}
pub(crate) const fn number(self) -> u64 {
self.0.get() & Self::NUMBER_MASK
}
pub const fn get(self) -> SpanKind {
let Some(id) = self.id() else { return SpanKind::Detached };
let num = self.number();
if let Some(packed_range) = num.checked_sub(Self::RANGE_BASE) {
let start = (packed_range >> Self::RANGE_VALUE_BITS) as usize;
let end = (packed_range & Self::RANGE_VALUE_MAX) as usize;
SpanKind::Range { id, range: start..end }
} else {
SpanKind::Number { id, num: SpanNumber(num) }
}
}
pub const fn into_raw(self) -> NonZeroU64 {
self.0
}
pub fn or(self, other: Self) -> Self {
if self.is_detached() { other } else { self }
}
pub fn find(iter: impl IntoIterator<Item = Self>) -> Self {
iter.into_iter()
.find(|span| !span.is_detached())
.unwrap_or(Span::detached())
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct DiagSpan {
span: Span,
extra: u64,
}
#[derive(Debug)]
pub enum DiagSpanKind {
Detached,
Number { id: FileId, num: SpanNumber, sub_range: Option<SubRange> },
Range { id: FileId, range: Range<usize> },
}
impl DiagSpan {
pub const fn detached() -> Self {
Self { span: Span::DETACHED, extra: 0 }
}
pub fn from_range(id: FileId, range: Range<usize>) -> Self {
let start = saturate(range.start, Span::EXTERNAL_VALUE_MAX);
let end = saturate(range.end, Span::EXTERNAL_VALUE_MAX);
Self {
span: Span::pack(id, Span::EXTERNAL_BASE + start),
extra: end,
}
}
pub fn from_span(span: Span, sub_range: Option<SubRange>) -> Self {
let extra = sub_range.map_or(0, |SubRange { start, end }| {
((start as u64) << 32) | (end.get() as u64)
});
Self { span, extra }
}
pub const fn is_detached(self) -> bool {
self.span.0.get() == Span::DETACHED.0.get()
}
pub fn id(self) -> Option<FileId> {
self.span.id()
}
pub fn or(self, other: Self) -> Self {
if self.is_detached() { other } else { self }
}
pub fn get(self) -> DiagSpanKind {
let DiagSpan { span, extra } = self;
match span.get() {
SpanKind::Detached => DiagSpanKind::Detached,
SpanKind::Number { id, num } => {
if let Some(start) = num.0.checked_sub(Span::EXTERNAL_BASE) {
let start = start as usize;
let end = extra as usize;
DiagSpanKind::Range { id, range: start..end }
} else {
let sub_range = {
let start = (extra >> 32) as u32;
let end = NonZeroU32::new(extra as u32); end.map(|end| SubRange { start, end })
};
DiagSpanKind::Number { id, num, sub_range }
}
}
SpanKind::Range { id, range } => {
if let Some(end) = NonZeroU32::new(extra as u32) {
let start = (extra >> 32) as u32;
let sub_range = SubRange { start, end };
let range = sub_range.to_absolute(range.start);
DiagSpanKind::Range { id, range }
} else {
DiagSpanKind::Range { id, range }
}
}
}
}
}
impl From<Span> for DiagSpan {
fn from(span: Span) -> Self {
Self::from_span(span, None)
}
}
const fn saturate(value: usize, max: u64) -> u64 {
if value as u64 > max { max } else { value as u64 }
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct SubRange {
start: u32,
end: NonZeroU32,
}
impl SubRange {
pub fn new(start: usize, end: usize) -> Option<Self> {
if start < end {
Some(Self {
start: to_u32_saturated(start),
end: NonZeroU32::new(to_u32_saturated(end)).unwrap(),
})
} else {
None
}
}
pub fn to_relative(self) -> Range<usize> {
Range {
start: self.start as usize,
end: self.end.get() as usize,
}
}
pub fn to_absolute(self, offset: usize) -> Range<usize> {
Range {
start: self.start as usize + offset,
end: self.end.get() as usize + offset,
}
}
}
fn to_u32_saturated(value: usize) -> u32 {
value.try_into().unwrap_or(u32::MAX)
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[expect(private_bounds)]
pub struct Spanned<T, S: Copy + SpanDetached = Span> {
pub v: T,
pub span: S,
}
#[expect(private_bounds)]
impl<T, S: Copy + SpanDetached> Spanned<T, S> {
pub const fn new(v: T, span: S) -> Self {
Self { v, span }
}
pub const fn detached(v: T) -> Self {
Self { v, span: S::SPAN_DETACHED }
}
pub const fn as_ref(&self) -> Spanned<&T, S> {
Spanned { v: &self.v, span: self.span }
}
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U, S> {
Spanned { v: f(self.v), span: self.span }
}
}
impl<T: Debug, S: Copy + SpanDetached> Debug for Spanned<T, S> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.v.fmt(f)
}
}
trait SpanDetached {
const SPAN_DETACHED: Self;
}
impl SpanDetached for Span {
const SPAN_DETACHED: Self = Self::detached();
}
impl SpanDetached for DiagSpan {
const SPAN_DETACHED: Self = Self::detached();
}
#[derive(Hash)]
pub struct RangeMapper {
vec: Vec<Mapping>,
total: usize,
}
#[derive(Hash, Clone, Copy)]
struct Mapping {
old: usize,
new: usize,
}
impl RangeMapper {
pub fn new(
segments: impl IntoIterator<Item = Range<usize>>,
) -> Result<Self, EcoString> {
let mut map = Mapping { old: 0, new: 0 };
let vec = segments
.into_iter()
.map(|Range { start, end }| {
if start > end || map.new > start {
return Err(eco_format!("invalid mapper segment: ({start}, {end})"));
}
map.new = start;
let segment_map = map;
map.old += end - start;
Ok(segment_map)
})
.collect::<Result<Vec<Mapping>, EcoString>>()?;
if vec.is_empty() {
Ok(Self { vec: vec![map], total: 0 })
} else {
Ok(Self { vec, total: map.old })
}
}
pub(crate) fn total_len(&self) -> usize {
self.total
}
pub(crate) fn map(&self, range: Range<usize>) -> Range<usize> {
debug_assert!(range.start <= range.end);
if range.end == 0 {
let offset = self.vec[0].new;
offset..offset
} else if range.start == range.end {
let offset = self.map_end(range.start);
offset..offset
} else {
let start = self.map_start(range.start);
let end = self.map_end(range.end);
start..end
}
}
pub(crate) fn map_sub_range(&self, offset: usize, sub_range: SubRange) -> SubRange {
let range = sub_range.to_absolute(offset);
let new_offset = self.map_start(offset);
let start = self.map_start(range.start);
let end = self.map_end(range.end); SubRange::new(start - new_offset, end - new_offset).unwrap()
}
fn map_start(&self, offset: usize) -> usize {
let idx = self.vec.partition_point(|&Mapping { old, new: _ }| old <= offset);
let Mapping { old, new } = &self.vec[idx - 1];
new + (offset - old)
}
fn map_end(&self, offset: usize) -> usize {
debug_assert_ne!(offset, 0);
let idx = self.vec.partition_point(|&Mapping { old, new: _ }| old < offset);
let Mapping { old, new } = &self.vec[idx - 1];
new + (offset - old)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_span_detached() {
let span = Span::detached();
assert!(span.is_detached());
assert_eq!(span.id(), None);
}
#[test]
fn test_span_number_encoding() {
let id = FileId::from_raw(NonZeroU16::new(5).unwrap());
let span = Span::from_number(id, SpanNumber(10));
assert_eq!(span.id(), Some(id));
assert_eq!(span.number(), 10);
}
#[test]
fn test_span_range_encoding() {
let file_id = FileId::from_raw(NonZeroU16::new(u16::MAX).unwrap());
let roundtrip = |range: Range<usize>| {
let span = Span::from_range(file_id, range.clone());
let SpanKind::Range { id, range: actual } = span.get() else {
panic!("bad span kind")
};
assert_eq!(id, file_id);
assert_eq!(actual, range);
};
roundtrip(0..0);
roundtrip(177..233);
roundtrip(0..8388607);
roundtrip(8388606..8388607); }
#[test]
fn test_diag_span_range() {
let file_id = FileId::from_raw(NonZeroU16::new(u16::MAX).unwrap());
let roundtrip = |range: Range<usize>| {
let span = DiagSpan::from_range(file_id, range.clone());
let DiagSpanKind::Range { id, range: actual } = span.get() else {
panic!("bad diagspan kind")
};
assert_eq!(id, file_id);
assert_eq!(actual, range);
};
roundtrip(0..0);
roundtrip(177..233);
roundtrip(0..8388607);
roundtrip(8388606..8388607); roundtrip(8388608..8388609); #[cfg(target_pointer_width = "64")]
roundtrip(70368744177662..70368744177663); }
#[test]
fn test_sub_range_constructor() {
let max = u32::MAX as usize;
assert!(SubRange::new(0, 1).is_some());
assert!(SubRange::new(4, 5).is_some());
assert!(SubRange::new(0, max).is_some());
assert!(SubRange::new(0, max - 1).is_some());
assert!(SubRange::new(max - 1, max).is_some());
assert!(SubRange::new(0, 0).is_none());
assert!(SubRange::new(5, 5).is_none());
assert!(SubRange::new(5, 4).is_none());
assert!(SubRange::new(max - 1, max - 1).is_none());
assert!(SubRange::new(max, max).is_none());
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_sub_range_saturating() {
let max = u32::MAX as usize;
let maxxed = SubRange::new(max, max + 1).unwrap();
assert_eq!(maxxed.start, maxxed.end.get());
assert_eq!(SubRange::new(1 << 47, 1 << 63), Some(maxxed));
}
#[test]
fn test_range_mapper() {
let base = "-- Hello\n-- world\n";
let ranges = [(3..9), (12..18)];
let mapped = ranges.iter().map(|r| &base[r.clone()]).collect::<String>();
let m = RangeMapper::new(ranges).unwrap();
assert_eq!(mapped, "Hello\nworld\n");
assert_eq!(m.map(2..3), 5..6); assert_eq!(m.map(4..6), (7..9)); assert_eq!(m.map(6..8), (12..14)); assert_eq!(m.map(8..11), (14..17)); assert_eq!(m.map(2..12), (5..18));
assert_eq!(m.map(0..0), (3..3));
assert_eq!(m.map(6..6), (9..9)); assert_eq!(m.map(12..12), (18..18));
}
#[test]
fn test_range_mapper_exhaustive() {
let empty = RangeMapper::new([]).unwrap();
assert_eq!(empty.map(0..0), 0..0);
let exact = RangeMapper::new(Some(0..1)).unwrap();
assert_eq!(exact.map(0..0), 0..0);
assert_eq!(exact.map(0..1), 0..1);
assert_eq!(exact.map(1..1), 1..1);
let plus = RangeMapper::new(Some(10..11)).unwrap();
assert_eq!(plus.map(0..0), 10..10);
assert_eq!(plus.map(0..1), 10..11);
assert_eq!(plus.map(1..1), 11..11);
let disjoint = RangeMapper::new([(10..11), (21..22)]).unwrap();
assert_eq!(disjoint.map(0..0), 10..10);
assert_eq!(disjoint.map(0..1), 10..11);
assert_eq!(disjoint.map(0..2), 10..22);
assert_eq!(disjoint.map(1..1), 11..11);
assert_eq!(disjoint.map(1..2), 21..22);
assert_eq!(disjoint.map(2..2), 22..22);
let with_empty = RangeMapper::new([
(10..10),
(10..11),
(11..11),
(16..16),
(21..21),
(21..22),
(22..22),
])
.unwrap();
assert_eq!(with_empty.map(0..0), 10..10);
assert_eq!(with_empty.map(0..1), 10..11);
assert_eq!(with_empty.map(0..2), 10..22);
assert_eq!(with_empty.map(1..1), 11..11);
assert_eq!(with_empty.map(1..2), 21..22);
assert_eq!(with_empty.map(2..2), 22..22);
}
#[test]
fn test_sub_range_mapping() {
let base = "01_23__45";
let ranges = [(0..2), (3..5), (7..9)];
let mapped = ranges.iter().map(|r| &base[r.clone()]).collect::<String>();
assert_eq!(mapped, "012345");
let m = RangeMapper::new(ranges).unwrap();
let map_at = |at: usize, sr: Option<SubRange>| {
let sub_range = sr.unwrap();
m.map_sub_range(at, sub_range).to_relative()
};
assert_eq!(map_at(0, SubRange::new(0, 1)), 0..1); assert_eq!(map_at(0, SubRange::new(2, 3)), 3..4); assert_eq!(map_at(0, SubRange::new(2, 4)), 3..5); assert_eq!(map_at(0, SubRange::new(4, 5)), 7..8); assert_eq!(map_at(1, SubRange::new(0, 1)), 0..1); assert_eq!(map_at(3, SubRange::new(0, 1)), 0..1); assert_eq!(map_at(4, SubRange::new(0, 2)), 0..2); assert_eq!(map_at(1, SubRange::new(0, 2)), 0..3); assert_eq!(map_at(0, SubRange::new(1, 3)), 1..4); assert_eq!(map_at(3, SubRange::new(0, 2)), 0..4); assert_eq!(map_at(0, SubRange::new(3, 5)), 4..8); assert_eq!(map_at(1, SubRange::new(0, 4)), 0..7); assert_eq!(map_at(0, SubRange::new(1, 5)), 1..8); assert_eq!(map_at(0, SubRange::new(0, 6)), 0..9); }
}