solar_data_structures/
index.rs

1//! Index types. See [`::index_vec`].
2
3use std::fmt;
4
5pub use index_vec::{
6    Idx, IdxRangeBounds, IdxSliceIndex, IndexBox, IndexSlice, IndexVec, index_box, index_vec,
7};
8
9/// Creates a new index to use with [`::index_vec`].
10#[macro_export]
11macro_rules! newtype_index {
12    () => {};
13    ($(#[$attr:meta])* $vis:vis struct $name:ident; $($rest:tt)*) => {
14        $(#[$attr])*
15        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16        #[repr(transparent)]
17        $vis struct $name($crate::index::BaseIndex32);
18
19        impl std::fmt::Debug for $name {
20            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21                write!(f, "{}({:?})", stringify!($name), self.get())
22            }
23        }
24
25        impl $crate::index::Idx for $name {
26            #[inline(always)]
27            fn from_usize(value: usize) -> Self {
28                Self::from_usize(value)
29            }
30
31            #[inline(always)]
32            fn index(self) -> usize {
33                self.index()
34            }
35        }
36
37        impl $name {
38            /// The maximum index value.
39            $vis const MAX: Self = Self($crate::index::BaseIndex32::MAX);
40
41            #[doc = "Creates a new `"]
42            #[doc = stringify!($name)]
43            #[doc = "` from the given `value`."]
44            ///
45            /// # Panics
46            ///
47            /// Panics if `value` exceeds `MAX`.
48            #[inline(always)]
49            #[must_use]
50            $vis const fn new(value: u32) -> Self {
51                Self($crate::index::BaseIndex32::new(value))
52            }
53
54            #[doc = "Creates a new `"]
55            #[doc = stringify!($name)]
56            #[doc = "` from the given `value`."]
57            ///
58            /// # Safety
59            ///
60            /// The caller must ensure that `value` is less than or equal to `MAX`.
61            #[inline(always)]
62            #[must_use]
63            $vis const unsafe fn new_unchecked(value: u32) -> Self {
64                Self($crate::index::BaseIndex32::new_unchecked(value))
65            }
66
67            #[doc = "Creates a new `"]
68            #[doc = stringify!($name)]
69            #[doc = "` from the given `value`."]
70            ///
71            /// Returns `None` if `value` exceeds `MAX`.
72            #[inline(always)]
73            #[must_use]
74            $vis const fn try_new(value: u32) -> Option<Self> {
75                match $crate::index::BaseIndex32::try_new(value) {
76                    Some(value) => Some(Self(value)),
77                    None => None,
78                }
79            }
80
81            #[doc = "Creates a new `"]
82            #[doc = stringify!($name)]
83            #[doc = "` from the given `value`."]
84            ///
85            /// # Panics
86            ///
87            /// Panics if `value` exceeds `MAX`.
88            #[inline(always)]
89            #[must_use]
90            $vis const fn from_usize(value: usize) -> Self {
91                Self($crate::index::BaseIndex32::from_usize(value))
92            }
93
94            #[doc = "Creates a new `"]
95            #[doc = stringify!($name)]
96            #[doc = "` from the given `value`."]
97            ///
98            /// # Safety
99            ///
100            /// The caller must ensure that `value` is less than or equal to `MAX`.
101            #[inline(always)]
102            #[must_use]
103            $vis const unsafe fn from_usize_unchecked(value: usize) -> Self {
104                Self($crate::index::BaseIndex32::from_usize_unchecked(value))
105            }
106
107            #[doc = "Creates a new `"]
108            #[doc = stringify!($name)]
109            #[doc = "` from the given `value`."]
110            ///
111            /// Returns `None` if `value` exceeds `MAX`.
112            #[inline(always)]
113            #[must_use]
114            $vis const fn try_from_usize(value: usize) -> Option<Self> {
115                match $crate::index::BaseIndex32::try_from_usize(value) {
116                    Some(value) => Some(Self(value)),
117                    None => None,
118                }
119            }
120
121            /// Returns the underlying index value.
122            #[inline(always)]
123            #[must_use]
124            $vis const fn get(self) -> u32 {
125                self.0.get()
126            }
127
128            /// Returns the underlying index value.
129            #[inline(always)]
130            #[must_use]
131            $vis const fn index(self) -> usize {
132                self.0.index()
133            }
134        }
135
136        $crate::newtype_index!($($rest)*);
137    };
138}
139
140// NOTE: The max MUST be less than the maximum value of the underlying integer.
141macro_rules! base_index {
142    ($(#[$attr:meta])* $name:ident($primitive:ident <= $max:literal)) => {
143        /// A specialized wrapper around a primitive number.
144        ///
145        $(#[$attr])*
146        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
147        #[cfg_attr(feature = "nightly", rustc_layout_scalar_valid_range_end($max))]
148        #[cfg_attr(feature = "nightly", rustc_nonnull_optimization_guaranteed)]
149        #[cfg_attr(feature = "nightly", rustc_pass_by_value)]
150        #[repr(transparent)]
151        pub struct $name {
152            // NOTE: Use `value()` instead of projecting the field directly.
153            #[cfg(feature = "nightly")]
154            value: $primitive,
155            #[cfg(not(feature = "nightly"))]
156            value: std::num::NonZero<$primitive>,
157        }
158
159        impl fmt::Debug for $name {
160            #[inline]
161            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162                self.get().fmt(f)
163            }
164        }
165
166        impl Idx for $name {
167            #[inline(always)]
168            fn from_usize(value: usize) -> Self {
169                Self::from_usize(value)
170            }
171
172            #[inline(always)]
173            fn index(self) -> usize {
174                self.index()
175            }
176        }
177
178        impl $name {
179            /// The maximum index value, as the underlying primitive type.
180            pub const MAX_AS: $primitive = $max;
181
182            /// The maximum index value.
183            pub const MAX: Self = Self::new(Self::MAX_AS);
184
185            #[doc = "Creates a new `"]
186            #[doc = stringify!($name)]
187            #[doc = "` from the given `value`."]
188            ///
189            /// # Panics
190            ///
191            /// Panics if `value` exceeds `MAX`.
192            #[inline(always)]
193            #[must_use]
194            #[cfg_attr(debug_assertions, track_caller)]
195            pub const fn new(value: $primitive) -> Self {
196                match Self::try_new(value) {
197                    Some(value) => value,
198                    None => index_overflow(),
199                }
200            }
201
202            #[doc = "Creates a new `"]
203            #[doc = stringify!($name)]
204            #[doc = "` from the given `value`."]
205            ///
206            /// Returns `None` if `value` exceeds `MAX`.
207            #[inline(always)]
208            #[must_use]
209            pub const fn try_new(value: $primitive) -> Option<Self> {
210                if value > Self::MAX_AS {
211                    None
212                } else {
213                    // SAFETY: `value` is less than or equal to `MAX`.
214                    Some(unsafe { Self::new_unchecked(value) })
215                }
216            }
217
218            #[doc = "Creates a new `"]
219            #[doc = stringify!($name)]
220            #[doc = "` from the given `value`."]
221            ///
222            /// # Panics
223            ///
224            /// Panics if `value` exceeds `MAX`.
225            #[inline(always)]
226            #[must_use]
227            #[cfg_attr(debug_assertions, track_caller)]
228            pub const fn from_usize(value: usize) -> Self {
229                match Self::try_from_usize(value) {
230                    Some(value) => value,
231                    None => index_overflow(),
232                }
233            }
234
235            #[doc = "Creates a new `"]
236            #[doc = stringify!($name)]
237            #[doc = "` from the given `value`."]
238            ///
239            /// Returns `None` if `value` exceeds `MAX`.
240            #[inline(always)]
241            #[must_use]
242            pub const fn try_from_usize(value: usize) -> Option<Self> {
243                if value > Self::MAX_AS as usize {
244                    None
245                } else {
246                    // SAFETY: `value` is less than or equal to `MAX`.
247                    Some(unsafe { Self::new_unchecked(value as $primitive) })
248                }
249            }
250
251            #[doc = "Creates a new `"]
252            #[doc = stringify!($name)]
253            #[doc = "` from the given `value`, without checking for overflow."]
254            ///
255            /// # Safety
256            ///
257            /// The caller must ensure that `value` is less than or equal to `MAX`.
258            #[inline(always)]
259            #[must_use]
260            pub const unsafe fn new_unchecked(value: $primitive) -> Self {
261                debug_assert!(value <= Self::MAX_AS);
262
263                // SAFETY: guaranteed by the caller.
264                #[cfg(feature = "nightly")]
265                return unsafe { std::intrinsics::transmute_unchecked(value) };
266
267                #[cfg(not(feature = "nightly"))]
268                return unsafe { Self { value: std::num::NonZero::new_unchecked(value.unchecked_add(1)) } };
269            }
270
271
272            #[doc = "Creates a new `"]
273            #[doc = stringify!($name)]
274            #[doc = "` from the given `value`, without checking for overflow."]
275            ///
276            /// # Safety
277            ///
278            /// The caller must ensure that `value` is less than or equal to `MAX`.
279            #[inline(always)]
280            #[must_use]
281            pub const unsafe fn from_usize_unchecked(value: usize) -> Self {
282                debug_assert!(value <= Self::MAX_AS as usize);
283                unsafe { Self::new_unchecked(value as $primitive) }
284            }
285
286            /// Returns the underlying index value.
287            #[inline(always)]
288            #[must_use]
289            pub const fn get(self) -> $primitive {
290                // SAFETY: Transmute instead of projecting the field directly.
291                //
292                // See:
293                // - https://github.com/rust-lang/rust/pull/133651
294                // - https://github.com/rust-lang/compiler-team/issues/807
295                #[cfg(feature = "nightly")]
296                return unsafe { std::intrinsics::transmute_unchecked(self) };
297
298                // SAFETY: non-zero minus one doesn't overflow.
299                #[cfg(not(feature = "nightly"))]
300                return unsafe { self.value.get().unchecked_sub(1) };
301            }
302
303            /// Returns the underlying index value.
304            #[inline(always)]
305            #[must_use]
306            pub const fn index(self) -> usize {
307                self.get() as usize
308            }
309        }
310    };
311}
312
313base_index!(BaseIndex32(u32 <= 0xFFFF_FF00));
314
315#[inline(never)]
316#[cold]
317const fn index_overflow() -> ! {
318    panic!("index overflowed")
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324
325    #[test]
326    fn base_index() {
327        assert_eq!(BaseIndex32::new(0).get(), 0);
328        assert_eq!(BaseIndex32::new(1).get(), 1);
329        assert_eq!(BaseIndex32::MAX.get(), BaseIndex32::MAX_AS);
330        assert_eq!(BaseIndex32::MAX.get(), 0xFFFF_FF00);
331        assert_eq!(BaseIndex32::new(0xFFFF_FF00).get(), 0xFFFF_FF00);
332    }
333}