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 Span<u64> {
92    /// Cast to native pointer width; useful for indexing on native platforms.
93    #[inline]
94    pub fn range_usize(self) -> Range<usize> {
95        let Self { start, len } = self;
96        Range {
97            start: start as usize,
98            end: start as usize + len as usize,
99        }
100    }
101}
102
103impl<Idx: Unsigned + Copy> From<Span<Idx>> for Range<Idx> {
104    #[inline]
105    fn from(value: Span<Idx>) -> Self {
106        value.range()
107    }
108}
109
110/// span * scalar
111impl<Idx: Unsigned + Copy + Mul> Mul<Idx> for Span<Idx> {
112    type Output = Self;
113
114    fn mul(self, rhs: Idx) -> Self::Output {
115        let Self { start, len } = self;
116        Self {
117            start: rhs * start,
118            len: rhs * len,
119        }
120    }
121}