Skip to main content

re_span/
lib.rs

1//! An integer range that always has a non-negative length.
2//!
3//! The standard [`std::ops::Range`] can have `start > end`
4//! Taking a `Range` by argument thus means the callee must check for this eventuality and return an error.
5//!
6//! In contrast, [`Span`] always has a non-negative length, i.e. `len >= 0`.
7
8use std::ops::{Mul, Range};
9
10use num_traits::Unsigned;
11
12/// An integer range who's length is always at least zero.
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct Span<Idx: Unsigned + Copy> {
15    /// The index of the first element.
16    pub start: Idx,
17
18    /// The number of elements in the range.
19    pub len: Idx,
20}
21
22impl<Idx: Unsigned + Copy> Span<Idx> {
23    /// Construct from `start` and `len`.
24    #[inline]
25    pub fn from_start_len(start: Idx, len: Idx) -> Self {
26        Self { start, len }
27    }
28
29    /// Construct from `start` (inclusive) and `end` (exclusive).
30    ///
31    /// Expects `start <= end`, or you will get a panic in debug mode.
32    #[inline]
33    pub fn from_start_end(start: Idx, end: Idx) -> Self
34    where
35        Idx: PartialOrd,
36    {
37        #![expect(
38            clippy::disallowed_macros,
39            reason = "We don't want to depend on re_log for re_log::debug_assert"
40        )]
41        debug_assert!(
42            start <= end,
43            "DEBUG ASSERT: start must be less than or equal to end"
44        );
45
46        Self {
47            start,
48            len: end - start,
49        }
50    }
51
52    /// The next element, just outside the range.
53    #[inline]
54    pub fn end(&self) -> Idx {
55        self.start + self.len
56    }
57
58    /// Useful when slicing a slice
59    #[inline]
60    pub fn range(self) -> Range<Idx> {
61        let Self { start, len } = self;
62        Range {
63            start,
64            end: start + len,
65        }
66    }
67
68    pub fn try_cast<Narrow>(self) -> Option<Span<Narrow>>
69    where
70        Narrow: TryFrom<Idx> + Unsigned + Copy,
71    {
72        Some(Span {
73            start: self.start.try_into().ok()?,
74            len: self.len.try_into().ok()?,
75        })
76    }
77}
78
79impl Span<u32> {
80    /// Widening cast; useful for indexing.
81    #[inline]
82    pub fn range_usize(self) -> Range<usize> {
83        let Self { start, len } = self;
84        Range {
85            start: start as usize,
86            end: start as usize + len as usize,
87        }
88    }
89}
90
91impl<Idx: Unsigned + Copy> From<Span<Idx>> for Range<Idx> {
92    #[inline]
93    fn from(value: Span<Idx>) -> Self {
94        value.range()
95    }
96}
97
98/// span * scalar
99impl<Idx: Unsigned + Copy + Mul> Mul<Idx> for Span<Idx> {
100    type Output = Self;
101
102    fn mul(self, rhs: Idx) -> Self::Output {
103        let Self { start, len } = self;
104        Self {
105            start: rhs * start,
106            len: rhs * len,
107        }
108    }
109}