solar_data_structures/
index.rs1use std::fmt;
4
5pub use index_vec::{
6 Idx, IdxRangeBounds, IdxSliceIndex, IndexBox, IndexSlice, IndexVec, index_box, index_vec,
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 #[doc = "Creates a new `"]
42 #[doc = stringify!($name)]
43 #[doc = "` from the given `value`."]
44 #[inline(always)]
49 #[must_use]
50 #[cfg_attr(debug_assertions, track_caller)]
51 $vis const fn new(value: u32) -> Self {
52 Self($crate::index::BaseIndex32::new(value))
53 }
54
55 #[doc = "Creates a new `"]
56 #[doc = stringify!($name)]
57 #[doc = "` from the given `value`."]
58 #[inline(always)]
63 #[must_use]
64 $vis const unsafe fn new_unchecked(value: u32) -> Self {
65 Self(unsafe { $crate::index::BaseIndex32::new_unchecked(value) })
66 }
67
68 #[doc = "Creates a new `"]
69 #[doc = stringify!($name)]
70 #[doc = "` from the given `value`."]
71 #[inline(always)]
74 #[must_use]
75 $vis const fn try_new(value: u32) -> Option<Self> {
76 match $crate::index::BaseIndex32::try_new(value) {
77 Some(value) => Some(Self(value)),
78 None => None,
79 }
80 }
81
82 #[doc = "Creates a new `"]
83 #[doc = stringify!($name)]
84 #[doc = "` from the given `value`."]
85 #[inline(always)]
90 #[must_use]
91 #[cfg_attr(debug_assertions, track_caller)]
92 $vis const fn from_usize(value: usize) -> Self {
93 Self($crate::index::BaseIndex32::from_usize(value))
94 }
95
96 #[doc = "Creates a new `"]
97 #[doc = stringify!($name)]
98 #[doc = "` from the given `value`."]
99 #[inline(always)]
104 #[must_use]
105 $vis const unsafe fn from_usize_unchecked(value: usize) -> Self {
106 Self(unsafe { $crate::index::BaseIndex32::from_usize_unchecked(value) })
107 }
108
109 #[doc = "Creates a new `"]
110 #[doc = stringify!($name)]
111 #[doc = "` from the given `value`."]
112 #[inline(always)]
115 #[must_use]
116 $vis const fn try_from_usize(value: usize) -> Option<Self> {
117 match $crate::index::BaseIndex32::try_from_usize(value) {
118 Some(value) => Some(Self(value)),
119 None => None,
120 }
121 }
122
123 #[inline(always)]
125 #[must_use]
126 $vis const fn get(self) -> u32 {
127 self.0.get()
128 }
129
130 #[inline(always)]
132 #[must_use]
133 $vis const fn index(self) -> usize {
134 self.0.index()
135 }
136 }
137
138 $crate::newtype_index!($($rest)*);
139 };
140}
141
142macro_rules! base_index {
144 ($(#[$attr:meta])* $name:ident($primitive:ident <= $max:literal)) => {
145 $(#[$attr])*
148 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
149 #[cfg_attr(feature = "nightly", rustc_layout_scalar_valid_range_end($max))]
150 #[cfg_attr(feature = "nightly", rustc_nonnull_optimization_guaranteed)]
151 #[cfg_attr(feature = "nightly", rustc_pass_by_value)]
152 #[repr(transparent)]
153 pub struct $name {
154 #[cfg(feature = "nightly")]
156 value: $primitive,
157 #[cfg(not(feature = "nightly"))]
158 value: std::num::NonZero<$primitive>,
159 }
160
161 impl fmt::Debug for $name {
162 #[inline]
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 self.get().fmt(f)
165 }
166 }
167
168 impl Idx for $name {
169 #[inline(always)]
170 fn from_usize(value: usize) -> Self {
171 Self::from_usize(value)
172 }
173
174 #[inline(always)]
175 fn index(self) -> usize {
176 self.index()
177 }
178 }
179
180 impl $name {
181 pub const MAX_AS: $primitive = $max;
183
184 pub const MAX: Self = Self::new(Self::MAX_AS);
186
187 #[doc = "Creates a new `"]
188 #[doc = stringify!($name)]
189 #[doc = "` from the given `value`."]
190 #[inline(always)]
195 #[must_use]
196 #[cfg_attr(debug_assertions, track_caller)]
197 pub const fn new(value: $primitive) -> Self {
198 match Self::try_new(value) {
199 Some(value) => value,
200 None => index_overflow(),
201 }
202 }
203
204 #[doc = "Creates a new `"]
205 #[doc = stringify!($name)]
206 #[doc = "` from the given `value`."]
207 #[inline(always)]
210 #[must_use]
211 pub const fn try_new(value: $primitive) -> Option<Self> {
212 if value > Self::MAX_AS {
213 None
214 } else {
215 Some(unsafe { Self::new_unchecked(value) })
217 }
218 }
219
220 #[doc = "Creates a new `"]
221 #[doc = stringify!($name)]
222 #[doc = "` from the given `value`."]
223 #[inline(always)]
228 #[must_use]
229 #[cfg_attr(debug_assertions, track_caller)]
230 pub const fn from_usize(value: usize) -> Self {
231 match Self::try_from_usize(value) {
232 Some(value) => value,
233 None => index_overflow(),
234 }
235 }
236
237 #[doc = "Creates a new `"]
238 #[doc = stringify!($name)]
239 #[doc = "` from the given `value`."]
240 #[inline(always)]
243 #[must_use]
244 pub const fn try_from_usize(value: usize) -> Option<Self> {
245 if value > Self::MAX_AS as usize {
246 None
247 } else {
248 Some(unsafe { Self::new_unchecked(value as $primitive) })
250 }
251 }
252
253 #[doc = "Creates a new `"]
254 #[doc = stringify!($name)]
255 #[doc = "` from the given `value`, without checking for overflow."]
256 #[inline(always)]
261 #[must_use]
262 pub const unsafe fn new_unchecked(value: $primitive) -> Self {
263 debug_assert!(value <= Self::MAX_AS);
264
265 #[cfg(feature = "nightly")]
267 return unsafe { std::intrinsics::transmute_unchecked(value) };
268
269 #[cfg(not(feature = "nightly"))]
270 return unsafe { Self { value: std::num::NonZero::new_unchecked(value.unchecked_add(1)) } };
271 }
272
273
274 #[doc = "Creates a new `"]
275 #[doc = stringify!($name)]
276 #[doc = "` from the given `value`, without checking for overflow."]
277 #[inline(always)]
282 #[must_use]
283 pub const unsafe fn from_usize_unchecked(value: usize) -> Self {
284 debug_assert!(value <= Self::MAX_AS as usize);
285 unsafe { Self::new_unchecked(value as $primitive) }
286 }
287
288 #[inline(always)]
290 #[must_use]
291 pub const fn get(self) -> $primitive {
292 #[cfg(feature = "nightly")]
298 return unsafe { std::intrinsics::transmute_unchecked(self) };
299
300 #[cfg(not(feature = "nightly"))]
302 return unsafe { self.value.get().unchecked_sub(1) };
303 }
304
305 #[inline(always)]
307 #[must_use]
308 pub const fn index(self) -> usize {
309 self.get() as usize
310 }
311 }
312 };
313}
314
315base_index!(BaseIndex32(u32 <= 0xFFFF_FF00));
316
317#[inline(never)]
318#[cold]
319#[cfg_attr(debug_assertions, track_caller)]
320const fn index_overflow() -> ! {
321 panic!("index overflowed")
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn base_index() {
330 assert_eq!(BaseIndex32::new(0).get(), 0);
331 assert_eq!(BaseIndex32::new(1).get(), 1);
332 assert_eq!(BaseIndex32::MAX.get(), BaseIndex32::MAX_AS);
333 assert_eq!(BaseIndex32::MAX.get(), 0xFFFF_FF00);
334 assert_eq!(BaseIndex32::new(0xFFFF_FF00).get(), 0xFFFF_FF00);
335 }
336}