solar_data_structures/
index.rs1use std::fmt;
4
5pub use index_vec::{
6 index_box, index_vec, Idx, IdxRangeBounds, IdxSliceIndex, IndexBox, IndexSlice, IndexVec,
7};
8
9#[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 $vis const MAX: Self = Self($crate::index::BaseIndex32::MAX);
40
41 #[inline(always)]
47 #[must_use]
48 $vis const fn new(value: u32) -> Self {
49 Self($crate::index::BaseIndex32::new(value))
50 }
51
52 #[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 #[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 #[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 #[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 #[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 #[inline(always)]
111 #[must_use]
112 $vis const fn get(self) -> u32 {
113 self.0.get()
114 }
115
116 #[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
128macro_rules! base_index {
130 ($(#[$attr:meta])* $name:ident($primitive:ident <= $max:literal)) => {
131 $(#[$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 #[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 pub const MAX_AS: $primitive = $max;
169
170 pub const MAX: Self = Self::new(Self::MAX_AS);
172
173 #[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 #[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 Some(unsafe { Self::new_unchecked(value) })
199 }
200 }
201
202 #[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 #[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 Some(unsafe { Self::new_unchecked(value as $primitive) })
228 }
229 }
230
231 #[inline(always)]
237 #[must_use]
238 pub const unsafe fn new_unchecked(value: $primitive) -> Self {
239 debug_assert!(value <= Self::MAX_AS);
240
241 #[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 #[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 #[inline(always)]
264 #[must_use]
265 pub const fn get(self) -> $primitive {
266 #[cfg(feature = "nightly")]
272 return unsafe { std::intrinsics::transmute_unchecked(self) };
273
274 #[cfg(not(feature = "nightly"))]
276 return unsafe { self.value.get().unchecked_sub(1) };
277 }
278
279 #[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}