solar_data_structures/
index.rs

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