litcheck_core/
range.rs

1use core::{
2    cmp::Ordering,
3    fmt,
4    ops::{Add, AddAssign, Bound, Index, IndexMut, RangeBounds, Sub},
5};
6
7pub trait NumericRangeBound:
8    Add<Output = Self> + AddAssign + Sub<Output = Self> + Copy + PartialEq + Ord
9{
10    const ZERO: Self;
11    const ONE: Self;
12
13    fn saturating_sub(self, rhs: Self) -> Self;
14}
15
16macro_rules! numeric_bound_impl {
17    ($type:ty) => {
18        impl NumericRangeBound for $type {
19            const ZERO: Self = 0;
20            const ONE: Self = 0;
21
22            #[inline(always)]
23            fn saturating_sub(self, rhs: Self) -> Self {
24                <$type>::saturating_sub(self, rhs)
25            }
26        }
27    };
28}
29
30numeric_bound_impl!(usize);
31numeric_bound_impl!(u8);
32numeric_bound_impl!(u16);
33numeric_bound_impl!(u32);
34numeric_bound_impl!(u64);
35
36pub struct Range<T> {
37    pub start: T,
38    pub end: T,
39}
40impl<T: fmt::Debug> fmt::Debug for Range<T> {
41    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42        f.debug_struct("Range")
43            .field("start", &self.start)
44            .field("end", &self.end)
45            .finish()
46    }
47}
48impl<T: fmt::Display> fmt::Display for Range<T> {
49    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50        write!(f, "{}..{}", &self.start, &self.end)
51    }
52}
53impl<T> core::ops::RangeBounds<T> for Range<T> {
54    #[inline]
55    fn start_bound(&self) -> Bound<&T> {
56        Bound::Included(&self.start)
57    }
58    #[inline]
59    fn end_bound(&self) -> Bound<&T> {
60        Bound::Excluded(&self.end)
61    }
62}
63impl<T: PartialOrd + fmt::Debug> Range<T> {
64    pub fn new(start: T, end: T) -> Self {
65        assert!(
66            start <= end,
67            "invalid range: start {:?} must not be larger than end {:?}",
68            start,
69            end,
70        );
71        Self { start, end }
72    }
73}
74impl<T: PartialOrd> Range<T> {
75    #[inline]
76    pub fn is_empty(&self) -> bool {
77        !matches!(self.start.partial_cmp(&self.end), Some(Ordering::Less))
78    }
79
80    #[inline]
81    pub fn contains<U>(&self, item: &U) -> bool
82    where
83        T: PartialOrd<U>,
84        U: PartialOrd<T> + ?Sized,
85    {
86        <Self as core::ops::RangeBounds<T>>::contains(self, item)
87    }
88}
89impl<T: NumericRangeBound> Range<T> {
90    #[inline(always)]
91    pub fn len(&self) -> T {
92        self.end - self.start
93    }
94
95    /// Shrink the length of this range by one element from the front, returning the first item in the range.
96    #[inline]
97    pub fn pop_front(&mut self) -> Option<T> {
98        let item = self.start;
99        let next = item + <T as NumericRangeBound>::ONE;
100        if next <= self.end {
101            self.start = next;
102            Some(item)
103        } else {
104            None
105        }
106    }
107
108    /// Shrink the length of this range by one element from the end, returning the last item in the range.
109    #[inline]
110    pub fn pop_back(&mut self) -> Option<T> {
111        if self.start == self.end {
112            None
113        } else {
114            let item = self.end.saturating_sub(<T as NumericRangeBound>::ONE);
115            self.end = item;
116            Some(item)
117        }
118    }
119
120    /// Shrink this range by advancing the start of the range `n` elements, i.e. `(start + n)..end`
121    ///
122    /// NOTE: This function will saturate to `self.end` to ensure the range remains valid.
123    #[inline]
124    pub fn shrink_front(&mut self, n: T) {
125        self.start = core::cmp::min(self.start + n, self.end);
126    }
127
128    /// Truncate this range such that it's length is `new_len`, i.e. `start..(start + new_len)`
129    ///
130    /// This effectively drops elements from the back of the range;
131    ///
132    /// NOTE: This function will panic if `new_len` is greater than the current length
133    #[inline]
134    pub fn truncate(&mut self, new_len: T) {
135        assert!(self.len() > new_len);
136        self.end = self.start + new_len;
137    }
138}
139impl<T: Copy> Copy for Range<T> {}
140impl<T: Clone> Clone for Range<T> {
141    fn clone(&self) -> Self {
142        Self {
143            start: self.start.clone(),
144            end: self.end.clone(),
145        }
146    }
147}
148impl<T: Eq> Eq for Range<T> {}
149impl<T: PartialEq> PartialEq for Range<T> {
150    fn eq(&self, other: &Self) -> bool {
151        self.start.eq(&other.start) && self.end.eq(&other.end)
152    }
153}
154impl<T: PartialOrd> PartialOrd for Range<T> {
155    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
156        self.start.partial_cmp(&other.start).and_then(|o| match o {
157            Ordering::Equal => self.end.partial_cmp(&other.end),
158            o => Some(o),
159        })
160    }
161}
162impl<T: Ord> Ord for Range<T> {
163    fn cmp(&self, other: &Self) -> Ordering {
164        self.start
165            .cmp(&other.start)
166            .then_with(|| self.end.cmp(&other.end))
167    }
168}
169impl From<crate::diagnostics::SourceSpan> for Range<usize> {
170    #[inline]
171    fn from(span: crate::diagnostics::SourceSpan) -> Self {
172        let offset = span.offset();
173        Self::new(offset, offset + span.len())
174    }
175}
176impl From<Range<usize>> for crate::diagnostics::SourceSpan {
177    #[inline]
178    fn from(range: Range<usize>) -> Self {
179        crate::diagnostics::SourceSpan::from(range.start..range.end)
180    }
181}
182impl<T: NumericRangeBound> From<core::ops::Range<T>> for Range<T> {
183    #[inline(always)]
184    fn from(range: core::ops::Range<T>) -> Self {
185        debug_assert!(
186            range.end >= range.start,
187            "invalid range: start must not pass end"
188        );
189        Self {
190            start: range.start,
191            end: range.end,
192        }
193    }
194}
195impl<T: NumericRangeBound> From<Range<T>> for core::ops::Range<T> {
196    #[inline(always)]
197    fn from(range: Range<T>) -> core::ops::Range<T> {
198        range.start..range.end
199    }
200}
201impl<T> Index<Range<usize>> for [T] {
202    type Output = [T];
203
204    #[inline(always)]
205    fn index(&self, idx: Range<usize>) -> &Self::Output {
206        <[T] as Index<core::ops::Range<usize>>>::index(self, idx.start..idx.end)
207    }
208}
209impl<T> IndexMut<Range<usize>> for [T] {
210    #[inline(always)]
211    fn index_mut(&mut self, idx: Range<usize>) -> &mut Self::Output {
212        <[T] as IndexMut<core::ops::Range<usize>>>::index_mut(self, idx.start..idx.end)
213    }
214}
215
216pub fn range_from_bounds<R: RangeBounds<usize>>(
217    range: R,
218    allowed: Range<usize>,
219) -> Result<Range<usize>, Range<usize>> {
220    let start = match range.start_bound() {
221        Bound::Included(&i) => i,
222        Bound::Excluded(&i) => i + 1,
223        Bound::Unbounded => allowed.start,
224    };
225    let end = match range.end_bound() {
226        Bound::Included(&i) => i + 1,
227        Bound::Excluded(&i) => i,
228        Bound::Unbounded => allowed.end,
229    };
230    if start < allowed.start || end > allowed.end {
231        Err(Range::new(start, end))
232    } else {
233        Ok(Range::new(start, end))
234    }
235}
236
237pub fn range_from_bounds_with_defaults<R: RangeBounds<usize>>(
238    range: R,
239    default_start: usize,
240    default_end: usize,
241) -> Range<usize> {
242    let start = match range.start_bound() {
243        Bound::Included(&i) => i,
244        Bound::Excluded(&i) => i + 1,
245        Bound::Unbounded => default_start,
246    };
247    let end = match range.end_bound() {
248        Bound::Included(&i) => i + 1,
249        Bound::Excluded(&i) => i,
250        Bound::Unbounded => default_end,
251    };
252    Range::new(start, end)
253}