lasso/
keys.rs

1use core::{
2    fmt::{self, Debug, Write},
3    num::{NonZeroU16, NonZeroU32, NonZeroU8, NonZeroUsize},
4};
5
6/// Types implementing this trait can be used as keys for all Rodeos
7///
8/// # Safety
9///
10/// into/from must be perfectly symmetrical, any key that goes on must be perfectly reproduced with the other
11///
12/// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
13pub unsafe trait Key: Copy + Eq {
14    /// Returns the `usize` that represents the current key
15    fn into_usize(self) -> usize;
16
17    /// Attempts to create a key from a `usize`, returning `None` if it fails
18    fn try_from_usize(int: usize) -> Option<Self>;
19}
20
21/// A key type taking up `size_of::<usize>()` bytes of space (generally 4 or 8 bytes)
22///
23/// Internally is a `NonZeroUsize` to allow for space optimizations when stored inside of an [`Option`]
24///
25/// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
26/// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
27#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[repr(transparent)]
29pub struct LargeSpur {
30    key: NonZeroUsize,
31}
32
33impl LargeSpur {
34    /// Returns the [`NonZeroUsize`] backing the current `LargeSpur`
35    #[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    /// Returns `None` if `int` is greater than `usize::MAX - 1`
48    #[cfg_attr(feature = "inline-more", inline)]
49    fn try_from_usize(int: usize) -> Option<Self> {
50        if int < usize::MAX {
51            // Safety: The integer is less than the max value and then incremented by one, meaning that
52            // is is impossible for a zero to inhabit the NonZeroUsize
53            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/// The default key for every Rodeo, uses only 32 bits of space
81///
82/// Internally is a `NonZeroU32` to allow for space optimizations when stored inside of an [`Option`]
83///
84/// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
85/// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
86#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
87#[repr(transparent)]
88pub struct Spur {
89    key: NonZeroU32,
90}
91
92impl Spur {
93    /// Returns the [`NonZeroU32`] backing the current `Spur`
94    #[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    /// Returns `None` if `int` is greater than `u32::MAX - 1`
107    #[cfg_attr(feature = "inline-more", inline)]
108    fn try_from_usize(int: usize) -> Option<Self> {
109        if int < u32::MAX as usize {
110            // Safety: The integer is less than the max value and then incremented by one, meaning that
111            // is is impossible for a zero to inhabit the NonZeroU32
112            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/// A miniature Key utilizing only 16 bits of space
140///
141/// Internally is a `NonZeroU16` to allow for space optimizations when stored inside of an [`Option`]
142///
143/// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
144/// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
145#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
146#[repr(transparent)]
147pub struct MiniSpur {
148    key: NonZeroU16,
149}
150
151impl MiniSpur {
152    /// Returns the [`NonZeroU16`] backing the current `MiniSpur`
153    #[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    /// Returns `None` if `int` is greater than `u16::MAX - 1`
166    #[cfg_attr(feature = "inline-more", inline)]
167    fn try_from_usize(int: usize) -> Option<Self> {
168        if int < u16::MAX as usize {
169            // Safety: The integer is less than the max value and then incremented by one, meaning that
170            // is is impossible for a zero to inhabit the NonZeroU16
171            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/// A miniature Key utilizing only 8 bits of space
199///
200/// Internally is a `NonZeroU8` to allow for space optimizations when stored inside of an [`Option`]
201///
202/// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
203/// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
204#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
205#[repr(transparent)]
206pub struct MicroSpur {
207    key: NonZeroU8,
208}
209
210impl MicroSpur {
211    /// Returns the [`NonZeroU8`] backing the current `MicroSpur`
212    #[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    /// Returns `None` if `int` is greater than `u8::MAX - 1`
225    #[cfg_attr(feature = "inline-more", inline)]
226    fn try_from_usize(int: usize) -> Option<Self> {
227        if int < u8::MAX as usize {
228            // Safety: The integer is less than the max value and then incremented by one, meaning that
229            // is is impossible for a zero to inhabit the NonZeroU8
230            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
294// Implement `Serialize` and `Deserialize` when the `serde` feature is enabled
295impl_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
337// Implement `DeepSizeOf` when the `deepsize` feature is enabled
338impl_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
390// Implement `Abomonation` when the `abomonation` feature is enabled
391impl_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(&micro).unwrap();
508    }
509}