1use core::{
2 fmt::{self, Debug, Write},
3 num::{NonZeroU16, NonZeroU32, NonZeroU8, NonZeroUsize},
4};
5
6pub unsafe trait Key: Copy + Eq {
14 fn into_usize(self) -> usize;
16
17 fn try_from_usize(int: usize) -> Option<Self>;
19}
20
21#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[repr(transparent)]
29pub struct LargeSpur {
30 key: NonZeroUsize,
31}
32
33impl LargeSpur {
34 #[cfg_attr(feature = "inline-more", inline)]
36 pub const fn into_inner(self) -> NonZeroUsize {
37 self.key
38 }
39}
40
41unsafe impl Key for LargeSpur {
42 #[cfg_attr(feature = "inline-more", inline)]
43 fn into_usize(self) -> usize {
44 self.key.get() - 1
45 }
46
47 #[cfg_attr(feature = "inline-more", inline)]
49 fn try_from_usize(int: usize) -> Option<Self> {
50 if int < usize::MAX {
51 unsafe {
54 Some(Self {
55 key: NonZeroUsize::new_unchecked(int + 1),
56 })
57 }
58 } else {
59 None
60 }
61 }
62}
63
64impl Default for LargeSpur {
65 #[cfg_attr(feature = "inline-more", inline)]
66 fn default() -> Self {
67 Self::try_from_usize(0).unwrap()
68 }
69}
70
71impl Debug for LargeSpur {
72 #[cfg_attr(feature = "inline-more", inline)]
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.write_str("LargeSpur(")?;
75 Debug::fmt(&self.key, f)?;
76 f.write_char(')')
77 }
78}
79
80#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
87#[repr(transparent)]
88pub struct Spur {
89 key: NonZeroU32,
90}
91
92impl Spur {
93 #[cfg_attr(feature = "inline-more", inline)]
95 pub const fn into_inner(self) -> NonZeroU32 {
96 self.key
97 }
98}
99
100unsafe impl Key for Spur {
101 #[cfg_attr(feature = "inline-more", inline)]
102 fn into_usize(self) -> usize {
103 self.key.get() as usize - 1
104 }
105
106 #[cfg_attr(feature = "inline-more", inline)]
108 fn try_from_usize(int: usize) -> Option<Self> {
109 if int < u32::MAX as usize {
110 unsafe {
113 Some(Self {
114 key: NonZeroU32::new_unchecked(int as u32 + 1),
115 })
116 }
117 } else {
118 None
119 }
120 }
121}
122
123impl Default for Spur {
124 #[cfg_attr(feature = "inline-more", inline)]
125 fn default() -> Self {
126 Self::try_from_usize(0).unwrap()
127 }
128}
129
130impl Debug for Spur {
131 #[cfg_attr(feature = "inline-more", inline)]
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 f.write_str("Spur(")?;
134 Debug::fmt(&self.key, f)?;
135 f.write_char(')')
136 }
137}
138
139#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
146#[repr(transparent)]
147pub struct MiniSpur {
148 key: NonZeroU16,
149}
150
151impl MiniSpur {
152 #[cfg_attr(feature = "inline-more", inline)]
154 pub const fn into_inner(self) -> NonZeroU16 {
155 self.key
156 }
157}
158
159unsafe impl Key for MiniSpur {
160 #[cfg_attr(feature = "inline-more", inline)]
161 fn into_usize(self) -> usize {
162 self.key.get() as usize - 1
163 }
164
165 #[cfg_attr(feature = "inline-more", inline)]
167 fn try_from_usize(int: usize) -> Option<Self> {
168 if int < u16::MAX as usize {
169 unsafe {
172 Some(Self {
173 key: NonZeroU16::new_unchecked(int as u16 + 1),
174 })
175 }
176 } else {
177 None
178 }
179 }
180}
181
182impl Default for MiniSpur {
183 #[cfg_attr(feature = "inline-more", inline)]
184 fn default() -> Self {
185 Self::try_from_usize(0).unwrap()
186 }
187}
188
189impl Debug for MiniSpur {
190 #[cfg_attr(feature = "inline-more", inline)]
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 f.write_str("MiniSpur(")?;
193 Debug::fmt(&self.key, f)?;
194 f.write_char(')')
195 }
196}
197
198#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
205#[repr(transparent)]
206pub struct MicroSpur {
207 key: NonZeroU8,
208}
209
210impl MicroSpur {
211 #[cfg_attr(feature = "inline-more", inline)]
213 pub const fn into_inner(self) -> NonZeroU8 {
214 self.key
215 }
216}
217
218unsafe impl Key for MicroSpur {
219 #[cfg_attr(feature = "inline-more", inline)]
220 fn into_usize(self) -> usize {
221 self.key.get() as usize - 1
222 }
223
224 #[cfg_attr(feature = "inline-more", inline)]
226 fn try_from_usize(int: usize) -> Option<Self> {
227 if int < u8::MAX as usize {
228 unsafe {
231 Some(Self {
232 key: NonZeroU8::new_unchecked(int as u8 + 1),
233 })
234 }
235 } else {
236 None
237 }
238 }
239}
240
241impl Default for MicroSpur {
242 #[cfg_attr(feature = "inline-more", inline)]
243 fn default() -> Self {
244 Self::try_from_usize(0).unwrap()
245 }
246}
247
248impl Debug for MicroSpur {
249 #[cfg_attr(feature = "inline-more", inline)]
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 f.write_str("MicroSpur(")?;
252 Debug::fmt(&self.key, f)?;
253 f.write_char(')')
254 }
255}
256
257macro_rules! impl_serde {
258 ($($key:ident => $ty:ident),* $(,)?) => {
259 #[cfg(feature = "serialize")]
260 mod __serde {
261 use super::{$($key),*};
262 use serde::{
263 de::{Deserialize, Deserializer},
264 ser::{Serialize, Serializer},
265 };
266 use core::num::{$($ty),*};
267
268 $(
269 impl Serialize for $key {
270 #[cfg_attr(feature = "inline-more", inline)]
271 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
272 where
273 S: Serializer,
274 {
275 self.key.serialize(serializer)
276 }
277 }
278
279 impl<'de> Deserialize<'de> for $key {
280 #[cfg_attr(feature = "inline-more", inline)]
281 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
282 where
283 D: Deserializer<'de>,
284 {
285 let key = <$ty>::deserialize(deserializer)?;
286 Ok(Self { key })
287 }
288 }
289 )*
290 }
291 };
292}
293
294impl_serde! {
296 Spur => NonZeroU32,
297 MiniSpur => NonZeroU16,
298 MicroSpur => NonZeroU8,
299 LargeSpur => NonZeroUsize,
300}
301
302macro_rules! impl_deepsize {
303 ($($type:ident),* $(,)?) => {
304 #[cfg(feature = "deepsize")]
305 mod __deepsize {
306 use super::{$($type),*};
307 #[cfg(test)]
308 use super::Key;
309 use deepsize::{DeepSizeOf, Context};
310 use core::mem;
311
312 $(
313 impl DeepSizeOf for $type {
314 fn deep_size_of_children(&self, _context: &mut Context) -> usize {
315 0
316 }
317
318 fn deep_size_of(&self) -> usize {
319 mem::size_of::<$type>()
320 }
321 }
322 )*
323
324 #[test]
325 fn deepsize_implementations() {
326 $(
327 assert_eq!(
328 mem::size_of::<$type>(),
329 $type::try_from_usize(0).unwrap().deep_size_of(),
330 );
331 )*
332 }
333 }
334 };
335}
336
337impl_deepsize! {
339 Spur,
340 MiniSpur,
341 MicroSpur,
342 LargeSpur,
343}
344
345macro_rules! impl_abomonation {
346 ($($type:ident),* $(,)?) => {
347 #[cfg(all(feature = "abomonation", not(feature = "no-std")))]
348 mod __abomonation {
349 use super::{$($type),*};
350 #[cfg(test)]
351 use super::Key;
352 use abomonation::Abomonation;
353 use std::io::{self, Write};
354
355 $(
356 impl Abomonation for $type {
357 unsafe fn entomb<W: Write>(&self, write: &mut W) -> io::Result<()> {
358 self.key.entomb(write)
359 }
360
361 unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> {
362 self.key.exhume(bytes)
363 }
364
365 fn extent(&self) -> usize {
366 self.key.extent()
367 }
368 }
369 )*
370
371 #[test]
372 fn abomonation_implementations() {
373 let mut buf = Vec::new();
374
375 $(
376 unsafe {
377 let base = $type::try_from_usize(0).unwrap();
378
379 abomonation::encode(&base, &mut buf).unwrap();
380 assert_eq!(base, *abomonation::decode(&mut buf [..]).unwrap().0);
381 }
382
383 buf.clear();
384 )*
385 }
386 }
387 };
388}
389
390impl_abomonation! {
392 Spur,
393 MiniSpur,
394 MicroSpur,
395 LargeSpur,
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401
402 #[test]
403 fn large() {
404 let zero = LargeSpur::try_from_usize(0).unwrap();
405 let max = LargeSpur::try_from_usize(usize::MAX - 1).unwrap();
406 let default = LargeSpur::default();
407
408 assert_eq!(zero.into_usize(), 0);
409 assert_eq!(max.into_usize(), usize::MAX - 1);
410 assert_eq!(default.into_usize(), 0);
411 }
412
413 #[test]
414 fn large_max_returns_none() {
415 assert_eq!(None, LargeSpur::try_from_usize(usize::MAX));
416 }
417
418 #[test]
419 #[should_panic]
420 #[cfg(not(miri))]
421 fn large_max_panics() {
422 LargeSpur::try_from_usize(usize::MAX).unwrap();
423 }
424
425 #[test]
426 fn spur() {
427 let zero = Spur::try_from_usize(0).unwrap();
428 let max = Spur::try_from_usize(u32::MAX as usize - 1).unwrap();
429 let default = Spur::default();
430
431 assert_eq!(zero.into_usize(), 0);
432 assert_eq!(max.into_usize(), u32::MAX as usize - 1);
433 assert_eq!(default.into_usize(), 0);
434 }
435
436 #[test]
437 fn spur_returns_none() {
438 assert_eq!(None, Spur::try_from_usize(u32::MAX as usize));
439 }
440
441 #[test]
442 #[should_panic]
443 #[cfg(not(miri))]
444 fn spur_panics() {
445 Spur::try_from_usize(u32::MAX as usize).unwrap();
446 }
447
448 #[test]
449 fn mini() {
450 let zero = MiniSpur::try_from_usize(0).unwrap();
451 let max = MiniSpur::try_from_usize(u16::MAX as usize - 1).unwrap();
452 let default = MiniSpur::default();
453
454 assert_eq!(zero.into_usize(), 0);
455 assert_eq!(max.into_usize(), u16::MAX as usize - 1);
456 assert_eq!(default.into_usize(), 0);
457 }
458
459 #[test]
460 fn mini_returns_none() {
461 assert_eq!(None, MiniSpur::try_from_usize(u16::MAX as usize));
462 }
463
464 #[test]
465 #[should_panic]
466 #[cfg(not(miri))]
467 fn mini_panics() {
468 MiniSpur::try_from_usize(u16::MAX as usize).unwrap();
469 }
470
471 #[test]
472 fn micro() {
473 let zero = MicroSpur::try_from_usize(0).unwrap();
474 let max = MicroSpur::try_from_usize(u8::MAX as usize - 1).unwrap();
475 let default = MicroSpur::default();
476
477 assert_eq!(zero.into_usize(), 0);
478 assert_eq!(max.into_usize(), u8::MAX as usize - 1);
479 assert_eq!(default.into_usize(), 0);
480 }
481
482 #[test]
483 fn micro_returns_none() {
484 assert_eq!(None, MicroSpur::try_from_usize(u8::MAX as usize));
485 }
486
487 #[test]
488 #[should_panic]
489 #[cfg(not(miri))]
490 fn micro_panics() {
491 MicroSpur::try_from_usize(u8::MAX as usize).unwrap();
492 }
493
494 #[test]
495 #[cfg(feature = "serialize")]
496 fn all_serialize() {
497 let large = LargeSpur::try_from_usize(0).unwrap();
498 let _ = serde_json::to_string(&large).unwrap();
499
500 let normal = Spur::try_from_usize(0).unwrap();
501 let _ = serde_json::to_string(&normal).unwrap();
502
503 let mini = MiniSpur::try_from_usize(0).unwrap();
504 let _ = serde_json::to_string(&mini).unwrap();
505
506 let micro = MicroSpur::try_from_usize(0).unwrap();
507 let _ = serde_json::to_string(µ).unwrap();
508 }
509}